diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-s3c2410.c')
-rw-r--r-- | drivers/i2c/busses/i2c-s3c2410.c | 109 |
1 files changed, 65 insertions, 44 deletions
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 737f7218a32c..fa0b13490873 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c | |||
@@ -44,8 +44,12 @@ | |||
44 | #include <plat/regs-iic.h> | 44 | #include <plat/regs-iic.h> |
45 | #include <plat/iic.h> | 45 | #include <plat/iic.h> |
46 | 46 | ||
47 | /* i2c controller state */ | 47 | /* Treat S3C2410 as baseline hardware, anything else is supported via quirks */ |
48 | #define QUIRK_S3C2440 (1 << 0) | ||
49 | #define QUIRK_HDMIPHY (1 << 1) | ||
50 | #define QUIRK_NO_GPIO (1 << 2) | ||
48 | 51 | ||
52 | /* i2c controller state */ | ||
49 | enum s3c24xx_i2c_state { | 53 | enum s3c24xx_i2c_state { |
50 | STATE_IDLE, | 54 | STATE_IDLE, |
51 | STATE_START, | 55 | STATE_START, |
@@ -54,14 +58,10 @@ enum s3c24xx_i2c_state { | |||
54 | STATE_STOP | 58 | STATE_STOP |
55 | }; | 59 | }; |
56 | 60 | ||
57 | enum s3c24xx_i2c_type { | ||
58 | TYPE_S3C2410, | ||
59 | TYPE_S3C2440, | ||
60 | }; | ||
61 | |||
62 | struct s3c24xx_i2c { | 61 | struct s3c24xx_i2c { |
63 | spinlock_t lock; | 62 | spinlock_t lock; |
64 | wait_queue_head_t wait; | 63 | wait_queue_head_t wait; |
64 | unsigned int quirks; | ||
65 | unsigned int suspended:1; | 65 | unsigned int suspended:1; |
66 | 66 | ||
67 | struct i2c_msg *msg; | 67 | struct i2c_msg *msg; |
@@ -88,26 +88,45 @@ struct s3c24xx_i2c { | |||
88 | #endif | 88 | #endif |
89 | }; | 89 | }; |
90 | 90 | ||
91 | /* default platform data removed, dev should always carry data. */ | 91 | static struct platform_device_id s3c24xx_driver_ids[] = { |
92 | { | ||
93 | .name = "s3c2410-i2c", | ||
94 | .driver_data = 0, | ||
95 | }, { | ||
96 | .name = "s3c2440-i2c", | ||
97 | .driver_data = QUIRK_S3C2440, | ||
98 | }, { | ||
99 | .name = "s3c2440-hdmiphy-i2c", | ||
100 | .driver_data = QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO, | ||
101 | }, { }, | ||
102 | }; | ||
103 | MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); | ||
104 | |||
105 | #ifdef CONFIG_OF | ||
106 | static const struct of_device_id s3c24xx_i2c_match[] = { | ||
107 | { .compatible = "samsung,s3c2410-i2c", .data = (void *)0 }, | ||
108 | { .compatible = "samsung,s3c2440-i2c", .data = (void *)QUIRK_S3C2440 }, | ||
109 | { .compatible = "samsung,s3c2440-hdmiphy-i2c", | ||
110 | .data = (void *)(QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO) }, | ||
111 | {}, | ||
112 | }; | ||
113 | MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match); | ||
114 | #endif | ||
92 | 115 | ||
93 | /* s3c24xx_i2c_is2440() | 116 | /* s3c24xx_get_device_quirks |
94 | * | 117 | * |
95 | * return true is this is an s3c2440 | 118 | * Get controller type either from device tree or platform device variant. |
96 | */ | 119 | */ |
97 | 120 | ||
98 | static inline int s3c24xx_i2c_is2440(struct s3c24xx_i2c *i2c) | 121 | static inline unsigned int s3c24xx_get_device_quirks(struct platform_device *pdev) |
99 | { | 122 | { |
100 | struct platform_device *pdev = to_platform_device(i2c->dev); | 123 | if (pdev->dev.of_node) { |
101 | enum s3c24xx_i2c_type type; | 124 | const struct of_device_id *match; |
102 | 125 | match = of_match_node(&s3c24xx_i2c_match, pdev->dev.of_node); | |
103 | #ifdef CONFIG_OF | 126 | return (unsigned int)match->data; |
104 | if (i2c->dev->of_node) | 127 | } |
105 | return of_device_is_compatible(i2c->dev->of_node, | ||
106 | "samsung,s3c2440-i2c"); | ||
107 | #endif | ||
108 | 128 | ||
109 | type = platform_get_device_id(pdev)->driver_data; | 129 | return platform_get_device_id(pdev)->driver_data; |
110 | return type == TYPE_S3C2440; | ||
111 | } | 130 | } |
112 | 131 | ||
113 | /* s3c24xx_i2c_master_complete | 132 | /* s3c24xx_i2c_master_complete |
@@ -471,6 +490,13 @@ static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c) | |||
471 | unsigned long iicstat; | 490 | unsigned long iicstat; |
472 | int timeout = 400; | 491 | int timeout = 400; |
473 | 492 | ||
493 | /* the timeout for HDMIPHY is reduced to 10 ms because | ||
494 | * the hangup is expected to happen, so waiting 400 ms | ||
495 | * causes only unnecessary system hangup | ||
496 | */ | ||
497 | if (i2c->quirks & QUIRK_HDMIPHY) | ||
498 | timeout = 10; | ||
499 | |||
474 | while (timeout-- > 0) { | 500 | while (timeout-- > 0) { |
475 | iicstat = readl(i2c->regs + S3C2410_IICSTAT); | 501 | iicstat = readl(i2c->regs + S3C2410_IICSTAT); |
476 | 502 | ||
@@ -480,6 +506,15 @@ static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c) | |||
480 | msleep(1); | 506 | msleep(1); |
481 | } | 507 | } |
482 | 508 | ||
509 | /* hang-up of bus dedicated for HDMIPHY occurred, resetting */ | ||
510 | if (i2c->quirks & QUIRK_HDMIPHY) { | ||
511 | writel(0, i2c->regs + S3C2410_IICCON); | ||
512 | writel(0, i2c->regs + S3C2410_IICSTAT); | ||
513 | writel(0, i2c->regs + S3C2410_IICDS); | ||
514 | |||
515 | return 0; | ||
516 | } | ||
517 | |||
483 | return -ETIMEDOUT; | 518 | return -ETIMEDOUT; |
484 | } | 519 | } |
485 | 520 | ||
@@ -676,7 +711,7 @@ static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got) | |||
676 | 711 | ||
677 | writel(iiccon, i2c->regs + S3C2410_IICCON); | 712 | writel(iiccon, i2c->regs + S3C2410_IICCON); |
678 | 713 | ||
679 | if (s3c24xx_i2c_is2440(i2c)) { | 714 | if (i2c->quirks & QUIRK_S3C2440) { |
680 | unsigned long sda_delay; | 715 | unsigned long sda_delay; |
681 | 716 | ||
682 | if (pdata->sda_delay) { | 717 | if (pdata->sda_delay) { |
@@ -761,6 +796,9 @@ static int s3c24xx_i2c_parse_dt_gpio(struct s3c24xx_i2c *i2c) | |||
761 | { | 796 | { |
762 | int idx, gpio, ret; | 797 | int idx, gpio, ret; |
763 | 798 | ||
799 | if (i2c->quirks & QUIRK_NO_GPIO) | ||
800 | return 0; | ||
801 | |||
764 | for (idx = 0; idx < 2; idx++) { | 802 | for (idx = 0; idx < 2; idx++) { |
765 | gpio = of_get_gpio(i2c->dev->of_node, idx); | 803 | gpio = of_get_gpio(i2c->dev->of_node, idx); |
766 | if (!gpio_is_valid(gpio)) { | 804 | if (!gpio_is_valid(gpio)) { |
@@ -785,6 +823,10 @@ free_gpio: | |||
785 | static void s3c24xx_i2c_dt_gpio_free(struct s3c24xx_i2c *i2c) | 823 | static void s3c24xx_i2c_dt_gpio_free(struct s3c24xx_i2c *i2c) |
786 | { | 824 | { |
787 | unsigned int idx; | 825 | unsigned int idx; |
826 | |||
827 | if (i2c->quirks & QUIRK_NO_GPIO) | ||
828 | return; | ||
829 | |||
788 | for (idx = 0; idx < 2; idx++) | 830 | for (idx = 0; idx < 2; idx++) |
789 | gpio_free(i2c->gpios[idx]); | 831 | gpio_free(i2c->gpios[idx]); |
790 | } | 832 | } |
@@ -906,6 +948,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) | |||
906 | goto err_noclk; | 948 | goto err_noclk; |
907 | } | 949 | } |
908 | 950 | ||
951 | i2c->quirks = s3c24xx_get_device_quirks(pdev); | ||
909 | if (pdata) | 952 | if (pdata) |
910 | memcpy(i2c->pdata, pdata, sizeof(*pdata)); | 953 | memcpy(i2c->pdata, pdata, sizeof(*pdata)); |
911 | else | 954 | else |
@@ -1110,28 +1153,6 @@ static const struct dev_pm_ops s3c24xx_i2c_dev_pm_ops = { | |||
1110 | 1153 | ||
1111 | /* device driver for platform bus bits */ | 1154 | /* device driver for platform bus bits */ |
1112 | 1155 | ||
1113 | static struct platform_device_id s3c24xx_driver_ids[] = { | ||
1114 | { | ||
1115 | .name = "s3c2410-i2c", | ||
1116 | .driver_data = TYPE_S3C2410, | ||
1117 | }, { | ||
1118 | .name = "s3c2440-i2c", | ||
1119 | .driver_data = TYPE_S3C2440, | ||
1120 | }, { }, | ||
1121 | }; | ||
1122 | MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); | ||
1123 | |||
1124 | #ifdef CONFIG_OF | ||
1125 | static const struct of_device_id s3c24xx_i2c_match[] = { | ||
1126 | { .compatible = "samsung,s3c2410-i2c" }, | ||
1127 | { .compatible = "samsung,s3c2440-i2c" }, | ||
1128 | {}, | ||
1129 | }; | ||
1130 | MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match); | ||
1131 | #else | ||
1132 | #define s3c24xx_i2c_match NULL | ||
1133 | #endif | ||
1134 | |||
1135 | static struct platform_driver s3c24xx_i2c_driver = { | 1156 | static struct platform_driver s3c24xx_i2c_driver = { |
1136 | .probe = s3c24xx_i2c_probe, | 1157 | .probe = s3c24xx_i2c_probe, |
1137 | .remove = s3c24xx_i2c_remove, | 1158 | .remove = s3c24xx_i2c_remove, |
@@ -1140,7 +1161,7 @@ static struct platform_driver s3c24xx_i2c_driver = { | |||
1140 | .owner = THIS_MODULE, | 1161 | .owner = THIS_MODULE, |
1141 | .name = "s3c-i2c", | 1162 | .name = "s3c-i2c", |
1142 | .pm = S3C24XX_DEV_PM_OPS, | 1163 | .pm = S3C24XX_DEV_PM_OPS, |
1143 | .of_match_table = s3c24xx_i2c_match, | 1164 | .of_match_table = of_match_ptr(s3c24xx_i2c_match), |
1144 | }, | 1165 | }, |
1145 | }; | 1166 | }; |
1146 | 1167 | ||