Commit 244fbbb81c46eefcc5f3a9cc37c4668f9d8ff801

Authored by Wolfram Sang
Committed by Jean Delvare
1 parent c01b083105

i2c: Add platform driver on top of the new pca-algorithm

Tested on a blackfin.

Signed-off-by: Wolfram Sang <w.sang@pengutronix.de>
Signed-off-by: Jean Delvare <khali@linux-fr.org>

Showing 4 changed files with 324 additions and 2 deletions Side-by-side Diff

drivers/i2c/busses/Kconfig
... ... @@ -632,8 +632,8 @@
632 632 select I2C_ALGOPCA
633 633 default n
634 634 help
635   - This driver supports ISA boards using the Philips PCA 9564
636   - Parallel bus to I2C bus controller
  635 + This driver supports ISA boards using the Philips PCA9564
  636 + parallel bus to I2C bus controller.
637 637  
638 638 This driver can also be built as a module. If so, the module
639 639 will be called i2c-pca-isa.
... ... @@ -642,6 +642,17 @@
642 642 system which doesn't have this device will result in long
643 643 delays when I2C/SMBus chip drivers are loaded (e.g. at boot
644 644 time). If unsure, say N.
  645 +
  646 +config I2C_PCA_PLATFORM
  647 + tristate "PCA9564 as platform device"
  648 + select I2C_ALGOPCA
  649 + default n
  650 + help
  651 + This driver supports a memory mapped Philips PCA9564
  652 + parallel bus to I2C bus controller.
  653 +
  654 + This driver can also be built as a module. If so, the module
  655 + will be called i2c-pca-platform.
645 656  
646 657 config I2C_MV64XXX
647 658 tristate "Marvell mv64xxx I2C Controller"
drivers/i2c/busses/Makefile
... ... @@ -30,6 +30,7 @@
30 30 obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
31 31 obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi.o
32 32 obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
  33 +obj-$(CONFIG_I2C_PCA_PLATFORM) += i2c-pca-platform.o
