xref: /linux/drivers/i2c/busses/i2c-elektor.c (revision 0b255e927d47b550620dfd3475ee74b0f52e09c8)
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 
2525985edcSLucas De Marchi /* Partially 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/init.h>
331da177e4SLinus Torvalds #include <linux/interrupt.h>
341da177e4SLinus Torvalds #include <linux/pci.h>
351da177e4SLinus Torvalds #include <linux/wait.h>
361da177e4SLinus Torvalds 
374a5d3030SJean Delvare #include <linux/isa.h>
381da177e4SLinus Torvalds #include <linux/i2c.h>
391da177e4SLinus Torvalds #include <linux/i2c-algo-pcf.h>
4021782180SH Hartley Sweeten #include <linux/io.h>
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds #include <asm/irq.h>
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds #include "../algos/i2c-algo-pcf.h"
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds #define DEFAULT_BASE 0x330
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds static int base;
493634ff6aSStig Telfer static u8 __iomem *base_iomem;
503634ff6aSStig Telfer 
511da177e4SLinus Torvalds static int irq;
521da177e4SLinus Torvalds static int clock  = 0x1c;
531da177e4SLinus Torvalds static int own    = 0x55;
541da177e4SLinus Torvalds static int mmapped;
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds /* vdovikin: removed static struct i2c_pcf_isa gpi; code -
571da177e4SLinus Torvalds   this module in real supports only one device, due to missing arguments
581da177e4SLinus Torvalds   in some functions, called from the algo-pcf module. Sometimes it's
591da177e4SLinus Torvalds   need to be rewriten - but for now just remove this for simpler reading */
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds static wait_queue_head_t pcf_wait;
621da177e4SLinus Torvalds static int pcf_pending;
631da177e4SLinus Torvalds static spinlock_t lock;
641da177e4SLinus Torvalds 
65fe3d6a99SStig Telfer static struct i2c_adapter pcf_isa_ops;
66fe3d6a99SStig Telfer 
671da177e4SLinus Torvalds /* ----- local functions ----------------------------------------------	*/
681da177e4SLinus Torvalds 
691da177e4SLinus Torvalds static void pcf_isa_setbyte(void *data, int ctl, int val)
701da177e4SLinus Torvalds {
713634ff6aSStig Telfer 	u8 __iomem *address = ctl ? (base_iomem + 1) : base_iomem;
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds 	/* enable irq if any specified for serial operation */
741da177e4SLinus Torvalds 	if (ctl && irq && (val & I2C_PCF_ESO)) {
751da177e4SLinus Torvalds 		val |= I2C_PCF_ENI;
761da177e4SLinus Torvalds 	}
771da177e4SLinus Torvalds 
78fe3d6a99SStig Telfer 	pr_debug("%s: Write %p 0x%02X\n", pcf_isa_ops.name, address, val);
793634ff6aSStig Telfer 	iowrite8(val, address);
803634ff6aSStig Telfer #ifdef __alpha__
813634ff6aSStig Telfer 	/* API UP2000 needs some hardware fudging to make the write stick */
823634ff6aSStig Telfer 	iowrite8(val, address);
833634ff6aSStig Telfer #endif
841da177e4SLinus Torvalds }
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds static int pcf_isa_getbyte(void *data, int ctl)
871da177e4SLinus Torvalds {
883634ff6aSStig Telfer 	u8 __iomem *address = ctl ? (base_iomem + 1) : base_iomem;
893634ff6aSStig Telfer 	int val = ioread8(address);
901da177e4SLinus Torvalds 
91fe3d6a99SStig Telfer 	pr_debug("%s: Read %p 0x%02X\n", pcf_isa_ops.name, address, val);
921da177e4SLinus Torvalds 	return (val);
931da177e4SLinus Torvalds }
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds static int pcf_isa_getown(void *data)
961da177e4SLinus Torvalds {
971da177e4SLinus Torvalds 	return (own);
981da177e4SLinus Torvalds }
991da177e4SLinus Torvalds 
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds static int pcf_isa_getclock(void *data)
1021da177e4SLinus Torvalds {
1031da177e4SLinus Torvalds 	return (clock);
1041da177e4SLinus Torvalds }
1051da177e4SLinus Torvalds 
10608e5338dSDavid Miller static void pcf_isa_waitforpin(void *data)
10708e5338dSDavid Miller {
1081da177e4SLinus Torvalds 	DEFINE_WAIT(wait);
1091da177e4SLinus Torvalds 	int timeout = 2;
1101da177e4SLinus Torvalds 	unsigned long flags;
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds 	if (irq > 0) {
1131da177e4SLinus Torvalds 		spin_lock_irqsave(&lock, flags);
1141da177e4SLinus Torvalds 		if (pcf_pending == 0) {
1151da177e4SLinus Torvalds 			spin_unlock_irqrestore(&lock, flags);
1161da177e4SLinus Torvalds 			prepare_to_wait(&pcf_wait, &wait, TASK_INTERRUPTIBLE);
1171da177e4SLinus Torvalds 			if (schedule_timeout(timeout*HZ)) {
1181da177e4SLinus Torvalds 				spin_lock_irqsave(&lock, flags);
1191da177e4SLinus Torvalds 				if (pcf_pending == 1) {
1201da177e4SLinus Torvalds 					pcf_pending = 0;
1211da177e4SLinus Torvalds 				}
1221da177e4SLinus Torvalds 				spin_unlock_irqrestore(&lock, flags);
1231da177e4SLinus Torvalds 			}
1241da177e4SLinus Torvalds 			finish_wait(&pcf_wait, &wait);
1251da177e4SLinus Torvalds 		} else {
1261da177e4SLinus Torvalds 			pcf_pending = 0;
1271da177e4SLinus Torvalds 			spin_unlock_irqrestore(&lock, flags);
1281da177e4SLinus Torvalds 		}
1291da177e4SLinus Torvalds 	} else {
1301da177e4SLinus Torvalds 		udelay(100);
1311da177e4SLinus Torvalds 	}
1321da177e4SLinus Torvalds }
1331da177e4SLinus Torvalds 
1341da177e4SLinus Torvalds 
1357d12e780SDavid Howells static irqreturn_t pcf_isa_handler(int this_irq, void *dev_id) {
1361da177e4SLinus Torvalds 	spin_lock(&lock);
1371da177e4SLinus Torvalds 	pcf_pending = 1;
1381da177e4SLinus Torvalds 	spin_unlock(&lock);
1391da177e4SLinus Torvalds 	wake_up_interruptible(&pcf_wait);
1401da177e4SLinus Torvalds 	return IRQ_HANDLED;
1411da177e4SLinus Torvalds }
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds 
1441da177e4SLinus Torvalds static int pcf_isa_init(void)
1451da177e4SLinus Torvalds {
1461da177e4SLinus Torvalds 	spin_lock_init(&lock);
1471da177e4SLinus Torvalds 	if (!mmapped) {
148fe3d6a99SStig Telfer 		if (!request_region(base, 2, pcf_isa_ops.name)) {
149fe3d6a99SStig Telfer 			printk(KERN_ERR "%s: requested I/O region (%#x:2) is "
150fe3d6a99SStig Telfer 			       "in use\n", pcf_isa_ops.name, base);
1511da177e4SLinus Torvalds 			return -ENODEV;
1521da177e4SLinus Torvalds 		}
1533634ff6aSStig Telfer 		base_iomem = ioport_map(base, 2);
1543634ff6aSStig Telfer 		if (!base_iomem) {
155fe3d6a99SStig Telfer 			printk(KERN_ERR "%s: remap of I/O region %#x failed\n",
156fe3d6a99SStig Telfer 			       pcf_isa_ops.name, base);
1573634ff6aSStig Telfer 			release_region(base, 2);
1583634ff6aSStig Telfer 			return -ENODEV;
1591da177e4SLinus Torvalds 		}
1603634ff6aSStig Telfer 	} else {
161fe3d6a99SStig Telfer 		if (!request_mem_region(base, 2, pcf_isa_ops.name)) {
162fe3d6a99SStig Telfer 			printk(KERN_ERR "%s: requested memory region (%#x:2) "
163fe3d6a99SStig Telfer 			       "is in use\n", pcf_isa_ops.name, base);
1643634ff6aSStig Telfer 			return -ENODEV;
1653634ff6aSStig Telfer 		}
1663634ff6aSStig Telfer 		base_iomem = ioremap(base, 2);
1673634ff6aSStig Telfer 		if (base_iomem == NULL) {
168fe3d6a99SStig Telfer 			printk(KERN_ERR "%s: remap of memory region %#x "
169fe3d6a99SStig Telfer 			       "failed\n", pcf_isa_ops.name, base);
1703634ff6aSStig Telfer 			release_mem_region(base, 2);
1713634ff6aSStig Telfer 			return -ENODEV;
1723634ff6aSStig Telfer 		}
1733634ff6aSStig Telfer 	}
174fe3d6a99SStig Telfer 	pr_debug("%s: registers %#x remapped to %p\n", pcf_isa_ops.name, base,
1753634ff6aSStig Telfer 		 base_iomem);
1763634ff6aSStig Telfer 
1771da177e4SLinus Torvalds 	if (irq > 0) {
178fe3d6a99SStig Telfer 		if (request_irq(irq, pcf_isa_handler, 0, pcf_isa_ops.name,
179fe3d6a99SStig Telfer 				NULL) < 0) {
180fe3d6a99SStig Telfer 			printk(KERN_ERR "%s: Request irq%d failed\n",
181fe3d6a99SStig Telfer 			       pcf_isa_ops.name, irq);
1821da177e4SLinus Torvalds 			irq = 0;
1831da177e4SLinus Torvalds 		} else
1841da177e4SLinus Torvalds 			enable_irq(irq);
1851da177e4SLinus Torvalds 	}
1861da177e4SLinus Torvalds 	return 0;
1871da177e4SLinus Torvalds }
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds /* ------------------------------------------------------------------------
1901da177e4SLinus Torvalds  * Encapsulate the above functions in the correct operations structure.
1911da177e4SLinus Torvalds  * This is only done when more than one hardware adapter is supported.
1921da177e4SLinus Torvalds  */
1931da177e4SLinus Torvalds static struct i2c_algo_pcf_data pcf_isa_data = {
1941da177e4SLinus Torvalds 	.setpcf	    = pcf_isa_setbyte,
1951da177e4SLinus Torvalds 	.getpcf	    = pcf_isa_getbyte,
1961da177e4SLinus Torvalds 	.getown	    = pcf_isa_getown,
1971da177e4SLinus Torvalds 	.getclock   = pcf_isa_getclock,
1981da177e4SLinus Torvalds 	.waitforpin = pcf_isa_waitforpin,
1991da177e4SLinus Torvalds };
2001da177e4SLinus Torvalds 
2011da177e4SLinus Torvalds static struct i2c_adapter pcf_isa_ops = {
2021da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
2033401b2ffSJean Delvare 	.class		= I2C_CLASS_HWMON | I2C_CLASS_SPD,
2041da177e4SLinus Torvalds 	.algo_data	= &pcf_isa_data,
205fe3d6a99SStig Telfer 	.name		= "i2c-elektor",
2061da177e4SLinus Torvalds };
2071da177e4SLinus Torvalds 
208*0b255e92SBill Pemberton static int elektor_match(struct device *dev, unsigned int id)
2091da177e4SLinus Torvalds {
2101da177e4SLinus Torvalds #ifdef __alpha__
2111da177e4SLinus Torvalds 	/* check to see we have memory mapped PCF8584 connected to the
2121da177e4SLinus Torvalds 	Cypress cy82c693 PCI-ISA bridge as on UP2000 board */
2131da177e4SLinus Torvalds 	if (base == 0) {
2141da177e4SLinus Torvalds 		struct pci_dev *cy693_dev;
2151da177e4SLinus Torvalds 
2161da177e4SLinus Torvalds 		cy693_dev = pci_get_device(PCI_VENDOR_ID_CONTAQ,
2171da177e4SLinus Torvalds 					   PCI_DEVICE_ID_CONTAQ_82C693, NULL);
2181da177e4SLinus Torvalds 		if (cy693_dev) {
2193634ff6aSStig Telfer 			unsigned char config;
2201da177e4SLinus Torvalds 			/* yeap, we've found cypress, let's check config */
2211da177e4SLinus Torvalds 			if (!pci_read_config_byte(cy693_dev, 0x47, &config)) {
2221da177e4SLinus Torvalds 
2234a5d3030SJean Delvare 				dev_dbg(dev, "found cy82c693, config "
2244a5d3030SJean Delvare 					"register 0x47 = 0x%02x\n", config);
2251da177e4SLinus Torvalds 
2261da177e4SLinus Torvalds 				/* UP2000 board has this register set to 0xe1,
2271da177e4SLinus Torvalds 				   but the most significant bit as seems can be
2281da177e4SLinus Torvalds 				   reset during the proper initialisation
2291da177e4SLinus Torvalds 				   sequence if guys from API decides to do that
2301da177e4SLinus Torvalds 				   (so, we can even enable Tsunami Pchip
2311da177e4SLinus Torvalds 				   window for the upper 1 Gb) */
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds 				/* so just check for ROMCS at 0xe0000,
2341da177e4SLinus Torvalds 				   ROMCS enabled for writes
2351da177e4SLinus Torvalds 				   and external XD Bus buffer in use. */
2361da177e4SLinus Torvalds 				if ((config & 0x7f) == 0x61) {
2371da177e4SLinus Torvalds 					/* seems to be UP2000 like board */
2381da177e4SLinus Torvalds 					base = 0xe0000;
2393634ff6aSStig Telfer 					mmapped = 1;
2401da177e4SLinus Torvalds 					/* UP2000 drives ISA with
2411da177e4SLinus Torvalds 					   8.25 MHz (PCI/4) clock
2421da177e4SLinus Torvalds 					   (this can be read from cypress) */
2431da177e4SLinus Torvalds 					clock = I2C_PCF_CLK | I2C_PCF_TRNS90;
2444a5d3030SJean Delvare 					dev_info(dev, "found API UP2000 like "
245fe3d6a99SStig Telfer 						 "board, will probe PCF8584 "
2464a5d3030SJean Delvare 						 "later\n");
2471da177e4SLinus Torvalds 				}
2481da177e4SLinus Torvalds 			}
2491da177e4SLinus Torvalds 			pci_dev_put(cy693_dev);
2501da177e4SLinus Torvalds 		}
2511da177e4SLinus Torvalds 	}
2521da177e4SLinus Torvalds #endif
2531da177e4SLinus Torvalds 
2541da177e4SLinus Torvalds 	/* sanity checks for mmapped I/O */
2551da177e4SLinus Torvalds 	if (mmapped && base < 0xc8000) {
2564a5d3030SJean Delvare 		dev_err(dev, "incorrect base address (%#x) specified "
2574a5d3030SJean Delvare 		       "for mmapped I/O\n", base);
2584a5d3030SJean Delvare 		return 0;
2591da177e4SLinus Torvalds 	}
2601da177e4SLinus Torvalds 
2611da177e4SLinus Torvalds 	if (base == 0) {
2621da177e4SLinus Torvalds 		base = DEFAULT_BASE;
2631da177e4SLinus Torvalds 	}
2644a5d3030SJean Delvare 	return 1;
2654a5d3030SJean Delvare }
2661da177e4SLinus Torvalds 
267*0b255e92SBill Pemberton static int elektor_probe(struct device *dev, unsigned int id)
2684a5d3030SJean Delvare {
2691da177e4SLinus Torvalds 	init_waitqueue_head(&pcf_wait);
2701da177e4SLinus Torvalds 	if (pcf_isa_init())
2711da177e4SLinus Torvalds 		return -ENODEV;
2724a5d3030SJean Delvare 	pcf_isa_ops.dev.parent = dev;
2731da177e4SLinus Torvalds 	if (i2c_pcf_add_bus(&pcf_isa_ops) < 0)
2741da177e4SLinus Torvalds 		goto fail;
2751da177e4SLinus Torvalds 
2764a5d3030SJean Delvare 	dev_info(dev, "found device at %#x\n", base);
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds 	return 0;
2791da177e4SLinus Torvalds 
2801da177e4SLinus Torvalds  fail:
2811da177e4SLinus Torvalds 	if (irq > 0) {
2821da177e4SLinus Torvalds 		disable_irq(irq);
2831da177e4SLinus Torvalds 		free_irq(irq, NULL);
2841da177e4SLinus Torvalds 	}
2851da177e4SLinus Torvalds 
2863634ff6aSStig Telfer 	if (!mmapped) {
2873634ff6aSStig Telfer 		ioport_unmap(base_iomem);
2881da177e4SLinus Torvalds 		release_region(base, 2);
2893634ff6aSStig Telfer 	} else {
2903634ff6aSStig Telfer 		iounmap(base_iomem);
2913634ff6aSStig Telfer 		release_mem_region(base, 2);
2923634ff6aSStig Telfer 	}
2931da177e4SLinus Torvalds 	return -ENODEV;
2941da177e4SLinus Torvalds }
2951da177e4SLinus Torvalds 
296*0b255e92SBill Pemberton static int elektor_remove(struct device *dev, unsigned int id)
2971da177e4SLinus Torvalds {
2983269711bSJean Delvare 	i2c_del_adapter(&pcf_isa_ops);
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds 	if (irq > 0) {
3011da177e4SLinus Torvalds 		disable_irq(irq);
3021da177e4SLinus Torvalds 		free_irq(irq, NULL);
3031da177e4SLinus Torvalds 	}
3041da177e4SLinus Torvalds 
3053634ff6aSStig Telfer 	if (!mmapped) {
3063634ff6aSStig Telfer 		ioport_unmap(base_iomem);
3071da177e4SLinus Torvalds 		release_region(base, 2);
3083634ff6aSStig Telfer 	} else {
3093634ff6aSStig Telfer 		iounmap(base_iomem);
3103634ff6aSStig Telfer 		release_mem_region(base, 2);
3113634ff6aSStig Telfer 	}
3124a5d3030SJean Delvare 
3134a5d3030SJean Delvare 	return 0;
3144a5d3030SJean Delvare }
3154a5d3030SJean Delvare 
3164a5d3030SJean Delvare static struct isa_driver i2c_elektor_driver = {
3174a5d3030SJean Delvare 	.match		= elektor_match,
3184a5d3030SJean Delvare 	.probe		= elektor_probe,
319*0b255e92SBill Pemberton 	.remove		= elektor_remove,
3204a5d3030SJean Delvare 	.driver = {
3214a5d3030SJean Delvare 		.owner	= THIS_MODULE,
3224a5d3030SJean Delvare 		.name	= "i2c-elektor",
3234a5d3030SJean Delvare 	},
3244a5d3030SJean Delvare };
3254a5d3030SJean Delvare 
3264a5d3030SJean Delvare static int __init i2c_pcfisa_init(void)
3274a5d3030SJean Delvare {
3284a5d3030SJean Delvare 	return isa_register_driver(&i2c_elektor_driver, 1);
3294a5d3030SJean Delvare }
3304a5d3030SJean Delvare 
3314a5d3030SJean Delvare static void __exit i2c_pcfisa_exit(void)
3324a5d3030SJean Delvare {
3334a5d3030SJean Delvare 	isa_unregister_driver(&i2c_elektor_driver);
3341da177e4SLinus Torvalds }
3351da177e4SLinus Torvalds 
3361da177e4SLinus Torvalds MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>");
3371da177e4SLinus Torvalds MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter");
3381da177e4SLinus Torvalds MODULE_LICENSE("GPL");
3391da177e4SLinus Torvalds 
3401da177e4SLinus Torvalds module_param(base, int, 0);
3411da177e4SLinus Torvalds module_param(irq, int, 0);
3421da177e4SLinus Torvalds module_param(clock, int, 0);
3431da177e4SLinus Torvalds module_param(own, int, 0);
3441da177e4SLinus Torvalds module_param(mmapped, int, 0);
3451da177e4SLinus Torvalds 
3461da177e4SLinus Torvalds module_init(i2c_pcfisa_init);
3471da177e4SLinus Torvalds module_exit(i2c_pcfisa_exit);
348