i2c-pca-platform.c 6.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 *  i2c_pca_platform.c
 *
 *  Platform driver for the PCA9564 I2C controller.
 *
 *  Copyright (C) 2008 Pengutronix
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.

 */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
18
#include <linux/jiffies.h>
19 20 21 22 23 24 25
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/i2c-algo-pca.h>
#include <linux/i2c-pca-platform.h>
#include <linux/gpio.h>
26
#include <linux/io.h>
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82

#include <asm/irq.h>

struct i2c_pca_pf_data {
	void __iomem			*reg_base;
	int				irq;	/* if 0, use polling */
	int				gpio;
	wait_queue_head_t		wait;
	struct i2c_adapter		adap;
	struct i2c_algo_pca_data	algo_data;
	unsigned long			io_base;
	unsigned long			io_size;
};

/* Read/Write functions for different register alignments */

static int i2c_pca_pf_readbyte8(void *pd, int reg)
{
	struct i2c_pca_pf_data *i2c = pd;
	return ioread8(i2c->reg_base + reg);
}

static int i2c_pca_pf_readbyte16(void *pd, int reg)
{
	struct i2c_pca_pf_data *i2c = pd;
	return ioread8(i2c->reg_base + reg * 2);
}

static int i2c_pca_pf_readbyte32(void *pd, int reg)
{
	struct i2c_pca_pf_data *i2c = pd;
	return ioread8(i2c->reg_base + reg * 4);
}

static void i2c_pca_pf_writebyte8(void *pd, int reg, int val)
{
	struct i2c_pca_pf_data *i2c = pd;
	iowrite8(val, i2c->reg_base + reg);
}

static void i2c_pca_pf_writebyte16(void *pd, int reg, int val)
{
	struct i2c_pca_pf_data *i2c = pd;
	iowrite8(val, i2c->reg_base + reg * 2);
}

static void i2c_pca_pf_writebyte32(void *pd, int reg, int val)
{
	struct i2c_pca_pf_data *i2c = pd;
	iowrite8(val, i2c->reg_base + reg * 4);
}


static int i2c_pca_pf_waitforcompletion(void *pd)
{
	struct i2c_pca_pf_data *i2c = pd;
83
	unsigned long timeout;
84
	long ret;
85 86

	if (i2c->irq) {
87
		ret = wait_event_timeout(i2c->wait,
88
			i2c->algo_data.read_byte(i2c, I2C_PCA_CON)
89
			& I2C_PCA_CON_SI, i2c->adap.timeout);
90
	} else {
91 92
		/* Do polling */
		timeout = jiffies + i2c->adap.timeout;
93 94 95 96 97
		do {
			ret = time_before(jiffies, timeout);
			if (i2c->algo_data.read_byte(i2c, I2C_PCA_CON)
					& I2C_PCA_CON_SI)
				break;
98
			udelay(100);
99
		} while (ret);
100 101
	}

102
	return ret > 0;
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
}

static void i2c_pca_pf_dummyreset(void *pd)
{
	struct i2c_pca_pf_data *i2c = pd;
	printk(KERN_WARNING "%s: No reset-pin found. Chip may get stuck!\n",
		i2c->adap.name);
}

static void i2c_pca_pf_resetchip(void *pd)
{
	struct i2c_pca_pf_data *i2c = pd;

	gpio_set_value(i2c->gpio, 0);
	ndelay(100);
	gpio_set_value(i2c->gpio, 1);
}

static irqreturn_t i2c_pca_pf_handler(int this_irq, void *dev_id)
{
	struct i2c_pca_pf_data *i2c = dev_id;

	if ((i2c->algo_data.read_byte(i2c, I2C_PCA_CON) & I2C_PCA_CON_SI) == 0)
		return IRQ_NONE;

128
	wake_up(&i2c->wait);
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151

	return IRQ_HANDLED;
}


