2 * Driver for CC770 CAN controllers on the OpenFirmware platform bus
4 * Copyright (C) 2009 Wolfgang Grandegger <wg@grandegger.com>
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
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.
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.
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:
25 * compatible = "bosch,cc770";
26 * reg = <3 0x100 0x80>;
28 * interrupt-parent = <&mpic>;
29 * bosch,external-clock-frequency = <16000000>;
32 * See "Documentation/powerpc/dts-bindings/can/cc770.txt" for further
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>
45 #include <linux/of_platform.h>
50 #define DRV_NAME "cc770_of_platform"
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");
56 #define CC770_OFP_CAN_CLOCK 16000000
58 static u8 cc770_ofp_read_reg(const struct cc770_priv *priv, int reg)
60 return in_8(priv->reg_base + reg);
63 static void cc770_ofp_write_reg(const struct cc770_priv *priv, int reg, u8 val)
65 out_8(priv->reg_base + reg, val);
68 static int __devexit cc770_ofp_remove(struct of_device *ofdev)
70 struct net_device *dev = dev_get_drvdata(&ofdev->dev);
71 struct cc770_priv *priv = netdev_priv(dev);
74 dev_set_drvdata(&ofdev->dev, NULL);
76 unregister_cc770dev(dev);
77 iounmap(priv->reg_base);
78 /* irq_dispose_mapping(dev->irq);*/ /* will not work for shared IRQs */
81 of_address_to_resource(ofdev->node, 0, &res);
82 release_mem_region(res.start, resource_size(&res));
87 static int __devinit cc770_ofp_probe(struct of_device *ofdev,
88 const struct of_device_id *id)
90 struct device_node *np = ofdev->node;
91 struct net_device *dev;
92 struct cc770_priv *priv;
96 int err, irq, res_size, prop_size;
99 err = of_address_to_resource(np, 0, &res);
101 dev_err(&ofdev->dev, "invalid address\n");
105 res_size = resource_size(&res);
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);
114 base = ioremap_nocache(res.start, res_size);
116 dev_err(&ofdev->dev, "couldn't ioremap %#llx..%#llx\n",
117 (unsigned long long)res.start,
118 (unsigned long long)res.end);
120 goto exit_release_mem;
123 irq = irq_of_parse_and_map(np, 0);
125 dev_err(&ofdev->dev, "no irq found\n");
130 dev = alloc_cc770dev(0);
133 goto exit_dispose_irq;
136 priv = netdev_priv(dev);
138 priv->read_reg = cc770_ofp_read_reg;
139 priv->write_reg = cc770_ofp_write_reg;
141 prop = of_get_property(np, "bosch,external-clock-frequency",
143 if (prop && (prop_size == sizeof(u32)))
146 clkext = CC770_OFP_CAN_CLOCK; /* default */
147 priv->can.clock.freq = clkext;
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;
155 /* The memory clock may not exceed 8 MHz */
156 if (priv->can.clock.freq > 8000000)
157 priv->cpu_interface |= CPUIF_DMC;
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;
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;
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;
180 if (cdv > 0 && cdv < 16) {
181 priv->cpu_interface |= CPUIF_CEN;
182 priv->clkout |= (cdv - 1) & CLKOUT_CD_MASK;
184 prop = of_get_property(np, "bosch,slew-rate",
186 if (prop && (prop_size == sizeof(u32))) {
189 /* Determine default slew rate */
190 slew = (CLKOUT_SL_MASK >> CLKOUT_SL_SHIFT) -
191 ((cdv * clkext - 1) / 8000000);
195 priv->clkout |= (slew << CLKOUT_SL_SHIFT) &
198 dev_dbg(ND2D(dev), "invalid clock-out-frequency\n");
203 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
204 priv->irq_flags = SA_SHIRQ;
206 priv->irq_flags = IRQF_SHARED;
208 priv->reg_base = base;
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);
218 dev_set_drvdata(&ofdev->dev, dev);
219 SET_NETDEV_DEV(dev, &ofdev->dev);
221 err = register_cc770dev(dev);
223 dev_err(&ofdev->dev, "registering %s failed (err=%d)\n",
225 goto exit_free_cc770;
233 /* irq_dispose_mapping(dev->irq);*/ /* will not work for shared IRQs */
237 release_mem_region(res.start, res_size);
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 */
248 static struct of_platform_driver cc770_ofp_driver = {
249 .owner = THIS_MODULE,
251 .probe = cc770_ofp_probe,
252 .remove = __devexit_p(cc770_ofp_remove),
253 .match_table = cc770_ofp_table,
256 static int __init cc770_ofp_init(void)
258 return of_register_platform_driver(&cc770_ofp_driver);
260 module_init(cc770_ofp_init);
262 static void __exit cc770_ofp_exit(void)
264 return of_unregister_platform_driver(&cc770_ofp_driver);
266 module_exit(cc770_ofp_exit);