brintos

brintos / linux-shallow public Read only

0
0
Text · 9.2 KiB · d067628 Raw
372 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Driver for CC770 and AN82527 CAN controllers on the legacy ISA bus4 *5 * Copyright (C) 2009, 2011 Wolfgang Grandegger <wg@grandegger.com>6 */7 8/*9 * Bosch CC770 and Intel AN82527 CAN controllers on the ISA or PC-104 bus.10 * The I/O port or memory address and the IRQ number must be specified via11 * module parameters:12 *13 *   insmod cc770_isa.ko port=0x310,0x380 irq=7,1114 *15 * for ISA devices using I/O ports or:16 *17 *   insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,1118 *19 * for memory mapped ISA devices.20 *21 * Indirect access via address and data port is supported as well:22 *23 *   insmod cc770_isa.ko port=0x310,0x380 indirect=1 irq=7,1124 *25 * Furthermore, the following mode parameter can be defined:26 *27 *   clk: External oscillator clock frequency (default=16000000 [16 MHz])28 *   cir: CPU interface register (default=0x40 [DSC])29 *   bcr: Bus configuration register (default=0x40 [CBY])30 *   cor: Clockout register (default=0x00)31 *32 * Note: for clk, cir, bcr and cor, the first argument re-defines the33 * default for all other devices, e.g.:34 *35 *   insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11 clk=2400000036 *37 * is equivalent to38 *39 *   insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11 clk=24000000,2400000040 */41 42#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt43 44#include <linux/kernel.h>45#include <linux/module.h>46#include <linux/platform_device.h>47#include <linux/interrupt.h>48#include <linux/netdevice.h>49#include <linux/delay.h>50#include <linux/irq.h>51#include <linux/io.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 MAXDEV 859 60MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");61MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the ISA bus");62MODULE_LICENSE("GPL v2");63 64#define CLK_DEFAULT	16000000	/* 16 MHz */65#define COR_DEFAULT	0x0066#define BCR_DEFAULT	BUSCFG_CBY67 68static unsigned long port[MAXDEV];69static unsigned long mem[MAXDEV];70static int irq[MAXDEV];71static int clk[MAXDEV];72static u8 cir[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};73static u8 cor[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};74static u8 bcr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};75static int indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};76 77module_param_hw_array(port, ulong, ioport, NULL, 0444);78MODULE_PARM_DESC(port, "I/O port number");79 80module_param_hw_array(mem, ulong, iomem, NULL, 0444);81MODULE_PARM_DESC(mem, "I/O memory address");82 83module_param_hw_array(indirect, int, ioport, NULL, 0444);84MODULE_PARM_DESC(indirect, "Indirect access via address and data port");85 86module_param_hw_array(irq, int, irq, NULL, 0444);87MODULE_PARM_DESC(irq, "IRQ number");88 89module_param_array(clk, int, NULL, 0444);90MODULE_PARM_DESC(clk, "External oscillator clock frequency "91		 "(default=16000000 [16 MHz])");92 93module_param_array(cir, byte, NULL, 0444);94MODULE_PARM_DESC(cir, "CPU interface register (default=0x40 [DSC])");95 96module_param_array(cor, byte, NULL, 0444);97MODULE_PARM_DESC(cor, "Clockout register (default=0x00)");98 99module_param_array(bcr, byte, NULL, 0444);100MODULE_PARM_DESC(bcr, "Bus configuration register (default=0x40 [CBY])");101 102#define CC770_IOSIZE          0x20103#define CC770_IOSIZE_INDIRECT 0x02104 105/* Spinlock for cc770_isa_port_write_reg_indirect106 * and cc770_isa_port_read_reg_indirect107 */108static DEFINE_SPINLOCK(cc770_isa_port_lock);109 110static struct platform_device *cc770_isa_devs[MAXDEV];111 112static u8 cc770_isa_mem_read_reg(const struct cc770_priv *priv, int reg)113{114	return readb(priv->reg_base + reg);115}116 117static void cc770_isa_mem_write_reg(const struct cc770_priv *priv,118				      int reg, u8 val)119{120	writeb(val, priv->reg_base + reg);121}122 123static u8 cc770_isa_port_read_reg(const struct cc770_priv *priv, int reg)124{125	return inb((unsigned long)priv->reg_base + reg);126}127 128static void cc770_isa_port_write_reg(const struct cc770_priv *priv,129				       int reg, u8 val)130{131	outb(val, (unsigned long)priv->reg_base + reg);132}133 134static u8 cc770_isa_port_read_reg_indirect(const struct cc770_priv *priv,135					     int reg)136{137	unsigned long base = (unsigned long)priv->reg_base;138	unsigned long flags;139	u8 val;140 141	spin_lock_irqsave(&cc770_isa_port_lock, flags);142	outb(reg, base);143	val = inb(base + 1);144	spin_unlock_irqrestore(&cc770_isa_port_lock, flags);145 146	return val;147}148 149static void cc770_isa_port_write_reg_indirect(const struct cc770_priv *priv,150						int reg, u8 val)151{152	unsigned long base = (unsigned long)priv->reg_base;153	unsigned long flags;154 155	spin_lock_irqsave(&cc770_isa_port_lock, flags);156	outb(reg, base);157	outb(val, base + 1);158	spin_unlock_irqrestore(&cc770_isa_port_lock, flags);159}160 161static int cc770_isa_probe(struct platform_device *pdev)162{163	struct net_device *dev;164	struct cc770_priv *priv;165	void __iomem *base = NULL;166	int iosize = CC770_IOSIZE;167	int idx = pdev->id;168	int err;169	u32 clktmp;170 171	dev_dbg(&pdev->dev, "probing idx=%d: port=%#lx, mem=%#lx, irq=%d\n",172		idx, port[idx], mem[idx], irq[idx]);173	if (mem[idx]) {174		if (!request_mem_region(mem[idx], iosize, KBUILD_MODNAME)) {175			err = -EBUSY;176			goto exit;177		}178		base = ioremap(mem[idx], iosize);179		if (!base) {180			err = -ENOMEM;181			goto exit_release;182		}183	} else {184		if (indirect[idx] > 0 ||185		    (indirect[idx] == -1 && indirect[0] > 0))186			iosize = CC770_IOSIZE_INDIRECT;187		if (!request_region(port[idx], iosize, KBUILD_MODNAME)) {188			err = -EBUSY;189			goto exit;190		}191	}192 193	dev = alloc_cc770dev(0);194	if (!dev) {195		err = -ENOMEM;196		goto exit_unmap;197	}198	priv = netdev_priv(dev);199 200	dev->irq = irq[idx];201	priv->irq_flags = IRQF_SHARED;202	if (mem[idx]) {203		priv->reg_base = base;204		dev->base_addr = mem[idx];205		priv->read_reg = cc770_isa_mem_read_reg;206		priv->write_reg = cc770_isa_mem_write_reg;207	} else {208		priv->reg_base = (void __iomem *)port[idx];209		dev->base_addr = port[idx];210 211		if (iosize == CC770_IOSIZE_INDIRECT) {212			priv->read_reg = cc770_isa_port_read_reg_indirect;213			priv->write_reg = cc770_isa_port_write_reg_indirect;214		} else {215			priv->read_reg = cc770_isa_port_read_reg;216			priv->write_reg = cc770_isa_port_write_reg;217		}218	}219 220	if (clk[idx])221		clktmp = clk[idx];222	else if (clk[0])223		clktmp = clk[0];224	else225		clktmp = CLK_DEFAULT;226	priv->can.clock.freq = clktmp;227 228	if (cir[idx] != 0xff) {229		priv->cpu_interface = cir[idx];230	} else if (cir[0] != 0xff) {231		priv->cpu_interface = cir[0];232	} else {233		/* The system clock may not exceed 10 MHz */234		if (clktmp > 10000000) {235			priv->cpu_interface |= CPUIF_DSC;236			clktmp /= 2;237		}238		/* The memory clock may not exceed 8 MHz */239		if (clktmp > 8000000)240			priv->cpu_interface |= CPUIF_DMC;241	}242 243	if (priv->cpu_interface & CPUIF_DSC)244		priv->can.clock.freq /= 2;245 246	if (bcr[idx] != 0xff)247		priv->bus_config = bcr[idx];248	else if (bcr[0] != 0xff)249		priv->bus_config = bcr[0];250	else251		priv->bus_config = BCR_DEFAULT;252 253	if (cor[idx] != 0xff)254		priv->clkout = cor[idx];255	else if (cor[0] != 0xff)256		priv->clkout = cor[0];257	else258		priv->clkout = COR_DEFAULT;259 260	platform_set_drvdata(pdev, dev);261	SET_NETDEV_DEV(dev, &pdev->dev);262 263	err = register_cc770dev(dev);264	if (err) {265		dev_err(&pdev->dev,266			"couldn't register device (err=%d)\n", err);267		goto exit_free;268	}269 270	dev_info(&pdev->dev, "device registered (reg_base=0x%p, irq=%d)\n",271		 priv->reg_base, dev->irq);272	return 0;273 274exit_free:275	free_cc770dev(dev);276exit_unmap:277	if (mem[idx])278		iounmap(base);279exit_release:280	if (mem[idx])281		release_mem_region(mem[idx], iosize);282	else283		release_region(port[idx], iosize);284exit:285	return err;286}287 288static void cc770_isa_remove(struct platform_device *pdev)289{290	struct net_device *dev = platform_get_drvdata(pdev);291	struct cc770_priv *priv = netdev_priv(dev);292	int idx = pdev->id;293 294	unregister_cc770dev(dev);295 296	if (mem[idx]) {297		iounmap(priv->reg_base);298		release_mem_region(mem[idx], CC770_IOSIZE);299	} else {300		if (priv->read_reg == cc770_isa_port_read_reg_indirect)301			release_region(port[idx], CC770_IOSIZE_INDIRECT);302		else303			release_region(port[idx], CC770_IOSIZE);304	}305	free_cc770dev(dev);306}307 308static struct platform_driver cc770_isa_driver = {309	.probe = cc770_isa_probe,310	.remove = cc770_isa_remove,311	.driver = {312		.name = KBUILD_MODNAME,313	},314};315 316static int __init cc770_isa_init(void)317{318	int idx, err;319 320	for (idx = 0; idx < ARRAY_SIZE(cc770_isa_devs); idx++) {321		if ((port[idx] || mem[idx]) && irq[idx]) {322			cc770_isa_devs[idx] =323				platform_device_alloc(KBUILD_MODNAME, idx);324			if (!cc770_isa_devs[idx]) {325				err = -ENOMEM;326				goto exit_free_devices;327			}328			err = platform_device_add(cc770_isa_devs[idx]);329			if (err) {330				platform_device_put(cc770_isa_devs[idx]);331				goto exit_free_devices;332			}333			pr_debug("platform device %d: port=%#lx, mem=%#lx, "334				 "irq=%d\n",335				 idx, port[idx], mem[idx], irq[idx]);336		} else if (idx == 0 || port[idx] || mem[idx]) {337			pr_err("insufficient parameters supplied\n");338			err = -EINVAL;339			goto exit_free_devices;340		}341	}342 343	err = platform_driver_register(&cc770_isa_driver);344	if (err)345		goto exit_free_devices;346 347	pr_info("driver for max. %d devices registered\n", MAXDEV);348 349	return 0;350 351exit_free_devices:352	while (--idx >= 0) {353		if (cc770_isa_devs[idx])354			platform_device_unregister(cc770_isa_devs[idx]);355	}356 357	return err;358}359module_init(cc770_isa_init);360 361static void __exit cc770_isa_exit(void)362{363	int idx;364 365	platform_driver_unregister(&cc770_isa_driver);366	for (idx = 0; idx < ARRAY_SIZE(cc770_isa_devs); idx++) {367		if (cc770_isa_devs[idx])368			platform_device_unregister(cc770_isa_devs[idx]);369	}370}371module_exit(cc770_isa_exit);372