Commit 18f98b1e3147afdb51e545cc6ff2b016c7d088a7

Authored by Peter Korsgaard
Committed by Greg Kroah-Hartman
1 parent aee6230534

[PATCH] i2c: New bus driver for the OpenCores I2C controller

The following patch adds support for the OpenCores I2C controller IP
core (See http://www.opencores.org/projects.cgi/web/i2c/overview).

Signed-off-by: Peter Korsgaard <jacmet@sunsite.dk>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

Showing 5 changed files with 425 additions and 0 deletions Side-by-side Diff

Documentation/i2c/busses/i2c-ocores
  1 +Kernel driver i2c-ocores
  2 +
  3 +Supported adapters:
  4 + * OpenCores.org I2C controller by Richard Herveille (see datasheet link)
  5 + Datasheet: http://www.opencores.org/projects.cgi/web/i2c/overview
  6 +
  7 +Author: Peter Korsgaard <jacmet@sunsite.dk>
  8 +
  9 +Description
  10 +-----------
  11 +
  12 +i2c-ocores is an i2c bus driver for the OpenCores.org I2C controller
  13 +IP core by Richard Herveille.
  14 +
  15 +Usage
  16 +-----
  17 +
  18 +i2c-ocores uses the platform bus, so you need to provide a struct
  19 +platform_device with the base address and interrupt number. The
  20 +dev.platform_data of the device should also point to a struct
  21 +ocores_i2c_platform_data (see linux/i2c-ocores.h) describing the
  22 +distance between registers and the input clock speed.
  23 +
  24 +E.G. something like:
  25 +
  26 +static struct resource ocores_resources[] = {
  27 + [0] = {
  28 + .start = MYI2C_BASEADDR,
  29 + .end = MYI2C_BASEADDR + 8,
  30 + .flags = IORESOURCE_MEM,
  31 + },
  32 + [1] = {
  33 + .start = MYI2C_IRQ,
  34 + .end = MYI2C_IRQ,
  35 + .flags = IORESOURCE_IRQ,
  36 + },
  37 +};
  38 +
  39 +static struct ocores_i2c_platform_data myi2c_data = {
  40 + .regstep = 2, /* two bytes between registers */
  41 + .clock_khz = 50000, /* input clock of 50MHz */
  42 +};
  43 +
  44 +static struct platform_device myi2c = {
  45 + .name = "ocores-i2c",
  46 + .dev = {
  47 + .platform_data = &myi2c_data,
  48 + },
  49 + .num_resources = ARRAY_SIZE(ocores_resources),
  50 + .resource = ocores_resources,
  51 +};
drivers/i2c/busses/Kconfig
... ... @@ -276,6 +276,17 @@
276 276 This driver can also be built as a module. If so, the module
277 277 will be called i2c-nforce2.
278 278  
  279 +config I2C_OCORES
  280 + tristate "OpenCores I2C Controller"
  281 + depends on I2C && EXPERIMENTAL
  282 + help
  283 + If you say yes to this option, support will be included for the
  284 + OpenCores I2C controller. For details see
  285 + http://www.opencores.org/projects.cgi/web/i2c/overview
  286 +
  287 + This driver can also be built as a module. If so, the module
  288 + will be called i2c-ocores.
  289 +
279 290 config I2C_PARPORT
280 291 tristate "Parallel port adapter"
281 292 depends on I2C && PARPORT
drivers/i2c/busses/Makefile
... ... @@ -23,6 +23,7 @@
23 23 obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
24 24 obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
25 25 obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o
  26 +obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o
26 27 obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
27 28 obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
28 29 obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
drivers/i2c/busses/i2c-ocores.c
  1 +/*
  2 + * i2c-ocores.c: I2C bus driver for OpenCores I2C controller
  3 + * (http://www.opencores.org/projects.cgi/web/i2c/overview).
  4 + *
  5 + * Peter Korsgaard <jacmet@sunsite.dk>
  6 + *
  7 + * This file is licensed under the terms of the GNU General Public License
  8 + * version 2. This program is licensed "as is" without any warranty of any
  9 + * kind, whether express or implied.
  10 + */
  11 +
  12 +#include <linux/config.h>
  13 +#include <linux/kernel.h>
  14 +#include <linux/module.h>
  15 +#include <linux/sched.h>
  16 +#include <linux/init.h>
  17 +#include <linux/errno.h>
  18 +#include <linux/platform_device.h>
  19 +#include <linux/i2c.h>
  20 +#include <linux/interrupt.h>
  21 +#include <linux/wait.h>
  22 +#include <linux/i2c-ocores.h>
  23 +#include <asm/io.h>
  24 +
  25 +struct ocores_i2c {
  26 + void __iomem *base;
  27 + int regstep;
  28 + wait_queue_head_t wait;
  29 + struct i2c_adapter adap;
  30 + struct i2c_msg *msg;
  31 + int pos;
  32 + int nmsgs;
  33 + int state; /* see STATE_ */
  34 +};
  35 +
  36 +/* registers */
  37 +#define OCI2C_PRELOW 0
  38 +#define OCI2C_PREHIGH 1
  39 +#define OCI2C_CONTROL 2
  40 +#define OCI2C_DATA 3
  41 +#define OCI2C_CMD 4
  42 +#define OCI2C_STATUS 4
  43 +
  44 +#define OCI2C_CTRL_IEN 0x40
  45 +#define OCI2C_CTRL_EN 0x80
  46 +
  47 +#define OCI2C_CMD_START 0x91
  48 +#define OCI2C_CMD_STOP 0x41
  49 +#define OCI2C_CMD_READ 0x21
  50 +#define OCI2C_CMD_WRITE 0x11
  51 +#define OCI2C_CMD_READ_ACK 0x21
  52 +#define OCI2C_CMD_READ_NACK 0x29
  53 +#define OCI2C_CMD_IACK 0x01
  54 +
  55 +#define OCI2C_STAT_IF 0x01
  56 +#define OCI2C_STAT_TIP 0x02
  57 +#define OCI2C_STAT_ARBLOST 0x20
  58 +#define OCI2C_STAT_BUSY 0x40
  59 +#define OCI2C_STAT_NACK 0x80
  60 +
  61 +#define STATE_DONE 0
  62 +#define STATE_START 1
  63 +#define STATE_WRITE 2
  64 +#define STATE_READ 3
  65 +#define STATE_ERROR 4
  66 +
  67 +static inline void oc_setreg(struct ocores_i2c *i2c, int reg, u8 value)
  68 +{
  69 + iowrite8(value, i2c->base + reg * i2c->regstep);
  70 +}
  71 +
  72 +static inline u8 oc_getreg(struct ocores_i2c *i2c, int reg)
  73 +{
  74 + return ioread8(i2c->base + reg * i2c->regstep);
  75 +}
  76 +
  77 +static void ocores_process(struct ocores_i2c *i2c)
  78 +{
  79 + struct i2c_msg *msg = i2c->msg;
  80 + u8 stat = oc_getreg(i2c, OCI2C_STATUS);
  81 +
  82 + if ((i2c->state == STATE_DONE) || (i2c->state == STATE_ERROR)) {
  83 + /* stop has been sent */
  84 + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK);
  85 + wake_up(&i2c->wait);
  86 + return;
  87 + }
  88 +
  89 + /* error? */
  90 + if (stat & OCI2C_STAT_ARBLOST) {
  91 + i2c->state = STATE_ERROR;
  92 + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);
  93 + return;
  94 + }
  95 +
  96 + if ((i2c->state == STATE_START) || (i2c->state == STATE_WRITE)) {
  97 + i2c->state =
  98 + (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE;
  99 +
  100 + if (stat & OCI2C_STAT_NACK) {
  101 + i2c->state = STATE_ERROR;
  102 + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);
  103 + return;
  104 + }
  105 + } else
  106 + msg->buf[i2c->pos++] = oc_getreg(i2c, OCI2C_DATA);
  107 +
  108 + /* end of msg? */
  109 + if (i2c->pos == msg->len) {
  110 + i2c->nmsgs--;
  111 + i2c->msg++;
  112 + i2c->pos = 0;
  113 + msg = i2c->msg;
  114 +
  115 + if (i2c->nmsgs) { /* end? */
  116 + /* send start? */
  117 + if (!(msg->flags & I2C_M_NOSTART)) {
  118 + u8 addr = (msg->addr << 1);
  119 +
  120 + if (msg->flags & I2C_M_RD)
  121 + addr |= 1;
  122 +
  123 + i2c->state = STATE_START;
  124 +
  125 + oc_setreg(i2c, OCI2C_DATA, addr);
  126 + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START);
  127 + return;
  128 + } else
  129 + i2c->state = (msg->flags & I2C_M_RD)
  130 + ? STATE_READ : STATE_WRITE;
  131 + } else {
  132 + i2c->state = STATE_DONE;
  133 + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);
  134 + return;
  135 + }
  136 + }
  137 +
  138 + if (i2c->state == STATE_READ) {
  139 + oc_setreg(i2c, OCI2C_CMD, i2c->pos == (msg->len-1) ?
  140 + OCI2C_CMD_READ_NACK : OCI2C_CMD_READ_ACK);
  141 + } else {
  142 + oc_setreg(i2c, OCI2C_DATA, msg->buf[i2c->pos++]);
  143 + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_WRITE);
  144 + }
  145 +}
  146 +
  147 +static irqreturn_t ocores_isr(int irq, void *dev_id, struct pt_regs *regs)
  148 +{
  149 + struct ocores_i2c *i2c = dev_id;
  150 +
  151 + ocores_process(i2c);
  152 +
  153 + return IRQ_HANDLED;
  154 +}
  155 +
  156 +static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
  157 +{
  158 + struct ocores_i2c *i2c = i2c_get_adapdata(adap);
  159 +
  160 + i2c->msg = msgs;
  161 + i2c->pos = 0;
  162 + i2c->nmsgs = num;
  163 + i2c->state = STATE_START;
  164 +
  165 + oc_setreg(i2c, OCI2C_DATA,
  166 + (i2c->msg->addr << 1) |
  167 + ((i2c->msg->flags & I2C_M_RD) ? 1:0));
  168 +
  169 + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START);
  170 +
  171 + if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) ||
  172 + (i2c->state == STATE_DONE), HZ))
  173 + return (i2c->state == STATE_DONE) ? num : -EIO;
  174 + else
  175 + return -ETIMEDOUT;
  176 +}
  177 +
  178 +static void ocores_init(struct ocores_i2c *i2c,
  179 + struct ocores_i2c_platform_data *pdata)
  180 +{
  181 + int prescale;
  182 + u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL);
  183 +
  184 + /* make sure the device is disabled */
  185 + oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN));
  186 +
  187 + prescale = (pdata->clock_khz / (5*100)) - 1;
  188 + oc_setreg(i2c, OCI2C_PRELOW, prescale & 0xff);
  189 + oc_setreg(i2c, OCI2C_PREHIGH, prescale >> 8);
  190 +
  191 + /* Init the device */
  192 + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK);
  193 + oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_IEN | OCI2C_CTRL_EN);
  194 +}
  195 +
  196 +
  197 +static u32 ocores_func(struct i2c_adapter *adap)
  198 +{
  199 + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
  200 +}
  201 +
  202 +static struct i2c_algorithm ocores_algorithm = {
  203 + .master_xfer = ocores_xfer,
  204 + .functionality = ocores_func,
  205 +};
  206 +
  207 +static struct i2c_adapter ocores_adapter = {
  208 + .owner = THIS_MODULE,
  209 + .name = "i2c-ocores",
  210 + .class = I2C_CLASS_HWMON,
  211 + .algo = &ocores_algorithm,
  212 + .timeout = 2,
  213 + .retries = 1,
  214 +};
  215 +
  216 +
  217 +static int __devinit ocores_i2c_probe(struct platform_device *pdev)
  218 +{
  219 + struct ocores_i2c *i2c;
  220 + struct ocores_i2c_platform_data *pdata;
  221 + struct resource *res, *res2;
  222 + int ret;
  223 +
  224 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  225 + if (!res)
  226 + return -ENODEV;
  227 +
  228 + res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
  229 + if (!res2)
  230 + return -ENODEV;
  231 +
  232 + pdata = (struct ocores_i2c_platform_data*) pdev->dev.platform_data;
  233 + if (!pdata)
  234 + return -ENODEV;
  235 +
  236 + i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
  237 + if (!i2c)
  238 + return -ENOMEM;
  239 +
  240 + if (!request_mem_region(res->start, res->end - res->start + 1,
  241 + pdev->name)) {
  242 + dev_err(&pdev->dev, "Memory region busy\n");
  243 + ret = -EBUSY;
  244 + goto request_mem_failed;
  245 + }
  246 +
  247 + i2c->base = ioremap(res->start, res->end - res->start + 1);
  248 + if (!i2c->base) {
  249 + dev_err(&pdev->dev, "Unable to map registers\n");
  250 + ret = -EIO;
  251 + goto map_failed;
  252 + }
  253 +
  254 + i2c->regstep = pdata->regstep;
  255 + ocores_init(i2c, pdata);
  256 +
  257 + init_waitqueue_head(&i2c->wait);
  258 + ret = request_irq(res2->start, ocores_isr, 0, pdev->name, i2c);
  259 + if (ret) {
  260 + dev_err(&pdev->dev, "Cannot claim IRQ\n");
  261 + goto request_irq_failed;
  262 + }
  263 +
  264 + /* hook up driver to tree */
  265 + platform_set_drvdata(pdev, i2c);
  266 + i2c->adap = ocores_adapter;
  267 + i2c_set_adapdata(&i2c->adap, i2c);
  268 + i2c->adap.dev.parent = &pdev->dev;
  269 +
  270 + /* add i2c adapter to i2c tree */
  271 + ret = i2c_add_adapter(&i2c->adap);
  272 + if (ret) {
  273 + dev_err(&pdev->dev, "Failed to add adapter\n");
  274 + goto add_adapter_failed;
  275 + }
  276 +
  277 + return 0;
  278 +
  279 +add_adapter_failed:
  280 + free_irq(res2->start, i2c);
  281 +request_irq_failed:
  282 + iounmap(i2c->base);
  283 +map_failed:
  284 + release_mem_region(res->start, res->end - res->start + 1);
  285 +request_mem_failed:
  286 + kfree(i2c);
  287 +
  288 + return ret;
  289 +}
  290 +
  291 +static int __devexit ocores_i2c_remove(struct platform_device* pdev)
  292 +{
  293 + struct ocores_i2c *i2c = platform_get_drvdata(pdev);
  294 + struct resource *res;
  295 +
  296 + /* disable i2c logic */
  297 + oc_setreg(i2c, OCI2C_CONTROL, oc_getreg(i2c, OCI2C_CONTROL)
  298 + & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN));
  299 +
  300 + /* remove adapter & data */
  301 + i2c_del_adapter(&i2c->adap);
  302 + platform_set_drvdata(pdev, NULL);
  303 +
  304 + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
  305 + if (res)
  306 + free_irq(res->start, i2c);
  307 +
  308 + iounmap(i2c->base);
  309 +
  310 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  311 + if (res)
  312 + release_mem_region(res->start, res->end - res->start + 1);
  313 +
  314 + kfree(i2c);
  315 +
  316 + return 0;
  317 +}
  318 +
  319 +static struct platform_driver ocores_i2c_driver = {
  320 + .probe = ocores_i2c_probe,
  321 + .remove = __devexit_p(ocores_i2c_remove),
  322 + .driver = {
  323 + .owner = THIS_MODULE,
  324 + .name = "ocores-i2c",
  325 + },
  326 +};
  327 +
  328 +static int __init ocores_i2c_init(void)
  329 +{
  330 + return platform_driver_register(&ocores_i2c_driver);
  331 +}
  332 +
  333 +static void __exit ocores_i2c_exit(void)
  334 +{
  335 + platform_driver_unregister(&ocores_i2c_driver);
  336 +}
  337 +
  338 +module_init(ocores_i2c_init);
  339 +module_exit(ocores_i2c_exit);
  340 +
  341 +MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
  342 +MODULE_DESCRIPTION("OpenCores I2C bus driver");
  343 +MODULE_LICENSE("GPL");
include/linux/i2c-ocores.h
  1 +/*
  2 + * i2c-ocores.h - definitions for the i2c-ocores interface
  3 + *
  4 + * Peter Korsgaard <jacmet@sunsite.dk>
  5 + *
  6 + * This file is licensed under the terms of the GNU General Public License
  7 + * version 2. This program is licensed "as is" without any warranty of any
  8 + * kind, whether express or implied.
  9 + */
  10 +
  11 +#ifndef _LINUX_I2C_OCORES_H
  12 +#define _LINUX_I2C_OCORES_H
  13 +
  14 +struct ocores_i2c_platform_data {
  15 + u32 regstep; /* distance between registers */
  16 + u32 clock_khz; /* input clock in kHz */
  17 +};
  18 +
  19 +#endif /* _LINUX_I2C_OCORES_H */