i2c-elektor.c 8.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/* ------------------------------------------------------------------------- */
/* i2c-elektor.c i2c-hw access for PCF8584 style isa bus adaptes             */
/* ------------------------------------------------------------------------- */
/*   Copyright (C) 1995-97 Simon G. Vogl
                   1998-99 Hans Berglund

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.		     */
/* ------------------------------------------------------------------------- */

22
/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
L
Linus Torvalds 已提交
23 24
   Frodo Looijaard <frodol@dds.nl> */

S
Stig Telfer 已提交
25
/* Partialy rewriten by Oleg I. Vdovikin for mmapped support of
L
Linus Torvalds 已提交
26 27 28 29 30 31 32 33 34 35 36 37
   for Alpha Processor Inc. UP-2000(+) boards */

#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/wait.h>

38
#include <linux/isa.h>
L
Linus Torvalds 已提交
39 40 41 42 43 44 45 46 47 48 49
#include <linux/i2c.h>
#include <linux/i2c-algo-pcf.h>

#include <asm/io.h>
#include <asm/irq.h>

#include "../algos/i2c-algo-pcf.h"

#define DEFAULT_BASE 0x330

static int base;
50 51
static u8 __iomem *base_iomem;

L
Linus Torvalds 已提交
52 53 54 55 56
static int irq;
static int clock  = 0x1c;
static int own    = 0x55;
static int mmapped;

S
Stig Telfer 已提交
57
/* vdovikin: removed static struct i2c_pcf_isa gpi; code -
L
Linus Torvalds 已提交
58 59 60 61 62 63 64 65
  this module in real supports only one device, due to missing arguments
  in some functions, called from the algo-pcf module. Sometimes it's
  need to be rewriten - but for now just remove this for simpler reading */

static wait_queue_head_t pcf_wait;
static int pcf_pending;
static spinlock_t lock;

S
Stig Telfer 已提交
66 67
static struct i2c_adapter pcf_isa_ops;

L
Linus Torvalds 已提交
68 69 70 71
/* ----- local functions ----------------------------------------------	*/

static void pcf_isa_setbyte(void *data, int ctl, int val)
{
72
	u8 __iomem *address = ctl ? (base_iomem + 1) : base_iomem;
L
Linus Torvalds 已提交
73 74 75 76 77 78

	/* enable irq if any specified for serial operation */
	if (ctl && irq && (val & I2C_PCF_ESO)) {
		val |= I2C_PCF_ENI;
	}

S
Stig Telfer 已提交
79
	pr_debug("%s: Write %p 0x%02X\n", pcf_isa_ops.name, address, val);
80 81 82 83 84
	iowrite8(val, address);
#ifdef __alpha__
	/* API UP2000 needs some hardware fudging to make the write stick */
	iowrite8(val, address);
#endif
L
Linus Torvalds 已提交
85 86 87 88
}

static int pcf_isa_getbyte(void *data, int ctl)
{
89 90
	u8 __iomem *address = ctl ? (base_iomem + 1) : base_iomem;
	int val = ioread8(address);
L
Linus Torvalds 已提交
91

S
Stig Telfer 已提交
92
	pr_debug("%s: Read %p 0x%02X\n", pcf_isa_ops.name, address, val);
L
Linus Torvalds 已提交
93 94 95 96 97 98 99 100 101 102 103 104 105 106
	return (val);
}

static int pcf_isa_getown(void *data)
{
	return (own);
}


static int pcf_isa_getclock(void *data)
{
	return (clock);
}

107 108
static void pcf_isa_waitforpin(void *data)
{
L
Linus Torvalds 已提交
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
	DEFINE_WAIT(wait);
	int timeout = 2;
	unsigned long flags;

	if (irq > 0) {
		spin_lock_irqsave(&lock, flags);
		if (pcf_pending == 0) {
			spin_unlock_irqrestore(&lock, flags);
			prepare_to_wait(&pcf_wait, &wait, TASK_INTERRUPTIBLE);
			if (schedule_timeout(timeout*HZ)) {
				spin_lock_irqsave(&lock, flags);
				if (pcf_pending == 1) {
					pcf_pending = 0;
				}
				spin_unlock_irqrestore(&lock, flags);
			}
			finish_wait(&pcf_wait, &wait);
		} else {
			pcf_pending = 0;
			spin_unlock_irqrestore(&lock, flags);
		}
	} else {
		udelay(100);
	}
}


