brintos

brintos / linux-shallow public Read only

0
0
Text · 6.4 KiB · b6c4f02 Raw
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