1*c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* ------------------------------------------------------------------------- */ 31da177e4SLinus Torvalds /* i2c-elektor.c i2c-hw access for PCF8584 style isa bus adaptes */ 41da177e4SLinus Torvalds /* ------------------------------------------------------------------------- */ 51da177e4SLinus Torvalds /* Copyright (C) 1995-97 Simon G. Vogl 61da177e4SLinus Torvalds 1998-99 Hans Berglund 71da177e4SLinus Torvalds 8*c942fddfSThomas Gleixner */ 91da177e4SLinus Torvalds /* ------------------------------------------------------------------------- */ 101da177e4SLinus Torvalds 1196de0e25SJan Engelhardt /* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even 121da177e4SLinus Torvalds Frodo Looijaard <frodol@dds.nl> */ 131da177e4SLinus Torvalds 1425985edcSLucas De Marchi /* Partially rewriten by Oleg I. Vdovikin for mmapped support of 151da177e4SLinus Torvalds for Alpha Processor Inc. UP-2000(+) boards */ 161da177e4SLinus Torvalds 171da177e4SLinus Torvalds #include <linux/kernel.h> 181da177e4SLinus Torvalds #include <linux/ioport.h> 191da177e4SLinus Torvalds #include <linux/module.h> 201da177e4SLinus Torvalds #include <linux/delay.h> 211da177e4SLinus Torvalds #include <linux/init.h> 221da177e4SLinus Torvalds #include <linux/interrupt.h> 231da177e4SLinus Torvalds #include <linux/pci.h> 241da177e4SLinus Torvalds #include <linux/wait.h> 251da177e4SLinus Torvalds 264a5d3030SJean Delvare #include <linux/isa.h> 271da177e4SLinus Torvalds #include <linux/i2c.h> 281da177e4SLinus Torvalds #include <linux/i2c-algo-pcf.h> 2921782180SH Hartley Sweeten #include <linux/io.h> 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds #include <asm/irq.h> 321da177e4SLinus Torvalds 331da177e4SLinus Torvalds #include "../algos/i2c-algo-pcf.h" 341da177e4SLinus Torvalds 351da177e4SLinus Torvalds #define DEFAULT_BASE 0x330 361da177e4SLinus Torvalds 371da177e4SLinus Torvalds static int base; 383634ff6aSStig Telfer static u8 __iomem *base_iomem; 393634ff6aSStig Telfer 401da177e4SLinus Torvalds static int irq; 411da177e4SLinus Torvalds static int clock = 0x1c; 421da177e4SLinus Torvalds static int own = 0x55; 431da177e4SLinus Torvalds static int mmapped; 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds /* vdovikin: removed static struct i2c_pcf_isa gpi; code - 461da177e4SLinus Torvalds this module in real supports only one device, due to missing arguments 471da177e4SLinus Torvalds in some functions, called from the algo-pcf module. Sometimes it's 481da177e4SLinus Torvalds need to be rewriten - but for now just remove this for simpler reading */ 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds static wait_queue_head_t pcf_wait; 511da177e4SLinus Torvalds static int pcf_pending; 521da177e4SLinus Torvalds static spinlock_t lock; 531da177e4SLinus Torvalds 54fe3d6a99SStig Telfer static struct i2c_adapter pcf_isa_ops; 55fe3d6a99SStig Telfer 561da177e4SLinus Torvalds /* ----- local functions ---------------------------------------------- */ 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds static void pcf_isa_setbyte(void *data, int ctl, int val) 591da177e4SLinus Torvalds { 603634ff6aSStig Telfer u8 __iomem *address = ctl ? (base_iomem + 1) : base_iomem; 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds /* enable irq if any specified for serial operation */ 631da177e4SLinus Torvalds if (ctl && irq && (val & I2C_PCF_ESO)) { 641da177e4SLinus Torvalds val |= I2C_PCF_ENI; 651da177e4SLinus Torvalds } 661da177e4SLinus Torvalds 67fe3d6a99SStig Telfer pr_debug("%s: Write %p 0x%02X\n", pcf_isa_ops.name, address, val); 683634ff6aSStig Telfer iowrite8(val, address); 693634ff6aSStig Telfer #ifdef __alpha__ 703634ff6aSStig Telfer /* API UP2000 needs some hardware fudging to make the write stick */ 713634ff6aSStig Telfer iowrite8(val, address); 723634ff6aSStig Telfer #endif 731da177e4SLinus Torvalds } 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds static int pcf_isa_getbyte(void *data, int ctl) 761da177e4SLinus Torvalds { 773634ff6aSStig Telfer u8 __iomem *address = ctl ? (base_iomem + 1) : base_iomem; 783634ff6aSStig Telfer int val = ioread8(address); 791da177e4SLinus Torvalds 80fe3d6a99SStig Telfer pr_debug("%s: Read %p 0x%02X\n", pcf_isa_ops.name, address, val); 811da177e4SLinus Torvalds return (val); 821da177e4SLinus Torvalds } 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds static int pcf_isa_getown(void *data) 851da177e4SLinus Torvalds { 861da177e4SLinus Torvalds return (own); 871da177e4SLinus Torvalds } 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds static int pcf_isa_getclock(void *data) 911da177e4SLinus Torvalds { 921da177e4SLinus Torvalds return (clock); 931da177e4SLinus Torvalds } 941da177e4SLinus Torvalds 9508e5338dSDavid Miller static void pcf_isa_waitforpin(void *data) 9608e5338dSDavid Miller { 971da177e4SLinus Torvalds DEFINE_WAIT(wait); 981da177e4SLinus Torvalds int timeout = 2; 991da177e4SLinus Torvalds unsigned long flags; 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds if (irq > 0) { 1021da177e4SLinus Torvalds spin_lock_irqsave(&lock, flags); 1031da177e4SLinus Torvalds if (pcf_pending == 0) { 1041da177e4SLinus Torvalds spin_unlock_irqrestore(&lock, flags); 1051da177e4SLinus Torvalds prepare_to_wait(&pcf_wait, &wait, TASK_INTERRUPTIBLE); 1061da177e4SLinus Torvalds if (schedule_timeout(timeout*HZ)) { 1071da177e4SLinus Torvalds spin_lock_irqsave(&lock, flags); 1081da177e4SLinus Torvalds if (pcf_pending == 1) { 1091da177e4SLinus Torvalds pcf_pending = 0; 1101da177e4SLinus Torvalds } 1111da177e4SLinus Torvalds spin_unlock_irqrestore(&lock, flags); 1121da177e4SLinus Torvalds } 1131da177e4SLinus Torvalds finish_wait(&pcf_wait, &wait); 1141da177e4SLinus Torvalds } else { 1151da177e4SLinus Torvalds pcf_pending = 0; 1161da177e4SLinus Torvalds spin_unlock_irqrestore(&lock, flags); 1171da177e4SLinus Torvalds } 1181da177e4SLinus Torvalds } else { 1191da177e4SLinus Torvalds udelay(100); 1201da177e4SLinus Torvalds } 1211da177e4SLinus Torvalds } 1221da177e4SLinus Torvalds 1231da177e4SLinus Torvalds 1247d12e780SDavid Howells static irqreturn_t pcf_isa_handler(int this_irq, void *dev_id) { 1251da177e4SLinus Torvalds spin_lock(&lock); 1261da177e4SLinus Torvalds pcf_pending = 1; 1271da177e4SLinus Torvalds spin_unlock(&lock); 1281da177e4SLinus Torvalds wake_up_interruptible(&pcf_wait); 1291da177e4SLinus Torvalds return IRQ_HANDLED; 1301da177e4SLinus Torvalds } 1311da177e4SLinus Torvalds 1321da177e4SLinus Torvalds 1331da177e4SLinus Torvalds static int pcf_isa_init(void) 1341da177e4SLinus Torvalds { 1351da177e4SLinus Torvalds spin_lock_init(&lock); 1361da177e4SLinus Torvalds if (!mmapped) { 137fe3d6a99SStig Telfer if (!request_region(base, 2, pcf_isa_ops.name)) { 138fe3d6a99SStig Telfer printk(KERN_ERR "%s: requested I/O region (%#x:2) is " 139fe3d6a99SStig Telfer "in use\n", pcf_isa_ops.name, base); 1401da177e4SLinus Torvalds return -ENODEV; 1411da177e4SLinus Torvalds } 1423634ff6aSStig Telfer base_iomem = ioport_map(base, 2); 1433634ff6aSStig Telfer if (!base_iomem) { 144fe3d6a99SStig Telfer printk(KERN_ERR "%s: remap of I/O region %#x failed\n", 145fe3d6a99SStig Telfer pcf_isa_ops.name, base); 1463634ff6aSStig Telfer release_region(base, 2); 1473634ff6aSStig Telfer return -ENODEV; 1481da177e4SLinus Torvalds } 1493634ff6aSStig Telfer } else { 150fe3d6a99SStig Telfer if (!request_mem_region(base, 2, pcf_isa_ops.name)) { 151fe3d6a99SStig Telfer printk(KERN_ERR "%s: requested memory region (%#x:2) " 152fe3d6a99SStig Telfer "is in use\n", pcf_isa_ops.name, base); 1533634ff6aSStig Telfer return -ENODEV; 1543634ff6aSStig Telfer } 1553634ff6aSStig Telfer base_iomem = ioremap(base, 2); 1563634ff6aSStig Telfer if (base_iomem == NULL) { 157fe3d6a99SStig Telfer printk(KERN_ERR "%s: remap of memory region %#x " 158fe3d6a99SStig Telfer "failed\n", pcf_isa_ops.name, base); 1593634ff6aSStig Telfer release_mem_region(base, 2); 1603634ff6aSStig Telfer return -ENODEV; 1613634ff6aSStig Telfer } 1623634ff6aSStig Telfer } 163fe3d6a99SStig Telfer pr_debug("%s: registers %#x remapped to %p\n", pcf_isa_ops.name, base, 1643634ff6aSStig Telfer base_iomem); 1653634ff6aSStig Telfer 1661da177e4SLinus Torvalds if (irq > 0) { 167fe3d6a99SStig Telfer if (request_irq(irq, pcf_isa_handler, 0, pcf_isa_ops.name, 168fe3d6a99SStig Telfer NULL) < 0) { 169fe3d6a99SStig Telfer printk(KERN_ERR "%s: Request irq%d failed\n", 170fe3d6a99SStig Telfer pcf_isa_ops.name, irq); 1711da177e4SLinus Torvalds irq = 0; 1721da177e4SLinus Torvalds } else 1731da177e4SLinus Torvalds enable_irq(irq); 1741da177e4SLinus Torvalds } 1751da177e4SLinus Torvalds return 0; 1761da177e4SLinus Torvalds } 1771da177e4SLinus Torvalds 1781da177e4SLinus Torvalds /* ------------------------------------------------------------------------ 1791da177e4SLinus Torvalds * Encapsulate the above functions in the correct operations structure. 1801da177e4SLinus Torvalds * This is only done when more than one hardware adapter is supported. 1811da177e4SLinus Torvalds */ 1821da177e4SLinus Torvalds static struct i2c_algo_pcf_data pcf_isa_data = { 1831da177e4SLinus Torvalds .setpcf = pcf_isa_setbyte, 1841da177e4SLinus Torvalds .getpcf = pcf_isa_getbyte, 1851da177e4SLinus Torvalds .getown = pcf_isa_getown, 1861da177e4SLinus Torvalds .getclock = pcf_isa_getclock, 1871da177e4SLinus Torvalds .waitforpin = pcf_isa_waitforpin, 1881da177e4SLinus Torvalds }; 1891da177e4SLinus Torvalds 1901da177e4SLinus Torvalds static struct i2c_adapter pcf_isa_ops = { 1911da177e4SLinus Torvalds .owner = THIS_MODULE, 1923401b2ffSJean Delvare .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, 1931da177e4SLinus Torvalds .algo_data = &pcf_isa_data, 194fe3d6a99SStig Telfer .name = "i2c-elektor", 1951da177e4SLinus Torvalds }; 1961da177e4SLinus Torvalds 1970b255e92SBill Pemberton static int elektor_match(struct device *dev, unsigned int id) 1981da177e4SLinus Torvalds { 1991da177e4SLinus Torvalds #ifdef __alpha__ 2001da177e4SLinus Torvalds /* check to see we have memory mapped PCF8584 connected to the 2011da177e4SLinus Torvalds Cypress cy82c693 PCI-ISA bridge as on UP2000 board */ 2021da177e4SLinus Torvalds if (base == 0) { 2031da177e4SLinus Torvalds struct pci_dev *cy693_dev; 2041da177e4SLinus Torvalds 2051da177e4SLinus Torvalds cy693_dev = pci_get_device(PCI_VENDOR_ID_CONTAQ, 2061da177e4SLinus Torvalds PCI_DEVICE_ID_CONTAQ_82C693, NULL); 2071da177e4SLinus Torvalds if (cy693_dev) { 2083634ff6aSStig Telfer unsigned char config; 2091da177e4SLinus Torvalds /* yeap, we've found cypress, let's check config */ 2101da177e4SLinus Torvalds if (!pci_read_config_byte(cy693_dev, 0x47, &config)) { 2111da177e4SLinus Torvalds 2124a5d3030SJean Delvare dev_dbg(dev, "found cy82c693, config " 2134a5d3030SJean Delvare "register 0x47 = 0x%02x\n", config); 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds /* UP2000 board has this register set to 0xe1, 2161da177e4SLinus Torvalds but the most significant bit as seems can be 2171da177e4SLinus Torvalds reset during the proper initialisation 2181da177e4SLinus Torvalds sequence if guys from API decides to do that 2191da177e4SLinus Torvalds (so, we can even enable Tsunami Pchip 2201da177e4SLinus Torvalds window for the upper 1 Gb) */ 2211da177e4SLinus Torvalds 2221da177e4SLinus Torvalds /* so just check for ROMCS at 0xe0000, 2231da177e4SLinus Torvalds ROMCS enabled for writes 2241da177e4SLinus Torvalds and external XD Bus buffer in use. */ 2251da177e4SLinus Torvalds if ((config & 0x7f) == 0x61) { 2261da177e4SLinus Torvalds /* seems to be UP2000 like board */ 2271da177e4SLinus Torvalds base = 0xe0000; 2283634ff6aSStig Telfer mmapped = 1; 2291da177e4SLinus Torvalds /* UP2000 drives ISA with 2301da177e4SLinus Torvalds 8.25 MHz (PCI/4) clock 2311da177e4SLinus Torvalds (this can be read from cypress) */ 2321da177e4SLinus Torvalds clock = I2C_PCF_CLK | I2C_PCF_TRNS90; 2334a5d3030SJean Delvare dev_info(dev, "found API UP2000 like " 234fe3d6a99SStig Telfer "board, will probe PCF8584 " 2354a5d3030SJean Delvare "later\n"); 2361da177e4SLinus Torvalds } 2371da177e4SLinus Torvalds } 2381da177e4SLinus Torvalds pci_dev_put(cy693_dev); 2391da177e4SLinus Torvalds } 2401da177e4SLinus Torvalds } 2411da177e4SLinus Torvalds #endif 2421da177e4SLinus Torvalds 2431da177e4SLinus Torvalds /* sanity checks for mmapped I/O */ 2441da177e4SLinus Torvalds if (mmapped && base < 0xc8000) { 2454a5d3030SJean Delvare dev_err(dev, "incorrect base address (%#x) specified " 2464a5d3030SJean Delvare "for mmapped I/O\n", base); 2474a5d3030SJean Delvare return 0; 2481da177e4SLinus Torvalds } 2491da177e4SLinus Torvalds 2501da177e4SLinus Torvalds if (base == 0) { 2511da177e4SLinus Torvalds base = DEFAULT_BASE; 2521da177e4SLinus Torvalds } 2534a5d3030SJean Delvare return 1; 2544a5d3030SJean Delvare } 2551da177e4SLinus Torvalds 2560b255e92SBill Pemberton static int elektor_probe(struct device *dev, unsigned int id) 2574a5d3030SJean Delvare { 2581da177e4SLinus Torvalds init_waitqueue_head(&pcf_wait); 2591da177e4SLinus Torvalds if (pcf_isa_init()) 2601da177e4SLinus Torvalds return -ENODEV; 2614a5d3030SJean Delvare pcf_isa_ops.dev.parent = dev; 2621da177e4SLinus Torvalds if (i2c_pcf_add_bus(&pcf_isa_ops) < 0) 2631da177e4SLinus Torvalds goto fail; 2641da177e4SLinus Torvalds 2654a5d3030SJean Delvare dev_info(dev, "found device at %#x\n", base); 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds return 0; 2681da177e4SLinus Torvalds 2691da177e4SLinus Torvalds fail: 2701da177e4SLinus Torvalds if (irq > 0) { 2711da177e4SLinus Torvalds disable_irq(irq); 2721da177e4SLinus Torvalds free_irq(irq, NULL); 2731da177e4SLinus Torvalds } 2741da177e4SLinus Torvalds 2753634ff6aSStig Telfer if (!mmapped) { 2763634ff6aSStig Telfer ioport_unmap(base_iomem); 2771da177e4SLinus Torvalds release_region(base, 2); 2783634ff6aSStig Telfer } else { 2793634ff6aSStig Telfer iounmap(base_iomem); 2803634ff6aSStig Telfer release_mem_region(base, 2); 2813634ff6aSStig Telfer } 2821da177e4SLinus Torvalds return -ENODEV; 2831da177e4SLinus Torvalds } 2841da177e4SLinus Torvalds 2850b255e92SBill Pemberton static int elektor_remove(struct device *dev, unsigned int id) 2861da177e4SLinus Torvalds { 2873269711bSJean Delvare i2c_del_adapter(&pcf_isa_ops); 2881da177e4SLinus Torvalds 2891da177e4SLinus Torvalds if (irq > 0) { 2901da177e4SLinus Torvalds disable_irq(irq); 2911da177e4SLinus Torvalds free_irq(irq, NULL); 2921da177e4SLinus Torvalds } 2931da177e4SLinus Torvalds 2943634ff6aSStig Telfer if (!mmapped) { 2953634ff6aSStig Telfer ioport_unmap(base_iomem); 2961da177e4SLinus Torvalds release_region(base, 2); 2973634ff6aSStig Telfer } else { 2983634ff6aSStig Telfer iounmap(base_iomem); 2993634ff6aSStig Telfer release_mem_region(base, 2); 3003634ff6aSStig Telfer } 3014a5d3030SJean Delvare 3024a5d3030SJean Delvare return 0; 3034a5d3030SJean Delvare } 3044a5d3030SJean Delvare 3054a5d3030SJean Delvare static struct isa_driver i2c_elektor_driver = { 3064a5d3030SJean Delvare .match = elektor_match, 3074a5d3030SJean Delvare .probe = elektor_probe, 3080b255e92SBill Pemberton .remove = elektor_remove, 3094a5d3030SJean Delvare .driver = { 3104a5d3030SJean Delvare .owner = THIS_MODULE, 3114a5d3030SJean Delvare .name = "i2c-elektor", 3124a5d3030SJean Delvare }, 3134a5d3030SJean Delvare }; 3144a5d3030SJean Delvare 3151da177e4SLinus Torvalds MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>"); 3161da177e4SLinus Torvalds MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter"); 3171da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 3181da177e4SLinus Torvalds 319c78babccSDavid Howells module_param_hw(base, int, ioport_or_iomem, 0); 320c78babccSDavid Howells module_param_hw(irq, int, irq, 0); 3211da177e4SLinus Torvalds module_param(clock, int, 0); 3221da177e4SLinus Torvalds module_param(own, int, 0); 323c78babccSDavid Howells module_param_hw(mmapped, int, other, 0); 3249e55c073SWilliam Breathitt Gray module_isa_driver(i2c_elektor_driver, 1); 325