136
static irqreturn_t pcf_isa_handler(int this_irq, void *dev_id) {
L
Linus Torvalds 已提交
137 138 139 140 141 142 143 144 145 146 147 148
	spin_lock(&lock);
	pcf_pending = 1;
	spin_unlock(&lock);
	wake_up_interruptible(&pcf_wait);
	return IRQ_HANDLED;
}


static int pcf_isa_init(void)
{
	spin_lock_init(&lock);
	if (!mmapped) {
S
Stig Telfer 已提交
149 150 151
		if (!request_region(base, 2, pcf_isa_ops.name)) {
			printk(KERN_ERR "%s: requested I/O region (%#x:2) is "
			       "in use\n", pcf_isa_ops.name, base);
L
Linus Torvalds 已提交
152 153
			return -ENODEV;
		}
154 155
		base_iomem = ioport_map(base, 2);
		if (!base_iomem) {
S
Stig Telfer 已提交
156 157
			printk(KERN_ERR "%s: remap of I/O region %#x failed\n",
			       pcf_isa_ops.name, base);
158 159 160 161
			release_region(base, 2);
			return -ENODEV;
		}
	} else {
S
Stig Telfer 已提交
162 163 164
		if (!request_mem_region(base, 2, pcf_isa_ops.name)) {
			printk(KERN_ERR "%s: requested memory region (%#x:2) "
			       "is in use\n", pcf_isa_ops.name, base);
165 166 167 168
			return -ENODEV;
		}
		base_iomem = ioremap(base, 2);
		if (base_iomem == NULL) {
S
Stig Telfer 已提交
169 170
			printk(KERN_ERR "%s: remap of memory region %#x "
			       "failed\n", pcf_isa_ops.name, base);
171 172 173
			release_mem_region(base, 2);
			return -ENODEV;
		}
L
Linus Torvalds 已提交
174
	}
S
Stig Telfer 已提交
175
	pr_debug("%s: registers %#x remapped to %p\n", pcf_isa_ops.name, base,
176 177
		 base_iomem);

L
Linus Torvalds 已提交
178
	if (irq > 0) {
S
Stig Telfer 已提交
179 180 181 182
		if (request_irq(irq, pcf_isa_handler, 0, pcf_isa_ops.name,
				NULL) < 0) {
			printk(KERN_ERR "%s: Request irq%d failed\n",
			       pcf_isa_ops.name, irq);
L
Linus Torvalds 已提交
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
			irq = 0;
		} else
			enable_irq(irq);
	}
	return 0;
}

/* ------------------------------------------------------------------------
 * Encapsulate the above functions in the correct operations structure.
 * This is only done when more than one hardware adapter is supported.
 */
static struct i2c_algo_pcf_data pcf_isa_data = {
	.setpcf	    = pcf_isa_setbyte,
	.getpcf	    = pcf_isa_getbyte,
	.getown	    = pcf_isa_getown,
	.getclock   = pcf_isa_getclock,
	.waitforpin = pcf_isa_waitforpin,
};

static struct i2c_adapter pcf_isa_ops = {
	.owner		= THIS_MODULE,
204
	.class		= I2C_CLASS_HWMON | I2C_CLASS_SPD,
L
Linus Torvalds 已提交
205
	.algo_data	= &pcf_isa_data,
S
Stig Telfer 已提交
206
	.name		= "i2c-elektor",
L
Linus Torvalds 已提交
207 208
};

