i2c: exynos5: Add support for ExynosAutoV9 SoC
ExynosAutoV9 functioning logic mostly follows I2C_TYPE_EXYNOS7, but timing calculation and configuration procedure is changed: e.g. only timing_s3 has to be set now. Another change of HSI2C controller in ExynosAutoV9 SoC is that it's now a part of USIv2 IP-core. No changes is needed for I2C driver though, as all USI related configuration is done in USI driver. Signed-off-by: Jaewon Kim <jaewon02.kim@samsung.com> Signed-off-by: Sam Protsenko <semen.protsenko@linaro.org> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com> Signed-off-by: Wolfram Sang <wsa@kernel.org>
This commit is contained in:
parent
ea8491a28b
commit
3f68910259
@ -169,6 +169,7 @@
|
||||
enum i2c_type_exynos {
|
||||
I2C_TYPE_EXYNOS5,
|
||||
I2C_TYPE_EXYNOS7,
|
||||
I2C_TYPE_EXYNOSAUTOV9,
|
||||
};
|
||||
|
||||
struct exynos5_i2c {
|
||||
@ -230,6 +231,11 @@ static const struct exynos_hsi2c_variant exynos7_hsi2c_data = {
|
||||
.hw = I2C_TYPE_EXYNOS7,
|
||||
};
|
||||
|
||||
static const struct exynos_hsi2c_variant exynosautov9_hsi2c_data = {
|
||||
.fifo_depth = 64,
|
||||
.hw = I2C_TYPE_EXYNOSAUTOV9,
|
||||
};
|
||||
|
||||
static const struct of_device_id exynos5_i2c_match[] = {
|
||||
{
|
||||
.compatible = "samsung,exynos5-hsi2c",
|
||||
@ -243,6 +249,9 @@ static const struct of_device_id exynos5_i2c_match[] = {
|
||||
}, {
|
||||
.compatible = "samsung,exynos7-hsi2c",
|
||||
.data = &exynos7_hsi2c_data
|
||||
}, {
|
||||
.compatible = "samsung,exynosautov9-hsi2c",
|
||||
.data = &exynosautov9_hsi2c_data
|
||||
}, {},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, exynos5_i2c_match);
|
||||
@ -281,6 +290,31 @@ static int exynos5_i2c_set_timing(struct exynos5_i2c *i2c, bool hs_timings)
|
||||
i2c->op_clock;
|
||||
int div, clk_cycle, temp;
|
||||
|
||||
/*
|
||||
* In case of HSI2C controllers in ExynosAutoV9:
|
||||
*
|
||||
* FSCL = IPCLK / ((CLK_DIV + 1) * 16)
|
||||
* T_SCL_LOW = IPCLK * (CLK_DIV + 1) * (N + M)
|
||||
* [N : number of 0's in the TSCL_H_HS]
|
||||
* [M : number of 0's in the TSCL_L_HS]
|
||||
* T_SCL_HIGH = IPCLK * (CLK_DIV + 1) * (N + M)
|
||||
* [N : number of 1's in the TSCL_H_HS]
|
||||
* [M : number of 1's in the TSCL_L_HS]
|
||||
*
|
||||
* Result of (N + M) is always 8.
|
||||
* In general case, we don't need to control timing_s1 and timing_s2.
|
||||
*/
|
||||
if (i2c->variant->hw == I2C_TYPE_EXYNOSAUTOV9) {
|
||||
div = ((clkin / (16 * i2c->op_clock)) - 1);
|
||||
i2c_timing_s3 = div << 16;
|
||||
if (hs_timings)
|
||||
writel(i2c_timing_s3, i2c->regs + HSI2C_TIMING_HS3);
|
||||
else
|
||||
writel(i2c_timing_s3, i2c->regs + HSI2C_TIMING_FS3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* In case of HSI2C controller in Exynos5 series
|
||||
* FPCLK / FI2C =
|
||||
@ -422,7 +456,10 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
|
||||
writel(int_status, i2c->regs + HSI2C_INT_STATUS);
|
||||
|
||||
/* handle interrupt related to the transfer status */
|
||||
if (i2c->variant->hw == I2C_TYPE_EXYNOS7) {
|
||||
switch (i2c->variant->hw) {
|
||||
case I2C_TYPE_EXYNOSAUTOV9:
|
||||
fallthrough;
|
||||
case I2C_TYPE_EXYNOS7:
|
||||
if (int_status & HSI2C_INT_TRANS_DONE) {
|
||||
i2c->trans_done = 1;
|
||||
i2c->state = 0;
|
||||
@ -443,7 +480,12 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
|
||||
i2c->state = -ETIMEDOUT;
|
||||
goto stop;
|
||||
}
|
||||
} else if (int_status & HSI2C_INT_I2C) {
|
||||
|
||||
break;
|
||||
case I2C_TYPE_EXYNOS5:
|
||||
if (!(int_status & HSI2C_INT_I2C))
|
||||
break;
|
||||
|
||||
trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
|
||||
if (trans_status & HSI2C_NO_DEV_ACK) {
|
||||
dev_dbg(i2c->dev, "No ACK from device\n");
|
||||
@ -465,6 +507,8 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
|
||||
i2c->trans_done = 1;
|
||||
i2c->state = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ((i2c->msg->flags & I2C_M_RD) && (int_status &
|
||||
@ -569,13 +613,13 @@ static void exynos5_i2c_bus_check(struct exynos5_i2c *i2c)
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
||||
if (i2c->variant->hw != I2C_TYPE_EXYNOS7)
|
||||
if (i2c->variant->hw == I2C_TYPE_EXYNOS5)
|
||||
return;
|
||||
|
||||
/*
|
||||
* HSI2C_MASTER_ST_LOSE state in EXYNOS7 variant before transaction
|
||||
* indicates that bus is stuck (SDA is low). In such case bus recovery
|
||||
* can be performed.
|
||||
* HSI2C_MASTER_ST_LOSE state (in Exynos7 and ExynosAutoV9 variants)
|
||||
* before transaction indicates that bus is stuck (SDA is low).
|
||||
* In such case bus recovery can be performed.
|
||||
*/
|
||||
timeout = jiffies + msecs_to_jiffies(100);
|
||||
for (;;) {
|
||||
@ -611,10 +655,10 @@ static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop)
|
||||
unsigned long flags;
|
||||
unsigned short trig_lvl;
|
||||
|
||||
if (i2c->variant->hw == I2C_TYPE_EXYNOS7)
|
||||
int_en |= HSI2C_INT_I2C_TRANS;
|
||||
else
|
||||
if (i2c->variant->hw == I2C_TYPE_EXYNOS5)
|
||||
int_en |= HSI2C_INT_I2C;
|
||||
else
|
||||
int_en |= HSI2C_INT_I2C_TRANS;
|
||||
|
||||
i2c_ctl = readl(i2c->regs + HSI2C_CTL);
|
||||
i2c_ctl &= ~(HSI2C_TXCHON | HSI2C_RXCHON);
|
||||
|
Loading…
Reference in New Issue
Block a user