33 34 obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o
34 35 obj-$(CONFIG_I2C_PMCMSP) += i2c-pmcmsp.o
35 36 obj-$(CONFIG_I2C_PNX) += i2c-pnx.o
drivers/i2c/busses/i2c-pca-platform.c
  1 +/*
  2 + * i2c_pca_platform.c
  3 + *
  4 + * Platform driver for the PCA9564 I2C controller.
  5 + *
  6 + * Copyright (C) 2008 Pengutronix
  7 + *
  8 + * This program is free software; you can redistribute it and/or modify
  9 + * it under the terms of the GNU General Public License version 2 as
  10 + * published by the Free Software Foundation.
  11 +
  12 + */
  13 +#include <linux/kernel.h>
  14 +#include <linux/module.h>
  15 +#include <linux/init.h>
  16 +#include <linux/slab.h>
  17 +#include <linux/delay.h>
  18 +#include <linux/errno.h>
  19 +#include <linux/i2c.h>
  20 +#include <linux/interrupt.h>
  21 +#include <linux/platform_device.h>
  22 +#include <linux/i2c-algo-pca.h>
  23 +#include <linux/i2c-pca-platform.h>
  24 +#include <linux/gpio.h>
  25 +
  26 +#include <asm/irq.h>
  27 +#include <asm/io.h>
  28 +
  29 +#define res_len(r) ((r)->end - (r)->start + 1)
  30 +
  31 +struct i2c_pca_pf_data {
  32 + void __iomem *reg_base;
  33 + int irq; /* if 0, use polling */
  34 + int gpio;
  35 + wait_queue_head_t wait;
  36 + struct i2c_adapter adap;
  37 + struct i2c_algo_pca_data algo_data;
  38 + unsigned long io_base;
  39 + unsigned long io_size;
  40 +};
  41 +
  42 +/* Read/Write functions for different register alignments */
  43 +
  44 +static int i2c_pca_pf_readbyte8(void *pd, int reg)
  45 +{
  46 + struct i2c_pca_pf_data *i2c = pd;
  47 + return ioread8(i2c->reg_base + reg);
  48 +}
  49 +
  50 +static int i2c_pca_pf_readbyte16(void *pd, int reg)
  51 +{
  52 + struct i2c_pca_pf_data *i2c = pd;
  53 + return ioread8(i2c->reg_base + reg * 2);
  54 +}
  55 +
  56 +static int i2c_pca_pf_readbyte32(void *pd, int reg)
  57 +{
  58 + struct i2c_pca_pf_data *i2c = pd;
  59 + return ioread8(i2c->reg_base + reg * 4);
  60 +}
  61 +
  62 +static void i2c_pca_pf_writebyte8(void *pd, int reg, int val)
  63 +{
  64 + struct i2c_pca_pf_data *i2c = pd;
  65 + iowrite8(val, i2c->reg_base + reg);
  66 +}
  67 +
  68 +static void i2c_pca_pf_writebyte16(void *pd, int reg, int val)
  69 +{
  70 + struct i2c_pca_pf_data *i2c = pd;
  71 + iowrite8(val, i2c->reg_base + reg * 2);
  72 +}
  73 +
  74 +static void i2c_pca_pf_writebyte32(void *pd, int reg, int val)
  75 +{
  76 + struct i2c_pca_pf_data *i2c = pd;
  77 + iowrite8(val, i2c->reg_base + reg * 4);
  78 +}
  79 +
  80 +
  81 +static int i2c_pca_pf_waitforcompletion(void *pd)
  82 +{
  83 + struct i2c_pca_pf_data *i2c = pd;
  84 + int ret = 0;
  85 +
  86 + if (i2c->irq) {
  87 + ret = wait_event_interruptible(i2c->wait,
  88 + i2c->algo_data.read_byte(i2c, I2C_PCA_CON)
  89 + & I2C_PCA_CON_SI);
  90 + } else {
  91 + /*
  92 + * Do polling...
  93 + * XXX: Could get stuck in extreme cases!
  94 + * Maybe add timeout, but using irqs is preferred anyhow.
  95 + */
  96 + while ((i2c->algo_data.read_byte(i2c, I2C_PCA_CON)
  97 + & I2C_PCA_CON_SI) == 0)
  98 + udelay(100);
  99 + }
  100 +
  101 + return ret;
  102 +}
  103 +
  104 +static void i2c_pca_pf_dummyreset(void *pd)
  105 +{
  106 + struct i2c_pca_pf_data *i2c = pd;
  107 + printk(KERN_WARNING "%s: No reset-pin found. Chip may get stuck!\n",
  108 + i2c->adap.name);
  109 +}
  110 +
  111 +static void i2c_pca_pf_resetchip(void *pd)
  112 +{
  113 + struct i2c_pca_pf_data *i2c = pd;
  114 +
  115 + gpio_set_value(i2c->gpio, 0);
  116 + ndelay(100);
  117 + gpio_set_value(i2c->gpio, 1);
  118 +}
  119 +
  120 +static irqreturn_t i2c_pca_pf_handler(int this_irq, void *dev_id)
  121 +{
  122 + struct i2c_pca_pf_data *i2c = dev_id;
  123 +
  124 + if ((i2c->algo_data.read_byte(i2c, I2C_PCA_CON) & I2C_PCA_CON_SI) == 0)
  125 + return IRQ_NONE;
  126 +
  127 + wake_up_interruptible(&i2c->wait);
  128 +
  129 + return IRQ_HANDLED;
  130 +}
  131 +
  132 +
  133 +static int __devinit i2c_pca_pf_probe(struct platform_device *pdev)
  134 +{
  135 + struct i2c_pca_pf_data *i2c;
  136 + struct resource *res;
  137 + struct i2c_pca9564_pf_platform_data *platform_data =
  138 + pdev->dev.platform_data;
  139 + int ret = 0;
  140 + int irq;
  141 +
  142 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  143 + irq = platform_get_irq(pdev, 0);
  144 + /* If irq is 0, we do polling. */
  145 +
  146 + if (res == NULL) {
  147 + ret = -ENODEV;
  148 + goto e_print;
  149 + }
  150 +
  151 + if (!request_mem_region(res->start, res_len(res), res->name)) {
  152 + ret = -ENOMEM;
  153 + goto e_print;
  154 + }
  155 +
  156 + i2c = kzalloc(sizeof(struct i2c_pca_pf_data), GFP_KERNEL);
  157 + if (!i2c) {
  158 + ret = -ENOMEM;
  159 + goto e_alloc;
  160 + }
  161 +
  162 + init_waitqueue_head(&i2c->wait);
  163 +
  164 + i2c->reg_base = ioremap(res->start, res_len(res));
  165 + if (!i2c->reg_base) {
  166 + ret = -EIO;
  167 + goto e_remap;
  168 + }
  169 + i2c->io_base = res->start;
  170 + i2c->io_size = res_len(res);
  171 + i2c->irq = irq;
  172 +
  173 + i2c->adap.nr = pdev->id >= 0 ? pdev->id : 0;
  174 + i2c->adap.owner = THIS_MODULE;
  175 + snprintf(i2c->adap.name, sizeof(i2c->adap.name), "PCA9564 at 0x%08lx",
  176 + (unsigned long) res->start);
  177 + i2c->adap.algo_data = &i2c->algo_data;
  178 + i2c->adap.dev.parent = &pdev->dev;
  179 + i2c->adap.timeout = platform_data->timeout;
  180 +
  181 + i2c->algo_data.i2c_clock = platform_data->i2c_clock_speed;
  182 + i2c->algo_data.data = i2c;
  183 +
  184 + switch (res->flags & IORESOURCE_MEM_TYPE_MASK) {
  185 + case IORESOURCE_MEM_32BIT:
  186 + i2c->algo_data.write_byte = i2c_pca_pf_writebyte32;
  187 + i2c->algo_data.read_byte = i2c_pca_pf_readbyte32;
  188 + break;
  189 + case IORESOURCE_MEM_16BIT:
  190 + i2c->algo_data.write_byte = i2c_pca_pf_writebyte16;
  191 + i2c->algo_data.read_byte = i2c_pca_pf_readbyte16;
  192 + break;
  193 + case IORESOURCE_MEM_8BIT:
  194 + default:
  195 + i2c->algo_data.write_byte = i2c_pca_pf_writebyte8;
  196 + i2c->algo_data.read_byte = i2c_pca_pf_readbyte8;
  197 + break;
  198 + }
  199 +
  200 + i2c->algo_data.wait_for_completion = i2c_pca_pf_waitforcompletion;
  201 +
  202 + i2c->gpio = platform_data->gpio;
  203 + i2c->algo_data.reset_chip = i2c_pca_pf_dummyreset;
  204 +
  205 + /* Use gpio_is_valid() when in mainline */
  206 + if (i2c->gpio > -1) {
  207 + ret = gpio_request(i2c->gpio, i2c->adap.name);
  208 + if (ret == 0) {
  209 + gpio_direction_output(i2c->gpio, 1);
  210 + i2c->algo_data.reset_chip = i2c_pca_pf_resetchip;
  211 + } else {
  212 + printk(KERN_WARNING "%s: Registering gpio failed!\n",
  213 + i2c->adap.name);
  214 + i2c->gpio = ret;
  215 + }
  216 + }
  217 +
  218 + if (irq) {
  219 + ret = request_irq(irq, i2c_pca_pf_handler,
  220 + IRQF_TRIGGER_FALLING, i2c->adap.name, i2c);
  221 + if (ret)
  222 + goto e_reqirq;
  223 + }
  224 +
  225 + if (i2c_pca_add_numbered_bus(&i2c->adap) < 0) {
  226 + ret = -ENODEV;
  227 + goto e_adapt;
  228 + }
  229 +
  230 + platform_set_drvdata(pdev, i2c);
  231 +
  232 + printk(KERN_INFO "%s registered.\n", i2c->adap.name);
  233 +
  234 + return 0;
  235 +
  236 +e_adapt:
  237 + if (irq)
  238 + free_irq(irq, i2c);
  239 +e_reqirq:
  240 + if (i2c->gpio > -1)
  241 + gpio_free(i2c->gpio);
  242 +
  243 + iounmap(i2c->reg_base);
  244 +e_remap:
  245 + kfree(i2c);
  246 +e_alloc:
  247 + release_mem_region(res->start, res_len(res));
  248 +e_print:
  249 + printk(KERN_ERR "Registering PCA9564 FAILED! (%d)\n", ret);
  250 + return ret;
  251 +}
  252 +
  253 +static int __devexit i2c_pca_pf_remove(struct platform_device *pdev)
  254 +{
  255 + struct i2c_pca_pf_data *i2c = platform_get_drvdata(pdev);
  256 + platform_set_drvdata(pdev, NULL);
  257 +
  258 + i2c_del_adapter(&i2c->adap);
  259 +
  260 + if (i2c->irq)
  261 + free_irq(i2c->irq, i2c);
  262 +
  263 + if (i2c->gpio > -1)
  264 + gpio_free(i2c->gpio);
  265 +
  266 + iounmap(i2c->reg_base);
  267 + release_mem_region(i2c->io_base, i2c->io_size);
  268 + kfree(i2c);
  269 +
  270 + return 0;
  271 +}
  272 +
  273 +static struct platform_driver i2c_pca_pf_driver = {
  274 + .probe = i2c_pca_pf_probe,
  275 + .remove = __devexit_p(i2c_pca_pf_remove),
  276 + .driver = {
  277 + .name = "i2c-pca-platform",
  278 + .owner = THIS_MODULE,
  279 + },
  280 +};
  281 +
  282 +static int __init i2c_pca_pf_init(void)
  283 +{
  284 + return platform_driver_register(&i2c_pca_pf_driver);
  285 +}
  286 +
  287 +static void __exit i2c_pca_pf_exit(void)
  288 +{
  289 + platform_driver_unregister(&i2c_pca_pf_driver);
  290 +}
  291 +
  292 +MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
  293 +MODULE_DESCRIPTION("I2C-PCA9564 platform driver");
  294 +MODULE_LICENSE("GPL");
  295 +
  296 +module_init(i2c_pca_pf_init);
  297 +module_exit(i2c_pca_pf_exit);
include/linux/i2c-pca-platform.h
  1 +#ifndef I2C_PCA9564_PLATFORM_H
  2 +#define I2C_PCA9564_PLATFORM_H
  3 +
  4 +struct i2c_pca9564_pf_platform_data {
  5 + int gpio; /* pin to reset chip. driver will work when
  6 + * not supplied (negative value), but it
  7 + * cannot exit some error conditions then */
  8 + int i2c_clock_speed; /* values are defined in linux/i2c-algo-pca.h */
  9 + int timeout; /* timeout = this value * 10us */
  10 +};
  11 +
  12 +#endif /* I2C_PCA9564_PLATFORM_H */