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