Skip to content

Commit

Permalink
i2c: exynos: add support for HSI2C module on Exynos7
Browse files Browse the repository at this point in the history
The HSI2C module on Exynos7 differs in the transfer status
bits. Transfer status bits were moved to INT_ENABLE and
INT_STATUS registers

This patch adds support for the HSI2C module on Exynos7.
1. Implementes a "hw" field in the variant struct to distinguish
   the hardware.
2. Updates the dt-new compatible in dt-binding documenation

Signed-off-by: Naveen Krishna Chatradhi <[email protected]>
Signed-off-by: Wolfram Sang <[email protected]>
  • Loading branch information
Naveen Krishna Ch authored and Wolfram Sang committed Oct 3, 2014
1 parent 030f940 commit 2374a53
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 7 deletions.
2 changes: 2 additions & 0 deletions Documentation/devicetree/bindings/i2c/i2c-exynos5.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Required properties:
on Exynos5250 and Exynos5420 SoCs.
-> "samsung,exynos5260-hsi2c", for i2c compatible with HSI2C available
on Exynos5260 SoCs.
-> "samsung,exynos7-hsi2c", for i2c compatible with HSI2C available
on Exynos7 SoCs.

- reg: physical base address of the controller and length of memory mapped
region.
Expand Down
2 changes: 1 addition & 1 deletion drivers/i2c/busses/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ config I2C_EG20T

config I2C_EXYNOS5
tristate "Exynos5 high-speed I2C driver"
depends on ARCH_EXYNOS5 && OF
depends on ARCH_EXYNOS && OF
default y
help
High-speed I2C controller on Exynos5 based Samsung SoCs.
Expand Down
71 changes: 65 additions & 6 deletions drivers/i2c/busses/i2c-exynos5.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@
#define HSI2C_INT_TX_ALMOSTEMPTY_EN (1u << 0)
#define HSI2C_INT_RX_ALMOSTFULL_EN (1u << 1)
#define HSI2C_INT_TRAILING_EN (1u << 6)
#define HSI2C_INT_I2C_EN (1u << 9)

/* I2C_INT_STAT Register bits */
#define HSI2C_INT_TX_ALMOSTEMPTY (1u << 0)
Expand All @@ -95,6 +94,17 @@
#define HSI2C_INT_TRAILING (1u << 6)
#define HSI2C_INT_I2C (1u << 9)

#define HSI2C_INT_TRANS_DONE (1u << 7)
#define HSI2C_INT_TRANS_ABORT (1u << 8)
#define HSI2C_INT_NO_DEV_ACK (1u << 9)
#define HSI2C_INT_NO_DEV (1u << 10)
#define HSI2C_INT_TIMEOUT (1u << 11)
#define HSI2C_INT_I2C_TRANS (HSI2C_INT_TRANS_DONE | \
HSI2C_INT_TRANS_ABORT | \
HSI2C_INT_NO_DEV_ACK | \
HSI2C_INT_NO_DEV | \
HSI2C_INT_TIMEOUT)

/* I2C_FIFO_STAT Register bits */
#define HSI2C_RX_FIFO_EMPTY (1u << 24)
#define HSI2C_RX_FIFO_FULL (1u << 23)
Expand Down Expand Up @@ -143,6 +153,8 @@

#define EXYNOS5_I2C_TIMEOUT (msecs_to_jiffies(1000))

#define HSI2C_EXYNOS7 BIT(0)

