forked from Minki/linux
i2c-s3c2410: Add HDMIPHY quirk for S3C2440
This patch adds support for s3c2440 I2C bus controller dedicated HDMIPHY device on Exynos4 platform. Some quirks are introduced due to differences between HDMIPHY and other I2C controllers on Exynos4. These differences are: - no GPIOs, HDMIPHY is inside the SoC and the controller is connected internally - due to unknown reason (probably HW bug in HDMIPHY and/or the controller) a transfer fails to finish. The controller hangs after sending the last byte, the workaround for this bug is resetting the controller after each transfer Signed-off-by: Tomasz Stanislawski <t.stanislaws@samsung.com> Signed-off-by: Karol Lewandowski <k.lewandowsk@samsung.com> Tested-by: Tomasz Stanislawski <t.stanislaws@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Wolfram Sang <w.sang@pengutronix.de>
This commit is contained in:
parent
27452498a6
commit
ec39ef83eb
@ -6,14 +6,18 @@ Required properties:
|
|||||||
- compatible: value should be either of the following.
|
- compatible: value should be either of the following.
|
||||||
(a) "samsung, s3c2410-i2c", for i2c compatible with s3c2410 i2c.
|
(a) "samsung, s3c2410-i2c", for i2c compatible with s3c2410 i2c.
|
||||||
(b) "samsung, s3c2440-i2c", for i2c compatible with s3c2440 i2c.
|
(b) "samsung, s3c2440-i2c", for i2c compatible with s3c2440 i2c.
|
||||||
|
(c) "samsung, s3c2440-hdmiphy-i2c", for s3c2440-like i2c used
|
||||||
|
inside HDMIPHY block found on several samsung SoCs
|
||||||
- reg: physical base address of the controller and length of memory mapped
|
- reg: physical base address of the controller and length of memory mapped
|
||||||
region.
|
region.
|
||||||
- interrupts: interrupt number to the cpu.
|
- interrupts: interrupt number to the cpu.
|
||||||
- samsung,i2c-sda-delay: Delay (in ns) applied to data line (SDA) edges.
|
- samsung,i2c-sda-delay: Delay (in ns) applied to data line (SDA) edges.
|
||||||
- gpios: The order of the gpios should be the following: <SDA, SCL>.
|
|
||||||
The gpio specifier depends on the gpio controller.
|
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
|
- gpios: The order of the gpios should be the following: <SDA, SCL>.
|
||||||
|
The gpio specifier depends on the gpio controller. Required in all
|
||||||
|
cases except for "samsung,s3c2440-hdmiphy-i2c" whose input/output
|
||||||
|
lines are permanently wired to the respective client
|
||||||
- samsung,i2c-slave-addr: Slave address in multi-master enviroment. If not
|
- samsung,i2c-slave-addr: Slave address in multi-master enviroment. If not
|
||||||
specified, default value is 0.
|
specified, default value is 0.
|
||||||
- samsung,i2c-max-bus-freq: Desired frequency in Hz of the bus. If not
|
- samsung,i2c-max-bus-freq: Desired frequency in Hz of the bus. If not
|
||||||
|
@ -46,6 +46,8 @@
|
|||||||
|
|
||||||
/* Treat S3C2410 as baseline hardware, anything else is supported via quirks */
|
/* Treat S3C2410 as baseline hardware, anything else is supported via quirks */
|
||||||
#define QUIRK_S3C2440 (1 << 0)
|
#define QUIRK_S3C2440 (1 << 0)
|
||||||
|
#define QUIRK_HDMIPHY (1 << 1)
|
||||||
|
#define QUIRK_NO_GPIO (1 << 2)
|
||||||
|
|
||||||
/* i2c controller state */
|
/* i2c controller state */
|
||||||
enum s3c24xx_i2c_state {
|
enum s3c24xx_i2c_state {
|
||||||
@ -93,6 +95,9 @@ static struct platform_device_id s3c24xx_driver_ids[] = {
|
|||||||
}, {
|
}, {
|
||||||
.name = "s3c2440-i2c",
|
.name = "s3c2440-i2c",
|
||||||
.driver_data = QUIRK_S3C2440,
|
.driver_data = QUIRK_S3C2440,
|
||||||
|
}, {
|
||||||
|
.name = "s3c2440-hdmiphy-i2c",
|
||||||
|
.driver_data = QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO,
|
||||||
}, { },
|
}, { },
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
|
MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
|
||||||
@ -101,6 +106,8 @@ MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
|
|||||||
static const struct of_device_id s3c24xx_i2c_match[] = {
|
static const struct of_device_id s3c24xx_i2c_match[] = {
|
||||||
{ .compatible = "samsung,s3c2410-i2c", .data = (void *)0 },
|
{ .compatible = "samsung,s3c2410-i2c", .data = (void *)0 },
|
||||||
{ .compatible = "samsung,s3c2440-i2c", .data = (void *)QUIRK_S3C2440 },
|
{ .compatible = "samsung,s3c2440-i2c", .data = (void *)QUIRK_S3C2440 },
|
||||||
|
{ .compatible = "samsung,s3c2440-hdmiphy-i2c",
|
||||||
|
.data = (void *)(QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO) },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match);
|
MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match);
|
||||||
@ -483,6 +490,13 @@ static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c)
|
|||||||
unsigned long iicstat;
|
unsigned long iicstat;
|
||||||
int timeout = 400;
|
int timeout = 400;
|
||||||
|
|
||||||
|
/* the timeout for HDMIPHY is reduced to 10 ms because
|
||||||
|
* the hangup is expected to happen, so waiting 400 ms
|
||||||
|
* causes only unnecessary system hangup
|
||||||
|
*/
|
||||||
|
if (i2c->quirks & QUIRK_HDMIPHY)
|
||||||
|
timeout = 10;
|
||||||
|
|
||||||
while (timeout-- > 0) {
|
while (timeout-- > 0) {
|
||||||
iicstat = readl(i2c->regs + S3C2410_IICSTAT);
|
iicstat = readl(i2c->regs + S3C2410_IICSTAT);
|
||||||
|
|
||||||
@ -492,6 +506,15 @@ static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c)
|
|||||||
msleep(1);
|
msleep(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* hang-up of bus dedicated for HDMIPHY occurred, resetting */
|
||||||
|
if (i2c->quirks & QUIRK_HDMIPHY) {
|
||||||
|
writel(0, i2c->regs + S3C2410_IICCON);
|
||||||
|
writel(0, i2c->regs + S3C2410_IICSTAT);
|
||||||
|
writel(0, i2c->regs + S3C2410_IICDS);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -773,6 +796,9 @@ static int s3c24xx_i2c_parse_dt_gpio(struct s3c24xx_i2c *i2c)
|
|||||||
{
|
{
|
||||||
int idx, gpio, ret;
|
int idx, gpio, ret;
|
||||||
|
|
||||||
|
if (i2c->quirks & QUIRK_NO_GPIO)
|
||||||
|
return 0;
|
||||||
|
|
||||||
for (idx = 0; idx < 2; idx++) {
|
for (idx = 0; idx < 2; idx++) {
|
||||||
gpio = of_get_gpio(i2c->dev->of_node, idx);
|
gpio = of_get_gpio(i2c->dev->of_node, idx);
|
||||||
if (!gpio_is_valid(gpio)) {
|
if (!gpio_is_valid(gpio)) {
|
||||||
@ -797,6 +823,10 @@ free_gpio:
|
|||||||
static void s3c24xx_i2c_dt_gpio_free(struct s3c24xx_i2c *i2c)
|
static void s3c24xx_i2c_dt_gpio_free(struct s3c24xx_i2c *i2c)
|
||||||
{
|
{
|
||||||
unsigned int idx;
|
unsigned int idx;
|
||||||
|
|
||||||
|
if (i2c->quirks & QUIRK_NO_GPIO)
|
||||||
|
return;
|
||||||
|
|
||||||
for (idx = 0; idx < 2; idx++)
|
for (idx = 0; idx < 2; idx++)
|
||||||
gpio_free(i2c->gpios[idx]);
|
gpio_free(i2c->gpios[idx]);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user