135e62ae8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 22a6ba39aSWolfgang Grandegger /* 32a6ba39aSWolfgang Grandegger * Copyright (C) 2009 Wolfgang Grandegger <wg@grandegger.com> 42a6ba39aSWolfgang Grandegger */ 52a6ba39aSWolfgang Grandegger 62a6ba39aSWolfgang Grandegger #include <linux/kernel.h> 72a6ba39aSWolfgang Grandegger #include <linux/module.h> 81839a6c6SWolfgang Grandegger #include <linux/platform_device.h> 92a6ba39aSWolfgang Grandegger #include <linux/interrupt.h> 102a6ba39aSWolfgang Grandegger #include <linux/netdevice.h> 112a6ba39aSWolfgang Grandegger #include <linux/delay.h> 122a6ba39aSWolfgang Grandegger #include <linux/irq.h> 132a6ba39aSWolfgang Grandegger #include <linux/io.h> 142a6ba39aSWolfgang Grandegger #include <linux/can/dev.h> 152a6ba39aSWolfgang Grandegger #include <linux/can/platform/sja1000.h> 162a6ba39aSWolfgang Grandegger 172a6ba39aSWolfgang Grandegger #include "sja1000.h" 182a6ba39aSWolfgang Grandegger 192a6ba39aSWolfgang Grandegger #define DRV_NAME "sja1000_isa" 202a6ba39aSWolfgang Grandegger 212a6ba39aSWolfgang Grandegger #define MAXDEV 8 222a6ba39aSWolfgang Grandegger 232a6ba39aSWolfgang Grandegger MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); 242a6ba39aSWolfgang Grandegger MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the ISA bus"); 252a6ba39aSWolfgang Grandegger MODULE_LICENSE("GPL v2"); 262a6ba39aSWolfgang Grandegger 272a6ba39aSWolfgang Grandegger #define CLK_DEFAULT 16000000 /* 16 MHz */ 282a6ba39aSWolfgang Grandegger #define CDR_DEFAULT (CDR_CBP | CDR_CLK_OFF) 292a6ba39aSWolfgang Grandegger #define OCR_DEFAULT OCR_TX0_PUSHPULL 302a6ba39aSWolfgang Grandegger 312a6ba39aSWolfgang Grandegger static unsigned long port[MAXDEV]; 322a6ba39aSWolfgang Grandegger static unsigned long mem[MAXDEV]; 333c8ac0f2SBill Pemberton static int irq[MAXDEV]; 343c8ac0f2SBill Pemberton static int clk[MAXDEV]; 353c8ac0f2SBill Pemberton static unsigned char cdr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; 363c8ac0f2SBill Pemberton static unsigned char ocr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; 373c8ac0f2SBill Pemberton static int indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1}; 38a9edcdedSOliver Hartkopp static spinlock_t indirect_lock[MAXDEV]; /* lock for indirect access mode */ 392a6ba39aSWolfgang Grandegger 40d61e4038SJoe Perches module_param_hw_array(port, ulong, ioport, NULL, 0444); 412a6ba39aSWolfgang Grandegger MODULE_PARM_DESC(port, "I/O port number"); 422a6ba39aSWolfgang Grandegger 43d61e4038SJoe Perches module_param_hw_array(mem, ulong, iomem, NULL, 0444); 442a6ba39aSWolfgang Grandegger MODULE_PARM_DESC(mem, "I/O memory address"); 452a6ba39aSWolfgang Grandegger 46d61e4038SJoe Perches module_param_hw_array(indirect, int, ioport, NULL, 0444); 472a6ba39aSWolfgang Grandegger MODULE_PARM_DESC(indirect, "Indirect access via address and data port"); 482a6ba39aSWolfgang Grandegger 49d61e4038SJoe Perches module_param_hw_array(irq, int, irq, NULL, 0444); 502a6ba39aSWolfgang Grandegger MODULE_PARM_DESC(irq, "IRQ number"); 512a6ba39aSWolfgang Grandegger 52d61e4038SJoe Perches module_param_array(clk, int, NULL, 0444); 532a6ba39aSWolfgang Grandegger MODULE_PARM_DESC(clk, "External oscillator clock frequency " 542a6ba39aSWolfgang Grandegger "(default=16000000 [16 MHz])"); 552a6ba39aSWolfgang Grandegger 56d61e4038SJoe Perches module_param_array(cdr, byte, NULL, 0444); 572a6ba39aSWolfgang Grandegger MODULE_PARM_DESC(cdr, "Clock divider register " 582a6ba39aSWolfgang Grandegger "(default=0x48 [CDR_CBP | CDR_CLK_OFF])"); 592a6ba39aSWolfgang Grandegger 60d61e4038SJoe Perches module_param_array(ocr, byte, NULL, 0444); 612a6ba39aSWolfgang Grandegger MODULE_PARM_DESC(ocr, "Output control register " 622a6ba39aSWolfgang Grandegger "(default=0x18 [OCR_TX0_PUSHPULL])"); 632a6ba39aSWolfgang Grandegger 642a6ba39aSWolfgang Grandegger #define SJA1000_IOSIZE 0x20 652a6ba39aSWolfgang Grandegger #define SJA1000_IOSIZE_INDIRECT 0x02 662a6ba39aSWolfgang Grandegger 671839a6c6SWolfgang Grandegger static struct platform_device *sja1000_isa_devs[MAXDEV]; 681839a6c6SWolfgang Grandegger 692a6ba39aSWolfgang Grandegger static u8 sja1000_isa_mem_read_reg(const struct sja1000_priv *priv, int reg) 702a6ba39aSWolfgang Grandegger { 712a6ba39aSWolfgang Grandegger return readb(priv->reg_base + reg); 722a6ba39aSWolfgang Grandegger } 732a6ba39aSWolfgang Grandegger 742a6ba39aSWolfgang Grandegger static void sja1000_isa_mem_write_reg(const struct sja1000_priv *priv, 752a6ba39aSWolfgang Grandegger int reg, u8 val) 762a6ba39aSWolfgang Grandegger { 772a6ba39aSWolfgang Grandegger writeb(val, priv->reg_base + reg); 782a6ba39aSWolfgang Grandegger } 792a6ba39aSWolfgang Grandegger 802a6ba39aSWolfgang Grandegger static u8 sja1000_isa_port_read_reg(const struct sja1000_priv *priv, int reg) 812a6ba39aSWolfgang Grandegger { 822a6ba39aSWolfgang Grandegger return inb((unsigned long)priv->reg_base + reg); 832a6ba39aSWolfgang Grandegger } 842a6ba39aSWolfgang Grandegger 852a6ba39aSWolfgang Grandegger static void sja1000_isa_port_write_reg(const struct sja1000_priv *priv, 862a6ba39aSWolfgang Grandegger int reg, u8 val) 872a6ba39aSWolfgang Grandegger { 882a6ba39aSWolfgang Grandegger outb(val, (unsigned long)priv->reg_base + reg); 892a6ba39aSWolfgang Grandegger } 902a6ba39aSWolfgang Grandegger 912a6ba39aSWolfgang Grandegger static u8 sja1000_isa_port_read_reg_indirect(const struct sja1000_priv *priv, 922a6ba39aSWolfgang Grandegger int reg) 932a6ba39aSWolfgang Grandegger { 94a9edcdedSOliver Hartkopp unsigned long flags, base = (unsigned long)priv->reg_base; 95a9edcdedSOliver Hartkopp u8 readval; 962a6ba39aSWolfgang Grandegger 97a9edcdedSOliver Hartkopp spin_lock_irqsave(&indirect_lock[priv->dev->dev_id], flags); 982a6ba39aSWolfgang Grandegger outb(reg, base); 99a9edcdedSOliver Hartkopp readval = inb(base + 1); 100a9edcdedSOliver Hartkopp spin_unlock_irqrestore(&indirect_lock[priv->dev->dev_id], flags); 101a9edcdedSOliver Hartkopp 102a9edcdedSOliver Hartkopp return readval; 1032a6ba39aSWolfgang Grandegger } 1042a6ba39aSWolfgang Grandegger 1052a6ba39aSWolfgang Grandegger static void sja1000_isa_port_write_reg_indirect(const struct sja1000_priv *priv, 1062a6ba39aSWolfgang Grandegger int reg, u8 val) 1072a6ba39aSWolfgang Grandegger { 108a9edcdedSOliver Hartkopp unsigned long flags, base = (unsigned long)priv->reg_base; 1092a6ba39aSWolfgang Grandegger 110a9edcdedSOliver Hartkopp spin_lock_irqsave(&indirect_lock[priv->dev->dev_id], flags); 1112a6ba39aSWolfgang Grandegger outb(reg, base); 1122a6ba39aSWolfgang Grandegger outb(val, base + 1); 113a9edcdedSOliver Hartkopp spin_unlock_irqrestore(&indirect_lock[priv->dev->dev_id], flags); 1142a6ba39aSWolfgang Grandegger } 1152a6ba39aSWolfgang Grandegger 1163c8ac0f2SBill Pemberton static int sja1000_isa_probe(struct platform_device *pdev) 1172a6ba39aSWolfgang Grandegger { 1182a6ba39aSWolfgang Grandegger struct net_device *dev; 1192a6ba39aSWolfgang Grandegger struct sja1000_priv *priv; 1202a6ba39aSWolfgang Grandegger void __iomem *base = NULL; 1212a6ba39aSWolfgang Grandegger int iosize = SJA1000_IOSIZE; 1221839a6c6SWolfgang Grandegger int idx = pdev->id; 1232a6ba39aSWolfgang Grandegger int err; 1242a6ba39aSWolfgang Grandegger 1251839a6c6SWolfgang Grandegger dev_dbg(&pdev->dev, "probing idx=%d: port=%#lx, mem=%#lx, irq=%d\n", 1261839a6c6SWolfgang Grandegger idx, port[idx], mem[idx], irq[idx]); 1271839a6c6SWolfgang Grandegger 1282a6ba39aSWolfgang Grandegger if (mem[idx]) { 1292a6ba39aSWolfgang Grandegger if (!request_mem_region(mem[idx], iosize, DRV_NAME)) { 1302a6ba39aSWolfgang Grandegger err = -EBUSY; 1312a6ba39aSWolfgang Grandegger goto exit; 1322a6ba39aSWolfgang Grandegger } 133*4bdc0d67SChristoph Hellwig base = ioremap(mem[idx], iosize); 1342a6ba39aSWolfgang Grandegger if (!base) { 1352a6ba39aSWolfgang Grandegger err = -ENOMEM; 1362a6ba39aSWolfgang Grandegger goto exit_release; 1372a6ba39aSWolfgang Grandegger } 1382a6ba39aSWolfgang Grandegger } else { 1392a6ba39aSWolfgang Grandegger if (indirect[idx] > 0 || 1402a6ba39aSWolfgang Grandegger (indirect[idx] == -1 && indirect[0] > 0)) 1412a6ba39aSWolfgang Grandegger iosize = SJA1000_IOSIZE_INDIRECT; 1422a6ba39aSWolfgang Grandegger if (!request_region(port[idx], iosize, DRV_NAME)) { 1432a6ba39aSWolfgang Grandegger err = -EBUSY; 1442a6ba39aSWolfgang Grandegger goto exit; 1452a6ba39aSWolfgang Grandegger } 1462a6ba39aSWolfgang Grandegger } 1472a6ba39aSWolfgang Grandegger 1482a6ba39aSWolfgang Grandegger dev = alloc_sja1000dev(0); 1492a6ba39aSWolfgang Grandegger if (!dev) { 1502a6ba39aSWolfgang Grandegger err = -ENOMEM; 1512a6ba39aSWolfgang Grandegger goto exit_unmap; 1522a6ba39aSWolfgang Grandegger } 1532a6ba39aSWolfgang Grandegger priv = netdev_priv(dev); 1542a6ba39aSWolfgang Grandegger 1552a6ba39aSWolfgang Grandegger dev->irq = irq[idx]; 1562a6ba39aSWolfgang Grandegger priv->irq_flags = IRQF_SHARED; 1572a6ba39aSWolfgang Grandegger if (mem[idx]) { 1582a6ba39aSWolfgang Grandegger priv->reg_base = base; 1592a6ba39aSWolfgang Grandegger dev->base_addr = mem[idx]; 1602a6ba39aSWolfgang Grandegger priv->read_reg = sja1000_isa_mem_read_reg; 1612a6ba39aSWolfgang Grandegger priv->write_reg = sja1000_isa_mem_write_reg; 1622a6ba39aSWolfgang Grandegger } else { 1632a6ba39aSWolfgang Grandegger priv->reg_base = (void __iomem *)port[idx]; 1642a6ba39aSWolfgang Grandegger dev->base_addr = port[idx]; 1652a6ba39aSWolfgang Grandegger 1662a6ba39aSWolfgang Grandegger if (iosize == SJA1000_IOSIZE_INDIRECT) { 1672a6ba39aSWolfgang Grandegger priv->read_reg = sja1000_isa_port_read_reg_indirect; 1682a6ba39aSWolfgang Grandegger priv->write_reg = sja1000_isa_port_write_reg_indirect; 169a9edcdedSOliver Hartkopp spin_lock_init(&indirect_lock[idx]); 1702a6ba39aSWolfgang Grandegger } else { 1712a6ba39aSWolfgang Grandegger priv->read_reg = sja1000_isa_port_read_reg; 1722a6ba39aSWolfgang Grandegger priv->write_reg = sja1000_isa_port_write_reg; 1732a6ba39aSWolfgang Grandegger } 1742a6ba39aSWolfgang Grandegger } 1752a6ba39aSWolfgang Grandegger 1762a6ba39aSWolfgang Grandegger if (clk[idx]) 1772a6ba39aSWolfgang Grandegger priv->can.clock.freq = clk[idx] / 2; 1782a6ba39aSWolfgang Grandegger else if (clk[0]) 1792a6ba39aSWolfgang Grandegger priv->can.clock.freq = clk[0] / 2; 1802a6ba39aSWolfgang Grandegger else 1812a6ba39aSWolfgang Grandegger priv->can.clock.freq = CLK_DEFAULT / 2; 1822a6ba39aSWolfgang Grandegger 183115d2a3dSWolfgang Grandegger if (ocr[idx] != 0xff) 184115d2a3dSWolfgang Grandegger priv->ocr = ocr[idx]; 185115d2a3dSWolfgang Grandegger else if (ocr[0] != 0xff) 186115d2a3dSWolfgang Grandegger priv->ocr = ocr[0]; 1872a6ba39aSWolfgang Grandegger else 1882a6ba39aSWolfgang Grandegger priv->ocr = OCR_DEFAULT; 1892a6ba39aSWolfgang Grandegger 190115d2a3dSWolfgang Grandegger if (cdr[idx] != 0xff) 191115d2a3dSWolfgang Grandegger priv->cdr = cdr[idx]; 192115d2a3dSWolfgang Grandegger else if (cdr[0] != 0xff) 193115d2a3dSWolfgang Grandegger priv->cdr = cdr[0]; 1942a6ba39aSWolfgang Grandegger else 1952a6ba39aSWolfgang Grandegger priv->cdr = CDR_DEFAULT; 1962a6ba39aSWolfgang Grandegger 19700e4bbc8SJingoo Han platform_set_drvdata(pdev, dev); 1981839a6c6SWolfgang Grandegger SET_NETDEV_DEV(dev, &pdev->dev); 199a9edcdedSOliver Hartkopp dev->dev_id = idx; 2002a6ba39aSWolfgang Grandegger 2012a6ba39aSWolfgang Grandegger err = register_sja1000dev(dev); 2022a6ba39aSWolfgang Grandegger if (err) { 2031839a6c6SWolfgang Grandegger dev_err(&pdev->dev, "registering %s failed (err=%d)\n", 2042a6ba39aSWolfgang Grandegger DRV_NAME, err); 2052a6ba39aSWolfgang Grandegger goto exit_unmap; 2062a6ba39aSWolfgang Grandegger } 2072a6ba39aSWolfgang Grandegger 2081839a6c6SWolfgang Grandegger dev_info(&pdev->dev, "%s device registered (reg_base=0x%p, irq=%d)\n", 2092a6ba39aSWolfgang Grandegger DRV_NAME, priv->reg_base, dev->irq); 2102a6ba39aSWolfgang Grandegger return 0; 2112a6ba39aSWolfgang Grandegger 2122a6ba39aSWolfgang Grandegger exit_unmap: 2132a6ba39aSWolfgang Grandegger if (mem[idx]) 2142a6ba39aSWolfgang Grandegger iounmap(base); 2152a6ba39aSWolfgang Grandegger exit_release: 2162a6ba39aSWolfgang Grandegger if (mem[idx]) 2172a6ba39aSWolfgang Grandegger release_mem_region(mem[idx], iosize); 2182a6ba39aSWolfgang Grandegger else 2192a6ba39aSWolfgang Grandegger release_region(port[idx], iosize); 2202a6ba39aSWolfgang Grandegger exit: 2212a6ba39aSWolfgang Grandegger return err; 2222a6ba39aSWolfgang Grandegger } 2232a6ba39aSWolfgang Grandegger 2243c8ac0f2SBill Pemberton static int sja1000_isa_remove(struct platform_device *pdev) 2252a6ba39aSWolfgang Grandegger { 22600e4bbc8SJingoo Han struct net_device *dev = platform_get_drvdata(pdev); 2272a6ba39aSWolfgang Grandegger struct sja1000_priv *priv = netdev_priv(dev); 2281839a6c6SWolfgang Grandegger int idx = pdev->id; 2292a6ba39aSWolfgang Grandegger 2302a6ba39aSWolfgang Grandegger unregister_sja1000dev(dev); 2312a6ba39aSWolfgang Grandegger 2322a6ba39aSWolfgang Grandegger if (mem[idx]) { 2332a6ba39aSWolfgang Grandegger iounmap(priv->reg_base); 2342a6ba39aSWolfgang Grandegger release_mem_region(mem[idx], SJA1000_IOSIZE); 2352a6ba39aSWolfgang Grandegger } else { 2362a6ba39aSWolfgang Grandegger if (priv->read_reg == sja1000_isa_port_read_reg_indirect) 2372a6ba39aSWolfgang Grandegger release_region(port[idx], SJA1000_IOSIZE_INDIRECT); 2382a6ba39aSWolfgang Grandegger else 2392a6ba39aSWolfgang Grandegger release_region(port[idx], SJA1000_IOSIZE); 2402a6ba39aSWolfgang Grandegger } 2412a6ba39aSWolfgang Grandegger free_sja1000dev(dev); 2422a6ba39aSWolfgang Grandegger 2432a6ba39aSWolfgang Grandegger return 0; 2442a6ba39aSWolfgang Grandegger } 2452a6ba39aSWolfgang Grandegger 2461839a6c6SWolfgang Grandegger static struct platform_driver sja1000_isa_driver = { 2472a6ba39aSWolfgang Grandegger .probe = sja1000_isa_probe, 2483c8ac0f2SBill Pemberton .remove = sja1000_isa_remove, 2492a6ba39aSWolfgang Grandegger .driver = { 2502a6ba39aSWolfgang Grandegger .name = DRV_NAME, 2512a6ba39aSWolfgang Grandegger }, 2522a6ba39aSWolfgang Grandegger }; 2532a6ba39aSWolfgang Grandegger 2542a6ba39aSWolfgang Grandegger static int __init sja1000_isa_init(void) 2552a6ba39aSWolfgang Grandegger { 2561839a6c6SWolfgang Grandegger int idx, err; 2572a6ba39aSWolfgang Grandegger 2581839a6c6SWolfgang Grandegger for (idx = 0; idx < MAXDEV; idx++) { 2591839a6c6SWolfgang Grandegger if ((port[idx] || mem[idx]) && irq[idx]) { 2601839a6c6SWolfgang Grandegger sja1000_isa_devs[idx] = 2611839a6c6SWolfgang Grandegger platform_device_alloc(DRV_NAME, idx); 2621839a6c6SWolfgang Grandegger if (!sja1000_isa_devs[idx]) { 2631839a6c6SWolfgang Grandegger err = -ENOMEM; 2641839a6c6SWolfgang Grandegger goto exit_free_devices; 2651839a6c6SWolfgang Grandegger } 2661839a6c6SWolfgang Grandegger err = platform_device_add(sja1000_isa_devs[idx]); 2671839a6c6SWolfgang Grandegger if (err) { 2681839a6c6SWolfgang Grandegger platform_device_put(sja1000_isa_devs[idx]); 2691839a6c6SWolfgang Grandegger goto exit_free_devices; 2701839a6c6SWolfgang Grandegger } 2711839a6c6SWolfgang Grandegger pr_debug("%s: platform device %d: port=%#lx, mem=%#lx, " 2721839a6c6SWolfgang Grandegger "irq=%d\n", 2731839a6c6SWolfgang Grandegger DRV_NAME, idx, port[idx], mem[idx], irq[idx]); 2741839a6c6SWolfgang Grandegger } else if (idx == 0 || port[idx] || mem[idx]) { 2751839a6c6SWolfgang Grandegger pr_err("%s: insufficient parameters supplied\n", 2761839a6c6SWolfgang Grandegger DRV_NAME); 2771839a6c6SWolfgang Grandegger err = -EINVAL; 2781839a6c6SWolfgang Grandegger goto exit_free_devices; 2791839a6c6SWolfgang Grandegger } 2801839a6c6SWolfgang Grandegger } 2811839a6c6SWolfgang Grandegger 2821839a6c6SWolfgang Grandegger err = platform_driver_register(&sja1000_isa_driver); 2831839a6c6SWolfgang Grandegger if (err) 2841839a6c6SWolfgang Grandegger goto exit_free_devices; 2851839a6c6SWolfgang Grandegger 2861839a6c6SWolfgang Grandegger pr_info("Legacy %s driver for max. %d devices registered\n", 2872a6ba39aSWolfgang Grandegger DRV_NAME, MAXDEV); 2881839a6c6SWolfgang Grandegger 2891839a6c6SWolfgang Grandegger return 0; 2901839a6c6SWolfgang Grandegger 2911839a6c6SWolfgang Grandegger exit_free_devices: 2921839a6c6SWolfgang Grandegger while (--idx >= 0) { 2931839a6c6SWolfgang Grandegger if (sja1000_isa_devs[idx]) 2941839a6c6SWolfgang Grandegger platform_device_unregister(sja1000_isa_devs[idx]); 2951839a6c6SWolfgang Grandegger } 2961839a6c6SWolfgang Grandegger 2972a6ba39aSWolfgang Grandegger return err; 2982a6ba39aSWolfgang Grandegger } 2992a6ba39aSWolfgang Grandegger 3002a6ba39aSWolfgang Grandegger static void __exit sja1000_isa_exit(void) 3012a6ba39aSWolfgang Grandegger { 3021839a6c6SWolfgang Grandegger int idx; 3031839a6c6SWolfgang Grandegger 3041839a6c6SWolfgang Grandegger platform_driver_unregister(&sja1000_isa_driver); 3051839a6c6SWolfgang Grandegger for (idx = 0; idx < MAXDEV; idx++) { 3061839a6c6SWolfgang Grandegger if (sja1000_isa_devs[idx]) 3071839a6c6SWolfgang Grandegger platform_device_unregister(sja1000_isa_devs[idx]); 3081839a6c6SWolfgang Grandegger } 3092a6ba39aSWolfgang Grandegger } 3102a6ba39aSWolfgang Grandegger 3112a6ba39aSWolfgang Grandegger module_init(sja1000_isa_init); 3122a6ba39aSWolfgang Grandegger module_exit(sja1000_isa_exit); 313