11da177e4SLinus Torvalds /* ------------------------------------------------------------------------- */ 21da177e4SLinus Torvalds /* i2c-elektor.c i2c-hw access for PCF8584 style isa bus adaptes */ 31da177e4SLinus Torvalds /* ------------------------------------------------------------------------- */ 41da177e4SLinus Torvalds /* Copyright (C) 1995-97 Simon G. Vogl 51da177e4SLinus Torvalds 1998-99 Hans Berglund 61da177e4SLinus Torvalds 71da177e4SLinus Torvalds This program is free software; you can redistribute it and/or modify 81da177e4SLinus Torvalds it under the terms of the GNU General Public License as published by 91da177e4SLinus Torvalds the Free Software Foundation; either version 2 of the License, or 101da177e4SLinus Torvalds (at your option) any later version. 111da177e4SLinus Torvalds 121da177e4SLinus Torvalds This program is distributed in the hope that it will be useful, 131da177e4SLinus Torvalds but WITHOUT ANY WARRANTY; without even the implied warranty of 141da177e4SLinus Torvalds MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 151da177e4SLinus Torvalds GNU General Public License for more details. 161da177e4SLinus Torvalds 171da177e4SLinus Torvalds You should have received a copy of the GNU General Public License 181da177e4SLinus Torvalds along with this program; if not, write to the Free Software 191da177e4SLinus Torvalds Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ 201da177e4SLinus Torvalds /* ------------------------------------------------------------------------- */ 211da177e4SLinus Torvalds 2296de0e25SJan Engelhardt /* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even 231da177e4SLinus Torvalds Frodo Looijaard <frodol@dds.nl> */ 241da177e4SLinus Torvalds 251da177e4SLinus Torvalds /* Partialy rewriten by Oleg I. Vdovikin for mmapped support of 261da177e4SLinus Torvalds for Alpha Processor Inc. UP-2000(+) boards */ 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds #include <linux/kernel.h> 291da177e4SLinus Torvalds #include <linux/ioport.h> 301da177e4SLinus Torvalds #include <linux/module.h> 311da177e4SLinus Torvalds #include <linux/delay.h> 321da177e4SLinus Torvalds #include <linux/slab.h> 331da177e4SLinus Torvalds #include <linux/init.h> 341da177e4SLinus Torvalds #include <linux/interrupt.h> 351da177e4SLinus Torvalds #include <linux/pci.h> 361da177e4SLinus Torvalds #include <linux/wait.h> 371da177e4SLinus Torvalds 384a5d3030SJean Delvare #include <linux/isa.h> 391da177e4SLinus Torvalds #include <linux/i2c.h> 401da177e4SLinus Torvalds #include <linux/i2c-algo-pcf.h> 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds #include <asm/io.h> 431da177e4SLinus Torvalds #include <asm/irq.h> 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds #include "../algos/i2c-algo-pcf.h" 461da177e4SLinus Torvalds 471da177e4SLinus Torvalds #define DEFAULT_BASE 0x330 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds static int base; 503634ff6aSStig Telfer static u8 __iomem *base_iomem; 513634ff6aSStig Telfer 521da177e4SLinus Torvalds static int irq; 531da177e4SLinus Torvalds static int clock = 0x1c; 541da177e4SLinus Torvalds static int own = 0x55; 551da177e4SLinus Torvalds static int mmapped; 561da177e4SLinus Torvalds 571da177e4SLinus Torvalds /* vdovikin: removed static struct i2c_pcf_isa gpi; code - 581da177e4SLinus Torvalds this module in real supports only one device, due to missing arguments 591da177e4SLinus Torvalds in some functions, called from the algo-pcf module. Sometimes it's 601da177e4SLinus Torvalds need to be rewriten - but for now just remove this for simpler reading */ 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds static wait_queue_head_t pcf_wait; 631da177e4SLinus Torvalds static int pcf_pending; 641da177e4SLinus Torvalds static spinlock_t lock; 651da177e4SLinus Torvalds 66fe3d6a99SStig Telfer static struct i2c_adapter pcf_isa_ops; 67fe3d6a99SStig Telfer 681da177e4SLinus Torvalds /* ----- local functions ---------------------------------------------- */ 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds static void pcf_isa_setbyte(void *data, int ctl, int val) 711da177e4SLinus Torvalds { 723634ff6aSStig Telfer u8 __iomem *address = ctl ? (base_iomem + 1) : base_iomem; 731da177e4SLinus Torvalds 741da177e4SLinus Torvalds /* enable irq if any specified for serial operation */ 751da177e4SLinus Torvalds if (ctl && irq && (val & I2C_PCF_ESO)) { 761da177e4SLinus Torvalds val |= I2C_PCF_ENI; 771da177e4SLinus Torvalds } 781da177e4SLinus Torvalds 79fe3d6a99SStig Telfer pr_debug("%s: Write %p 0x%02X\n", pcf_isa_ops.name, address, val); 803634ff6aSStig Telfer iowrite8(val, address); 813634ff6aSStig Telfer #ifdef __alpha__ 823634ff6aSStig Telfer /* API UP2000 needs some hardware fudging to make the write stick */ 833634ff6aSStig Telfer iowrite8(val, address); 843634ff6aSStig Telfer #endif 851da177e4SLinus Torvalds } 861da177e4SLinus Torvalds 871da177e4SLinus Torvalds static int pcf_isa_getbyte(void *data, int ctl) 881da177e4SLinus Torvalds { 893634ff6aSStig Telfer u8 __iomem *address = ctl ? (base_iomem + 1) : base_iomem; 903634ff6aSStig Telfer int val = ioread8(address); 911da177e4SLinus Torvalds 92fe3d6a99SStig Telfer pr_debug("%s: Read %p 0x%02X\n", pcf_isa_ops.name, address, val); 931da177e4SLinus Torvalds return (val); 941da177e4SLinus Torvalds } 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds static int pcf_isa_getown(void *data) 971da177e4SLinus Torvalds { 981da177e4SLinus Torvalds return (own); 991da177e4SLinus Torvalds } 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds 1021da177e4SLinus Torvalds static int pcf_isa_getclock(void *data) 1031da177e4SLinus Torvalds { 1041da177e4SLinus Torvalds return (clock); 1051da177e4SLinus Torvalds } 1061da177e4SLinus Torvalds 107*08e5338dSDavid Miller static void pcf_isa_waitforpin(void *data) 108*08e5338dSDavid Miller { 1091da177e4SLinus Torvalds DEFINE_WAIT(wait); 1101da177e4SLinus Torvalds int timeout = 2; 1111da177e4SLinus Torvalds unsigned long flags; 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds if (irq > 0) { 1141da177e4SLinus Torvalds spin_lock_irqsave(&lock, flags); 1151da177e4SLinus Torvalds if (pcf_pending == 0) { 1161da177e4SLinus Torvalds spin_unlock_irqrestore(&lock, flags); 1171da177e4SLinus Torvalds prepare_to_wait(&pcf_wait, &wait, TASK_INTERRUPTIBLE); 1181da177e4SLinus Torvalds if (schedule_timeout(timeout*HZ)) { 1191da177e4SLinus Torvalds spin_lock_irqsave(&lock, flags); 1201da177e4SLinus Torvalds if (pcf_pending == 1) { 1211da177e4SLinus Torvalds pcf_pending = 0; 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds spin_unlock_irqrestore(&lock, flags); 1241da177e4SLinus Torvalds } 1251da177e4SLinus Torvalds finish_wait(&pcf_wait, &wait); 1261da177e4SLinus Torvalds } else { 1271da177e4SLinus Torvalds pcf_pending = 0; 1281da177e4SLinus Torvalds spin_unlock_irqrestore(&lock, flags); 1291da177e4SLinus Torvalds } 1301da177e4SLinus Torvalds } else { 1311da177e4SLinus Torvalds udelay(100); 1321da177e4SLinus Torvalds } 1331da177e4SLinus Torvalds } 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds 1367d12e780SDavid Howells static irqreturn_t pcf_isa_handler(int this_irq, void *dev_id) { 1371da177e4SLinus Torvalds spin_lock(&lock); 1381da177e4SLinus Torvalds pcf_pending = 1; 1391da177e4SLinus Torvalds spin_unlock(&lock); 1401da177e4SLinus Torvalds wake_up_interruptible(&pcf_wait); 1411da177e4SLinus Torvalds return IRQ_HANDLED; 1421da177e4SLinus Torvalds } 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds 1451da177e4SLinus Torvalds static int pcf_isa_init(void) 1461da177e4SLinus Torvalds { 1471da177e4SLinus Torvalds spin_lock_init(&lock); 1481da177e4SLinus Torvalds if (!mmapped) { 149fe3d6a99SStig Telfer if (!request_region(base, 2, pcf_isa_ops.name)) { 150fe3d6a99SStig Telfer printk(KERN_ERR "%s: requested I/O region (%#x:2) is " 151fe3d6a99SStig Telfer "in use\n", pcf_isa_ops.name, base); 1521da177e4SLinus Torvalds return -ENODEV; 1531da177e4SLinus Torvalds } 1543634ff6aSStig Telfer base_iomem = ioport_map(base, 2); 1553634ff6aSStig Telfer if (!base_iomem) { 156fe3d6a99SStig Telfer printk(KERN_ERR "%s: remap of I/O region %#x failed\n", 157fe3d6a99SStig Telfer pcf_isa_ops.name, base); 1583634ff6aSStig Telfer release_region(base, 2); 1593634ff6aSStig Telfer return -ENODEV; 1601da177e4SLinus Torvalds } 1613634ff6aSStig Telfer } else { 162fe3d6a99SStig Telfer if (!request_mem_region(base, 2, pcf_isa_ops.name)) { 163fe3d6a99SStig Telfer printk(KERN_ERR "%s: requested memory region (%#x:2) " 164fe3d6a99SStig Telfer "is in use\n", pcf_isa_ops.name, base); 1653634ff6aSStig Telfer return -ENODEV; 1663634ff6aSStig Telfer } 1673634ff6aSStig Telfer base_iomem = ioremap(base, 2); 1683634ff6aSStig Telfer if (base_iomem == NULL) { 169fe3d6a99SStig Telfer printk(KERN_ERR "%s: remap of memory region %#x " 170fe3d6a99SStig Telfer "failed\n", pcf_isa_ops.name, base); 1713634ff6aSStig Telfer release_mem_region(base, 2); 1723634ff6aSStig Telfer return -ENODEV; 1733634ff6aSStig Telfer } 1743634ff6aSStig Telfer } 175fe3d6a99SStig Telfer pr_debug("%s: registers %#x remapped to %p\n", pcf_isa_ops.name, base, 1763634ff6aSStig Telfer base_iomem); 1773634ff6aSStig Telfer 1781da177e4SLinus Torvalds if (irq > 0) { 179fe3d6a99SStig Telfer if (request_irq(irq, pcf_isa_handler, 0, pcf_isa_ops.name, 180fe3d6a99SStig Telfer NULL) < 0) { 181fe3d6a99SStig Telfer printk(KERN_ERR "%s: Request irq%d failed\n", 182fe3d6a99SStig Telfer pcf_isa_ops.name, irq); 1831da177e4SLinus Torvalds irq = 0; 1841da177e4SLinus Torvalds } else 1851da177e4SLinus Torvalds enable_irq(irq); 1861da177e4SLinus Torvalds } 1871da177e4SLinus Torvalds return 0; 1881da177e4SLinus Torvalds } 1891da177e4SLinus Torvalds 1901da177e4SLinus Torvalds /* ------------------------------------------------------------------------ 1911da177e4SLinus Torvalds * Encapsulate the above functions in the correct operations structure. 1921da177e4SLinus Torvalds * This is only done when more than one hardware adapter is supported. 1931da177e4SLinus Torvalds */ 1941da177e4SLinus Torvalds static struct i2c_algo_pcf_data pcf_isa_data = { 1951da177e4SLinus Torvalds .setpcf = pcf_isa_setbyte, 1961da177e4SLinus Torvalds .getpcf = pcf_isa_getbyte, 1971da177e4SLinus Torvalds .getown = pcf_isa_getown, 1981da177e4SLinus Torvalds .getclock = pcf_isa_getclock, 1991da177e4SLinus Torvalds .waitforpin = pcf_isa_waitforpin, 2001da177e4SLinus Torvalds }; 2011da177e4SLinus Torvalds 2021da177e4SLinus Torvalds static struct i2c_adapter pcf_isa_ops = { 2031da177e4SLinus Torvalds .owner = THIS_MODULE, 2043401b2ffSJean Delvare .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, 2051da177e4SLinus Torvalds .id = I2C_HW_P_ELEK, 2061da177e4SLinus Torvalds .algo_data = &pcf_isa_data, 207fe3d6a99SStig Telfer .name = "i2c-elektor", 2081da177e4SLinus Torvalds }; 2091da177e4SLinus Torvalds 2104a5d3030SJean Delvare static int __devinit elektor_match(struct device *dev, unsigned int id) 2111da177e4SLinus Torvalds { 2121da177e4SLinus Torvalds #ifdef __alpha__ 2131da177e4SLinus Torvalds /* check to see we have memory mapped PCF8584 connected to the 2141da177e4SLinus Torvalds Cypress cy82c693 PCI-ISA bridge as on UP2000 board */ 2151da177e4SLinus Torvalds if (base == 0) { 2161da177e4SLinus Torvalds struct pci_dev *cy693_dev; 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds cy693_dev = pci_get_device(PCI_VENDOR_ID_CONTAQ, 2191da177e4SLinus Torvalds PCI_DEVICE_ID_CONTAQ_82C693, NULL); 2201da177e4SLinus Torvalds if (cy693_dev) { 2213634ff6aSStig Telfer unsigned char config; 2221da177e4SLinus Torvalds /* yeap, we've found cypress, let's check config */ 2231da177e4SLinus Torvalds if (!pci_read_config_byte(cy693_dev, 0x47, &config)) { 2241da177e4SLinus Torvalds 2254a5d3030SJean Delvare dev_dbg(dev, "found cy82c693, config " 2264a5d3030SJean Delvare "register 0x47 = 0x%02x\n", config); 2271da177e4SLinus Torvalds 2281da177e4SLinus Torvalds /* UP2000 board has this register set to 0xe1, 2291da177e4SLinus Torvalds but the most significant bit as seems can be 2301da177e4SLinus Torvalds reset during the proper initialisation 2311da177e4SLinus Torvalds sequence if guys from API decides to do that 2321da177e4SLinus Torvalds (so, we can even enable Tsunami Pchip 2331da177e4SLinus Torvalds window for the upper 1 Gb) */ 2341da177e4SLinus Torvalds 2351da177e4SLinus Torvalds /* so just check for ROMCS at 0xe0000, 2361da177e4SLinus Torvalds ROMCS enabled for writes 2371da177e4SLinus Torvalds and external XD Bus buffer in use. */ 2381da177e4SLinus Torvalds if ((config & 0x7f) == 0x61) { 2391da177e4SLinus Torvalds /* seems to be UP2000 like board */ 2401da177e4SLinus Torvalds base = 0xe0000; 2413634ff6aSStig Telfer mmapped = 1; 2421da177e4SLinus Torvalds /* UP2000 drives ISA with 2431da177e4SLinus Torvalds 8.25 MHz (PCI/4) clock 2441da177e4SLinus Torvalds (this can be read from cypress) */ 2451da177e4SLinus Torvalds clock = I2C_PCF_CLK | I2C_PCF_TRNS90; 2464a5d3030SJean Delvare dev_info(dev, "found API UP2000 like " 247fe3d6a99SStig Telfer "board, will probe PCF8584 " 2484a5d3030SJean Delvare "later\n"); 2491da177e4SLinus Torvalds } 2501da177e4SLinus Torvalds } 2511da177e4SLinus Torvalds pci_dev_put(cy693_dev); 2521da177e4SLinus Torvalds } 2531da177e4SLinus Torvalds } 2541da177e4SLinus Torvalds #endif 2551da177e4SLinus Torvalds 2561da177e4SLinus Torvalds /* sanity checks for mmapped I/O */ 2571da177e4SLinus Torvalds if (mmapped && base < 0xc8000) { 2584a5d3030SJean Delvare dev_err(dev, "incorrect base address (%#x) specified " 2594a5d3030SJean Delvare "for mmapped I/O\n", base); 2604a5d3030SJean Delvare return 0; 2611da177e4SLinus Torvalds } 2621da177e4SLinus Torvalds 2631da177e4SLinus Torvalds if (base == 0) { 2641da177e4SLinus Torvalds base = DEFAULT_BASE; 2651da177e4SLinus Torvalds } 2664a5d3030SJean Delvare return 1; 2674a5d3030SJean Delvare } 2681da177e4SLinus Torvalds 2694a5d3030SJean Delvare static int __devinit elektor_probe(struct device *dev, unsigned int id) 2704a5d3030SJean Delvare { 2711da177e4SLinus Torvalds init_waitqueue_head(&pcf_wait); 2721da177e4SLinus Torvalds if (pcf_isa_init()) 2731da177e4SLinus Torvalds return -ENODEV; 2744a5d3030SJean Delvare pcf_isa_ops.dev.parent = dev; 2751da177e4SLinus Torvalds if (i2c_pcf_add_bus(&pcf_isa_ops) < 0) 2761da177e4SLinus Torvalds goto fail; 2771da177e4SLinus Torvalds 2784a5d3030SJean Delvare dev_info(dev, "found device at %#x\n", base); 2791da177e4SLinus Torvalds 2801da177e4SLinus Torvalds return 0; 2811da177e4SLinus Torvalds 2821da177e4SLinus Torvalds fail: 2831da177e4SLinus Torvalds if (irq > 0) { 2841da177e4SLinus Torvalds disable_irq(irq); 2851da177e4SLinus Torvalds free_irq(irq, NULL); 2861da177e4SLinus Torvalds } 2871da177e4SLinus Torvalds 2883634ff6aSStig Telfer if (!mmapped) { 2893634ff6aSStig Telfer ioport_unmap(base_iomem); 2901da177e4SLinus Torvalds release_region(base, 2); 2913634ff6aSStig Telfer } else { 2923634ff6aSStig Telfer iounmap(base_iomem); 2933634ff6aSStig Telfer release_mem_region(base, 2); 2943634ff6aSStig Telfer } 2951da177e4SLinus Torvalds return -ENODEV; 2961da177e4SLinus Torvalds } 2971da177e4SLinus Torvalds 2984a5d3030SJean Delvare static int __devexit elektor_remove(struct device *dev, unsigned int id) 2991da177e4SLinus Torvalds { 3003269711bSJean Delvare i2c_del_adapter(&pcf_isa_ops); 3011da177e4SLinus Torvalds 3021da177e4SLinus Torvalds if (irq > 0) { 3031da177e4SLinus Torvalds disable_irq(irq); 3041da177e4SLinus Torvalds free_irq(irq, NULL); 3051da177e4SLinus Torvalds } 3061da177e4SLinus Torvalds 3073634ff6aSStig Telfer if (!mmapped) { 3083634ff6aSStig Telfer ioport_unmap(base_iomem); 3091da177e4SLinus Torvalds release_region(base, 2); 3103634ff6aSStig Telfer } else { 3113634ff6aSStig Telfer iounmap(base_iomem); 3123634ff6aSStig Telfer release_mem_region(base, 2); 3133634ff6aSStig Telfer } 3144a5d3030SJean Delvare 3154a5d3030SJean Delvare return 0; 3164a5d3030SJean Delvare } 3174a5d3030SJean Delvare 3184a5d3030SJean Delvare static struct isa_driver i2c_elektor_driver = { 3194a5d3030SJean Delvare .match = elektor_match, 3204a5d3030SJean Delvare .probe = elektor_probe, 3214a5d3030SJean Delvare .remove = __devexit_p(elektor_remove), 3224a5d3030SJean Delvare .driver = { 3234a5d3030SJean Delvare .owner = THIS_MODULE, 3244a5d3030SJean Delvare .name = "i2c-elektor", 3254a5d3030SJean Delvare }, 3264a5d3030SJean Delvare }; 3274a5d3030SJean Delvare 3284a5d3030SJean Delvare static int __init i2c_pcfisa_init(void) 3294a5d3030SJean Delvare { 3304a5d3030SJean Delvare return isa_register_driver(&i2c_elektor_driver, 1); 3314a5d3030SJean Delvare } 3324a5d3030SJean Delvare 3334a5d3030SJean Delvare static void __exit i2c_pcfisa_exit(void) 3344a5d3030SJean Delvare { 3354a5d3030SJean Delvare isa_unregister_driver(&i2c_elektor_driver); 3361da177e4SLinus Torvalds } 3371da177e4SLinus Torvalds 3381da177e4SLinus Torvalds MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>"); 3391da177e4SLinus Torvalds MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter"); 3401da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 3411da177e4SLinus Torvalds 3421da177e4SLinus Torvalds module_param(base, int, 0); 3431da177e4SLinus Torvalds module_param(irq, int, 0); 3441da177e4SLinus Torvalds module_param(clock, int, 0); 3451da177e4SLinus Torvalds module_param(own, int, 0); 3461da177e4SLinus Torvalds module_param(mmapped, int, 0); 3471da177e4SLinus Torvalds 3481da177e4SLinus Torvalds module_init(i2c_pcfisa_init); 3491da177e4SLinus Torvalds module_exit(i2c_pcfisa_exit); 350