struct exynos5_i2c {
struct i2c_adapter adap;
unsigned int suspended:1;
Expand Down Expand Up @@ -192,6 +204,7 @@ struct exynos5_i2c {
*/
struct exynos_hsi2c_variant {
unsigned int fifo_depth;
unsigned int hw;
};

static const struct exynos_hsi2c_variant exynos5250_hsi2c_data = {
Expand All @@ -202,6 +215,11 @@ static const struct exynos_hsi2c_variant exynos5260_hsi2c_data = {
.fifo_depth = 16,
};

static const struct exynos_hsi2c_variant exynos7_hsi2c_data = {
.fifo_depth = 16,
.hw = HSI2C_EXYNOS7,
};

static const struct of_device_id exynos5_i2c_match[] = {
{
.compatible = "samsung,exynos5-hsi2c",
Expand All @@ -212,6 +230,9 @@ static const struct of_device_id exynos5_i2c_match[] = {
}, {
.compatible = "samsung,exynos5260-hsi2c",
.data = &exynos5260_hsi2c_data
}, {
.compatible = "samsung,exynos7-hsi2c",
.data = &exynos7_hsi2c_data
}, {},
};
MODULE_DEVICE_TABLE(of, exynos5_i2c_match);
Expand Down Expand Up @@ -256,13 +277,24 @@ static int exynos5_i2c_set_timing(struct exynos5_i2c *i2c, int mode)
i2c->hs_clock : i2c->fs_clock;

/*
* In case of HSI2C controller in Exynos5 series
* FPCLK / FI2C =
* (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + 8 + 2 * FLT_CYCLE
*
* In case of HSI2C controllers in Exynos7 series
* FPCLK / FI2C =
* (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + 8 + FLT_CYCLE
*
* utemp0 = (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2)
* utemp1 = (TSCLK_L + TSCLK_H + 2)
*/
t_ftl_cycle = (readl(i2c->regs + HSI2C_CONF) >> 16) & 0x7;
utemp0 = (clkin / op_clk) - 8 - 2 * t_ftl_cycle;
utemp0 = (clkin / op_clk) - 8;

if (i2c->variant->hw == HSI2C_EXYNOS7)
utemp0 -= t_ftl_cycle;
else
utemp0 -= 2 * t_ftl_cycle;

/* CLK_DIV max is 256 */
for (div = 0; div < 256; div++) {
Expand Down Expand Up @@ -407,7 +439,28 @@ 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 (int_status & HSI2C_INT_I2C) {
if (i2c->variant->hw == HSI2C_EXYNOS7) {
if (int_status & HSI2C_INT_TRANS_DONE) {
i2c->trans_done = 1;
i2c->state = 0;
} else if (int_status & HSI2C_INT_TRANS_ABORT) {
dev_dbg(i2c->dev, "Deal with arbitration lose\n");
i2c->state = -EAGAIN;
goto stop;
} else if (int_status & HSI2C_INT_NO_DEV_ACK) {
dev_dbg(i2c->dev, "No ACK from device\n");
i2c->state = -ENXIO;
goto stop;
} else if (int_status & HSI2C_INT_NO_DEV) {
dev_dbg(i2c->dev, "No device\n");
i2c->state = -ENXIO;
goto stop;
} else if (int_status & HSI2C_INT_TIMEOUT) {
dev_dbg(i2c->dev, "Accessing device timed out\n");
i2c->state = -EAGAIN;
goto stop;
}
} else if (int_status & HSI2C_INT_I2C) {
trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
if (trans_status & HSI2C_NO_DEV_ACK) {
dev_dbg(i2c->dev, "No ACK from device\n");
Expand Down Expand Up @@ -512,12 +565,17 @@ static int exynos5_i2c_wait_bus_idle(struct exynos5_i2c *i2c)
static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop)
{
u32 i2c_ctl;
u32 int_en = HSI2C_INT_I2C_EN;
u32 int_en = 0;
u32 i2c_auto_conf = 0;
u32 fifo_ctl;
unsigned long flags;
unsigned short trig_lvl;

if (i2c->variant->hw == HSI2C_EXYNOS7)
int_en |= HSI2C_INT_I2C_TRANS;
else
int_en |= HSI2C_INT_I2C;

i2c_ctl = readl(i2c->regs + HSI2C_CTL);
i2c_ctl &= ~(HSI2C_TXCHON | HSI2C_RXCHON);
fifo_ctl = HSI2C_RXFIFO_EN | HSI2C_TXFIFO_EN;
Expand Down Expand Up @@ -724,12 +782,13 @@ static int exynos5_i2c_probe(struct platform_device *pdev)
goto err_clk;
}

/* Need to check the variant before setting up. */
i2c->variant = exynos5_i2c_get_variant(pdev);

ret = exynos5_hsi2c_clock_setup(i2c);
if (ret)
goto err_clk;

i2c->variant = exynos5_i2c_get_variant(pdev);

exynos5_i2c_reset(i2c);

ret = i2c_add_adapter(&i2c->adap);
Expand Down

0 comments on commit 2374a53

Please sign in to comment.