]> rtime.felk.cvut.cz Git - socketcan-devel.git/blob - kernel/2.6/drivers/net/can/cc770/cc770_of_platform.c
merged branches/netlink in rev. 1037 back to trunk.
[socketcan-devel.git] / kernel / 2.6 / drivers / net / can / cc770 / cc770_of_platform.c
1 /*
2  * Driver for CC770 CAN controllers on the OpenFirmware platform bus
3  *
4  * Copyright (C) 2009 Wolfgang Grandegger <wg@grandegger.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the version 2 of the GNU General Public License
8  * as published by the Free Software Foundation
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 /* This is a generic driver for CC770 chips on the OpenFirmware platform
21  * bus found on embedded PowerPC systems. You need a CC770 CAN node
22  * definition in your flattened device tree source (DTS) file similar to:
23  *
24  *   can@3,100 {
25  *           compatible = "bosch,cc770";
26  *           reg = <3 0x100 0x80>;
27  *           interrupts = <2 0>;
28  *           interrupt-parent = <&mpic>;
29  *           bosch,external-clock-frequency = <16000000>;
30  *   };
31  *
32  * See "Documentation/powerpc/dts-bindings/can/cc770.txt" for further
33  * information.
34  */
35
36 #include <linux/kernel.h>
37 #include <linux/version.h>
38 #include <linux/module.h>
39 #include <linux/interrupt.h>
40 #include <linux/netdevice.h>
41 #include <linux/delay.h>
42 #include <socketcan/can.h>
43 #include <socketcan/can/dev.h>
44
45 #include <linux/of_platform.h>
46 #include <asm/prom.h>
47
48 #include "cc770.h"
49
50 #define DRV_NAME "cc770_of_platform"
51
52 MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
53 MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the OF platform bus");
54 MODULE_LICENSE("GPL v2");
55
56 #define CC770_OFP_CAN_CLOCK  16000000
57
58 static u8 cc770_ofp_read_reg(const struct cc770_priv *priv, int reg)
59 {
60         return in_8(priv->reg_base + reg);
61 }
62
63 static void cc770_ofp_write_reg(const struct cc770_priv *priv, int reg, u8 val)
64 {
65         out_8(priv->reg_base + reg, val);
66 }
67
68 static int __devexit cc770_ofp_remove(struct of_device *ofdev)
69 {
70         struct net_device *dev = dev_get_drvdata(&ofdev->dev);
71         struct cc770_priv *priv = netdev_priv(dev);
72         struct resource res;
73
74         dev_set_drvdata(&ofdev->dev, NULL);
75
76         unregister_cc770dev(dev);
77         iounmap(priv->reg_base);
78         /* irq_dispose_mapping(dev->irq);*/ /* will not work for shared IRQs */
79         free_cc770dev(dev);
80
81         of_address_to_resource(ofdev->node, 0, &res);
82         release_mem_region(res.start, resource_size(&res));
83
84         return 0;
85 }
86
87 static int __devinit cc770_ofp_probe(struct of_device *ofdev,
88                                      const struct of_device_id *id)
89 {
90         struct device_node *np = ofdev->node;
91         struct net_device *dev;
92         struct cc770_priv *priv;
93         struct resource res;
94         const u32 *prop;
95         u32 clkext;
96         int err, irq, res_size, prop_size;
97         void __iomem *base;
98
99         err = of_address_to_resource(np, 0, &res);
100         if (err) {
101                 dev_err(&ofdev->dev, "invalid address\n");
102                 return err;
103         }
104
105         res_size = resource_size(&res);
106
107         if (!request_mem_region(res.start, res_size, DRV_NAME)) {
108                 dev_err(&ofdev->dev, "couldn't request %#llx..%#llx\n",
109                         (unsigned long long)res.start,
110                         (unsigned long long)res.end);
111                 return -EBUSY;
112         }
113
114         base = ioremap_nocache(res.start, res_size);
115         if (!base) {
116                 dev_err(&ofdev->dev, "couldn't ioremap %#llx..%#llx\n",
117                         (unsigned long long)res.start,
118                         (unsigned long long)res.end);
119                 err = -ENOMEM;
120                 goto exit_release_mem;
121         }
122
123         irq = irq_of_parse_and_map(np, 0);
124         if (irq == NO_IRQ) {
125                 dev_err(&ofdev->dev, "no irq found\n");
126                 err = -ENODEV;
127                 goto exit_unmap_mem;
128         }
129
130         dev = alloc_cc770dev(0);
131         if (!dev) {
132                 err = -ENOMEM;
133                 goto exit_dispose_irq;
134         }
135
136         priv = netdev_priv(dev);
137
138         priv->read_reg = cc770_ofp_read_reg;
139         priv->write_reg = cc770_ofp_write_reg;
140
141         prop = of_get_property(np, "bosch,external-clock-frequency",
142                                &prop_size);
143         if (prop && (prop_size ==  sizeof(u32)))
144                 clkext = *prop;
145         else
146                 clkext = CC770_OFP_CAN_CLOCK; /* default */
147         priv->can.clock.freq = clkext;
148
149         /* The system clock may not exceed 10 MHz */
150         if (priv->can.clock.freq > 10000000) {
151                 priv->cpu_interface |= CPUIF_DSC;
152                 priv->can.clock.freq /= 2;
153         }
154
155         /* The memory clock may not exceed 8 MHz */
156         if (priv->can.clock.freq > 8000000)
157                 priv->cpu_interface |= CPUIF_DMC;
158
159         if (of_get_property(np, "bosch,divide-memory-clock", NULL))
160                 priv->cpu_interface |= CPUIF_DMC;
161         if (of_get_property(np, "bosch,iso-low-speed-mux", NULL))
162                 priv->cpu_interface |= CPUIF_MUX;
163
164         if (of_get_property(np, "bosch,comperator-bypass", NULL))
165                 priv->bus_config |= BUSCFG_CBY;
166         if (of_get_property(np, "bosch,disconnect-rx0-input", NULL))
167                 priv->bus_config |= BUSCFG_DR0;
168         if (of_get_property(np, "bosch,disconnect-rx1-input", NULL))
169                 priv->bus_config |= BUSCFG_DR1;
170         if (of_get_property(np, "bosch,disconnect-tx1-output", NULL))
171                 priv->bus_config |= BUSCFG_DT1;
172         if (of_get_property(np, "bosch,polarity-dominant", NULL))
173                 priv->bus_config |= BUSCFG_POL;
174
175         prop = of_get_property(np, "bosch,clock-out-frequency", &prop_size);
176         if (prop && (prop_size == sizeof(u32)) && *prop > 0) {
177                 u32 cdv = clkext / *prop;
178                 int slew;
179
180                 if (cdv > 0 && cdv < 16) {
181                         priv->cpu_interface |= CPUIF_CEN;
182                         priv->clkout |= (cdv - 1) & CLKOUT_CD_MASK;
183
184                         prop = of_get_property(np, "bosch,slew-rate",
185                                                &prop_size);
186                         if (prop && (prop_size == sizeof(u32))) {
187                                 slew = *prop;
188                         } else {
189                                 /* Determine default slew rate */
190                                 slew = (CLKOUT_SL_MASK >> CLKOUT_SL_SHIFT) -
191                                         ((cdv * clkext - 1) / 8000000);
192                                 if (slew < 0)
193                                         slew = 0;
194                         }
195                         priv->clkout |= (slew << CLKOUT_SL_SHIFT) &
196                                 CLKOUT_SL_MASK;
197                 } else {
198                         dev_dbg(ND2D(dev), "invalid clock-out-frequency\n");
199                 }
200
201         }
202
203 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
204         priv->irq_flags = SA_SHIRQ;
205 #else
206         priv->irq_flags = IRQF_SHARED;
207 #endif
208         priv->reg_base = base;
209
210         dev->irq = irq;
211
212         dev_info(&ofdev->dev,
213                  "reg_base=0x%p irq=%d clock=%d cpu_interface=0x%02x "
214                  "bus_config=0x%02x clkout=0x%02x\n",
215                  priv->reg_base, dev->irq, priv->can.clock.freq,
216                  priv->cpu_interface, priv->bus_config, priv->clkout);
217
218         dev_set_drvdata(&ofdev->dev, dev);
219         SET_NETDEV_DEV(dev, &ofdev->dev);
220
221         err = register_cc770dev(dev);
222         if (err) {
223                 dev_err(&ofdev->dev, "registering %s failed (err=%d)\n",
224                         DRV_NAME, err);
225                 goto exit_free_cc770;
226         }
227
228         return 0;
229
230 exit_free_cc770:
231         free_cc770dev(dev);
232 exit_dispose_irq:
233         /* irq_dispose_mapping(dev->irq);*/ /* will not work for shared IRQs */
234 exit_unmap_mem:
235         iounmap(base);
236 exit_release_mem:
237         release_mem_region(res.start, res_size);
238
239         return err;
240 }
241
242 static struct of_device_id __devinitdata cc770_ofp_table[] = {
243         {.compatible = "bosch,cc770"}, /* CC770 from Bosch */
244         {.compatible = "intc,82527"},  /* AN82527 from Intel CP */
245         {},
246 };
247
248 static struct of_platform_driver cc770_ofp_driver = {
249         .owner = THIS_MODULE,
250         .name = DRV_NAME,
251         .probe = cc770_ofp_probe,
252         .remove = __devexit_p(cc770_ofp_remove),
253         .match_table = cc770_ofp_table,
254 };
255
256 static int __init cc770_ofp_init(void)
257 {
258         return of_register_platform_driver(&cc770_ofp_driver);
259 }
260 module_init(cc770_ofp_init);
261
262 static void __exit cc770_ofp_exit(void)
263 {
264         return of_unregister_platform_driver(&cc770_ofp_driver);
265 };
266 module_exit(cc770_ofp_exit);