Manuel Lauss | a26c20b | 2008-04-22 22:16:47 +0200 | [diff] [blame] | 1 | /* |
| 2 | * I2C bus driver for the SH7760 I2C Interfaces. |
| 3 | * |
| 4 | * (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau@msc-ge.com> |
| 5 | * |
| 6 | * licensed under the terms outlined in the file COPYING. |
| 7 | * |
| 8 | */ |
| 9 | |
| 10 | #include <linux/completion.h> |
| 11 | #include <linux/delay.h> |
| 12 | #include <linux/err.h> |
| 13 | #include <linux/i2c.h> |
| 14 | #include <linux/init.h> |
| 15 | #include <linux/interrupt.h> |
| 16 | #include <linux/ioport.h> |
| 17 | #include <linux/platform_device.h> |
| 18 | #include <linux/slab.h> |
| 19 | |
| 20 | #include <asm/clock.h> |
| 21 | #include <asm/i2c-sh7760.h> |
| 22 | #include <asm/io.h> |
| 23 | |
| 24 | /* register offsets */ |
| 25 | #define I2CSCR 0x0 /* slave ctrl */ |
| 26 | #define I2CMCR 0x4 /* master ctrl */ |
| 27 | #define I2CSSR 0x8 /* slave status */ |
| 28 | #define I2CMSR 0xC /* master status */ |
| 29 | #define I2CSIER 0x10 /* slave irq enable */ |
| 30 | #define I2CMIER 0x14 /* master irq enable */ |
| 31 | #define I2CCCR 0x18 /* clock dividers */ |
| 32 | #define I2CSAR 0x1c /* slave address */ |
| 33 | #define I2CMAR 0x20 /* master address */ |
| 34 | #define I2CRXTX 0x24 /* data port */ |
| 35 | #define I2CFCR 0x28 /* fifo control */ |
| 36 | #define I2CFSR 0x2C /* fifo status */ |
| 37 | #define I2CFIER 0x30 /* fifo irq enable */ |
| 38 | #define I2CRFDR 0x34 /* rx fifo count */ |
| 39 | #define I2CTFDR 0x38 /* tx fifo count */ |
| 40 | |
| 41 | #define REGSIZE 0x3C |
| 42 | |
| 43 | #define MCR_MDBS 0x80 /* non-fifo mode switch */ |
| 44 | #define MCR_FSCL 0x40 /* override SCL pin */ |
| 45 | #define MCR_FSDA 0x20 /* override SDA pin */ |
| 46 | #define MCR_OBPC 0x10 /* override pins */ |
| 47 | #define MCR_MIE 0x08 /* master if enable */ |
| 48 | #define MCR_TSBE 0x04 |
| 49 | #define MCR_FSB 0x02 /* force stop bit */ |
| 50 | #define MCR_ESG 0x01 /* en startbit gen. */ |
| 51 | |
| 52 | #define MSR_MNR 0x40 /* nack received */ |
| 53 | #define MSR_MAL 0x20 /* arbitration lost */ |
| 54 | #define MSR_MST 0x10 /* sent a stop */ |
| 55 | #define MSR_MDE 0x08 |
| 56 | #define MSR_MDT 0x04 |
| 57 | #define MSR_MDR 0x02 |
| 58 | #define MSR_MAT 0x01 /* slave addr xfer done */ |
| 59 | |
| 60 | #define MIE_MNRE 0x40 /* nack irq en */ |
| 61 | #define MIE_MALE 0x20 /* arblos irq en */ |
| 62 | #define MIE_MSTE 0x10 /* stop irq en */ |
| 63 | #define MIE_MDEE 0x08 |
| 64 | #define MIE_MDTE 0x04 |
| 65 | #define MIE_MDRE 0x02 |
| 66 | #define MIE_MATE 0x01 /* address sent irq en */ |
| 67 | |
| 68 | #define FCR_RFRST 0x02 /* reset rx fifo */ |
| 69 | #define FCR_TFRST 0x01 /* reset tx fifo */ |
| 70 | |
| 71 | #define FSR_TEND 0x04 /* last byte sent */ |
| 72 | #define FSR_RDF 0x02 /* rx fifo trigger */ |
| 73 | #define FSR_TDFE 0x01 /* tx fifo empty */ |
| 74 | |
| 75 | #define FIER_TEIE 0x04 /* tx fifo empty irq en */ |
| 76 | #define FIER_RXIE 0x02 /* rx fifo trig irq en */ |
| 77 | #define FIER_TXIE 0x01 /* tx fifo trig irq en */ |
| 78 | |
| 79 | #define FIFO_SIZE 16 |
| 80 | |
| 81 | struct cami2c { |
| 82 | void __iomem *iobase; |
| 83 | struct i2c_adapter adap; |
| 84 | |
| 85 | /* message processing */ |
| 86 | struct i2c_msg *msg; |
| 87 | #define IDF_SEND 1 |
| 88 | #define IDF_RECV 2 |
| 89 | #define IDF_STOP 4 |
| 90 | int flags; |
| 91 | |
| 92 | #define IDS_DONE 1 |
| 93 | #define IDS_ARBLOST 2 |
| 94 | #define IDS_NACK 4 |
| 95 | int status; |
| 96 | struct completion xfer_done; |
| 97 | |
| 98 | int irq; |
| 99 | struct resource *ioarea; |
| 100 | }; |
| 101 | |
| 102 | static inline void OUT32(struct cami2c *cam, int reg, unsigned long val) |
| 103 | { |
| 104 | ctrl_outl(val, (unsigned long)cam->iobase + reg); |
| 105 | } |
| 106 | |
| 107 | static inline unsigned long IN32(struct cami2c *cam, int reg) |
| 108 | { |
| 109 | return ctrl_inl((unsigned long)cam->iobase + reg); |
| 110 | } |
| 111 | |
| 112 | static irqreturn_t sh7760_i2c_irq(int irq, void *ptr) |
| 113 | { |
| 114 | struct cami2c *id = ptr; |
| 115 | struct i2c_msg *msg = id->msg; |
| 116 | char *data = msg->buf; |
| 117 | unsigned long msr, fsr, fier, len; |
| 118 | |
| 119 | msr = IN32(id, I2CMSR); |
| 120 | fsr = IN32(id, I2CFSR); |
| 121 | |
| 122 | /* arbitration lost */ |
| 123 | if (msr & MSR_MAL) { |
| 124 | OUT32(id, I2CMCR, 0); |
| 125 | OUT32(id, I2CSCR, 0); |
| 126 | OUT32(id, I2CSAR, 0); |
| 127 | id->status |= IDS_DONE | IDS_ARBLOST; |
| 128 | goto out; |
| 129 | } |
| 130 | |
| 131 | if (msr & MSR_MNR) { |
| 132 | /* NACK handling is very screwed up. After receiving a |
| 133 | * NAK IRQ one has to wait a bit before writing to any |
| 134 | * registers, or the ctl will lock up. After that delay |
| 135 | * do a normal i2c stop. Then wait at least 1 ms before |
| 136 | * attempting another transfer or ctl will stop working |
| 137 | */ |
| 138 | udelay(100); /* wait or risk ctl hang */ |
| 139 | OUT32(id, I2CFCR, FCR_RFRST | FCR_TFRST); |
| 140 | OUT32(id, I2CMCR, MCR_MIE | MCR_FSB); |
| 141 | OUT32(id, I2CFIER, 0); |
| 142 | OUT32(id, I2CMIER, MIE_MSTE); |
| 143 | OUT32(id, I2CSCR, 0); |
| 144 | OUT32(id, I2CSAR, 0); |
| 145 | id->status |= IDS_NACK; |
| 146 | msr &= ~MSR_MAT; |
| 147 | fsr = 0; |
| 148 | /* In some cases the MST bit is also set. */ |
| 149 | } |
| 150 | |
| 151 | /* i2c-stop was sent */ |
| 152 | if (msr & MSR_MST) { |
| 153 | id->status |= IDS_DONE; |
| 154 | goto out; |
| 155 | } |
| 156 | |
| 157 | /* i2c slave addr was sent; set to "normal" operation */ |
| 158 | if (msr & MSR_MAT) |
| 159 | OUT32(id, I2CMCR, MCR_MIE); |
| 160 | |
| 161 | fier = IN32(id, I2CFIER); |
| 162 | |
| 163 | if (fsr & FSR_RDF) { |
| 164 | len = IN32(id, I2CRFDR); |
| 165 | if (msg->len <= len) { |
| 166 | if (id->flags & IDF_STOP) { |
| 167 | OUT32(id, I2CMCR, MCR_MIE | MCR_FSB); |
| 168 | OUT32(id, I2CFIER, 0); |
| 169 | /* manual says: wait >= 0.5 SCL times */ |
| 170 | udelay(5); |
| 171 | /* next int should be MST */ |
| 172 | } else { |
| 173 | id->status |= IDS_DONE; |
| 174 | /* keep the RDF bit: ctrl holds SCL low |
| 175 | * until the setup for the next i2c_msg |
| 176 | * clears this bit. |
| 177 | */ |
| 178 | fsr &= ~FSR_RDF; |
| 179 | } |
| 180 | } |
| 181 | while (msg->len && len) { |
| 182 | *data++ = IN32(id, I2CRXTX); |
| 183 | msg->len--; |
| 184 | len--; |
| 185 | } |
| 186 | |
| 187 | if (msg->len) { |
| 188 | len = (msg->len >= FIFO_SIZE) ? FIFO_SIZE - 1 |
| 189 | : msg->len - 1; |
| 190 | |
| 191 | OUT32(id, I2CFCR, FCR_TFRST | ((len & 0xf) << 4)); |
| 192 | } |
| 193 | |
| 194 | } else if (id->flags & IDF_SEND) { |
| 195 | if ((fsr & FSR_TEND) && (msg->len < 1)) { |
| 196 | if (id->flags & IDF_STOP) { |
| 197 | OUT32(id, I2CMCR, MCR_MIE | MCR_FSB); |
| 198 | } else { |
| 199 | id->status |= IDS_DONE; |
| 200 | /* keep the TEND bit: ctl holds SCL low |
| 201 | * until the setup for the next i2c_msg |
| 202 | * clears this bit. |
| 203 | */ |
| 204 | fsr &= ~FSR_TEND; |
| 205 | } |
| 206 | } |
| 207 | if (fsr & FSR_TDFE) { |
| 208 | while (msg->len && (IN32(id, I2CTFDR) < FIFO_SIZE)) { |
| 209 | OUT32(id, I2CRXTX, *data++); |
| 210 | msg->len--; |
| 211 | } |
| 212 | |
| 213 | if (msg->len < 1) { |
| 214 | fier &= ~FIER_TXIE; |
| 215 | OUT32(id, I2CFIER, fier); |
| 216 | } else { |
| 217 | len = (msg->len >= FIFO_SIZE) ? 2 : 0; |
| 218 | OUT32(id, I2CFCR, |
| 219 | FCR_RFRST | ((len & 3) << 2)); |
| 220 | } |
| 221 | } |
| 222 | } |
| 223 | out: |
| 224 | if (id->status & IDS_DONE) { |
| 225 | OUT32(id, I2CMIER, 0); |
| 226 | OUT32(id, I2CFIER, 0); |
| 227 | id->msg = NULL; |
| 228 | complete(&id->xfer_done); |
| 229 | } |
| 230 | /* clear status flags and ctrl resumes work */ |
| 231 | OUT32(id, I2CMSR, ~msr); |
| 232 | OUT32(id, I2CFSR, ~fsr); |
| 233 | OUT32(id, I2CSSR, 0); |
| 234 | |
| 235 | return IRQ_HANDLED; |
| 236 | } |
| 237 | |
| 238 | |
| 239 | /* prepare and start a master receive operation */ |
| 240 | static void sh7760_i2c_mrecv(struct cami2c *id) |
| 241 | { |
| 242 | int len; |
| 243 | |
| 244 | id->flags |= IDF_RECV; |
| 245 | |
| 246 | /* set the slave addr reg; otherwise rcv wont work! */ |
| 247 | OUT32(id, I2CSAR, 0xfe); |
| 248 | OUT32(id, I2CMAR, (id->msg->addr << 1) | 1); |
| 249 | |
| 250 | /* adjust rx fifo trigger */ |
| 251 | if (id->msg->len >= FIFO_SIZE) |
| 252 | len = FIFO_SIZE - 1; /* trigger at fifo full */ |
| 253 | else |
| 254 | len = id->msg->len - 1; /* trigger before all received */ |
| 255 | |
| 256 | OUT32(id, I2CFCR, FCR_RFRST | FCR_TFRST); |
| 257 | OUT32(id, I2CFCR, FCR_TFRST | ((len & 0xF) << 4)); |
| 258 | |
| 259 | OUT32(id, I2CMSR, 0); |
| 260 | OUT32(id, I2CMCR, MCR_MIE | MCR_ESG); |
| 261 | OUT32(id, I2CMIER, MIE_MNRE | MIE_MALE | MIE_MSTE | MIE_MATE); |
| 262 | OUT32(id, I2CFIER, FIER_RXIE); |
| 263 | } |
| 264 | |
| 265 | /* prepare and start a master send operation */ |
| 266 | static void sh7760_i2c_msend(struct cami2c *id) |
| 267 | { |
| 268 | int len; |
| 269 | |
| 270 | id->flags |= IDF_SEND; |
| 271 | |
| 272 | /* set the slave addr reg; otherwise xmit wont work! */ |
| 273 | OUT32(id, I2CSAR, 0xfe); |
| 274 | OUT32(id, I2CMAR, (id->msg->addr << 1) | 0); |
| 275 | |
| 276 | /* adjust tx fifo trigger */ |
| 277 | if (id->msg->len >= FIFO_SIZE) |
| 278 | len = 2; /* trig: 2 bytes left in TX fifo */ |
| 279 | else |
| 280 | len = 0; /* trig: 8 bytes left in TX fifo */ |
| 281 | |
| 282 | OUT32(id, I2CFCR, FCR_RFRST | FCR_TFRST); |
| 283 | OUT32(id, I2CFCR, FCR_RFRST | ((len & 3) << 2)); |
| 284 | |
| 285 | while (id->msg->len && IN32(id, I2CTFDR) < FIFO_SIZE) { |
| 286 | OUT32(id, I2CRXTX, *(id->msg->buf)); |
| 287 | (id->msg->len)--; |
| 288 | (id->msg->buf)++; |
| 289 | } |
| 290 | |
| 291 | OUT32(id, I2CMSR, 0); |
| 292 | OUT32(id, I2CMCR, MCR_MIE | MCR_ESG); |
| 293 | OUT32(id, I2CFSR, 0); |
| 294 | OUT32(id, I2CMIER, MIE_MNRE | MIE_MALE | MIE_MSTE | MIE_MATE); |
| 295 | OUT32(id, I2CFIER, FIER_TEIE | (id->msg->len ? FIER_TXIE : 0)); |
| 296 | } |
| 297 | |
| 298 | static inline int sh7760_i2c_busy_check(struct cami2c *id) |
| 299 | { |
| 300 | return (IN32(id, I2CMCR) & MCR_FSDA); |
| 301 | } |
| 302 | |
| 303 | static int sh7760_i2c_master_xfer(struct i2c_adapter *adap, |
| 304 | struct i2c_msg *msgs, |
| 305 | int num) |
| 306 | { |
| 307 | struct cami2c *id = adap->algo_data; |
| 308 | int i, retr; |
| 309 | |
| 310 | if (sh7760_i2c_busy_check(id)) { |
| 311 | dev_err(&adap->dev, "sh7760-i2c%d: bus busy!\n", adap->nr); |
| 312 | return -EBUSY; |
| 313 | } |
| 314 | |
| 315 | i = 0; |
| 316 | while (i < num) { |
| 317 | retr = adap->retries; |
| 318 | retry: |
| 319 | id->flags = ((i == (num-1)) ? IDF_STOP : 0); |
| 320 | id->status = 0; |
| 321 | id->msg = msgs; |
| 322 | init_completion(&id->xfer_done); |
| 323 | |
| 324 | if (msgs->flags & I2C_M_RD) |
| 325 | sh7760_i2c_mrecv(id); |
| 326 | else |
| 327 | sh7760_i2c_msend(id); |
| 328 | |
| 329 | wait_for_completion(&id->xfer_done); |
| 330 | |
| 331 | if (id->status == 0) { |
| 332 | num = -EIO; |
| 333 | break; |
| 334 | } |
| 335 | |
| 336 | if (id->status & IDS_NACK) { |
| 337 | /* wait a bit or i2c module stops working */ |
| 338 | mdelay(1); |
| 339 | num = -EREMOTEIO; |
| 340 | break; |
| 341 | } |
| 342 | |
| 343 | if (id->status & IDS_ARBLOST) { |
| 344 | if (retr--) { |
| 345 | mdelay(2); |
| 346 | goto retry; |
| 347 | } |
| 348 | num = -EREMOTEIO; |
| 349 | break; |
| 350 | } |
| 351 | |
| 352 | msgs++; |
| 353 | i++; |
| 354 | } |
| 355 | |
| 356 | id->msg = NULL; |
| 357 | id->flags = 0; |
| 358 | id->status = 0; |
| 359 | |
| 360 | OUT32(id, I2CMCR, 0); |
| 361 | OUT32(id, I2CMSR, 0); |
| 362 | OUT32(id, I2CMIER, 0); |
| 363 | OUT32(id, I2CFIER, 0); |
| 364 | |
| 365 | /* reset slave module registers too: master mode enables slave |
| 366 | * module for receive ops (ack, data). Without this reset, |
| 367 | * eternal bus activity might be reported after NACK / ARBLOST. |
| 368 | */ |
| 369 | OUT32(id, I2CSCR, 0); |
| 370 | OUT32(id, I2CSAR, 0); |
| 371 | OUT32(id, I2CSSR, 0); |
| 372 | |
| 373 | return num; |
| 374 | } |
| 375 | |
| 376 | static u32 sh7760_i2c_func(struct i2c_adapter *adap) |
| 377 | { |
| 378 | return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); |
| 379 | } |
| 380 | |
| 381 | static const struct i2c_algorithm sh7760_i2c_algo = { |
| 382 | .master_xfer = sh7760_i2c_master_xfer, |
| 383 | .functionality = sh7760_i2c_func, |
| 384 | }; |
| 385 | |
| 386 | /* calculate CCR register setting for a desired scl clock. SCL clock is |
| 387 | * derived from I2C module clock (iclk) which in turn is derived from |
| 388 | * peripheral module clock (mclk, usually around 33MHz): |
| 389 | * iclk = mclk/(CDF + 1). iclk must be < 20MHz. |
| 390 | * scl = iclk/(SCGD*8 + 20). |
| 391 | */ |
| 392 | static int __devinit calc_CCR(unsigned long scl_hz) |
| 393 | { |
| 394 | struct clk *mclk; |
| 395 | unsigned long mck, m1, dff, odff, iclk; |
| 396 | signed char cdf, cdfm; |
| 397 | int scgd, scgdm, scgds; |
| 398 | |
| 399 | mclk = clk_get(NULL, "module_clk"); |
| 400 | if (IS_ERR(mclk)) { |
| 401 | return PTR_ERR(mclk); |
| 402 | } else { |
| 403 | mck = mclk->rate; |
| 404 | clk_put(mclk); |
| 405 | } |
| 406 | |
| 407 | odff = scl_hz; |
| 408 | scgdm = cdfm = m1 = 0; |
| 409 | for (cdf = 3; cdf >= 0; cdf--) { |
| 410 | iclk = mck / (1 + cdf); |
| 411 | if (iclk >= 20000000) |
| 412 | continue; |
| 413 | scgds = ((iclk / scl_hz) - 20) >> 3; |
| 414 | for (scgd = scgds; (scgd < 63) && scgd <= scgds + 1; scgd++) { |
| 415 | m1 = iclk / (20 + (scgd << 3)); |
| 416 | dff = abs(scl_hz - m1); |
| 417 | if (dff < odff) { |
| 418 | odff = dff; |
| 419 | cdfm = cdf; |
| 420 | scgdm = scgd; |
| 421 | } |
| 422 | } |
| 423 | } |
| 424 | /* fail if more than 25% off of requested SCL */ |
| 425 | if (odff > (scl_hz >> 2)) |
| 426 | return -EINVAL; |
| 427 | |
| 428 | /* create a CCR register value */ |
| 429 | return ((scgdm << 2) | cdfm); |
| 430 | } |
| 431 | |
| 432 | static int __devinit sh7760_i2c_probe(struct platform_device *pdev) |
| 433 | { |
| 434 | struct sh7760_i2c_platdata *pd; |
| 435 | struct resource *res; |
| 436 | struct cami2c *id; |
| 437 | int ret; |
| 438 | |
| 439 | pd = pdev->dev.platform_data; |
| 440 | if (!pd) { |
| 441 | dev_err(&pdev->dev, "no platform_data!\n"); |
| 442 | ret = -ENODEV; |
| 443 | goto out0; |
| 444 | } |
| 445 | |
| 446 | id = kzalloc(sizeof(struct cami2c), GFP_KERNEL); |
| 447 | if (!id) { |
| 448 | dev_err(&pdev->dev, "no mem for private data\n"); |
| 449 | ret = -ENOMEM; |
| 450 | goto out0; |
| 451 | } |
| 452 | |
| 453 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 454 | if (!res) { |
| 455 | dev_err(&pdev->dev, "no mmio resources\n"); |
| 456 | ret = -ENODEV; |
| 457 | goto out1; |
| 458 | } |
| 459 | |
| 460 | id->ioarea = request_mem_region(res->start, REGSIZE, pdev->name); |
| 461 | if (!id->ioarea) { |
| 462 | dev_err(&pdev->dev, "mmio already reserved\n"); |
| 463 | ret = -EBUSY; |
| 464 | goto out1; |
| 465 | } |
| 466 | |
| 467 | id->iobase = ioremap(res->start, REGSIZE); |
| 468 | if (!id->iobase) { |
| 469 | dev_err(&pdev->dev, "cannot ioremap\n"); |
| 470 | ret = -ENODEV; |
| 471 | goto out2; |
| 472 | } |
| 473 | |
| 474 | id->irq = platform_get_irq(pdev, 0); |
| 475 | |
| 476 | id->adap.nr = pdev->id; |
| 477 | id->adap.algo = &sh7760_i2c_algo; |
Jean Delvare | e1995f6 | 2009-01-07 14:29:16 +0100 | [diff] [blame] | 478 | id->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; |
Manuel Lauss | a26c20b | 2008-04-22 22:16:47 +0200 | [diff] [blame] | 479 | id->adap.retries = 3; |
| 480 | id->adap.algo_data = id; |
| 481 | id->adap.dev.parent = &pdev->dev; |
| 482 | snprintf(id->adap.name, sizeof(id->adap.name), |
| 483 | "SH7760 I2C at %08lx", (unsigned long)res->start); |
| 484 | |
| 485 | OUT32(id, I2CMCR, 0); |
| 486 | OUT32(id, I2CMSR, 0); |
| 487 | OUT32(id, I2CMIER, 0); |
| 488 | OUT32(id, I2CMAR, 0); |
| 489 | OUT32(id, I2CSIER, 0); |
| 490 | OUT32(id, I2CSAR, 0); |
| 491 | OUT32(id, I2CSCR, 0); |
| 492 | OUT32(id, I2CSSR, 0); |
| 493 | OUT32(id, I2CFIER, 0); |
| 494 | OUT32(id, I2CFCR, FCR_RFRST | FCR_TFRST); |
| 495 | OUT32(id, I2CFSR, 0); |
| 496 | |
| 497 | ret = calc_CCR(pd->speed_khz * 1000); |
| 498 | if (ret < 0) { |
| 499 | dev_err(&pdev->dev, "invalid SCL clock: %dkHz\n", |
| 500 | pd->speed_khz); |
| 501 | goto out3; |
| 502 | } |
| 503 | OUT32(id, I2CCCR, ret); |
| 504 | |
| 505 | if (request_irq(id->irq, sh7760_i2c_irq, IRQF_DISABLED, |
| 506 | SH7760_I2C_DEVNAME, id)) { |
| 507 | dev_err(&pdev->dev, "cannot get irq %d\n", id->irq); |
| 508 | ret = -EBUSY; |
| 509 | goto out3; |
| 510 | } |
| 511 | |
| 512 | ret = i2c_add_numbered_adapter(&id->adap); |
| 513 | if (ret < 0) { |
| 514 | dev_err(&pdev->dev, "reg adap failed: %d\n", ret); |
| 515 | goto out4; |
| 516 | } |
| 517 | |
| 518 | platform_set_drvdata(pdev, id); |
| 519 | |
| 520 | dev_info(&pdev->dev, "%d kHz mmio %08x irq %d\n", |
| 521 | pd->speed_khz, res->start, id->irq); |
| 522 | |
| 523 | return 0; |
| 524 | |
| 525 | out4: |
| 526 | free_irq(id->irq, id); |
| 527 | out3: |
| 528 | iounmap(id->iobase); |
| 529 | out2: |
| 530 | release_resource(id->ioarea); |
| 531 | kfree(id->ioarea); |
| 532 | out1: |
| 533 | kfree(id); |
| 534 | out0: |
| 535 | return ret; |
| 536 | } |
| 537 | |
| 538 | static int __devexit sh7760_i2c_remove(struct platform_device *pdev) |
| 539 | { |
| 540 | struct cami2c *id = platform_get_drvdata(pdev); |
| 541 | |
| 542 | i2c_del_adapter(&id->adap); |
| 543 | free_irq(id->irq, id); |
| 544 | iounmap(id->iobase); |
| 545 | release_resource(id->ioarea); |
| 546 | kfree(id->ioarea); |
| 547 | kfree(id); |
| 548 | platform_set_drvdata(pdev, NULL); |
| 549 | |
| 550 | return 0; |
| 551 | } |
| 552 | |
| 553 | static struct platform_driver sh7760_i2c_drv = { |
| 554 | .driver = { |
| 555 | .name = SH7760_I2C_DEVNAME, |
| 556 | .owner = THIS_MODULE, |
| 557 | }, |
| 558 | .probe = sh7760_i2c_probe, |
| 559 | .remove = __devexit_p(sh7760_i2c_remove), |
| 560 | }; |
| 561 | |
| 562 | static int __init sh7760_i2c_init(void) |
| 563 | { |
| 564 | return platform_driver_register(&sh7760_i2c_drv); |
| 565 | } |
| 566 | |
| 567 | static void __exit sh7760_i2c_exit(void) |
| 568 | { |
| 569 | platform_driver_unregister(&sh7760_i2c_drv); |
| 570 | } |
| 571 | |
| 572 | module_init(sh7760_i2c_init); |
| 573 | module_exit(sh7760_i2c_exit); |
| 574 | |
| 575 | MODULE_LICENSE("GPL"); |
| 576 | MODULE_DESCRIPTION("SH7760 I2C bus driver"); |
| 577 | MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); |