Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Peter Korsgaard and committed by
Greg Kroah-Hartman
18f98b1e aee62305

+425
+51
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 + };
+11
drivers/i2c/busses/Kconfig
··· 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
+1
drivers/i2c/busses/Makefile
··· 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
+343
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");
+19
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 */