]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/net/can/d_can/d_can_platform.c
can: d_can: fix use after free bug in d_can_plat_remove()
[karo-tx-linux.git] / drivers / net / can / d_can / d_can_platform.c
1 /*
2  * Platform CAN bus driver for Bosch D_CAN controller
3  *
4  * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
5  *
6  * Borrowed from C_CAN driver
7  * Copyright (C) 2010 ST Microelectronics
8  * - Bhupesh Sharma <bhupesh.sharma@st.com>
9  *
10  * Borrowed heavily from the C_CAN driver originally written by:
11  * Copyright (C) 2007
12  * - Sascha Hauer, Marc Kleine-Budde, Pengutronix <s.hauer@pengutronix.de>
13  * - Simon Kallweit, intefo AG <simon.kallweit@intefo.ch>
14  *
15  * Bosch D_CAN controller is compliant to CAN protocol version 2.0 part A and B.
16  * Bosch D_CAN user manual can be obtained from:
17  * http://www.semiconductors.bosch.de/media/en/pdf/ipmodules_1/can/
18  * d_can_users_manual_111.pdf
19  *
20  * This program is free software; you can redistribute it and/or
21  * modify it under the terms of the GNU General Public License as
22  * published by the Free Software Foundation version 2.
23  *
24  * This program is distributed "as is" WITHOUT ANY WARRANTY of any
25  * kind, whether express or implied; without even the implied warranty
26  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  * GNU General Public License for more details.
28  */
29
30 /*
31  * Your platform definitions should specify module ram offsets and interrupt
32  * number to use as follows:
33  *
34  * static struct d_can_platform_data am33xx_evm_d_can_pdata = {
35  *      .num_of_msg_objs        = 64,
36  *      .dma_support            = false,
37  *      .ram_init               = d_can_hw_raminit,
38  * };
39  *
40  * Please see include/linux/can/platform/d_can.h for description of
41  * above fields.
42  *
43  */
44
45 #include <linux/kernel.h>
46 #include <linux/module.h>
47 #include <linux/interrupt.h>
48 #include <linux/delay.h>
49 #include <linux/netdevice.h>
50 #include <linux/if_arp.h>
51 #include <linux/if_ether.h>
52 #include <linux/list.h>
53 #include <linux/io.h>
54 #include <linux/platform_device.h>
55 #include <linux/can/platform/d_can.h>
56 #include <linux/clk.h>
57 #include <linux/pm_runtime.h>
58 #include <linux/slab.h>
59 #include <linux/can/dev.h>
60
61 #include "d_can.h"
62
63 static int __devinit d_can_plat_probe(struct platform_device *pdev)
64 {
65         int ret = 0;
66         void __iomem *addr;
67         struct net_device *ndev;
68         struct d_can_priv *priv;
69         struct resource *mem;
70         struct d_can_platform_data *pdata;
71         struct clk *fck;
72
73         pdata = pdev->dev.platform_data;
74         if (!pdata) {
75                 dev_err(&pdev->dev, "No platform data\n");
76                 goto exit;
77         }
78
79         /* allocate the d_can device */
80         ndev = alloc_d_can_dev(pdata->num_of_msg_objs);
81         if (!ndev) {
82                 ret = -ENOMEM;
83                 dev_err(&pdev->dev, "alloc_d_can_dev failed\n");
84                 goto exit;
85         }
86
87         priv = netdev_priv(ndev);
88         fck = clk_get(&pdev->dev, "fck");
89         if (IS_ERR(fck)) {
90                 dev_err(&pdev->dev, "fck is not found\n");
91                 ret = -ENODEV;
92                 goto exit_free_ndev;
93         }
94
95         /* get the platform data */
96         mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
97         if (!mem) {
98                 ret = -ENODEV;
99                 dev_err(&pdev->dev, "No mem resource\n");
100                 goto exit_clk_put;
101         }
102
103         if (!request_mem_region(mem->start, resource_size(mem),
104                                 D_CAN_DRV_NAME)) {
105                 dev_err(&pdev->dev, "resource unavailable\n");
106                 ret = -EBUSY;
107                 goto exit_clk_put;
108         }
109
110         addr = ioremap(mem->start, resource_size(mem));
111         if (!addr) {
112                 dev_err(&pdev->dev, "ioremap failed\n");
113                 ret = -ENOMEM;
114                 goto exit_release_mem;
115         }
116
117         /* IRQ specific to Error and status & can be used for Message Object */
118         ndev->irq = platform_get_irq_byname(pdev, "d_can_ms");
119         if (!ndev->irq) {
120                 dev_err(&pdev->dev, "No irq0 resource\n");
121                 goto exit_iounmap;
122         }
123
124         /* IRQ specific for Message Object */
125         priv->irq_obj = platform_get_irq_byname(pdev, "d_can_mo");
126         if (!priv->irq_obj) {
127                 dev_err(&pdev->dev, "No irq1 resource\n");
128                 goto exit_iounmap;
129         }
130
131         pm_runtime_enable(&pdev->dev);
132         pm_runtime_get_sync(&pdev->dev);
133         priv->pdev = pdev;
134         priv->base = addr;
135         priv->can.clock.freq = clk_get_rate(fck);
136         priv->ram_init = pdata->ram_init;
137         priv->opened = false;
138
139         platform_set_drvdata(pdev, ndev);
140         SET_NETDEV_DEV(ndev, &pdev->dev);
141
142         ret = register_d_can_dev(ndev);
143         if (ret) {
144                 dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
145                                 D_CAN_DRV_NAME, ret);
146                 goto exit_free_device;
147         }
148
149         /* Initialize DCAN RAM */
150         d_can_reset_ram(priv, pdev->id, 1);
151
152         dev_info(&pdev->dev, "device registered (irq=%d, irq_obj=%d)\n",
153                                                 ndev->irq, priv->irq_obj);
154
155         return 0;
156
157 exit_free_device:
158         platform_set_drvdata(pdev, NULL);
159         pm_runtime_disable(&pdev->dev);
160 exit_iounmap:
161         iounmap(addr);
162 exit_release_mem:
163         release_mem_region(mem->start, resource_size(mem));
164 exit_clk_put:
165         clk_put(fck);
166 exit_free_ndev:
167         free_d_can_dev(ndev);
168 exit:
169         dev_err(&pdev->dev, "probe failed\n");
170
171         return ret;
172 }
173
174 static int __devexit d_can_plat_remove(struct platform_device *pdev)
175 {
176         struct net_device *ndev = platform_get_drvdata(pdev);
177         struct d_can_priv *priv = netdev_priv(ndev);
178         struct resource *mem;
179         void __iomem *base = priv->base;
180
181         /* De-initialize DCAN RAM */
182         d_can_reset_ram(priv, pdev->id, 0);
183
184         unregister_d_can_dev(ndev);
185         platform_set_drvdata(pdev, NULL);
186
187         free_d_can_dev(ndev);
188         iounmap(base);
189
190         mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
191         release_mem_region(mem->start, resource_size(mem));
192
193         pm_runtime_put_sync(&pdev->dev);
194         pm_runtime_disable(&pdev->dev);
195
196         return 0;
197 }
198
199 #ifdef CONFIG_PM
200 static int d_can_suspend(struct platform_device *pdev, pm_message_t state)
201 {
202         int ret;
203         struct net_device *ndev = platform_get_drvdata(pdev);
204         struct d_can_priv *priv = netdev_priv(ndev);
205
206         if (netif_running(ndev)) {
207                 netif_stop_queue(ndev);
208                 netif_device_detach(ndev);
209         }
210
211         ret = d_can_power_down(priv);
212         if (ret) {
213                 dev_err(&pdev->dev, "Not entered power down mode\n");
214                 return ret;
215         }
216
217         priv->can.state = CAN_STATE_SLEEPING;
218
219         /* De-initialize DCAN RAM */
220         d_can_reset_ram(priv, pdev->id, 0);
221
222         /* Disable the module */
223         pm_runtime_put_sync(&pdev->dev);
224
225         return 0;
226 }
227
228 static int d_can_resume(struct platform_device *pdev)
229 {
230         int ret;
231
232         struct net_device *ndev = platform_get_drvdata(pdev);
233         struct d_can_priv *priv = netdev_priv(ndev);
234
235         /* Enable the module */
236         pm_runtime_get_sync(&pdev->dev);
237
238         /* Initialize DCAN RAM */
239         d_can_reset_ram(priv, pdev->id, 1);
240
241         ret = d_can_power_up(priv);
242         if (ret) {
243                 dev_err(&pdev->dev, "Not came out from power down mode\n");
244                 return ret;
245         }
246
247         priv->can.state = CAN_STATE_ERROR_ACTIVE;
248
249         if (netif_running(ndev)) {
250                 netif_device_attach(ndev);
251                 netif_start_queue(ndev);
252         }
253
254         return 0;
255 }
256 #else
257 #define d_can_suspend NULL
258 #define d_can_resume NULL
259 #endif
260
261 static struct platform_driver d_can_plat_driver = {
262         .driver = {
263                 .name   = D_CAN_DRV_NAME,
264                 .owner  = THIS_MODULE,
265         },
266         .probe          = d_can_plat_probe,
267         .remove         = __devexit_p(d_can_plat_remove),
268         .suspend        = d_can_suspend,
269         .resume         = d_can_resume,
270 };
271
272 static int __init d_can_plat_init(void)
273 {
274         printk(KERN_INFO D_CAN_DRV_DESC "\n");
275         return platform_driver_register(&d_can_plat_driver);
276 }
277 module_init(d_can_plat_init);
278
279 static void __exit d_can_plat_exit(void)
280 {
281         printk(KERN_INFO D_CAN_DRV_DESC " unloaded\n");
282         platform_driver_unregister(&d_can_plat_driver);
283 }
284 module_exit(d_can_plat_exit);
285
286 MODULE_AUTHOR("AnilKumar Ch <anilkumar@ti.com>");
287 MODULE_LICENSE("GPL v2");
288 MODULE_VERSION(D_CAN_VERSION);
289 MODULE_DESCRIPTION(D_CAN_DRV_DESC);