3 * CAN bus driver for the Freescale MPC52xx embedded CPU.
6 * Andrey Volkov <avolkov@varma-el.com>
9 * 2004-2005, Varma Electronics Oy
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 #include <linux/kernel.h>
32 #include <linux/module.h>
33 #include <linux/interrupt.h>
34 #include <linux/platform_device.h>
35 #include <linux/netdevice.h>
36 #include <socketcan/can.h>
37 #include <socketcan/can/dev.h>
39 #include <asm/mpc52xx.h>
43 #include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
49 struct platform_device *pdev[PDEV_MAX];
51 static int __devinit mpc52xx_can_probe(struct platform_device *pdev)
54 struct net_device *dev;
55 struct mscan_platform_data *pdata = pdev->dev.platform_data;
63 dev = alloc_mscandev();
66 can = netdev_priv(dev);
68 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
69 dev->irq = platform_get_irq(pdev, 0);
70 if (!mem || !dev->irq)
73 mem_size = mem->end - mem->start + 1;
74 if (!request_mem_region(mem->start, mem_size, pdev->dev.driver->name)) {
75 dev_err(&pdev->dev, "resource unavailable\n");
79 SET_NETDEV_DEV(dev, &pdev->dev);
81 dev->base_addr = (unsigned long)ioremap_nocache(mem->start, mem_size);
83 if (!dev->base_addr) {
84 dev_err(&pdev->dev, "failed to map can port\n");
89 can->can_sys_clock = pdata->clock_frq;
91 platform_set_drvdata(pdev, dev);
93 ret = register_mscandev(dev, pdata->clock_src);
95 dev_info(&pdev->dev, "probe for a port 0x%lX done\n",
100 iounmap((unsigned long *)dev->base_addr);
102 release_mem_region(mem->start, mem_size);
105 dev_err(&pdev->dev, "probe failed\n");
109 static int __devexit mpc52xx_can_remove(struct platform_device *pdev)
111 struct net_device *dev = platform_get_drvdata(pdev);
112 struct resource *mem;
114 platform_set_drvdata(pdev, NULL);
115 unregister_mscandev(dev);
117 iounmap((volatile void __iomem *)dev->base_addr);
118 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
119 release_mem_region(mem->start, mem->end - mem->start + 1);
125 static struct mscan_regs saved_regs;
126 static int mpc52xx_can_suspend(struct platform_device *pdev, pm_message_t state)
128 struct net_device *dev = platform_get_drvdata(pdev);
129 struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
131 _memcpy_fromio(&saved_regs, regs, sizeof(*regs));
136 static int mpc52xx_can_resume(struct platform_device *pdev)
138 struct net_device *dev = platform_get_drvdata(pdev);
139 struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
141 regs->canctl0 |= MSCAN_INITRQ;
142 while ((regs->canctl1 & MSCAN_INITAK) == 0)
145 regs->canctl1 = saved_regs.canctl1;
146 regs->canbtr0 = saved_regs.canbtr0;
147 regs->canbtr1 = saved_regs.canbtr1;
148 regs->canidac = saved_regs.canidac;
150 /* restore masks, buffers etc. */
151 _memcpy_toio(®s->canidar1_0, (void *)&saved_regs.canidar1_0,
152 sizeof(*regs) - offsetof(struct mscan_regs, canidar1_0));
154 regs->canctl0 &= ~MSCAN_INITRQ;
155 regs->cantbsel = saved_regs.cantbsel;
156 regs->canrier = saved_regs.canrier;
157 regs->cantier = saved_regs.cantier;
158 regs->canctl0 = saved_regs.canctl0;
164 static struct platform_driver mpc52xx_can_driver = {
166 .name = "mpc52xx-mscan",
168 .probe = mpc52xx_can_probe,
169 .remove = __devexit_p(mpc52xx_can_remove),
171 .suspend = mpc52xx_can_suspend,
172 .resume = mpc52xx_can_resume,
176 #ifdef CONFIG_PPC_MERGE
177 static int __init mpc52xx_of_to_pdev(void)
179 struct device_node *np = NULL;
184 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
185 (np = of_find_compatible_node(np, "mscan", "mpc5200-mscan"));
187 (np = of_find_compatible_node(np, NULL, "fsl,mpc5200-mscan"));
190 struct resource r[2] = { };
191 struct mscan_platform_data pdata;
194 printk(KERN_WARNING "%s: increase PDEV_MAX for more "
195 "than %i devices\n", __func__, PDEV_MAX);
199 err = of_address_to_resource(np, 0, &r[0]);
203 of_irq_to_resource(np, 0, &r[1]);
206 platform_device_register_simple("mpc52xx-mscan", i, r, 2);
207 if (IS_ERR(pdev[i])) {
208 err = PTR_ERR(pdev[i]);
212 pdata.clock_src = MSCAN_CLKSRC_BUS;
213 pdata.clock_frq = mpc52xx_find_ipb_freq(np);
214 err = platform_device_add_data(pdev[i], &pdata, sizeof(pdata));
221 #define mscan_of_to_pdev()
224 int __init mpc52xx_can_init(void)
227 "This %s driver is DEPRECATED, please switch!\n",
228 mpc52xx_can_driver.driver.name);
229 #ifdef CONFIG_PPC_MERGE
230 int err = mpc52xx_of_to_pdev();
233 printk(KERN_ERR "%s init failed with err=%d\n",
234 mpc52xx_can_driver.driver.name, err);
238 return platform_driver_register(&mpc52xx_can_driver);
241 void __exit mpc52xx_can_exit(void)
244 platform_driver_unregister(&mpc52xx_can_driver);
245 for (i = 0; i < PDEV_MAX; i++)
246 platform_device_unregister(pdev[i]);
247 printk(KERN_INFO "%s unloaded\n", mpc52xx_can_driver.driver.name);
250 module_init(mpc52xx_can_init);
251 module_exit(mpc52xx_can_exit);
253 MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
254 MODULE_DESCRIPTION("Freescale MPC5200 CAN driver");
255 MODULE_LICENSE("GPL v2");