209
static int __devinit elektor_match(struct device *dev, unsigned int id)
L
Linus Torvalds 已提交
210 211
{
#ifdef __alpha__
S
Stig Telfer 已提交
212
	/* check to see we have memory mapped PCF8584 connected to the
L
Linus Torvalds 已提交
213 214 215
	Cypress cy82c693 PCI-ISA bridge as on UP2000 board */
	if (base == 0) {
		struct pci_dev *cy693_dev;
S
Stig Telfer 已提交
216 217

		cy693_dev = pci_get_device(PCI_VENDOR_ID_CONTAQ,
L
Linus Torvalds 已提交
218 219
					   PCI_DEVICE_ID_CONTAQ_82C693, NULL);
		if (cy693_dev) {
220
			unsigned char config;
L
Linus Torvalds 已提交
221 222
			/* yeap, we've found cypress, let's check config */
			if (!pci_read_config_byte(cy693_dev, 0x47, &config)) {
S
Stig Telfer 已提交
223

224 225
				dev_dbg(dev, "found cy82c693, config "
					"register 0x47 = 0x%02x\n", config);
L
Linus Torvalds 已提交
226 227

				/* UP2000 board has this register set to 0xe1,
S
Stig Telfer 已提交
228
				   but the most significant bit as seems can be
L
Linus Torvalds 已提交
229
				   reset during the proper initialisation
S
Stig Telfer 已提交
230 231 232
				   sequence if guys from API decides to do that
				   (so, we can even enable Tsunami Pchip
				   window for the upper 1 Gb) */
L
Linus Torvalds 已提交
233 234

				/* so just check for ROMCS at 0xe0000,
S
Stig Telfer 已提交
235
				   ROMCS enabled for writes
L
Linus Torvalds 已提交
236 237 238 239
				   and external XD Bus buffer in use. */
				if ((config & 0x7f) == 0x61) {
					/* seems to be UP2000 like board */
					base = 0xe0000;
240
					mmapped = 1;
S
Stig Telfer 已提交
241
					/* UP2000 drives ISA with
L
Linus Torvalds 已提交
242 243 244
					   8.25 MHz (PCI/4) clock
					   (this can be read from cypress) */
					clock = I2C_PCF_CLK | I2C_PCF_TRNS90;
245 246 247
					dev_info(dev, "found API UP2000 like "
						 "board, will probe PCF8584 "
						 "later\n");
L
Linus Torvalds 已提交
248 249 250 251 252 253 254 255 256
				}
			}
			pci_dev_put(cy693_dev);
		}
	}
#endif

	/* sanity checks for mmapped I/O */
	if (mmapped && base < 0xc8000) {
257 258 259
		dev_err(dev, "incorrect base address (%#x) specified "
		       "for mmapped I/O\n", base);
		return 0;
L
Linus Torvalds 已提交
260 261 262 263 264
	}

	if (base == 0) {
		base = DEFAULT_BASE;
	}
265 266
	return 1;
}
L
Linus Torvalds 已提交
267

268 269
static int __devinit elektor_probe(struct device *dev, unsigned int id)
{
L
Linus Torvalds 已提交
270 271 272
	init_waitqueue_head(&pcf_wait);
	if (pcf_isa_init())
		return -ENODEV;
273
	pcf_isa_ops.dev.parent = dev;
L
Linus Torvalds 已提交
274 275
	if (i2c_pcf_add_bus(&pcf_isa_ops) < 0)
		goto fail;
S
Stig Telfer 已提交
276

277
	dev_info(dev, "found device at %#x\n", base);
L
Linus Torvalds 已提交
278 279 280 281 282 283 284 285 286

	return 0;

 fail:
	if (irq > 0) {
		disable_irq(irq);
		free_irq(irq, NULL);
	}

287 288
	if (!mmapped) {
		ioport_unmap(base_iomem);
S
Stig Telfer 已提交
289
		release_region(base, 2);
290 291 292 293
	} else {
		iounmap(base_iomem);
		release_mem_region(base, 2);
	}
L
Linus Torvalds 已提交
294 295 296
	return -ENODEV;
}

297
static int __devexit elektor_remove(struct device *dev, unsigned int id)
L
Linus Torvalds 已提交
298
{
299
	i2c_del_adapter(&pcf_isa_ops);
L
Linus Torvalds 已提交
300 301 302 303 304 305

	if (irq > 0) {
		disable_irq(irq);
		free_irq(irq, NULL);
	}

306 307
	if (!mmapped) {
		ioport_unmap(base_iomem);
S
Stig Telfer 已提交
308
		release_region(base, 2);
309 310 311 312
	} else {
		iounmap(base_iomem);
		release_mem_region(base, 2);
	}
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334

	return 0;
}

static struct isa_driver i2c_elektor_driver = {
	.match		= elektor_match,
	.probe		= elektor_probe,
	.remove		= __devexit_p(elektor_remove),
	.driver = {
		.owner	= THIS_MODULE,
		.name	= "i2c-elektor",
	},
};

static int __init i2c_pcfisa_init(void)
{
	return isa_register_driver(&i2c_elektor_driver, 1);
}

static void __exit i2c_pcfisa_exit(void)
{
	isa_unregister_driver(&i2c_elektor_driver);
L
Linus Torvalds 已提交
335 336 337 338 339 340 341 342 343 344 345 346 347 348
}

MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>");
MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter");
MODULE_LICENSE("GPL");

module_param(base, int, 0);
module_param(irq, int, 0);
module_param(clock, int, 0);
module_param(own, int, 0);
module_param(mmapped, int, 0);

module_init(i2c_pcfisa_init);
module_exit(i2c_pcfisa_exit);