diff options
author | Christopher Kenna <cjk@cs.unc.edu> | 2012-09-28 13:46:28 -0400 |
---|---|---|
committer | Christopher Kenna <cjk@cs.unc.edu> | 2012-09-28 14:50:15 -0400 |
commit | daa22703f14c007e93b464c45fa60019a36f546d (patch) | |
tree | a1a130b6e128dc9d57c35c026977e1b4953105e1 /drivers/i2c | |
parent | 5aa287dcf1b5879aa0150b0511833c52885f5b4c (diff) |
Apply k4412 kernel from HardKernel for ODROID-X.
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/i2c-s3c2410.c | 103 |
1 files changed, 90 insertions, 13 deletions
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index f84a63c6dd9..d7ae653cec0 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c | |||
@@ -54,6 +54,7 @@ enum s3c24xx_i2c_state { | |||
54 | enum s3c24xx_i2c_type { | 54 | enum s3c24xx_i2c_type { |
55 | TYPE_S3C2410, | 55 | TYPE_S3C2410, |
56 | TYPE_S3C2440, | 56 | TYPE_S3C2440, |
57 | TYPE_S3C2440_HDMIPHY, | ||
57 | }; | 58 | }; |
58 | 59 | ||
59 | struct s3c24xx_i2c { | 60 | struct s3c24xx_i2c { |
@@ -85,6 +86,17 @@ struct s3c24xx_i2c { | |||
85 | 86 | ||
86 | /* default platform data removed, dev should always carry data. */ | 87 | /* default platform data removed, dev should always carry data. */ |
87 | 88 | ||
89 | static inline void dump_i2c_register(struct s3c24xx_i2c *i2c) | ||
90 | { | ||
91 | dev_dbg(i2c->dev, "Register dump(%d) : %x %x %x %x %x\n" | ||
92 | , i2c->suspended | ||
93 | , readl(i2c->regs + S3C2410_IICCON) | ||
94 | , readl(i2c->regs + S3C2410_IICSTAT) | ||
95 | , readl(i2c->regs + S3C2410_IICADD) | ||
96 | , readl(i2c->regs + S3C2410_IICDS) | ||
97 | , readl(i2c->regs + S3C2440_IICLC)); | ||
98 | } | ||
99 | |||
88 | /* s3c24xx_i2c_is2440() | 100 | /* s3c24xx_i2c_is2440() |
89 | * | 101 | * |
90 | * return true is this is an s3c2440 | 102 | * return true is this is an s3c2440 |
@@ -96,7 +108,20 @@ static inline int s3c24xx_i2c_is2440(struct s3c24xx_i2c *i2c) | |||
96 | enum s3c24xx_i2c_type type; | 108 | enum s3c24xx_i2c_type type; |
97 | 109 | ||
98 | type = platform_get_device_id(pdev)->driver_data; | 110 | type = platform_get_device_id(pdev)->driver_data; |
99 | return type == TYPE_S3C2440; | 111 | return type == TYPE_S3C2440 || type == TYPE_S3C2440_HDMIPHY; |
112 | } | ||
113 | |||
114 | /* s3c24xx_i2c_is2440_hdmiphy() | ||
115 | * | ||
116 | * return true is this is an s3c2440 dedicated for HDMIPHY interface | ||
117 | */ | ||
118 | static inline int s3c24xx_i2c_is2440_hdmiphy(struct s3c24xx_i2c *i2c) | ||
119 | { | ||
120 | struct platform_device *pdev = to_platform_device(i2c->dev); | ||
121 | enum s3c24xx_i2c_type type; | ||
122 | |||
123 | type = platform_get_device_id(pdev)->driver_data; | ||
124 | return type == TYPE_S3C2440_HDMIPHY; | ||
100 | } | 125 | } |
101 | 126 | ||
102 | /* s3c24xx_i2c_master_complete | 127 | /* s3c24xx_i2c_master_complete |
@@ -201,18 +226,29 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, | |||
201 | 226 | ||
202 | static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret) | 227 | static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret) |
203 | { | 228 | { |
204 | unsigned long iicstat = readl(i2c->regs + S3C2410_IICSTAT); | 229 | unsigned long iicstat; |
230 | unsigned long iiccon; | ||
205 | 231 | ||
206 | dev_dbg(i2c->dev, "STOP\n"); | 232 | dev_dbg(i2c->dev, "STOP\n"); |
207 | 233 | ||
208 | /* stop the transfer */ | 234 | /* stop the transfer */ |
235 | |||
236 | /* Disable irq */ | ||
237 | s3c24xx_i2c_disable_irq(i2c); | ||
238 | |||
239 | /* STOP signal generation : MTx(0xD0) */ | ||
240 | iicstat = readl(i2c->regs + S3C2410_IICSTAT); | ||
209 | iicstat &= ~S3C2410_IICSTAT_START; | 241 | iicstat &= ~S3C2410_IICSTAT_START; |
210 | writel(iicstat, i2c->regs + S3C2410_IICSTAT); | 242 | writel(iicstat, i2c->regs + S3C2410_IICSTAT); |
211 | 243 | ||
212 | i2c->state = STATE_STOP; | 244 | /* Clear pending bit */ |
245 | iiccon = readl(i2c->regs + S3C2410_IICCON); | ||
246 | iiccon &= ~S3C2410_IICCON_IRQPEND; | ||
247 | writel(iiccon, i2c->regs + S3C2410_IICCON); | ||
213 | 248 | ||
214 | s3c24xx_i2c_master_complete(i2c, ret); | 249 | s3c24xx_i2c_master_complete(i2c, ret); |
215 | s3c24xx_i2c_disable_irq(i2c); | 250 | |
251 | i2c->state = STATE_STOP; | ||
216 | } | 252 | } |
217 | 253 | ||
218 | /* helper functions to determine the current state in the set of | 254 | /* helper functions to determine the current state in the set of |
@@ -348,12 +384,21 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) | |||
348 | * forces us to send a new START | 384 | * forces us to send a new START |
349 | * when we change direction */ | 385 | * when we change direction */ |
350 | 386 | ||
387 | dev_dbg(i2c->dev, "Cannot do this\n"); | ||
351 | s3c24xx_i2c_stop(i2c, -EINVAL); | 388 | s3c24xx_i2c_stop(i2c, -EINVAL); |
352 | } | 389 | } |
353 | 390 | ||
391 | /* For multiple messages, | ||
392 | * ex) | ||
393 | * Msg[0]: Slave Addr + Write(Addr) | ||
394 | * Msg[1]: Write(Data) */ | ||
354 | goto retry_write; | 395 | goto retry_write; |
355 | } else { | 396 | } else { |
356 | /* send the new start */ | 397 | /* send the new start */ |
398 | /* For multiple messages, | ||
399 | * ex) | ||
400 | * Msg[0]: Slave Addr + Write(Addr) | ||
401 | * Msg[1]: Slave Addr + Read/Write(Data) */ | ||
357 | s3c24xx_i2c_message_start(i2c, i2c->msg); | 402 | s3c24xx_i2c_message_start(i2c, i2c->msg); |
358 | i2c->state = STATE_START; | 403 | i2c->state = STATE_START; |
359 | } | 404 | } |
@@ -424,6 +469,8 @@ static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id) | |||
424 | unsigned long status; | 469 | unsigned long status; |
425 | unsigned long tmp; | 470 | unsigned long tmp; |
426 | 471 | ||
472 | spin_lock(&i2c->lock); | ||
473 | |||
427 | status = readl(i2c->regs + S3C2410_IICSTAT); | 474 | status = readl(i2c->regs + S3C2410_IICSTAT); |
428 | 475 | ||
429 | if (status & S3C2410_IICSTAT_ARBITR) { | 476 | if (status & S3C2410_IICSTAT_ARBITR) { |
@@ -446,6 +493,8 @@ static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id) | |||
446 | i2c_s3c_irq_nextbyte(i2c, status); | 493 | i2c_s3c_irq_nextbyte(i2c, status); |
447 | 494 | ||
448 | out: | 495 | out: |
496 | spin_unlock(&i2c->lock); | ||
497 | |||
449 | return IRQ_HANDLED; | 498 | return IRQ_HANDLED; |
450 | } | 499 | } |
451 | 500 | ||
@@ -490,6 +539,7 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, | |||
490 | ret = s3c24xx_i2c_set_master(i2c); | 539 | ret = s3c24xx_i2c_set_master(i2c); |
491 | if (ret != 0) { | 540 | if (ret != 0) { |
492 | dev_err(i2c->dev, "cannot get bus (error %d)\n", ret); | 541 | dev_err(i2c->dev, "cannot get bus (error %d)\n", ret); |
542 | dump_i2c_register(i2c); | ||
493 | ret = -EAGAIN; | 543 | ret = -EAGAIN; |
494 | goto out; | 544 | goto out; |
495 | } | 545 | } |
@@ -513,10 +563,13 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, | |||
513 | /* having these next two as dev_err() makes life very | 563 | /* having these next two as dev_err() makes life very |
514 | * noisy when doing an i2cdetect */ | 564 | * noisy when doing an i2cdetect */ |
515 | 565 | ||
516 | if (timeout == 0) | 566 | if (timeout == 0) { |
517 | dev_dbg(i2c->dev, "timeout\n"); | 567 | dev_dbg(i2c->dev, "timeout\n"); |
518 | else if (ret != num) | 568 | dump_i2c_register(i2c); |
569 | } else if (ret != num) { | ||
519 | dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret); | 570 | dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret); |
571 | dump_i2c_register(i2c); | ||
572 | } | ||
520 | 573 | ||
521 | /* ensure the stop has been through the bus */ | 574 | /* ensure the stop has been through the bus */ |
522 | 575 | ||
@@ -525,7 +578,7 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, | |||
525 | /* first, try busy waiting briefly */ | 578 | /* first, try busy waiting briefly */ |
526 | do { | 579 | do { |
527 | iicstat = readl(i2c->regs + S3C2410_IICSTAT); | 580 | iicstat = readl(i2c->regs + S3C2410_IICSTAT); |
528 | } while ((iicstat & S3C2410_IICSTAT_START) && --spins); | 581 | } while ((iicstat & S3C2410_IICSTAT_BUSBUSY) && --spins); |
529 | 582 | ||
530 | /* if that timed out sleep */ | 583 | /* if that timed out sleep */ |
531 | if (!spins) { | 584 | if (!spins) { |
@@ -533,8 +586,23 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, | |||
533 | iicstat = readl(i2c->regs + S3C2410_IICSTAT); | 586 | iicstat = readl(i2c->regs + S3C2410_IICSTAT); |
534 | } | 587 | } |
535 | 588 | ||
536 | if (iicstat & S3C2410_IICSTAT_START) | 589 | /* if still not finished, clean it up */ |
537 | dev_warn(i2c->dev, "timeout waiting for bus idle\n"); | 590 | spin_lock_irq(&i2c->lock); |
591 | if (iicstat & S3C2410_IICSTAT_BUSBUSY) { | ||
592 | dev_dbg(i2c->dev, "timeout waiting for bus idle\n"); | ||
593 | dump_i2c_register(i2c); | ||
594 | |||
595 | if (i2c->state != STATE_STOP) { | ||
596 | dev_dbg(i2c->dev, "timeout : i2c interrupt hasn't occurred\n"); | ||
597 | s3c24xx_i2c_stop(i2c, 0); | ||
598 | } | ||
599 | |||
600 | /* Disable Serial Out : To forcely terminate the connection */ | ||
601 | iicstat = readl(i2c->regs + S3C2410_IICSTAT); | ||
602 | iicstat &= ~S3C2410_IICSTAT_TXRXEN; | ||
603 | writel(iicstat, i2c->regs + S3C2410_IICSTAT); | ||
604 | } | ||
605 | spin_unlock_irq(&i2c->lock); | ||
538 | 606 | ||
539 | out: | 607 | out: |
540 | return ret; | 608 | return ret; |
@@ -553,6 +621,12 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap, | |||
553 | int retry; | 621 | int retry; |
554 | int ret; | 622 | int ret; |
555 | 623 | ||
624 | if (i2c->suspended) { | ||
625 | dev_err(i2c->dev, "I2C is not initialzed.\n"); | ||
626 | dump_i2c_register(i2c); | ||
627 | return -EIO; | ||
628 | } | ||
629 | |||
556 | clk_enable(i2c->clk); | 630 | clk_enable(i2c->clk); |
557 | 631 | ||
558 | for (retry = 0; retry < adap->retries; retry++) { | 632 | for (retry = 0; retry < adap->retries; retry++) { |
@@ -710,7 +784,7 @@ static int s3c24xx_i2c_cpufreq_transition(struct notifier_block *nb, | |||
710 | if (ret < 0) | 784 | if (ret < 0) |
711 | dev_err(i2c->dev, "cannot find frequency\n"); | 785 | dev_err(i2c->dev, "cannot find frequency\n"); |
712 | else | 786 | else |
713 | dev_info(i2c->dev, "setting freq %d\n", got); | 787 | dev_dbg(i2c->dev, "setting freq %d\n", got); |
714 | } | 788 | } |
715 | 789 | ||
716 | return 0; | 790 | return 0; |
@@ -765,7 +839,7 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) | |||
765 | 839 | ||
766 | writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD); | 840 | writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD); |
767 | 841 | ||
768 | dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr); | 842 | dev_dbg(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr); |
769 | 843 | ||
770 | writel(iicon, i2c->regs + S3C2410_IICCON); | 844 | writel(iicon, i2c->regs + S3C2410_IICCON); |
771 | 845 | ||
@@ -779,7 +853,7 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) | |||
779 | 853 | ||
780 | /* todo - check that the i2c lines aren't being dragged anywhere */ | 854 | /* todo - check that the i2c lines aren't being dragged anywhere */ |
781 | 855 | ||
782 | dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq); | 856 | dev_dbg(i2c->dev, "bus frequency set to %d KHz\n", freq); |
783 | dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon); | 857 | dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon); |
784 | 858 | ||
785 | return 0; | 859 | return 0; |
@@ -981,10 +1055,10 @@ static int s3c24xx_i2c_resume(struct device *dev) | |||
981 | struct platform_device *pdev = to_platform_device(dev); | 1055 | struct platform_device *pdev = to_platform_device(dev); |
982 | struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); | 1056 | struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); |
983 | 1057 | ||
984 | i2c->suspended = 0; | ||
985 | clk_enable(i2c->clk); | 1058 | clk_enable(i2c->clk); |
986 | s3c24xx_i2c_init(i2c); | 1059 | s3c24xx_i2c_init(i2c); |
987 | clk_disable(i2c->clk); | 1060 | clk_disable(i2c->clk); |
1061 | i2c->suspended = 0; | ||
988 | 1062 | ||
989 | return 0; | 1063 | return 0; |
990 | } | 1064 | } |
@@ -1008,6 +1082,9 @@ static struct platform_device_id s3c24xx_driver_ids[] = { | |||
1008 | }, { | 1082 | }, { |
1009 | .name = "s3c2440-i2c", | 1083 | .name = "s3c2440-i2c", |
1010 | .driver_data = TYPE_S3C2440, | 1084 | .driver_data = TYPE_S3C2440, |
1085 | }, { | ||
1086 | .name = "s3c2440-hdmiphy-i2c", | ||
1087 | .driver_data = TYPE_S3C2440_HDMIPHY, | ||
1011 | }, { }, | 1088 | }, { }, |
1012 | }; | 1089 | }; |
1013 | MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); | 1090 | MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); |