Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

can: cc770: add platform bus driver for the CC770 and AN82527

This driver works with both, static platform data and device tree
bindings. It has been tested on a TQM855L board with two AN82527
CAN controllers on the local bus.

CC: Devicetree-discuss@lists.ozlabs.org
CC: linuxppc-dev@ozlabs.org
CC: Kumar Gala <galak@kernel.crashing.org>
Signed-off-by: Wolfgang Grandegger <wg@grandegger.com>
Acked-by: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Wolfgang Grandegger and committed by
David S. Miller
e285e44d edd2c26f

+333
+53
Documentation/devicetree/bindings/net/can/cc770.txt
··· 1 + Memory mapped Bosch CC770 and Intel AN82527 CAN controller 2 + 3 + Note: The CC770 is a CAN controller from Bosch, which is 100% 4 + compatible with the old AN82527 from Intel, but with "bugs" being fixed. 5 + 6 + Required properties: 7 + 8 + - compatible : should be "bosch,cc770" for the CC770 and "intc,82527" 9 + for the AN82527. 10 + 11 + - reg : should specify the chip select, address offset and size required 12 + to map the registers of the controller. The size is usually 0x80. 13 + 14 + - interrupts : property with a value describing the interrupt source 15 + (number and sensitivity) required for the controller. 16 + 17 + Optional properties: 18 + 19 + - bosch,external-clock-frequency : frequency of the external oscillator 20 + clock in Hz. Note that the internal clock frequency used by the 21 + controller is half of that value. If not specified, a default 22 + value of 16000000 (16 MHz) is used. 23 + 24 + - bosch,clock-out-frequency : slock frequency in Hz on the CLKOUT pin. 25 + If not specified or if the specified value is 0, the CLKOUT pin 26 + will be disabled. 27 + 28 + - bosch,slew-rate : slew rate of the CLKOUT signal. If not specified, 29 + a resonable value will be calculated. 30 + 31 + - bosch,disconnect-rx0-input : see data sheet. 32 + 33 + - bosch,disconnect-rx1-input : see data sheet. 34 + 35 + - bosch,disconnect-tx1-output : see data sheet. 36 + 37 + - bosch,polarity-dominant : see data sheet. 38 + 39 + - bosch,divide-memory-clock : see data sheet. 40 + 41 + - bosch,iso-low-speed-mux : see data sheet. 42 + 43 + For further information, please have a look to the CC770 or AN82527. 44 + 45 + Examples: 46 + 47 + can@3,100 { 48 + compatible = "bosch,cc770"; 49 + reg = <3 0x100 0x80>; 50 + interrupts = <2 0>; 51 + interrupt-parent = <&mpic>; 52 + bosch,external-clock-frequency = <16000000>; 53 + };
+7
drivers/net/can/cc770/Kconfig
··· 11 11 connected to the ISA bus using I/O port, memory mapped or 12 12 indirect access. 13 13 14 + config CAN_CC770_PLATFORM 15 + tristate "Generic Platform Bus based CC770 driver" 16 + ---help--- 17 + This driver adds support for the CC770 and AN82527 chips 18 + connected to the "platform bus" (Linux abstraction for directly 19 + to the processor attached devices). 20 + 14 21 endif
+1
drivers/net/can/cc770/Makefile
··· 4 4 5 5 obj-$(CONFIG_CAN_CC770) += cc770.o 6 6 obj-$(CONFIG_CAN_CC770_ISA) += cc770_isa.o 7 + obj-$(CONFIG_CAN_CC770_PLATFORM) += cc770_platform.o 7 8 8 9 ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
+272
drivers/net/can/cc770/cc770_platform.c
··· 1 + /* 2 + * Driver for CC770 and AN82527 CAN controllers on the platform bus 3 + * 4 + * Copyright (C) 2009, 2011 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 + 16 + /* 17 + * If platform data are used you should have similar definitions 18 + * in your board-specific code: 19 + * 20 + * static struct cc770_platform_data myboard_cc770_pdata = { 21 + * .osc_freq = 16000000, 22 + * .cir = 0x41, 23 + * .cor = 0x20, 24 + * .bcr = 0x40, 25 + * }; 26 + * 27 + * Please see include/linux/can/platform/cc770.h for description of 28 + * above fields. 29 + * 30 + * If the device tree is used, you need a CAN node definition in your 31 + * DTS file similar to: 32 + * 33 + * can@3,100 { 34 + * compatible = "bosch,cc770"; 35 + * reg = <3 0x100 0x80>; 36 + * interrupts = <2 0>; 37 + * interrupt-parent = <&mpic>; 38 + * bosch,external-clock-frequency = <16000000>; 39 + * }; 40 + * 41 + * See "Documentation/devicetree/bindings/net/can/cc770.txt" for further 42 + * information. 43 + */ 44 + 45 + #include <linux/kernel.h> 46 + #include <linux/module.h> 47 + #include <linux/interrupt.h> 48 + #include <linux/netdevice.h> 49 + #include <linux/delay.h> 50 + #include <linux/platform_device.h> 51 + #include <linux/of.h> 52 + #include <linux/can.h> 53 + #include <linux/can/dev.h> 54 + #include <linux/can/platform/cc770.h> 55 + 56 + #include "cc770.h" 57 + 58 + #define DRV_NAME "cc770_platform" 59 + 60 + MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); 61 + MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the platform bus"); 62 + MODULE_LICENSE("GPL v2"); 63 + 64 + #define CC770_PLATFORM_CAN_CLOCK 16000000 65 + 66 + static u8 cc770_platform_read_reg(const struct cc770_priv *priv, int reg) 67 + { 68 + return ioread8(priv->reg_base + reg); 69 + } 70 + 71 + static void cc770_platform_write_reg(const struct cc770_priv *priv, int reg, 72 + u8 val) 73 + { 74 + iowrite8(val, priv->reg_base + reg); 75 + } 76 + 77 + static int __devinit cc770_get_of_node_data(struct platform_device *pdev, 78 + struct cc770_priv *priv) 79 + { 80 + struct device_node *np = pdev->dev.of_node; 81 + const u32 *prop; 82 + int prop_size; 83 + u32 clkext; 84 + 85 + prop = of_get_property(np, "bosch,external-clock-frequency", 86 + &prop_size); 87 + if (prop && (prop_size == sizeof(u32))) 88 + clkext = *prop; 89 + else 90 + clkext = CC770_PLATFORM_CAN_CLOCK; /* default */ 91 + priv->can.clock.freq = clkext; 92 + 93 + /* The system clock may not exceed 10 MHz */ 94 + if (priv->can.clock.freq > 10000000) { 95 + priv->cpu_interface |= CPUIF_DSC; 96 + priv->can.clock.freq /= 2; 97 + } 98 + 99 + /* The memory clock may not exceed 8 MHz */ 100 + if (priv->can.clock.freq > 8000000) 101 + priv->cpu_interface |= CPUIF_DMC; 102 + 103 + if (of_get_property(np, "bosch,divide-memory-clock", NULL)) 104 + priv->cpu_interface |= CPUIF_DMC; 105 + if (of_get_property(np, "bosch,iso-low-speed-mux", NULL)) 106 + priv->cpu_interface |= CPUIF_MUX; 107 + 108 + if (!of_get_property(np, "bosch,no-comperator-bypass", NULL)) 109 + priv->bus_config |= BUSCFG_CBY; 110 + if (of_get_property(np, "bosch,disconnect-rx0-input", NULL)) 111 + priv->bus_config |= BUSCFG_DR0; 112 + if (of_get_property(np, "bosch,disconnect-rx1-input", NULL)) 113 + priv->bus_config |= BUSCFG_DR1; 114 + if (of_get_property(np, "bosch,disconnect-tx1-output", NULL)) 115 + priv->bus_config |= BUSCFG_DT1; 116 + if (of_get_property(np, "bosch,polarity-dominant", NULL)) 117 + priv->bus_config |= BUSCFG_POL; 118 + 119 + prop = of_get_property(np, "bosch,clock-out-frequency", &prop_size); 120 + if (prop && (prop_size == sizeof(u32)) && *prop > 0) { 121 + u32 cdv = clkext / *prop; 122 + int slew; 123 + 124 + if (cdv > 0 && cdv < 16) { 125 + priv->cpu_interface |= CPUIF_CEN; 126 + priv->clkout |= (cdv - 1) & CLKOUT_CD_MASK; 127 + 128 + prop = of_get_property(np, "bosch,slew-rate", 129 + &prop_size); 130 + if (prop && (prop_size == sizeof(u32))) { 131 + slew = *prop; 132 + } else { 133 + /* Determine default slew rate */ 134 + slew = (CLKOUT_SL_MASK >> 135 + CLKOUT_SL_SHIFT) - 136 + ((cdv * clkext - 1) / 8000000); 137 + if (slew < 0) 138 + slew = 0; 139 + } 140 + priv->clkout |= (slew << CLKOUT_SL_SHIFT) & 141 + CLKOUT_SL_MASK; 142 + } else { 143 + dev_dbg(&pdev->dev, "invalid clock-out-frequency\n"); 144 + } 145 + } 146 + 147 + return 0; 148 + } 149 + 150 + static int __devinit cc770_get_platform_data(struct platform_device *pdev, 151 + struct cc770_priv *priv) 152 + { 153 + 154 + struct cc770_platform_data *pdata = pdev->dev.platform_data; 155 + 156 + priv->can.clock.freq = pdata->osc_freq; 157 + if (priv->cpu_interface | CPUIF_DSC) 158 + priv->can.clock.freq /= 2; 159 + priv->clkout = pdata->cor; 160 + priv->bus_config = pdata->bcr; 161 + priv->cpu_interface = pdata->cir; 162 + 163 + return 0; 164 + } 165 + 166 + static int __devinit cc770_platform_probe(struct platform_device *pdev) 167 + { 168 + struct net_device *dev; 169 + struct cc770_priv *priv; 170 + struct resource *mem; 171 + resource_size_t mem_size; 172 + void __iomem *base; 173 + int err, irq; 174 + 175 + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 176 + irq = platform_get_irq(pdev, 0); 177 + if (!mem || irq <= 0) 178 + return -ENODEV; 179 + 180 + mem_size = resource_size(mem); 181 + if (!request_mem_region(mem->start, mem_size, pdev->name)) 182 + return -EBUSY; 183 + 184 + base = ioremap(mem->start, mem_size); 185 + if (!base) { 186 + err = -ENOMEM; 187 + goto exit_release_mem; 188 + } 189 + 190 + dev = alloc_cc770dev(0); 191 + if (!dev) { 192 + err = -ENOMEM; 193 + goto exit_unmap_mem; 194 + } 195 + 196 + dev->irq = irq; 197 + priv = netdev_priv(dev); 198 + priv->read_reg = cc770_platform_read_reg; 199 + priv->write_reg = cc770_platform_write_reg; 200 + priv->irq_flags = IRQF_SHARED; 201 + priv->reg_base = base; 202 + 203 + if (pdev->dev.of_node) 204 + err = cc770_get_of_node_data(pdev, priv); 205 + else if (pdev->dev.platform_data) 206 + err = cc770_get_platform_data(pdev, priv); 207 + else 208 + err = -ENODEV; 209 + if (err) 210 + goto exit_free_cc770; 211 + 212 + dev_dbg(&pdev->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(&pdev->dev, dev); 219 + SET_NETDEV_DEV(dev, &pdev->dev); 220 + 221 + err = register_cc770dev(dev); 222 + if (err) { 223 + dev_err(&pdev->dev, 224 + "couldn't register CC700 device (err=%d)\n", err); 225 + goto exit_free_cc770; 226 + } 227 + 228 + return 0; 229 + 230 + exit_free_cc770: 231 + free_cc770dev(dev); 232 + exit_unmap_mem: 233 + iounmap(base); 234 + exit_release_mem: 235 + release_mem_region(mem->start, mem_size); 236 + 237 + return err; 238 + } 239 + 240 + static int __devexit cc770_platform_remove(struct platform_device *pdev) 241 + { 242 + struct net_device *dev = dev_get_drvdata(&pdev->dev); 243 + struct cc770_priv *priv = netdev_priv(dev); 244 + struct resource *mem; 245 + 246 + unregister_cc770dev(dev); 247 + iounmap(priv->reg_base); 248 + free_cc770dev(dev); 249 + 250 + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 251 + release_mem_region(mem->start, resource_size(mem)); 252 + 253 + return 0; 254 + } 255 + 256 + static struct of_device_id __devinitdata cc770_platform_table[] = { 257 + {.compatible = "bosch,cc770"}, /* CC770 from Bosch */ 258 + {.compatible = "intc,82527"}, /* AN82527 from Intel CP */ 259 + {}, 260 + }; 261 + 262 + static struct platform_driver cc770_platform_driver = { 263 + .driver = { 264 + .name = DRV_NAME, 265 + .owner = THIS_MODULE, 266 + .of_match_table = cc770_platform_table, 267 + }, 268 + .probe = cc770_platform_probe, 269 + .remove = __devexit_p(cc770_platform_remove), 270 + }; 271 + 272 + module_platform_driver(cc770_platform_driver);