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 DEFINE_SPINLOCK(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	if (!mmapped) {
136		if (!request_region(base, 2, pcf_isa_ops.name)) {
137			printk(KERN_ERR "%s: requested I/O region (%#x:2) is "
138			       "in use\n", pcf_isa_ops.name, base);
139			return -ENODEV;
140		}
141		base_iomem = ioport_map(base, 2);
142		if (!base_iomem) {
143			printk(KERN_ERR "%s: remap of I/O region %#x failed\n",
144			       pcf_isa_ops.name, base);
145			release_region(base, 2);
146			return -ENODEV;
147		}
148	} else {
149		if (!request_mem_region(base, 2, pcf_isa_ops.name)) {
150			printk(KERN_ERR "%s: requested memory region (%#x:2) "
151			       "is in use\n", pcf_isa_ops.name, base);
152			return -ENODEV;
153		}
154		base_iomem = ioremap(base, 2);
155		if (base_iomem == NULL) {
156			printk(KERN_ERR "%s: remap of memory region %#x "
157			       "failed\n", pcf_isa_ops.name, base);
158			release_mem_region(base, 2);
159			return -ENODEV;
160		}
161	}
162	pr_debug("%s: registers %#x remapped to %p\n", pcf_isa_ops.name, base,
163		 base_iomem);
164
165	if (irq > 0) {
166		if (request_irq(irq, pcf_isa_handler, 0, pcf_isa_ops.name,
167				NULL) < 0) {
168			printk(KERN_ERR "%s: Request irq%d failed\n",
169			       pcf_isa_ops.name, irq);
170			irq = 0;
171		} else
172			enable_irq(irq);
173	}
174	return 0;
175}
176
177/* ------------------------------------------------------------------------
178 * Encapsulate the above functions in the correct operations structure.
179 * This is only done when more than one hardware adapter is supported.
180 */
181static struct i2c_algo_pcf_data pcf_isa_data = {
182	.setpcf	    = pcf_isa_setbyte,
183	.getpcf	    = pcf_isa_getbyte,
184	.getown	    = pcf_isa_getown,
185	.getclock   = pcf_isa_getclock,
186	.waitforpin = pcf_isa_waitforpin,
187};
188
189static struct i2c_adapter pcf_isa_ops = {
190	.owner		= THIS_MODULE,
191	.class		= I2C_CLASS_HWMON,
192	.algo_data	= &pcf_isa_data,
193	.name		= "i2c-elektor",
194};
195
196static int elektor_match(struct device *dev, unsigned int id)
197{
198#ifdef __alpha__
199	/* check to see we have memory mapped PCF8584 connected to the
200	Cypress cy82c693 PCI-ISA bridge as on UP2000 board */
201	if (base == 0) {
202		struct pci_dev *cy693_dev;
203
204		cy693_dev = pci_get_device(PCI_VENDOR_ID_CONTAQ,
205					   PCI_DEVICE_ID_CONTAQ_82C693, NULL);
206		if (cy693_dev) {
207			unsigned char config;
208			/* yeap, we've found cypress, let's check config */
209			if (!pci_read_config_byte(cy693_dev, 0x47, &config)) {
210
211				dev_dbg(dev, "found cy82c693, config "
212					"register 0x47 = 0x%02x\n", config);
213
214				/* UP2000 board has this register set to 0xe1,
215				   but the most significant bit as seems can be
216				   reset during the proper initialisation
217				   sequence if guys from API decides to do that
218				   (so, we can even enable Tsunami Pchip
219				   window for the upper 1 Gb) */
220
221				/* so just check for ROMCS at 0xe0000,
222				   ROMCS enabled for writes
223				   and external XD Bus buffer in use. */
224				if ((config & 0x7f) == 0x61) {
225					/* seems to be UP2000 like board */
226					base = 0xe0000;
227					mmapped = 1;
228					/* UP2000 drives ISA with
229					   8.25 MHz (PCI/4) clock
230					   (this can be read from cypress) */
231					clock = I2C_PCF_CLK | I2C_PCF_TRNS90;
232					dev_info(dev, "found API UP2000 like "
233						 "board, will probe PCF8584 "
234						 "later\n");
235				}
236			}
237			pci_dev_put(cy693_dev);
238		}
239	}
240#endif
241
242	/* sanity checks for mmapped I/O */
243	if (mmapped && base < 0xc8000) {
244		dev_err(dev, "incorrect base address (%#x) specified "
245		       "for mmapped I/O\n", base);
246		return 0;
247	}
248
249	if (base == 0) {
250		base = DEFAULT_BASE;
251	}
252	return 1;
253}
254
255static int elektor_probe(struct device *dev, unsigned int id)
256{
257	init_waitqueue_head(&pcf_wait);
258	if (pcf_isa_init())
259		return -ENODEV;
260	pcf_isa_ops.dev.parent = dev;
261	if (i2c_pcf_add_bus(&pcf_isa_ops) < 0)
262		goto fail;
263
264	dev_info(dev, "found device at %#x\n", base);
265
266	return 0;
267
268 fail:
269	if (irq > 0) {
270		disable_irq(irq);
271		free_irq(irq, NULL);
272	}
273
274	if (!mmapped) {
275		ioport_unmap(base_iomem);
276		release_region(base, 2);
277	} else {
278		iounmap(base_iomem);
279		release_mem_region(base, 2);
280	}
281	return -ENODEV;
282}
283
284static void elektor_remove(struct device *dev, unsigned int id)
285{
286	i2c_del_adapter(&pcf_isa_ops);
287
288	if (irq > 0) {
289		disable_irq(irq);
290		free_irq(irq, NULL);
291	}
292
293	if (!mmapped) {
294		ioport_unmap(base_iomem);
295		release_region(base, 2);
296	} else {
297		iounmap(base_iomem);
298		release_mem_region(base, 2);
299	}
300}
301
302static struct isa_driver i2c_elektor_driver = {
303	.match		= elektor_match,
304	.probe		= elektor_probe,
305	.remove		= elektor_remove,
306	.driver = {
307		.owner	= THIS_MODULE,
308		.name	= "i2c-elektor",
309	},
310};
311
312MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>");
313MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter");
314MODULE_LICENSE("GPL");
315
316module_param_hw(base, int, ioport_or_iomem, 0);
317module_param_hw(irq, int, irq, 0);
318module_param(clock, int, 0);
319module_param(own, int, 0);
320module_param_hw(mmapped, int, other, 0);
321module_isa_driver(i2c_elektor_driver, 1);
322