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 206
	.id		= I2C_HW_P_ELEK,
	.algo_data	= &pcf_isa_data,
S
Stig Telfer 已提交
207
	.name		= "i2c-elektor",
L
Linus Torvalds 已提交
208 209
};

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

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

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

				/* UP2000 board has this register set to 0xe1,
S
Stig Telfer 已提交
229
				   but the most significant bit as seems can be
L
Linus Torvalds 已提交
230
				   reset during the proper initialisation
S
Stig Telfer 已提交
231 232 233
				   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 已提交
234 235

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

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

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

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

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

	return 0;

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

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

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

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

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

	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 已提交
336 337 338 339 340 341 342 343 344 345 346 347 348 349
}

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);