]> rtime.felk.cvut.cz Git - socketcan-devel.git/blob - kernel/2.6/drivers/net/can/old/mscan/mpc52xx_can.c
merged branches/netlink in rev. 1037 back to trunk.
[socketcan-devel.git] / kernel / 2.6 / drivers / net / can / old / mscan / mpc52xx_can.c
1 /*
2  * DESCRIPTION:
3  *  CAN bus driver for the Freescale MPC52xx embedded CPU.
4  *
5  * AUTHOR:
6  *  Andrey Volkov <avolkov@varma-el.com>
7  *
8  * COPYRIGHT:
9  *  2004-2005, Varma Electronics Oy
10  *
11  * LICENCE:
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.
16  *
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.
21  *
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
25  *
26  * HISTORY:
27  *       2005-02-03 created
28  *
29  */
30
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>
38 #include <asm/io.h>
39 #include <asm/mpc52xx.h>
40
41 #include "mscan.h"
42
43 #include <socketcan/can/version.h>      /* for RCSID. Removed by mkpatch script */
44
45 RCSID("$Id$");
46
47 #define PDEV_MAX 2
48
49 struct platform_device *pdev[PDEV_MAX];
50
51 static int __devinit mpc52xx_can_probe(struct platform_device *pdev)
52 {
53         struct resource *mem;
54         struct net_device *dev;
55         struct mscan_platform_data *pdata = pdev->dev.platform_data;
56         struct can_priv *can;
57         u32 mem_size;
58         int ret = -ENODEV;
59
60         if (!pdata)
61                 return ret;
62
63         dev = alloc_mscandev();
64         if (!dev)
65                 return -ENOMEM;
66         can = netdev_priv(dev);
67
68         mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
69         dev->irq = platform_get_irq(pdev, 0);
70         if (!mem || !dev->irq)
71                 goto req_error;
72
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");
76                 goto req_error;
77         }
78
79         SET_NETDEV_DEV(dev, &pdev->dev);
80
81         dev->base_addr = (unsigned long)ioremap_nocache(mem->start, mem_size);
82
83         if (!dev->base_addr) {
84                 dev_err(&pdev->dev, "failed to map can port\n");
85                 ret = -ENOMEM;
86                 goto fail_map;
87         }
88
89         can->can_sys_clock = pdata->clock_frq;
90
91         platform_set_drvdata(pdev, dev);
92
93         ret = register_mscandev(dev, pdata->clock_src);
94         if (ret >= 0) {
95                 dev_info(&pdev->dev, "probe for a port 0x%lX done\n",
96                          dev->base_addr);
97                 return ret;
98         }
99
100         iounmap((unsigned long *)dev->base_addr);
101       fail_map:
102         release_mem_region(mem->start, mem_size);
103       req_error:
104         free_candev(dev);
105         dev_err(&pdev->dev, "probe failed\n");
106         return ret;
107 }
108
109 static int __devexit mpc52xx_can_remove(struct platform_device *pdev)
110 {
111         struct net_device *dev = platform_get_drvdata(pdev);
112         struct resource *mem;
113
114         platform_set_drvdata(pdev, NULL);
115         unregister_mscandev(dev);
116
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);
120         free_candev(dev);
121         return 0;
122 }
123
124 #ifdef CONFIG_PM
125 static struct mscan_regs saved_regs;
126 static int mpc52xx_can_suspend(struct platform_device *pdev, pm_message_t state)
127 {
128         struct net_device *dev = platform_get_drvdata(pdev);
129         struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
130
131         _memcpy_fromio(&saved_regs, regs, sizeof(*regs));
132
133         return 0;
134 }
135
136 static int mpc52xx_can_resume(struct platform_device *pdev)
137 {
138         struct net_device *dev = platform_get_drvdata(pdev);
139         struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
140
141         regs->canctl0 |= MSCAN_INITRQ;
142         while ((regs->canctl1 & MSCAN_INITAK) == 0)
143                 udelay(10);
144
145         regs->canctl1 = saved_regs.canctl1;
146         regs->canbtr0 = saved_regs.canbtr0;
147         regs->canbtr1 = saved_regs.canbtr1;
148         regs->canidac = saved_regs.canidac;
149
150         /* restore masks, buffers etc. */
151         _memcpy_toio(&regs->canidar1_0, (void *)&saved_regs.canidar1_0,
152                      sizeof(*regs) - offsetof(struct mscan_regs, canidar1_0));
153
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;
159
160         return 0;
161 }
162 #endif
163
164 static struct platform_driver mpc52xx_can_driver = {
165         .driver = {
166                    .name = "mpc52xx-mscan",
167                    },
168         .probe = mpc52xx_can_probe,
169         .remove = __devexit_p(mpc52xx_can_remove),
170 #ifdef CONFIG_PM
171         .suspend = mpc52xx_can_suspend,
172         .resume = mpc52xx_can_resume,
173 #endif
174 };
175
176 #ifdef CONFIG_PPC_MERGE
177 static int __init mpc52xx_of_to_pdev(void)
178 {
179         struct device_node *np = NULL;
180         unsigned int i;
181         int err = -ENODEV;
182
183         for (i = 0;
184 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
185              (np = of_find_compatible_node(np, "mscan", "mpc5200-mscan"));
186 #else
187              (np = of_find_compatible_node(np, NULL, "fsl,mpc5200-mscan"));
188 #endif
189              i++) {
190                 struct resource r[2] = { };
191                 struct mscan_platform_data pdata;
192
193                 if (i >= PDEV_MAX) {
194                         printk(KERN_WARNING "%s: increase PDEV_MAX for more "
195                                "than %i devices\n", __func__, PDEV_MAX);
196                         break;
197                 }
198
199                 err = of_address_to_resource(np, 0, &r[0]);
200                 if (err)
201                         break;
202
203                 of_irq_to_resource(np, 0, &r[1]);
204
205                 pdev[i] =
206                     platform_device_register_simple("mpc52xx-mscan", i, r, 2);
207                 if (IS_ERR(pdev[i])) {
208                         err = PTR_ERR(pdev[i]);
209                         break;
210                 }
211
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));
215                 if (err)
216                         break;
217         }
218         return err;
219 }
220 #else
221 #define mscan_of_to_pdev()
222 #endif
223
224 int __init mpc52xx_can_init(void)
225 {
226         printk(KERN_WARNING
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();
231
232         if (err) {
233                 printk(KERN_ERR "%s init failed with err=%d\n",
234                        mpc52xx_can_driver.driver.name, err);
235                 return err;
236         }
237 #endif
238         return platform_driver_register(&mpc52xx_can_driver);
239 }
240
241 void __exit mpc52xx_can_exit(void)
242 {
243         int i;
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);
248 }
249
250 module_init(mpc52xx_can_init);
251 module_exit(mpc52xx_can_exit);
252
253 MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
254 MODULE_DESCRIPTION("Freescale MPC5200 CAN driver");
255 MODULE_LICENSE("GPL v2");