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
37static int base;
38static u8 __iomem *base_iomem;
39
40static int irq;
41static int clock  = 0x1c;
42static int own    = 0x55;
43static 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
50static wait_queue_head_t pcf_wait;
51static int pcf_pending;
52static spinlock_t lock;
53
54static struct i2c_adapter pcf_isa_ops;
55
56/* ----- local functions ----------------------------------------------	*/
57
58static 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
75static 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
84static int pcf_isa_getown(void *data)
85{
86	return (own);
87}
88
89
90static int pcf_isa_getclock(void *data)
91{
92	return (clock);
93}
94
95static 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
124static 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
133static 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 */
182static 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
190static 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
197static 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
256static 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
285static 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
305static 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
315MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>");
316MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter");
317MODULE_LICENSE("GPL");
318
319module_param_hw(base, int, ioport_or_iomem, 0);
320module_param_hw(irq, int, irq, 0);
321module_param(clock, int, 0);
322module_param(own, int, 0);
323module_param_hw(mmapped, int, other, 0);
324module_isa_driver(i2c_elektor_driver, 1);
325