254 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Driver for CC770 and AN82527 CAN controllers on the platform bus4 *5 * Copyright (C) 2009, 2011 Wolfgang Grandegger <wg@grandegger.com>6 */7 8/*9 * If platform data are used you should have similar definitions10 * in your board-specific code:11 *12 * static struct cc770_platform_data myboard_cc770_pdata = {13 * .osc_freq = 16000000,14 * .cir = 0x41,15 * .cor = 0x20,16 * .bcr = 0x40,17 * };18 *19 * Please see include/linux/can/platform/cc770.h for description of20 * above fields.21 *22 * If the device tree is used, you need a CAN node definition in your23 * DTS file similar to:24 *25 * can@3,100 {26 * compatible = "bosch,cc770";27 * reg = <3 0x100 0x80>;28 * interrupts = <2 0>;29 * interrupt-parent = <&mpic>;30 * bosch,external-clock-frequency = <16000000>;31 * };32 *33 * See "Documentation/devicetree/bindings/net/can/cc770.txt" for further34 * information.35 */36 37#include <linux/kernel.h>38#include <linux/module.h>39#include <linux/interrupt.h>40#include <linux/netdevice.h>41#include <linux/delay.h>42#include <linux/platform_device.h>43#include <linux/of.h>44#include <linux/can.h>45#include <linux/can/dev.h>46#include <linux/can/platform/cc770.h>47 48#include "cc770.h"49 50#define DRV_NAME "cc770_platform"51 52MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");53MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the platform bus");54MODULE_LICENSE("GPL v2");55MODULE_ALIAS("platform:" DRV_NAME);56 57#define CC770_PLATFORM_CAN_CLOCK 1600000058 59static u8 cc770_platform_read_reg(const struct cc770_priv *priv, int reg)60{61 return ioread8(priv->reg_base + reg);62}63 64static void cc770_platform_write_reg(const struct cc770_priv *priv, int reg,65 u8 val)66{67 iowrite8(val, priv->reg_base + reg);68}69 70static int cc770_get_of_node_data(struct platform_device *pdev,71 struct cc770_priv *priv)72{73 u32 clkext = CC770_PLATFORM_CAN_CLOCK, clkout = 0;74 struct device_node *np = pdev->dev.of_node;75 76 of_property_read_u32(np, "bosch,external-clock-frequency", &clkext);77 priv->can.clock.freq = clkext;78 79 /* The system clock may not exceed 10 MHz */80 if (priv->can.clock.freq > 10000000) {81 priv->cpu_interface |= CPUIF_DSC;82 priv->can.clock.freq /= 2;83 }84 85 /* The memory clock may not exceed 8 MHz */86 if (priv->can.clock.freq > 8000000)87 priv->cpu_interface |= CPUIF_DMC;88 89 if (of_property_read_bool(np, "bosch,divide-memory-clock"))90 priv->cpu_interface |= CPUIF_DMC;91 if (of_property_read_bool(np, "bosch,iso-low-speed-mux"))92 priv->cpu_interface |= CPUIF_MUX;93 94 if (!of_property_read_bool(np, "bosch,no-comperator-bypass"))95 priv->bus_config |= BUSCFG_CBY;96 if (of_property_read_bool(np, "bosch,disconnect-rx0-input"))97 priv->bus_config |= BUSCFG_DR0;98 if (of_property_read_bool(np, "bosch,disconnect-rx1-input"))99 priv->bus_config |= BUSCFG_DR1;100 if (of_property_read_bool(np, "bosch,disconnect-tx1-output"))101 priv->bus_config |= BUSCFG_DT1;102 if (of_property_read_bool(np, "bosch,polarity-dominant"))103 priv->bus_config |= BUSCFG_POL;104 105 of_property_read_u32(np, "bosch,clock-out-frequency", &clkout);106 if (clkout > 0) {107 u32 cdv = clkext / clkout;108 109 if (cdv > 0 && cdv < 16) {110 u32 slew;111 112 priv->cpu_interface |= CPUIF_CEN;113 priv->clkout |= (cdv - 1) & CLKOUT_CD_MASK;114 115 if (of_property_read_u32(np, "bosch,slew-rate", &slew)) {116 /* Determine default slew rate */117 slew = (CLKOUT_SL_MASK >>118 CLKOUT_SL_SHIFT) -119 ((cdv * clkext - 1) / 8000000);120 if (slew > (CLKOUT_SL_MASK >> CLKOUT_SL_SHIFT))121 slew = 0;122 }123 priv->clkout |= (slew << CLKOUT_SL_SHIFT) &124 CLKOUT_SL_MASK;125 } else {126 dev_dbg(&pdev->dev, "invalid clock-out-frequency\n");127 }128 }129 130 return 0;131}132 133static int cc770_get_platform_data(struct platform_device *pdev,134 struct cc770_priv *priv)135{136 137 struct cc770_platform_data *pdata = dev_get_platdata(&pdev->dev);138 139 priv->can.clock.freq = pdata->osc_freq;140 if (priv->cpu_interface & CPUIF_DSC)141 priv->can.clock.freq /= 2;142 priv->clkout = pdata->cor;143 priv->bus_config = pdata->bcr;144 priv->cpu_interface = pdata->cir;145 146 return 0;147}148 149static int cc770_platform_probe(struct platform_device *pdev)150{151 struct net_device *dev;152 struct cc770_priv *priv;153 struct resource *mem;154 resource_size_t mem_size;155 void __iomem *base;156 int err, irq;157 158 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);159 irq = platform_get_irq(pdev, 0);160 if (!mem || irq <= 0)161 return -ENODEV;162 163 mem_size = resource_size(mem);164 if (!request_mem_region(mem->start, mem_size, pdev->name))165 return -EBUSY;166 167 base = ioremap(mem->start, mem_size);168 if (!base) {169 err = -ENOMEM;170 goto exit_release_mem;171 }172 173 dev = alloc_cc770dev(0);174 if (!dev) {175 err = -ENOMEM;176 goto exit_unmap_mem;177 }178 179 dev->irq = irq;180 priv = netdev_priv(dev);181 priv->read_reg = cc770_platform_read_reg;182 priv->write_reg = cc770_platform_write_reg;183 priv->irq_flags = IRQF_SHARED;184 priv->reg_base = base;185 186 if (pdev->dev.of_node)187 err = cc770_get_of_node_data(pdev, priv);188 else if (dev_get_platdata(&pdev->dev))189 err = cc770_get_platform_data(pdev, priv);190 else191 err = -ENODEV;192 if (err)193 goto exit_free_cc770;194 195 dev_dbg(&pdev->dev,196 "reg_base=0x%p irq=%d clock=%d cpu_interface=0x%02x "197 "bus_config=0x%02x clkout=0x%02x\n",198 priv->reg_base, dev->irq, priv->can.clock.freq,199 priv->cpu_interface, priv->bus_config, priv->clkout);200 201 platform_set_drvdata(pdev, dev);202 SET_NETDEV_DEV(dev, &pdev->dev);203 204 err = register_cc770dev(dev);205 if (err) {206 dev_err(&pdev->dev,207 "couldn't register CC700 device (err=%d)\n", err);208 goto exit_free_cc770;209 }210 211 return 0;212 213exit_free_cc770:214 free_cc770dev(dev);215exit_unmap_mem:216 iounmap(base);217exit_release_mem:218 release_mem_region(mem->start, mem_size);219 220 return err;221}222 223static void cc770_platform_remove(struct platform_device *pdev)224{225 struct net_device *dev = platform_get_drvdata(pdev);226 struct cc770_priv *priv = netdev_priv(dev);227 struct resource *mem;228 229 unregister_cc770dev(dev);230 iounmap(priv->reg_base);231 free_cc770dev(dev);232 233 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);234 release_mem_region(mem->start, resource_size(mem));235}236 237static const struct of_device_id cc770_platform_table[] = {238 {.compatible = "bosch,cc770"}, /* CC770 from Bosch */239 {.compatible = "intc,82527"}, /* AN82527 from Intel CP */240 {},241};242MODULE_DEVICE_TABLE(of, cc770_platform_table);243 244static struct platform_driver cc770_platform_driver = {245 .driver = {246 .name = DRV_NAME,247 .of_match_table = cc770_platform_table,248 },249 .probe = cc770_platform_probe,250 .remove = cc770_platform_remove,251};252 253module_platform_driver(cc770_platform_driver);254