xref: /linux/drivers/i2c/busses/i2c-elektor.c (revision b7019ac550eb3916f34d79db583e9b7ea2524afa)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* ------------------------------------------------------------------------- */
3 /* i2c-elektor.c i2c-hw access for PCF8584 style isa bus adaptes             */
4 /* ------------------------------------------------------------------------- */
5 /*   Copyright (C) 1995-97 Simon G. Vogl
6                    1998-99 Hans Berglund
7 
8  */
9 /* ------------------------------------------------------------------------- */
10 
11 /* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
12    Frodo Looijaard <frodol@dds.nl> */
13 
14 /* Partially rewriten by Oleg I. Vdovikin for mmapped support of
15    for Alpha Processor Inc. UP-2000(+) boards */
16 
17 #include <linux/kernel.h>
18 #include <linux/ioport.h>
19 #include <linux/module.h>
20 #include <linux/delay.h>
21 #include <linux/init.h>
22 #include <linux/interrupt.h>
23 #include <linux/pci.h>
24 #include <linux/wait.h>
25 
26 #include <linux/isa.h>
27 #include <linux/i2c.h>
28 #include <linux/i2c-algo-pcf.h>
29 #include <linux/io.h>
30 
31 #include <asm/irq.h>
32 
33 #include "../algos/i2c-algo-pcf.h"
34 
35 #define DEFAULT_BASE 0x330
36 
37 static int base;
38 static u8 __iomem *base_iomem;
39 
40 static int irq;
41 static int clock  = 0x1c;
42 static int own    = 0x55;
43 static int mmapped;
44 
45 /* vdovikin: removed static struct i2c_pcf_isa gpi; code -
46   this module in real supports only one device, due to missing arguments
47   in some functions, called from the algo-pcf module. Sometimes it's
48   need to be rewriten - but for now just remove this for simpler reading */
49 
50 static wait_queue_head_t pcf_wait;
51 static int pcf_pending;
52 static spinlock_t lock;
53 
54 static struct i2c_adapter pcf_isa_ops;
55 
56 /* ----- local functions ----------------------------------------------	*/
57 
58 static void pcf_isa_setbyte(void *data, int ctl, int val)
59 {
60 	u8 __iomem *address = ctl ? (base_iomem + 1) : base_iomem;
61 
62 	/* enable irq if any specified for serial operation */
63 	if (ctl && irq && (val & I2C_PCF_ESO)) {
64 		val |= I2C_PCF_ENI;
65 	}
66 
67 	pr_debug("%s: Write %p 0x%02X\n", pcf_isa_ops.name, address, val);
68 	iowrite8(val, address);
69 #ifdef __alpha__
70 	/* API UP2000 needs some hardware fudging to make the write stick */
71 	iowrite8(val, address);
72 #endif
73 }
74 
75 static int pcf_isa_getbyte(void *data, int ctl)
76 {
77 	u8 __iomem *address = ctl ? (base_iomem + 1) : base_iomem;
78 	int val = ioread8(address);
79 
80 	pr_debug("%s: Read %p 0x%02X\n", pcf_isa_ops.name, address, val);
81 	return (val);
82 }
83 
84 static int pcf_isa_getown(void *data)
85 {
86 	return (own);
87 }
88 
89 
90 static int pcf_isa_getclock(void *data)
91 {
92 	return (clock);
93 }
94 
95 static void pcf_isa_waitforpin(void *data)
96 {
97 	DEFINE_WAIT(wait);
98 	int timeout = 2;
99 	unsigned long flags;
100 
101 	if (irq > 0) {
102 		spin_lock_irqsave(&lock, flags);
103 		if (pcf_pending == 0) {
104 			spin_unlock_irqrestore(&lock, flags);
105 			prepare_to_wait(&pcf_wait, &wait, TASK_INTERRUPTIBLE);
106 			if (schedule_timeout(timeout*HZ)) {
107 				spin_lock_irqsave(&lock, flags);
108 				if (pcf_pending == 1) {
109 					pcf_pending = 0;
110 				}
111 				spin_unlock_irqrestore(&lock, flags);
112 			}
113 			finish_wait(&pcf_wait, &wait);
114 		} else {
115 			pcf_pending = 0;
116 			spin_unlock_irqrestore(&lock, flags);
117 		}
118 	} else {
119 		udelay(100);
120 	}
121 }
122 
123 
124 static irqreturn_t pcf_isa_handler(int this_irq, void *dev_id) {
125 	spin_lock(&lock);
126 	pcf_pending = 1;
127 	spin_unlock(&lock);
128 	wake_up_interruptible(&pcf_wait);
129 	return IRQ_HANDLED;
130 }
131 
132 
133 static int pcf_isa_init(void)
134 {
135 	spin_lock_init(&lock);
136 	if (!mmapped) {
137 		if (!request_region(base, 2, pcf_isa_ops.name)) {
138 			printk(KERN_ERR "%s: requested I/O region (%#x:2) is "
139 			       "in use\n", pcf_isa_ops.name, base);
140 			return -ENODEV;
141 		}
142 		base_iomem = ioport_map(base, 2);
143 		if (!base_iomem) {
144 			printk(KERN_ERR "%s: remap of I/O region %#x failed\n",
145 			       pcf_isa_ops.name, base);
146 			release_region(base, 2);
147 			return -ENODEV;
148 		}
149 	} else {
150 		if (!request_mem_region(base, 2, pcf_isa_ops.name)) {
151 			printk(KERN_ERR "%s: requested memory region (%#x:2) "
152 			       "is in use\n", pcf_isa_ops.name, base);
153 			return -ENODEV;
154 		}
155 		base_iomem = ioremap(base, 2);
156 		if (base_iomem == NULL) {
157 			printk(KERN_ERR "%s: remap of memory region %#x "
158 			       "failed\n", pcf_isa_ops.name, base);
159 			release_mem_region(base, 2);
160 			return -ENODEV;
161 		}
162 	}
163 	pr_debug("%s: registers %#x remapped to %p\n", pcf_isa_ops.name, base,
164 		 base_iomem);
165 
166 	if (irq > 0) {
167 		if (request_irq(irq, pcf_isa_handler, 0, pcf_isa_ops.name,
168 				NULL) < 0) {
169 			printk(KERN_ERR "%s: Request irq%d failed\n",
170 			       pcf_isa_ops.name, irq);
171 			irq = 0;
172 		} else
173 			enable_irq(irq);
174 	}
175 	return 0;
176 }
177 
178 /* ------------------------------------------------------------------------
179  * Encapsulate the above functions in the correct operations structure.
180  * This is only done when more than one hardware adapter is supported.
181  */
182 static struct i2c_algo_pcf_data pcf_isa_data = {
183 	.setpcf	    = pcf_isa_setbyte,
184 	.getpcf	    = pcf_isa_getbyte,
185 	.getown	    = pcf_isa_getown,
186 	.getclock   = pcf_isa_getclock,
187 	.waitforpin = pcf_isa_waitforpin,
188 };
189 
190 static struct i2c_adapter pcf_isa_ops = {
191 	.owner		= THIS_MODULE,
192 	.class		= I2C_CLASS_HWMON | I2C_CLASS_SPD,
193 	.algo_data	= &pcf_isa_data,
194 	.name		= "i2c-elektor",
195 };
196 
197 static int elektor_match(struct device *dev, unsigned int id)
198 {
199 #ifdef __alpha__
200 	/* check to see we have memory mapped PCF8584 connected to the
201 	Cypress cy82c693 PCI-ISA bridge as on UP2000 board */
202 	if (base == 0) {
203 		struct pci_dev *cy693_dev;
204 
205 		cy693_dev = pci_get_device(PCI_VENDOR_ID_CONTAQ,
206 					   PCI_DEVICE_ID_CONTAQ_82C693, NULL);
207 		if (cy693_dev) {
208 			unsigned char config;
209 			/* yeap, we've found cypress, let's check config */
210 			if (!pci_read_config_byte(cy693_dev, 0x47, &config)) {
211 
212 				dev_dbg(dev, "found cy82c693, config "
213 					"register 0x47 = 0x%02x\n", config);
214 
215 				/* UP2000 board has this register set to 0xe1,
216 				   but the most significant bit as seems can be
217 				   reset during the proper initialisation
218 				   sequence if guys from API decides to do that
219 				   (so, we can even enable Tsunami Pchip
220 				   window for the upper 1 Gb) */
221 
222 				/* so just check for ROMCS at 0xe0000,
223 				   ROMCS enabled for writes
224 				   and external XD Bus buffer in use. */
225 				if ((config & 0x7f) == 0x61) {
226 					/* seems to be UP2000 like board */
227 					base = 0xe0000;
228 					mmapped = 1;
229 					/* UP2000 drives ISA with
230 					   8.25 MHz (PCI/4) clock
231 					   (this can be read from cypress) */
232 					clock = I2C_PCF_CLK | I2C_PCF_TRNS90;
233 					dev_info(dev, "found API UP2000 like "
234 						 "board, will probe PCF8584 "
235 						 "later\n");
236 				}
237 			}
238 			pci_dev_put(cy693_dev);
239 		}
240 	}
241 #endif
242 
243 	/* sanity checks for mmapped I/O */
244 	if (mmapped && base < 0xc8000) {
245 		dev_err(dev, "incorrect base address (%#x) specified "
246 		       "for mmapped I/O\n", base);
247 		return 0;
248 	}
249 
250 	if (base == 0) {
251 		base = DEFAULT_BASE;
252 	}
253 	return 1;
254 }
255 
256 static int elektor_probe(struct device *dev, unsigned int id)
257 {
258 	init_waitqueue_head(&pcf_wait);
259 	if (pcf_isa_init())
260 		return -ENODEV;
261 	pcf_isa_ops.dev.parent = dev;
262 	if (i2c_pcf_add_bus(&pcf_isa_ops) < 0)
263 		goto fail;
264 
265 	dev_info(dev, "found device at %#x\n", base);
266 
267 	return 0;
268 
269  fail:
270 	if (irq > 0) {
271 		disable_irq(irq);
272 		free_irq(irq, NULL);
273 	}
274 
275 	if (!mmapped) {
276 		ioport_unmap(base_iomem);
277 		release_region(base, 2);
278 	} else {
279 		iounmap(base_iomem);
280 		release_mem_region(base, 2);
281 	}
282 	return -ENODEV;
283 }
284 
285 static int elektor_remove(struct device *dev, unsigned int id)
286 {
287 	i2c_del_adapter(&pcf_isa_ops);
288 
289 	if (irq > 0) {
290 		disable_irq(irq);
291 		free_irq(irq, NULL);
292 	}
293 
294 	if (!mmapped) {
295 		ioport_unmap(base_iomem);
296 		release_region(base, 2);
297 	} else {
298 		iounmap(base_iomem);
299 		release_mem_region(base, 2);
300 	}
301 
302 	return 0;
303 }
304 
305 static struct isa_driver i2c_elektor_driver = {
306 	.match		= elektor_match,
307 	.probe		= elektor_probe,
308 	.remove		= elektor_remove,
309 	.driver = {
310 		.owner	= THIS_MODULE,
311 		.name	= "i2c-elektor",
312 	},
313 };
314 
315 MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>");
316 MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter");
317 MODULE_LICENSE("GPL");
318 
319 module_param_hw(base, int, ioport_or_iomem, 0);
320 module_param_hw(irq, int, irq, 0);
321 module_param(clock, int, 0);
322 module_param(own, int, 0);
323 module_param_hw(mmapped, int, other, 0);
324 module_isa_driver(i2c_elektor_driver, 1);
325