xref: /linux/drivers/i2c/busses/i2c-elektor.c (revision 06d07429858317ded2db7986113a9e0129cd599b)
1c942fddfSThomas 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 
8c942fddfSThomas 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;
5245c6c873SZheng Yongjun static DEFINE_SPINLOCK(lock);
531da177e4SLinus Torvalds 
54fe3d6a99SStig Telfer static struct i2c_adapter pcf_isa_ops;
55fe3d6a99SStig Telfer 
561da177e4SLinus Torvalds /* ----- local functions ----------------------------------------------	*/
571da177e4SLinus Torvalds 
pcf_isa_setbyte(void * data,int ctl,int val)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 
pcf_isa_getbyte(void * data,int ctl)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 
pcf_isa_getown(void * data)841da177e4SLinus Torvalds static int pcf_isa_getown(void *data)
851da177e4SLinus Torvalds {
861da177e4SLinus Torvalds 	return (own);
871da177e4SLinus Torvalds }
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds 
pcf_isa_getclock(void * data)901da177e4SLinus Torvalds static int pcf_isa_getclock(void *data)
911da177e4SLinus Torvalds {
921da177e4SLinus Torvalds 	return (clock);
931da177e4SLinus Torvalds }
941da177e4SLinus Torvalds 
pcf_isa_waitforpin(void * data)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 
pcf_isa_handler(int this_irq,void * dev_id)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 
pcf_isa_init(void)1331da177e4SLinus Torvalds static int pcf_isa_init(void)
1341da177e4SLinus Torvalds {
1351da177e4SLinus Torvalds 	if (!mmapped) {
136fe3d6a99SStig Telfer 		if (!request_region(base, 2, pcf_isa_ops.name)) {
137fe3d6a99SStig Telfer 			printk(KERN_ERR "%s: requested I/O region (%#x:2) is "
138fe3d6a99SStig Telfer 			       "in use\n", pcf_isa_ops.name, base);
1391da177e4SLinus Torvalds 			return -ENODEV;
1401da177e4SLinus Torvalds 		}
1413634ff6aSStig Telfer 		base_iomem = ioport_map(base, 2);
1423634ff6aSStig Telfer 		if (!base_iomem) {
143fe3d6a99SStig Telfer 			printk(KERN_ERR "%s: remap of I/O region %#x failed\n",
144fe3d6a99SStig Telfer 			       pcf_isa_ops.name, base);
1453634ff6aSStig Telfer 			release_region(base, 2);
1463634ff6aSStig Telfer 			return -ENODEV;
1471da177e4SLinus Torvalds 		}
1483634ff6aSStig Telfer 	} else {
149fe3d6a99SStig Telfer 		if (!request_mem_region(base, 2, pcf_isa_ops.name)) {
150fe3d6a99SStig Telfer 			printk(KERN_ERR "%s: requested memory region (%#x:2) "
151fe3d6a99SStig Telfer 			       "is in use\n", pcf_isa_ops.name, base);
1523634ff6aSStig Telfer 			return -ENODEV;
1533634ff6aSStig Telfer 		}
1543634ff6aSStig Telfer 		base_iomem = ioremap(base, 2);
1553634ff6aSStig Telfer 		if (base_iomem == NULL) {
156fe3d6a99SStig Telfer 			printk(KERN_ERR "%s: remap of memory region %#x "
157fe3d6a99SStig Telfer 			       "failed\n", pcf_isa_ops.name, base);
1583634ff6aSStig Telfer 			release_mem_region(base, 2);
1593634ff6aSStig Telfer 			return -ENODEV;
1603634ff6aSStig Telfer 		}
1613634ff6aSStig Telfer 	}
162fe3d6a99SStig Telfer 	pr_debug("%s: registers %#x remapped to %p\n", pcf_isa_ops.name, base,
1633634ff6aSStig Telfer 		 base_iomem);
1643634ff6aSStig Telfer 
1651da177e4SLinus Torvalds 	if (irq > 0) {
166fe3d6a99SStig Telfer 		if (request_irq(irq, pcf_isa_handler, 0, pcf_isa_ops.name,
167fe3d6a99SStig Telfer 				NULL) < 0) {
168fe3d6a99SStig Telfer 			printk(KERN_ERR "%s: Request irq%d failed\n",
169fe3d6a99SStig Telfer 			       pcf_isa_ops.name, irq);
1701da177e4SLinus Torvalds 			irq = 0;
1711da177e4SLinus Torvalds 		} else
1721da177e4SLinus Torvalds 			enable_irq(irq);
1731da177e4SLinus Torvalds 	}
1741da177e4SLinus Torvalds 	return 0;
1751da177e4SLinus Torvalds }
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds /* ------------------------------------------------------------------------
1781da177e4SLinus Torvalds  * Encapsulate the above functions in the correct operations structure.
1791da177e4SLinus Torvalds  * This is only done when more than one hardware adapter is supported.
1801da177e4SLinus Torvalds  */
1811da177e4SLinus Torvalds static struct i2c_algo_pcf_data pcf_isa_data = {
1821da177e4SLinus Torvalds 	.setpcf	    = pcf_isa_setbyte,
1831da177e4SLinus Torvalds 	.getpcf	    = pcf_isa_getbyte,
1841da177e4SLinus Torvalds 	.getown	    = pcf_isa_getown,
1851da177e4SLinus Torvalds 	.getclock   = pcf_isa_getclock,
1861da177e4SLinus Torvalds 	.waitforpin = pcf_isa_waitforpin,
1871da177e4SLinus Torvalds };
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds static struct i2c_adapter pcf_isa_ops = {
1901da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
191*9fd12f38SHeiner Kallweit 	.class		= I2C_CLASS_HWMON,
1921da177e4SLinus Torvalds 	.algo_data	= &pcf_isa_data,
193fe3d6a99SStig Telfer 	.name		= "i2c-elektor",
1941da177e4SLinus Torvalds };
1951da177e4SLinus Torvalds 
elektor_match(struct device * dev,unsigned int id)1960b255e92SBill Pemberton static int elektor_match(struct device *dev, unsigned int id)
1971da177e4SLinus Torvalds {
1981da177e4SLinus Torvalds #ifdef __alpha__
1991da177e4SLinus Torvalds 	/* check to see we have memory mapped PCF8584 connected to the
2001da177e4SLinus Torvalds 	Cypress cy82c693 PCI-ISA bridge as on UP2000 board */
2011da177e4SLinus Torvalds 	if (base == 0) {
2021da177e4SLinus Torvalds 		struct pci_dev *cy693_dev;
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds 		cy693_dev = pci_get_device(PCI_VENDOR_ID_CONTAQ,
2051da177e4SLinus Torvalds 					   PCI_DEVICE_ID_CONTAQ_82C693, NULL);
2061da177e4SLinus Torvalds 		if (cy693_dev) {
2073634ff6aSStig Telfer 			unsigned char config;
2081da177e4SLinus Torvalds 			/* yeap, we've found cypress, let's check config */
2091da177e4SLinus Torvalds 			if (!pci_read_config_byte(cy693_dev, 0x47, &config)) {
2101da177e4SLinus Torvalds 
2114a5d3030SJean Delvare 				dev_dbg(dev, "found cy82c693, config "
2124a5d3030SJean Delvare 					"register 0x47 = 0x%02x\n", config);
2131da177e4SLinus Torvalds 
2141da177e4SLinus Torvalds 				/* UP2000 board has this register set to 0xe1,
2151da177e4SLinus Torvalds 				   but the most significant bit as seems can be
2161da177e4SLinus Torvalds 				   reset during the proper initialisation
2171da177e4SLinus Torvalds 				   sequence if guys from API decides to do that
2181da177e4SLinus Torvalds 				   (so, we can even enable Tsunami Pchip
2191da177e4SLinus Torvalds 				   window for the upper 1 Gb) */
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds 				/* so just check for ROMCS at 0xe0000,
2221da177e4SLinus Torvalds 				   ROMCS enabled for writes
2231da177e4SLinus Torvalds 				   and external XD Bus buffer in use. */
2241da177e4SLinus Torvalds 				if ((config & 0x7f) == 0x61) {
2251da177e4SLinus Torvalds 					/* seems to be UP2000 like board */
2261da177e4SLinus Torvalds 					base = 0xe0000;
2273634ff6aSStig Telfer 					mmapped = 1;
2281da177e4SLinus Torvalds 					/* UP2000 drives ISA with
2291da177e4SLinus Torvalds 					   8.25 MHz (PCI/4) clock
2301da177e4SLinus Torvalds 					   (this can be read from cypress) */
2311da177e4SLinus Torvalds 					clock = I2C_PCF_CLK | I2C_PCF_TRNS90;
2324a5d3030SJean Delvare 					dev_info(dev, "found API UP2000 like "
233fe3d6a99SStig Telfer 						 "board, will probe PCF8584 "
2344a5d3030SJean Delvare 						 "later\n");
2351da177e4SLinus Torvalds 				}
2361da177e4SLinus Torvalds 			}
2371da177e4SLinus Torvalds 			pci_dev_put(cy693_dev);
2381da177e4SLinus Torvalds 		}
2391da177e4SLinus Torvalds 	}
2401da177e4SLinus Torvalds #endif
2411da177e4SLinus Torvalds 
2421da177e4SLinus Torvalds 	/* sanity checks for mmapped I/O */
2431da177e4SLinus Torvalds 	if (mmapped && base < 0xc8000) {
2444a5d3030SJean Delvare 		dev_err(dev, "incorrect base address (%#x) specified "
2454a5d3030SJean Delvare 		       "for mmapped I/O\n", base);
2464a5d3030SJean Delvare 		return 0;
2471da177e4SLinus Torvalds 	}
2481da177e4SLinus Torvalds 
2491da177e4SLinus Torvalds 	if (base == 0) {
2501da177e4SLinus Torvalds 		base = DEFAULT_BASE;
2511da177e4SLinus Torvalds 	}
2524a5d3030SJean Delvare 	return 1;
2534a5d3030SJean Delvare }
2541da177e4SLinus Torvalds 
elektor_probe(struct device * dev,unsigned int id)2550b255e92SBill Pemberton static int elektor_probe(struct device *dev, unsigned int id)
2564a5d3030SJean Delvare {
2571da177e4SLinus Torvalds 	init_waitqueue_head(&pcf_wait);
2581da177e4SLinus Torvalds 	if (pcf_isa_init())
2591da177e4SLinus Torvalds 		return -ENODEV;
2604a5d3030SJean Delvare 	pcf_isa_ops.dev.parent = dev;
2611da177e4SLinus Torvalds 	if (i2c_pcf_add_bus(&pcf_isa_ops) < 0)
2621da177e4SLinus Torvalds 		goto fail;
2631da177e4SLinus Torvalds 
2644a5d3030SJean Delvare 	dev_info(dev, "found device at %#x\n", base);
2651da177e4SLinus Torvalds 
2661da177e4SLinus Torvalds 	return 0;
2671da177e4SLinus Torvalds 
2681da177e4SLinus Torvalds  fail:
2691da177e4SLinus Torvalds 	if (irq > 0) {
2701da177e4SLinus Torvalds 		disable_irq(irq);
2711da177e4SLinus Torvalds 		free_irq(irq, NULL);
2721da177e4SLinus Torvalds 	}
2731da177e4SLinus Torvalds 
2743634ff6aSStig Telfer 	if (!mmapped) {
2753634ff6aSStig Telfer 		ioport_unmap(base_iomem);
2761da177e4SLinus Torvalds 		release_region(base, 2);
2773634ff6aSStig Telfer 	} else {
2783634ff6aSStig Telfer 		iounmap(base_iomem);
2793634ff6aSStig Telfer 		release_mem_region(base, 2);
2803634ff6aSStig Telfer 	}
2811da177e4SLinus Torvalds 	return -ENODEV;
2821da177e4SLinus Torvalds }
2831da177e4SLinus Torvalds 
elektor_remove(struct device * dev,unsigned int id)28430e88d01SUwe Kleine-König static void elektor_remove(struct device *dev, unsigned int id)
2851da177e4SLinus Torvalds {
2863269711bSJean Delvare 	i2c_del_adapter(&pcf_isa_ops);
2871da177e4SLinus Torvalds 
2881da177e4SLinus Torvalds 	if (irq > 0) {
2891da177e4SLinus Torvalds 		disable_irq(irq);
2901da177e4SLinus Torvalds 		free_irq(irq, NULL);
2911da177e4SLinus Torvalds 	}
2921da177e4SLinus Torvalds 
2933634ff6aSStig Telfer 	if (!mmapped) {
2943634ff6aSStig Telfer 		ioport_unmap(base_iomem);
2951da177e4SLinus Torvalds 		release_region(base, 2);
2963634ff6aSStig Telfer 	} else {
2973634ff6aSStig Telfer 		iounmap(base_iomem);
2983634ff6aSStig Telfer 		release_mem_region(base, 2);
2993634ff6aSStig Telfer 	}
3004a5d3030SJean Delvare }
3014a5d3030SJean Delvare 
3024a5d3030SJean Delvare static struct isa_driver i2c_elektor_driver = {
3034a5d3030SJean Delvare 	.match		= elektor_match,
3044a5d3030SJean Delvare 	.probe		= elektor_probe,
3050b255e92SBill Pemberton 	.remove		= elektor_remove,
3064a5d3030SJean Delvare 	.driver = {
3074a5d3030SJean Delvare 		.owner	= THIS_MODULE,
3084a5d3030SJean Delvare 		.name	= "i2c-elektor",
3094a5d3030SJean Delvare 	},
3104a5d3030SJean Delvare };
3114a5d3030SJean Delvare 
3121da177e4SLinus Torvalds MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>");
3131da177e4SLinus Torvalds MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter");
3141da177e4SLinus Torvalds MODULE_LICENSE("GPL");
3151da177e4SLinus Torvalds 
316c78babccSDavid Howells module_param_hw(base, int, ioport_or_iomem, 0);
317c78babccSDavid Howells module_param_hw(irq, int, irq, 0);
3181da177e4SLinus Torvalds module_param(clock, int, 0);
3191da177e4SLinus Torvalds module_param(own, int, 0);
320c78babccSDavid Howells module_param_hw(mmapped, int, other, 0);
3219e55c073SWilliam Breathitt Gray module_isa_driver(i2c_elektor_driver, 1);
322