cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cc770_isa.c (9382B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Driver for CC770 and AN82527 CAN controllers on the legacy ISA bus
      4 *
      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 via
     11 * module parameters:
     12 *
     13 *   insmod cc770_isa.ko port=0x310,0x380 irq=7,11
     14 *
     15 * for ISA devices using I/O ports or:
     16 *
     17 *   insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11
     18 *
     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,11
     24 *
     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 the
     33 * default for all other devices, e.g.:
     34 *
     35 *   insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11 clk=24000000
     36 *
     37 * is equivalent to
     38 *
     39 *   insmod cc770_isa.ko mem=0xd1000,0xd1000 irq=7,11 clk=24000000,24000000
     40 */
     41
     42#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     43
     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 8
     59
     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	0x00
     66#define BCR_DEFAULT	BUSCFG_CBY
     67
     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          0x20
    103#define CC770_IOSIZE_INDIRECT 0x02
    104
    105/* Spinlock for cc770_isa_port_write_reg_indirect
    106 * and cc770_isa_port_read_reg_indirect
    107 */
    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	else
    225		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	else
    251		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	else
    258		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_unmap;
    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
    274 exit_unmap:
    275	if (mem[idx])
    276		iounmap(base);
    277 exit_release:
    278	if (mem[idx])
    279		release_mem_region(mem[idx], iosize);
    280	else
    281		release_region(port[idx], iosize);
    282 exit:
    283	return err;
    284}
    285
    286static int cc770_isa_remove(struct platform_device *pdev)
    287{
    288	struct net_device *dev = platform_get_drvdata(pdev);
    289	struct cc770_priv *priv = netdev_priv(dev);
    290	int idx = pdev->id;
    291
    292	unregister_cc770dev(dev);
    293
    294	if (mem[idx]) {
    295		iounmap(priv->reg_base);
    296		release_mem_region(mem[idx], CC770_IOSIZE);
    297	} else {
    298		if (priv->read_reg == cc770_isa_port_read_reg_indirect)
    299			release_region(port[idx], CC770_IOSIZE_INDIRECT);
    300		else
    301			release_region(port[idx], CC770_IOSIZE);
    302	}
    303	free_cc770dev(dev);
    304
    305	return 0;
    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);