static int __devinit i2c_pca_pf_probe(struct platform_device *pdev)
{
	struct i2c_pca_pf_data *i2c;
	struct resource *res;
	struct i2c_pca9564_pf_platform_data *platform_data =
				pdev->dev.platform_data;
	int ret = 0;
	int irq;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	irq = platform_get_irq(pdev, 0);
	/* If irq is 0, we do polling. */

	if (res == NULL) {
		ret = -ENODEV;
		goto e_print;
	}

L
Linus Walleij 已提交
152
	if (!request_mem_region(res->start, resource_size(res), res->name)) {
153 154 155 156 157 158 159 160 161 162 163 164
		ret = -ENOMEM;
		goto e_print;
	}

	i2c = kzalloc(sizeof(struct i2c_pca_pf_data), GFP_KERNEL);
	if (!i2c) {
		ret = -ENOMEM;
		goto e_alloc;
	}

	init_waitqueue_head(&i2c->wait);

L
Linus Walleij 已提交
165
	i2c->reg_base = ioremap(res->start, resource_size(res));
166
	if (!i2c->reg_base) {
W
Wolfram Sang 已提交
167
		ret = -ENOMEM;
168 169 170
		goto e_remap;
	}
	i2c->io_base = res->start;
L
Linus Walleij 已提交
171
	i2c->io_size = resource_size(res);
172 173 174 175
	i2c->irq = irq;

	i2c->adap.nr = pdev->id >= 0 ? pdev->id : 0;
	i2c->adap.owner = THIS_MODULE;
176 177 178
	snprintf(i2c->adap.name, sizeof(i2c->adap.name),
		 "PCA9564/PCA9665 at 0x%08lx",
		 (unsigned long) res->start);
179 180 181
	i2c->adap.algo_data = &i2c->algo_data;
	i2c->adap.dev.parent = &pdev->dev;

182 183 184 185 186 187 188 189 190 191
	if (platform_data) {
		i2c->adap.timeout = platform_data->timeout;
		i2c->algo_data.i2c_clock = platform_data->i2c_clock_speed;
		i2c->gpio = platform_data->gpio;
	} else {
		i2c->adap.timeout = HZ;
		i2c->algo_data.i2c_clock = 59000;
		i2c->gpio = -1;
	}

192
	i2c->algo_data.data = i2c;
193 194
	i2c->algo_data.wait_for_completion = i2c_pca_pf_waitforcompletion;
	i2c->algo_data.reset_chip = i2c_pca_pf_dummyreset;
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226

	switch (res->flags & IORESOURCE_MEM_TYPE_MASK) {
	case IORESOURCE_MEM_32BIT:
		i2c->algo_data.write_byte = i2c_pca_pf_writebyte32;
		i2c->algo_data.read_byte = i2c_pca_pf_readbyte32;
		break;
	case IORESOURCE_MEM_16BIT:
		i2c->algo_data.write_byte = i2c_pca_pf_writebyte16;
		i2c->algo_data.read_byte = i2c_pca_pf_readbyte16;
		break;
	case IORESOURCE_MEM_8BIT:
	default:
		i2c->algo_data.write_byte = i2c_pca_pf_writebyte8;
		i2c->algo_data.read_byte = i2c_pca_pf_readbyte8;
		break;
	}

	/* Use gpio_is_valid() when in mainline */
	if (i2c->gpio > -1) {
		ret = gpio_request(i2c->gpio, i2c->adap.name);
		if (ret == 0) {
			gpio_direction_output(i2c->gpio, 1);
			i2c->algo_data.reset_chip = i2c_pca_pf_resetchip;
		} else {
			printk(KERN_WARNING "%s: Registering gpio failed!\n",
				i2c->adap.name);
			i2c->gpio = ret;
		}
	}

	if (irq) {
		ret = request_irq(irq, i2c_pca_pf_handler,
227
			IRQF_TRIGGER_FALLING, pdev->name, i2c);
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
		if (ret)
			goto e_reqirq;
	}

	if (i2c_pca_add_numbered_bus(&i2c->adap) < 0) {
		ret = -ENODEV;
		goto e_adapt;
	}

	platform_set_drvdata(pdev, i2c);

	printk(KERN_INFO "%s registered.\n", i2c->adap.name);

	return 0;

e_adapt:
	if (irq)
		free_irq(irq, i2c);
e_reqirq:
	if (i2c->gpio > -1)
		gpio_free(i2c->gpio);

	iounmap(i2c->reg_base);
e_remap:
	kfree(i2c);
e_alloc:
L
Linus Walleij 已提交
254
	release_mem_region(res->start, resource_size(res));
255
e_print:
256
	printk(KERN_ERR "Registering PCA9564/PCA9665 FAILED! (%d)\n", ret);
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
	return ret;
}

static int __devexit i2c_pca_pf_remove(struct platform_device *pdev)
{
	struct i2c_pca_pf_data *i2c = platform_get_drvdata(pdev);
	platform_set_drvdata(pdev, NULL);

	i2c_del_adapter(&i2c->adap);

	if (i2c->irq)
		free_irq(i2c->irq, i2c);

	if (i2c->gpio > -1)
		gpio_free(i2c->gpio);

	iounmap(i2c->reg_base);
	release_mem_region(i2c->io_base, i2c->io_size);
	kfree(i2c);

	return 0;
}

static struct platform_driver i2c_pca_pf_driver = {
	.probe = i2c_pca_pf_probe,
	.remove = __devexit_p(i2c_pca_pf_remove),
	.driver = {
		.name = "i2c-pca-platform",
		.owner = THIS_MODULE,
	},
};

289
module_platform_driver(i2c_pca_pf_driver);
290 291

MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
292
MODULE_DESCRIPTION("I2C-PCA9564/PCA9665 platform driver");
293
MODULE_LICENSE("GPL");