From: Youngmin Nam Date: Thu, 6 Apr 2017 06:02:25 +0000 (+0900) Subject: i2c: exynos5: Migration from 3.18 based kernel X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=eff77c10d56e24fb5def9f16703852eda1b97664;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git i2c: exynos5: Migration from 3.18 based kernel Change-Id: Ibfe5c46e2c7f383db94f973298e0c507140596f9 Signed-off-by: Youngmin Nam --- diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c index 3855e0b11877..84b986c19ddb 100644 --- a/drivers/i2c/busses/i2c-exynos5.c +++ b/drivers/i2c/busses/i2c-exynos5.c @@ -12,19 +12,31 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include #include #include #include #include -#include +#include +#include "../../pinctrl/core.h" + +#include +#ifdef CONFIG_CPU_IDLE +#include +#endif + +#if defined(CONFIG_CPU_IDLE) +static LIST_HEAD(drvdata_list); +#endif /* * HSI2C controller from Samsung supports 2 modes of operation @@ -69,14 +81,16 @@ #define HSI2C_MASTER (1u << 3) #define HSI2C_RXCHON (1u << 6) #define HSI2C_TXCHON (1u << 7) +#define HSI2C_EXT_MSB (1u << 29) +#define HSI2C_EXT_ADDR (1u << 30) #define HSI2C_SW_RST (1u << 31) /* I2C_FIFO_CTL Register bits */ #define HSI2C_RXFIFO_EN (1u << 0) #define HSI2C_TXFIFO_EN (1u << 1) -#define HSI2C_RXFIFO_TRIGGER_LEVEL(x) ((x) << 4) -#define HSI2C_TXFIFO_TRIGGER_LEVEL(x) ((x) << 16) - +#define HSI2C_FIFO_MAX (0x40) +#define HSI2C_RXFIFO_TRIGGER_LEVEL (0x8 << 4) +#define HSI2C_TXFIFO_TRIGGER_LEVEL (0x8 << 16) /* I2C_TRAILING_CTL Register bits */ #define HSI2C_TRAILING_COUNT (0xf) @@ -84,6 +98,9 @@ #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_TRANSFER_DONE (1u << 7) +#define HSI2C_INT_I2C_EN (1u << 9) +#define HSI2C_INT_CHK_TRANS_STATE (0xf << 8) /* I2C_INT_STAT Register bits */ #define HSI2C_INT_TX_ALMOSTEMPTY (1u << 0) @@ -94,30 +111,31 @@ #define HSI2C_INT_RX_OVERRUN (1u << 5) #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) +#define HSI2C_RX_INT (HSI2C_INT_RX_ALMOSTFULL | \ + HSI2C_INT_RX_UNDERRUN | \ + HSI2C_INT_RX_OVERRUN | \ + HSI2C_INT_TRAILING) /* I2C_FIFO_STAT Register bits */ #define HSI2C_RX_FIFO_EMPTY (1u << 24) #define HSI2C_RX_FIFO_FULL (1u << 23) #define HSI2C_RX_FIFO_LVL(x) ((x >> 16) & 0x7f) +#define HSI2C_RX_FIFO_LVL_MASK (0x7F << 16) #define HSI2C_TX_FIFO_EMPTY (1u << 8) #define HSI2C_TX_FIFO_FULL (1u << 7) #define HSI2C_TX_FIFO_LVL(x) ((x >> 0) & 0x7f) +#define HSI2C_TX_FIFO_LVL_MASK (0x7F << 0) +#define HSI2C_FIFO_EMPTY (HSI2C_RX_FIFO_EMPTY | \ + HSI2C_TX_FIFO_EMPTY) /* I2C_CONF Register bits */ #define HSI2C_AUTO_MODE (1u << 31) #define HSI2C_10BIT_ADDR_MODE (1u << 30) #define HSI2C_HS_MODE (1u << 29) +#define HSI2C_FILTER_EN_SCL (1u << 28) +#define HSI2C_FILTER_EN_SDA (1u << 27) +#define HSI2C_FTL_CYCLE_SCL_MASK (0x7 << 16) +#define HSI2C_FTL_CYCLE_SDA_MASK (0x7 << 13) /* I2C_AUTO_CONF Register bits */ #define HSI2C_READ_WRITE (1u << 16) @@ -126,7 +144,6 @@ /* I2C_TIMEOUT Register bits */ #define HSI2C_TIMEOUT_EN (1u << 31) -#define HSI2C_TIMEOUT_MASK 0xff /* I2C_TRANS_STATUS register bits */ #define HSI2C_MASTER_BUSY (1u << 17) @@ -138,6 +155,7 @@ #define HSI2C_NO_DEV_ACK (1u << 2) #define HSI2C_TRANS_ABORT (1u << 1) #define HSI2C_TRANS_DONE (1u << 0) +#define HSI2C_MAST_ST_MASK (0xf << 0) /* I2C_TRANS_STATUS register bits for Exynos7 variant */ #define HSI2C_MASTER_ST_MASK 0xf @@ -167,30 +185,38 @@ * Controller operating frequency, timing values for operation * are calculated against this frequency */ -#define HSI2C_HS_TX_CLOCK 1000000 -#define HSI2C_FS_TX_CLOCK 100000 +#define HSI2C_HS_TX_CLOCK 2500000 +#define HSI2C_FS_TX_CLOCK 400000 +#define HSI2C_HIGH_SPD 1 +#define HSI2C_FAST_SPD 0 + +#define HSI2C_POLLING 0 +#define HSI2C_INTERRUPT 1 #define EXYNOS5_I2C_TIMEOUT (msecs_to_jiffies(1000)) +#define EXYNOS5_FIFO_SIZE 16 -#define HSI2C_EXYNOS7 BIT(0) +#define EXYNOS5_HSI2C_RUNTIME_PM_DELAY (100) struct exynos5_i2c { + struct list_head node; struct i2c_adapter adap; + unsigned int need_hw_init; unsigned int suspended:1; struct i2c_msg *msg; struct completion msg_complete; unsigned int msg_ptr; + unsigned int msg_len; unsigned int irq; void __iomem *regs; struct clk *clk; + struct clk *rate_clk; struct device *dev; int state; - spinlock_t lock; /* IRQ synchronization */ - /* * Since the TRANS_DONE bit is cleared on read, and we may read it * either during an IRQ or after a transaction, keep track of its @@ -199,54 +225,162 @@ struct exynos5_i2c { int trans_done; /* Controller operating frequency */ - unsigned int op_clock; + unsigned int fs_clock; + unsigned int hs_clock; - /* Version of HS-I2C Hardware */ - const struct exynos_hsi2c_variant *variant; + /* + * HSI2C Controller can operate in + * 1. High speed upto 3.4Mbps + * 2. Fast speed upto 1Mbps + */ + int speed_mode; + int operation_mode; + int bus_id; + int scl_clk_stretch; + int stop_after_trans; + unsigned int transfer_delay; + int idle_ip_index; }; -/** - * struct exynos_hsi2c_variant - platform specific HSI2C driver data - * @fifo_depth: the fifo depth supported by the HSI2C module - * - * Specifies platform specific configuration of HSI2C module. - * Note: A structure for driver specific platform data is used for future - * expansion of its usage. - */ -struct exynos_hsi2c_variant { - unsigned int fifo_depth; - unsigned int hw; +static const struct of_device_id exynos5_i2c_match[] = { + { .compatible = "samsung,exynos5-hsi2c" }, + {}, }; +MODULE_DEVICE_TABLE(of, exynos5_i2c_match); -static const struct exynos_hsi2c_variant exynos5250_hsi2c_data = { - .fifo_depth = 64, -}; +#ifdef CONFIG_GPIOLIB +static void change_i2c_gpio(struct exynos5_i2c *i2c) +{ + struct pinctrl_state *default_i2c_pins; + struct pinctrl *default_i2c_pinctrl; + int status = 0; + + default_i2c_pinctrl = devm_pinctrl_get(i2c->dev); + if (IS_ERR(default_i2c_pinctrl)) { + dev_err(i2c->dev, "Can't get i2c pinctrl!!!\n"); + return ; + } -static const struct exynos_hsi2c_variant exynos5260_hsi2c_data = { - .fifo_depth = 16, -}; + default_i2c_pins = pinctrl_lookup_state(default_i2c_pinctrl, + "default"); + if (!IS_ERR(default_i2c_pins)) { + default_i2c_pinctrl->state = NULL; + status = pinctrl_select_state(default_i2c_pinctrl, default_i2c_pins); + if (status) + dev_err(i2c->dev, "Can't set default i2c pins!!!\n"); + } else { + dev_err(i2c->dev, "Can't get default pinstate!!!\n"); + } +} -static const struct exynos_hsi2c_variant exynos7_hsi2c_data = { - .fifo_depth = 16, - .hw = HSI2C_EXYNOS7, -}; +static void recover_gpio_pins(struct exynos5_i2c *i2c) +{ + int gpio_sda, gpio_scl; + int sda_val, scl_val, clk_cnt; + unsigned long timeout; + struct device_node *np = i2c->adap.dev.of_node; -static const struct of_device_id exynos5_i2c_match[] = { - { - .compatible = "samsung,exynos5-hsi2c", - .data = &exynos5250_hsi2c_data - }, { - .compatible = "samsung,exynos5250-hsi2c", - .data = &exynos5250_hsi2c_data - }, { - .compatible = "samsung,exynos5260-hsi2c", - .data = &exynos5260_hsi2c_data - }, { - .compatible = "samsung,exynos7-hsi2c", - .data = &exynos7_hsi2c_data - }, {}, -}; -MODULE_DEVICE_TABLE(of, exynos5_i2c_match); + dev_err(i2c->dev, "Recover GPIO pins\n"); + + gpio_sda = of_get_named_gpio(np, "gpio_sda", 0); + if (!gpio_is_valid(gpio_sda)) { + dev_err(i2c->dev, "Can't get gpio_sda!!!\n"); + return ; + } + gpio_scl = of_get_named_gpio(np, "gpio_scl", 0); + if (!gpio_is_valid(gpio_scl)) { + dev_err(i2c->dev, "Can't get gpio_scl!!!\n"); + return ; + } + + sda_val = gpio_get_value(gpio_sda); + scl_val = gpio_get_value(gpio_scl); + + dev_err(i2c->dev, "SDA line : %s, SCL line : %s\n", + sda_val ? "HIGH" : "LOW", scl_val ? "HIGH" : "LOW"); + + if (sda_val == 1) + return ; + + /* Wait for SCL as high for 500msec */ + if (scl_val == 0) { + timeout = jiffies + msecs_to_jiffies(500); + while (time_before(jiffies, timeout)) { + if (gpio_get_value(gpio_scl) != 0) { + timeout = 0; + break; + } + msleep(10); + } + if (timeout) + dev_err(i2c->dev, "SCL line is still LOW!!!\n"); + } + + sda_val = gpio_get_value(gpio_sda); + + if (sda_val == 0) { + gpio_direction_output(gpio_scl, 1); + + for (clk_cnt = 0; clk_cnt < 100; clk_cnt++) { + /* Make clock for slave */ + gpio_set_value(gpio_scl, 0); + udelay(5); + gpio_set_value(gpio_scl, 1); + udelay(5); + if (gpio_get_value(gpio_sda) == 1) { + dev_err(i2c->dev, "SDA line is recovered.\n"); + break; + } + } + if (clk_cnt == 100) + dev_err(i2c->dev, "SDA line is not recovered!!!\n"); + } + + /* Change I2C GPIO as default function */ + change_i2c_gpio(i2c); +} +#endif + +static inline void dump_i2c_register(struct exynos5_i2c *i2c) +{ + dev_err(i2c->dev, "Register dump(suspended : %d)\n", i2c->suspended); + dev_err(i2c->dev, ": CTL 0x%08x\n" + , readl(i2c->regs + HSI2C_CTL)); + dev_err(i2c->dev, ": FIFO_CTL 0x%08x\n" + , readl(i2c->regs + HSI2C_FIFO_CTL)); + dev_err(i2c->dev, ": INT_EN 0x%08x\n" + , readl(i2c->regs + HSI2C_INT_ENABLE)); + dev_err(i2c->dev, ": INT_STAT 0x%08x\n" + , readl(i2c->regs + HSI2C_INT_STATUS)); + dev_err(i2c->dev, ": FIFO_STAT 0x%08x\n" + , readl(i2c->regs + HSI2C_FIFO_STATUS)); + dev_err(i2c->dev, ": CONF 0x%08x\n" + , readl(i2c->regs + HSI2C_CONF)); + dev_err(i2c->dev, ": AUTO_CONF 0x%08x\n" + , readl(i2c->regs + HSI2C_AUTO_CONF)); + dev_err(i2c->dev, ": TRANS_STAT 0x%08x\n" + , readl(i2c->regs + HSI2C_TRANS_STATUS)); + dev_err(i2c->dev, ": TIMING_HS1 0x%08x\n" + , readl(i2c->regs + HSI2C_TIMING_HS1)); + dev_err(i2c->dev, ": TIMING_HS2 0x%08x\n" + , readl(i2c->regs + HSI2C_TIMING_HS2)); + dev_err(i2c->dev, ": TIMING_HS3 0x%08x\n" + , readl(i2c->regs + HSI2C_TIMING_HS3)); + dev_err(i2c->dev, ": TIMING_FS1 0x%08x\n" + , readl(i2c->regs + HSI2C_TIMING_FS1)); + dev_err(i2c->dev, ": TIMING_FS2 0x%08x\n" + , readl(i2c->regs + HSI2C_TIMING_FS2)); + dev_err(i2c->dev, ": TIMING_FS3 0x%08x\n" + , readl(i2c->regs + HSI2C_TIMING_FS3)); + dev_err(i2c->dev, ": TIMING_SLA 0x%08x\n" + , readl(i2c->regs + HSI2C_TIMING_SLA)); + dev_err(i2c->dev, ": ADDR 0x%08x\n" + , readl(i2c->regs + HSI2C_ADDR)); + +#ifdef CONFIG_GPIOLIB + recover_gpio_pins(i2c); +#endif +} static void exynos5_i2c_clr_pend_irq(struct exynos5_i2c *i2c) { @@ -273,39 +407,31 @@ static int exynos5_i2c_set_timing(struct exynos5_i2c *i2c, bool hs_timings) unsigned int t_scl_l, t_scl_h; unsigned int t_sr_release; unsigned int t_ftl_cycle; - unsigned int clkin = clk_get_rate(i2c->clk); - unsigned int op_clk = hs_timings ? i2c->op_clock : - (i2c->op_clock >= HSI2C_HS_TX_CLOCK) ? HSI2C_FS_TX_CLOCK : - i2c->op_clock; - int div, clk_cycle, temp; + unsigned int clkin = clk_get_rate(i2c->rate_clk); + unsigned int div, utemp0 = 0, utemp1 = 0, clk_cycle = 0; + unsigned int op_clk = (mode == HSI2C_HIGH_SPD) ? + 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 - * - * clk_cycle := TSCLK_L + TSCLK_H - * temp := (CLK_DIV + 1) * (clk_cycle + 2) - * - * Constraints: 4 <= temp, 0 <= CLK_DIV < 256, 2 <= clk_cycle <= 510 - * + * (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + {(FLT_CYCLE + 3) % + * (CLK_DIV + 1)} * 2 */ t_ftl_cycle = (readl(i2c->regs + HSI2C_CONF) >> 16) & 0x7; - temp = clkin / op_clk - 8 - t_ftl_cycle; - if (i2c->variant->hw != HSI2C_EXYNOS7) - temp -= t_ftl_cycle; - div = temp / 512; - clk_cycle = temp / (div + 1) - 2; - if (temp < 4 || div >= 256 || clk_cycle < 2) { - dev_err(i2c->dev, "%s clock set-up failed\n", - hs_timings ? "HS" : "FS"); - return -EINVAL; + utemp0 = (clkin / op_clk) - (t_ftl_cycle + 3) * 2; + + /* CLK_DIV max is 256 */ + for (div = 0; div < 256; div++) { + utemp1 = (utemp0 + ((t_ftl_cycle + 3) % (div + 1)) * 2) / (div + 1); + + if ((utemp1 < 512) && (utemp1 > 4)) { + clk_cycle = utemp1 - 2; + break; + } else if (div == 255) { + dev_warn(i2c->dev, "Failed to calculate divisor"); + return -EINVAL; + } } - t_scl_l = clk_cycle / 2; t_scl_h = clk_cycle / 2; t_start_su = t_scl_l; @@ -360,23 +486,19 @@ static int exynos5_hsi2c_clock_setup(struct exynos5_i2c *i2c) static void exynos5_i2c_init(struct exynos5_i2c *i2c) { u32 i2c_conf = readl(i2c->regs + HSI2C_CONF); - u32 i2c_timeout = readl(i2c->regs + HSI2C_TIMEOUT); - - /* Clear to disable Timeout */ - i2c_timeout &= ~HSI2C_TIMEOUT_EN; - writel(i2c_timeout, i2c->regs + HSI2C_TIMEOUT); - writel((HSI2C_FUNC_MODE_I2C | HSI2C_MASTER), - i2c->regs + HSI2C_CTL); + writel(HSI2C_MASTER, i2c->regs + HSI2C_CTL); writel(HSI2C_TRAILING_COUNT, i2c->regs + HSI2C_TRAILIG_CTL); - if (i2c->op_clock >= HSI2C_HS_TX_CLOCK) { - writel(HSI2C_MASTER_ID(MASTER_ID(i2c->adap.nr)), + if (i2c->speed_mode == HSI2C_HIGH_SPD) { + writel(HSI2C_MASTER_ID(MASTER_ID(i2c->bus_id)), i2c->regs + HSI2C_ADDR); i2c_conf |= HSI2C_HS_MODE; } writel(i2c_conf | HSI2C_AUTO_MODE, i2c->regs + HSI2C_CONF); + + i2c->need_hw_init = 0; } static void exynos5_i2c_reset(struct exynos5_i2c *i2c) @@ -398,6 +520,13 @@ static void exynos5_i2c_reset(struct exynos5_i2c *i2c) exynos5_i2c_init(i2c); } +static inline void exynos5_i2c_stop(struct exynos5_i2c *i2c) +{ + writel(0, i2c->regs + HSI2C_INT_ENABLE); + + complete(&i2c->msg_complete); +} + /* * exynos5_i2c_irq: top level IRQ servicing routine * @@ -405,291 +534,371 @@ static void exynos5_i2c_reset(struct exynos5_i2c *i2c) * FIFO_STATUS or TRANS_STATUS registers are to be check for detailed * state of the bus. */ + static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id) { struct exynos5_i2c *i2c = dev_id; - u32 fifo_level, int_status, fifo_status, trans_status; + unsigned long reg_val; + unsigned long trans_status; unsigned char byte; - int len = 0; - - i2c->state = -EINVAL; - - spin_lock(&i2c->lock); - - int_status = readl(i2c->regs + HSI2C_INT_STATUS); - writel(int_status, i2c->regs + HSI2C_INT_STATUS); - - /* handle interrupt related to the transfer status */ - 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 = -ETIMEDOUT; - goto stop; - } - - trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS); - if ((trans_status & HSI2C_MASTER_ST_MASK) == HSI2C_MASTER_ST_LOSE) { - 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"); - i2c->state = -ENXIO; - goto stop; - } else if (trans_status & HSI2C_NO_DEV) { - dev_dbg(i2c->dev, "No device\n"); - i2c->state = -ENXIO; - goto stop; - } else if (trans_status & HSI2C_TRANS_ABORT) { - dev_dbg(i2c->dev, "Deal with arbitration lose\n"); - i2c->state = -EAGAIN; - goto stop; - } else if (trans_status & HSI2C_TIMEOUT_AUTO) { - dev_dbg(i2c->dev, "Accessing device timed out\n"); - i2c->state = -ETIMEDOUT; - goto stop; - } else if (trans_status & HSI2C_TRANS_DONE) { - i2c->trans_done = 1; - i2c->state = 0; - } - } - - if ((i2c->msg->flags & I2C_M_RD) && (int_status & - (HSI2C_INT_TRAILING | HSI2C_INT_RX_ALMOSTFULL))) { - fifo_status = readl(i2c->regs + HSI2C_FIFO_STATUS); - fifo_level = HSI2C_RX_FIFO_LVL(fifo_status); - len = min(fifo_level, i2c->msg->len - i2c->msg_ptr); - while (len > 0) { - byte = (unsigned char) - readl(i2c->regs + HSI2C_RX_DATA); + if (i2c->msg->flags & I2C_M_RD) { + while ((readl(i2c->regs + HSI2C_FIFO_STATUS) & + 0x1000000) == 0) { + byte = (unsigned char)readl(i2c->regs + HSI2C_RX_DATA); i2c->msg->buf[i2c->msg_ptr++] = byte; - len--; - } - i2c->state = 0; - } else if (int_status & HSI2C_INT_TX_ALMOSTEMPTY) { - fifo_status = readl(i2c->regs + HSI2C_FIFO_STATUS); - fifo_level = HSI2C_TX_FIFO_LVL(fifo_status); - - len = i2c->variant->fifo_depth - fifo_level; - if (len > (i2c->msg->len - i2c->msg_ptr)) { - u32 int_en = readl(i2c->regs + HSI2C_INT_ENABLE); - - int_en &= ~HSI2C_INT_TX_ALMOSTEMPTY_EN; - writel(int_en, i2c->regs + HSI2C_INT_ENABLE); - len = i2c->msg->len - i2c->msg_ptr; } - while (len > 0) { + if (i2c->msg_ptr >= i2c->msg->len) { + reg_val = readl(i2c->regs + HSI2C_INT_ENABLE); + reg_val &= ~(HSI2C_INT_RX_ALMOSTFULL_EN); + writel(reg_val, i2c->regs + HSI2C_INT_ENABLE); + exynos5_i2c_stop(i2c); + } + } else { + while ((readl(i2c->regs + HSI2C_FIFO_STATUS) & + 0x80) == 0) { + if (i2c->msg_ptr >= i2c->msg->len) { + reg_val = readl(i2c->regs + HSI2C_INT_ENABLE); + reg_val &= ~(HSI2C_INT_TX_ALMOSTEMPTY_EN); + writel(reg_val, i2c->regs + HSI2C_INT_ENABLE); + break; + } byte = i2c->msg->buf[i2c->msg_ptr++]; writel(byte, i2c->regs + HSI2C_TX_DATA); - len--; } - i2c->state = 0; } - stop: - if ((i2c->trans_done && (i2c->msg->len == i2c->msg_ptr)) || - (i2c->state < 0)) { - writel(0, i2c->regs + HSI2C_INT_ENABLE); - exynos5_i2c_clr_pend_irq(i2c); - complete(&i2c->msg_complete); + reg_val = readl(i2c->regs + HSI2C_INT_STATUS); + + /* + * Checking Error State in INT_STATUS register + */ + if (reg_val & HSI2C_INT_CHK_TRANS_STATE) { + trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS); + dev_err(i2c->dev, "HSI2C Error Interrupt " + "occurred(IS:0x%08x, TR:0x%08x)\n", + (unsigned int)reg_val, (unsigned int)trans_status); + i2c->trans_done = -ENXIO; + exynos5_i2c_stop(i2c); + goto out; } + /* Checking INT_TRANSFER_DONE */ + if ((reg_val & HSI2C_INT_TRANSFER_DONE) && + (i2c->msg_ptr >= i2c->msg->len) && + !(i2c->msg->flags & I2C_M_RD)) + exynos5_i2c_stop(i2c); - spin_unlock(&i2c->lock); +out: + writel(reg_val, i2c->regs + HSI2C_INT_STATUS); return IRQ_HANDLED; } -/* - * exynos5_i2c_wait_bus_idle - * - * Wait for the bus to go idle, indicated by the MASTER_BUSY bit being - * cleared. - * - * Returns -EBUSY if the bus cannot be bought to idle - */ -static int exynos5_i2c_wait_bus_idle(struct exynos5_i2c *i2c) +static int exynos5_i2c_xfer_msg(struct exynos5_i2c *i2c, + struct i2c_msg *msgs, int stop) { - unsigned long stop_time; - u32 trans_status; - - /* wait for 100 milli seconds for the bus to be idle */ - stop_time = jiffies + msecs_to_jiffies(100) + 1; - do { - trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS); - if (!(trans_status & HSI2C_MASTER_BUSY)) - return 0; - - usleep_range(50, 200); - } while (time_before(jiffies, stop_time)); + unsigned long timeout; + unsigned long trans_status; + unsigned long i2c_ctl; + unsigned long i2c_auto_conf; + unsigned long i2c_timeout; + unsigned long i2c_addr; + unsigned long i2c_int_en; + unsigned long i2c_fifo_ctl; + unsigned char byte; + int ret = 0; + int operation_mode = i2c->operation_mode; - return -EBUSY; -} + i2c->msg = msgs; + i2c->msg_ptr = 0; + i2c->trans_done = 0; -/* - * exynos5_i2c_message_start: Configures the bus and starts the xfer - * i2c: struct exynos5_i2c pointer for the current bus - * stop: Enables stop after transfer if set. Set for last transfer of - * in the list of messages. - * - * Configures the bus for read/write function - * Sets chip address to talk to, message length to be sent. - * Enables appropriate interrupts and sends start xfer command. - */ -static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop) -{ - u32 i2c_ctl; - 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; + reinit_completion(&i2c->msg_complete); i2c_ctl = readl(i2c->regs + HSI2C_CTL); - i2c_ctl &= ~(HSI2C_TXCHON | HSI2C_RXCHON); - fifo_ctl = HSI2C_RXFIFO_EN | HSI2C_TXFIFO_EN; + i2c_auto_conf = readl(i2c->regs + HSI2C_AUTO_CONF); + i2c_timeout = readl(i2c->regs + HSI2C_TIMEOUT); + i2c_timeout &= ~HSI2C_TIMEOUT_EN; + writel(i2c_timeout, i2c->regs + HSI2C_TIMEOUT); - if (i2c->msg->flags & I2C_M_RD) { + i2c_fifo_ctl = HSI2C_RXFIFO_EN | HSI2C_TXFIFO_EN | + HSI2C_TXFIFO_TRIGGER_LEVEL | HSI2C_RXFIFO_TRIGGER_LEVEL; + writel(i2c_fifo_ctl, i2c->regs + HSI2C_FIFO_CTL); + + i2c_int_en = 0; + if (msgs->flags & I2C_M_RD) { + i2c_ctl &= ~HSI2C_TXCHON; i2c_ctl |= HSI2C_RXCHON; i2c_auto_conf |= HSI2C_READ_WRITE; - trig_lvl = (i2c->msg->len > i2c->variant->fifo_depth) ? - (i2c->variant->fifo_depth * 3 / 4) : i2c->msg->len; - fifo_ctl |= HSI2C_RXFIFO_TRIGGER_LEVEL(trig_lvl); - - int_en |= (HSI2C_INT_RX_ALMOSTFULL_EN | + i2c_int_en |= (HSI2C_INT_RX_ALMOSTFULL_EN | HSI2C_INT_TRAILING_EN); } else { + i2c_ctl &= ~HSI2C_RXCHON; i2c_ctl |= HSI2C_TXCHON; - trig_lvl = (i2c->msg->len > i2c->variant->fifo_depth) ? - (i2c->variant->fifo_depth * 1 / 4) : i2c->msg->len; - fifo_ctl |= HSI2C_TXFIFO_TRIGGER_LEVEL(trig_lvl); + i2c_auto_conf &= ~HSI2C_READ_WRITE; - int_en |= HSI2C_INT_TX_ALMOSTEMPTY_EN; + i2c_int_en |= HSI2C_INT_TX_ALMOSTEMPTY_EN; } - writel(HSI2C_SLV_ADDR_MAS(i2c->msg->addr), i2c->regs + HSI2C_ADDR); - - writel(fifo_ctl, i2c->regs + HSI2C_FIFO_CTL); - writel(i2c_ctl, i2c->regs + HSI2C_CTL); - - /* - * Enable interrupts before starting the transfer so that we don't - * miss any INT_I2C interrupts. - */ - spin_lock_irqsave(&i2c->lock, flags); - writel(int_en, i2c->regs + HSI2C_INT_ENABLE); + if (operation_mode == HSI2C_INTERRUPT) + exynos5_i2c_clr_pend_irq(i2c); - if (stop == 1) + if ((stop == 1) || (i2c->stop_after_trans == 1)) i2c_auto_conf |= HSI2C_STOP_AFTER_TRANS; - i2c_auto_conf |= i2c->msg->len; - i2c_auto_conf |= HSI2C_MASTER_RUN; - writel(i2c_auto_conf, i2c->regs + HSI2C_AUTO_CONF); - spin_unlock_irqrestore(&i2c->lock, flags); -} + else + i2c_auto_conf &= ~HSI2C_STOP_AFTER_TRANS; -static int exynos5_i2c_xfer_msg(struct exynos5_i2c *i2c, - struct i2c_msg *msgs, int stop) -{ - unsigned long timeout; - int ret; + i2c_addr = readl(i2c->regs + HSI2C_ADDR); + i2c_addr &= ~(0x3ff << 10); + i2c_addr &= ~(0x3ff << 0); + i2c_addr &= ~(0xff << 24); + i2c_addr |= ((msgs->addr & 0x7f) << 10); + writel(i2c_addr, i2c->regs + HSI2C_ADDR); - i2c->msg = msgs; - i2c->msg_ptr = 0; - i2c->trans_done = 0; + writel(i2c_ctl, i2c->regs + HSI2C_CTL); - reinit_completion(&i2c->msg_complete); + i2c_auto_conf &= ~(0xffff); + i2c_auto_conf |= i2c->msg->len; + writel(i2c_auto_conf, i2c->regs + HSI2C_AUTO_CONF); - exynos5_i2c_message_start(i2c, stop); + i2c_auto_conf = readl(i2c->regs + HSI2C_AUTO_CONF); + i2c_auto_conf |= HSI2C_MASTER_RUN; + writel(i2c_auto_conf, i2c->regs + HSI2C_AUTO_CONF); - timeout = wait_for_completion_timeout(&i2c->msg_complete, - EXYNOS5_I2C_TIMEOUT); - if (timeout == 0) - ret = -ETIMEDOUT; - else - ret = i2c->state; + if (operation_mode == HSI2C_INTERRUPT) { + i2c_int_en |= HSI2C_INT_CHK_TRANS_STATE | HSI2C_INT_TRANSFER_DONE; + writel(i2c_int_en, i2c->regs + HSI2C_INT_ENABLE); + enable_irq(i2c->irq); + } else { + writel(0x0, i2c->regs + HSI2C_INT_ENABLE); + } - /* - * If this is the last message to be transfered (stop == 1) - * Then check if the bus can be brought back to idle. - */ - if (ret == 0 && stop) - ret = exynos5_i2c_wait_bus_idle(i2c); + ret = -EAGAIN; + if (msgs->flags & I2C_M_RD) { + if (operation_mode == HSI2C_POLLING) { + timeout = jiffies + EXYNOS5_I2C_TIMEOUT; + while (time_before(jiffies, timeout)){ + if ((readl(i2c->regs + HSI2C_FIFO_STATUS) & + 0x1000000) == 0) { + byte = (unsigned char)readl + (i2c->regs + HSI2C_RX_DATA); + i2c->msg->buf[i2c->msg_ptr++] + = byte; + } + + if (i2c->msg_ptr >= i2c->msg->len) { + ret = 0; + break; + } + } + + if (ret == -EAGAIN) { + dump_i2c_register(i2c); + exynos5_i2c_reset(i2c); + dev_warn(i2c->dev, "rx timeout\n"); + return ret; + } + } else { + timeout = wait_for_completion_timeout + (&i2c->msg_complete, EXYNOS5_I2C_TIMEOUT); + + ret = 0; + if (i2c->scl_clk_stretch) { + unsigned long timeout = jiffies + msecs_to_jiffies(100); + + do { + trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS); + if ((!(trans_status & HSI2C_MAST_ST_MASK)) || + ((stop == 0) && (trans_status & HSI2C_MASTER_BUSY))){ + timeout = 0; + break; + } + } while(time_before(jiffies, timeout)); + + if (timeout) + dev_err(i2c->dev, "SDA check timeout!!! = 0x%8lx\n",trans_status); + } + disable_irq(i2c->irq); + + if (i2c->trans_done < 0) { + dev_err(i2c->dev, "ack was not received at read\n"); + ret = i2c->trans_done; + exynos5_i2c_reset(i2c); + } + + if (timeout == 0) { + dump_i2c_register(i2c); + exynos5_i2c_reset(i2c); + dev_warn(i2c->dev, "rx timeout\n"); + ret = -EAGAIN; + return ret; + } + } + } else { + if (operation_mode == HSI2C_POLLING) { + timeout = jiffies + EXYNOS5_I2C_TIMEOUT; + while (time_before(jiffies, timeout) && + (i2c->msg_ptr < i2c->msg->len)) { + if ((readl(i2c->regs + HSI2C_FIFO_STATUS) + & HSI2C_TX_FIFO_LVL_MASK) < EXYNOS5_FIFO_SIZE) { + byte = i2c->msg->buf + [i2c->msg_ptr++]; + writel(byte, + i2c->regs + HSI2C_TX_DATA); + } + } + + } else { + timeout = wait_for_completion_timeout + (&i2c->msg_complete, EXYNOS5_I2C_TIMEOUT); + disable_irq(i2c->irq); + + if (timeout == 0) { + dump_i2c_register(i2c); + exynos5_i2c_reset(i2c); + dev_warn(i2c->dev, "tx timeout\n"); + return ret; + } + + timeout = jiffies + timeout; + } - if (ret < 0) { - exynos5_i2c_reset(i2c); - if (ret == -ETIMEDOUT) - dev_warn(i2c->dev, "%s timeout\n", - (msgs->flags & I2C_M_RD) ? "rx" : "tx"); + if (operation_mode == HSI2C_POLLING) { + while (time_before(jiffies, timeout)) { + trans_status = readl(i2c->regs + HSI2C_INT_STATUS); + writel(trans_status, i2c->regs + HSI2C_INT_STATUS); + if (trans_status & HSI2C_INT_TRANSFER_DONE) { + ret = 0; + break; + } + udelay(100); + } + if (ret == -EAGAIN) { + dump_i2c_register(i2c); + exynos5_i2c_reset(i2c); + dev_warn(i2c->dev, "tx timeout\n"); + return ret; + } + } else { + if (i2c->trans_done < 0) { + dev_err(i2c->dev, "ack was not received at write\n"); + ret = i2c->trans_done; + exynos5_i2c_reset(i2c); + } else { + if (i2c->scl_clk_stretch) { + unsigned long timeout = jiffies + msecs_to_jiffies(100); + + do { + trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS); + if ((!(trans_status & HSI2C_MAST_ST_MASK)) || + ((stop == 0) && (trans_status & HSI2C_MASTER_BUSY))){ + timeout = 0; + break; + } + } while(time_before(jiffies, timeout)); + + if (timeout) + dev_err(i2c->dev, "SDA check timeout!!! = 0x%8lx\n",trans_status); + } + + ret = 0; + } + } } - /* Return the state as in interrupt routine */ return ret; } static int exynos5_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { - struct exynos5_i2c *i2c = adap->algo_data; - int i = 0, ret = 0, stop = 0; + struct exynos5_i2c *i2c = (struct exynos5_i2c *)adap->algo_data; + struct i2c_msg *msgs_ptr = msgs; + int retry, i = 0; + int ret = 0; + int stop = 0; + +#ifdef CONFIG_PM_RUNTIME + int clk_ret = 0; +#endif if (i2c->suspended) { dev_err(i2c->dev, "HS-I2C is not initialized.\n"); return -EIO; } - ret = clk_enable(i2c->clk); - if (ret) - return ret; +#ifdef CONFIG_PM_RUNTIME + clk_ret = pm_runtime_get_sync(i2c->dev); + if (clk_ret < 0) { + exynos_update_ip_idle_status(i2c->idle_ip_index, 0); + clk_prepare_enable(i2c->clk); + } +#else + exynos_update_ip_idle_status(i2c->idle_ip_index, 0); + clk_prepare_enable(i2c->clk); +#endif + if (i2c->need_hw_init) + exynos5_i2c_reset(i2c); + + if (unlikely(!(readl(i2c->regs + HSI2C_CONF) + & HSI2C_AUTO_MODE))) { + dev_err(i2c->dev, "HSI2C should be reconfigured\n"); + exynos5_hsi2c_clock_setup(i2c); + exynos5_i2c_init(i2c); + } + + for (retry = 0; retry < adap->retries; retry++) { + for (i = 0; i < num; i++) { + stop = (i == num - 1); + + if (i2c->transfer_delay) + udelay(i2c->transfer_delay); + + ret = exynos5_i2c_xfer_msg(i2c, msgs_ptr, stop); + + msgs_ptr++; + + if (ret == -EAGAIN) { + msgs_ptr = msgs; + break; + } else if (ret < 0) { + goto out; + } + } - for (i = 0; i < num; i++, msgs++) { - stop = (i == num - 1); + if ((i == num) && (ret != -EAGAIN)) + break; - ret = exynos5_i2c_xfer_msg(i2c, msgs, stop); + dev_dbg(i2c->dev, "retrying transfer (%d)\n", retry); - if (ret < 0) - goto out; + udelay(100); } if (i == num) { ret = num; } else { - /* Only one message, cannot access the device */ - if (i == 1) - ret = -EREMOTEIO; - else - ret = i; - + ret = -EREMOTEIO; dev_warn(i2c->dev, "xfer message failed\n"); } out: - clk_disable(i2c->clk); +#ifdef CONFIG_PM_RUNTIME + if (clk_ret < 0) { + clk_disable_unprepare(i2c->clk); + exynos_update_ip_idle_status(i2c->idle_ip_index, 1); + } else { + pm_runtime_mark_last_busy(i2c->dev); + pm_runtime_put_autosuspend(i2c->dev); + } +#else + clk_disable_unprepare(i2c->clk); + exynos_update_ip_idle_status(i2c->idle_ip_index, 1); +#endif + return ret; } @@ -703,6 +912,27 @@ static const struct i2c_algorithm exynos5_i2c_algorithm = { .functionality = exynos5_i2c_func, }; +#ifdef CONFIG_CPU_IDLE +static int exynos5_i2c_notifier(struct notifier_block *self, + unsigned long cmd, void *v) +{ + struct exynos5_i2c *i2c; + + switch (cmd) { + case LPA_EXIT: + list_for_each_entry(i2c, &drvdata_list, node) + i2c->need_hw_init = 1; + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block exynos5_i2c_notifier_block = { + .notifier_call = exynos5_i2c_notifier, +}; +#endif /* CONFIG_CPU_IDLE */ + static int exynos5_i2c_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -710,32 +940,82 @@ static int exynos5_i2c_probe(struct platform_device *pdev) struct resource *mem; int ret; + if (!np) { + dev_err(&pdev->dev, "no device node\n"); + return -ENOENT; + } + i2c = devm_kzalloc(&pdev->dev, sizeof(struct exynos5_i2c), GFP_KERNEL); - if (!i2c) + if (!i2c) { + dev_err(&pdev->dev, "no memory for state\n"); return -ENOMEM; + } + + /* Mode of operation High/Fast Speed mode */ + if (of_get_property(np, "samsung,hs-mode", NULL)) { + i2c->speed_mode = HSI2C_HIGH_SPD; + i2c->fs_clock = HSI2C_FS_TX_CLOCK; + if (of_property_read_u32(np, "clock-frequency", &i2c->hs_clock)) + i2c->hs_clock = HSI2C_HS_TX_CLOCK; + } else { + i2c->speed_mode = HSI2C_FAST_SPD; + if (of_property_read_u32(np, "clock-frequency", &i2c->fs_clock)) + i2c->fs_clock = HSI2C_FS_TX_CLOCK; + } - if (of_property_read_u32(np, "clock-frequency", &i2c->op_clock)) - i2c->op_clock = HSI2C_FS_TX_CLOCK; + /* Mode of operation Polling/Interrupt mode */ + if (of_get_property(np, "samsung,polling-mode", NULL)) { + i2c->operation_mode = HSI2C_POLLING; + } else { + i2c->operation_mode = HSI2C_INTERRUPT; + } + + if (of_get_property(np, "samsung,scl-clk-stretching", NULL)) + i2c->scl_clk_stretch = 1; + else + i2c->scl_clk_stretch = 0; + + ret = of_property_read_u32(np, "samsung,transfer_delay", &i2c->transfer_delay); + if (!ret) + dev_warn(&pdev->dev, "Transfer delay is not needed.\n"); + + if (of_get_property(np, "samsung,stop-after-trans", NULL)) + i2c->stop_after_trans = 1; + else + i2c->stop_after_trans = 0; + + i2c->idle_ip_index = exynos_get_idle_ip_index(dev_name(&pdev->dev)); strlcpy(i2c->adap.name, "exynos5-i2c", sizeof(i2c->adap.name)); i2c->adap.owner = THIS_MODULE; i2c->adap.algo = &exynos5_i2c_algorithm; - i2c->adap.retries = 3; + i2c->adap.retries = 2; + i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; i2c->dev = &pdev->dev; - i2c->clk = devm_clk_get(&pdev->dev, "hsi2c"); + i2c->clk = devm_clk_get(&pdev->dev, "gate_hsi2c"); if (IS_ERR(i2c->clk)) { dev_err(&pdev->dev, "cannot get clock\n"); return -ENOENT; } - ret = clk_prepare_enable(i2c->clk); - if (ret) - return ret; + i2c->rate_clk = devm_clk_get(&pdev->dev, "rate_hsi2c"); + if (IS_ERR(i2c->rate_clk)) { + dev_err(&pdev->dev, "cannot get rate clock\n"); + return -ENOENT; + } + +#ifdef CONFIG_PM_RUNTIME + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, + EXYNOS5_HSI2C_RUNTIME_PM_DELAY); + pm_runtime_enable(&pdev->dev); +#endif mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); i2c->regs = devm_ioremap_resource(&pdev->dev, mem); - if (IS_ERR(i2c->regs)) { + if (i2c->regs == NULL) { + dev_err(&pdev->dev, "cannot map HS-I2C IO\n"); ret = PTR_ERR(i2c->regs); goto err_clk; } @@ -744,48 +1024,71 @@ static int exynos5_i2c_probe(struct platform_device *pdev) i2c->adap.algo_data = i2c; i2c->adap.dev.parent = &pdev->dev; - /* Clear pending interrupts from u-boot or misc causes */ - exynos5_i2c_clr_pend_irq(i2c); - - spin_lock_init(&i2c->lock); init_completion(&i2c->msg_complete); - i2c->irq = ret = platform_get_irq(pdev, 0); - if (ret <= 0) { - dev_err(&pdev->dev, "cannot find HS-I2C IRQ\n"); - ret = -EINVAL; - goto err_clk; - } + if (i2c->operation_mode == HSI2C_INTERRUPT) { + i2c->irq = ret = irq_of_parse_and_map(np, 0); + if (ret <= 0) { + dev_err(&pdev->dev, "cannot find HS-I2C IRQ\n"); + ret = -EINVAL; + goto err_clk; + } - ret = devm_request_irq(&pdev->dev, i2c->irq, exynos5_i2c_irq, - IRQF_NO_SUSPEND | IRQF_ONESHOT, - dev_name(&pdev->dev), i2c); + ret = devm_request_irq(&pdev->dev, i2c->irq, + exynos5_i2c_irq, 0, dev_name(&pdev->dev), i2c); + disable_irq(i2c->irq); - if (ret != 0) { - dev_err(&pdev->dev, "cannot request HS-I2C IRQ %d\n", i2c->irq); - goto err_clk; + if (ret != 0) { + dev_err(&pdev->dev, "cannot request HS-I2C IRQ %d\n", + i2c->irq); + goto err_clk; + } } + platform_set_drvdata(pdev, i2c); +#ifdef CONFIG_PM_RUNTIME + pm_runtime_get_sync(&pdev->dev); +#else + exynos_update_ip_idle_status(i2c->idle_ip_index, 0); + clk_prepare_enable(i2c->clk); +#endif - i2c->variant = of_device_get_match_data(&pdev->dev); + /* Clear pending interrupts from u-boot or misc causes */ + exynos5_i2c_clr_pend_irq(i2c); + /* Reset i2c SFR from u-boot or misc causes */ + exynos5_i2c_reset(i2c); ret = exynos5_hsi2c_clock_setup(i2c); if (ret) goto err_clk; - exynos5_i2c_reset(i2c); + i2c->bus_id = of_alias_get_id(i2c->adap.dev.of_node, "hsi2c"); + + exynos5_i2c_init(i2c); - ret = i2c_add_adapter(&i2c->adap); - if (ret < 0) + i2c->adap.nr = -1; + ret = i2c_add_numbered_adapter(&i2c->adap); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add bus to i2c core\n"); goto err_clk; + } - platform_set_drvdata(pdev, i2c); +#ifdef CONFIG_PM_RUNTIME + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); +#else + clk_disable_unprepare(i2c->clk); + exynos_update_ip_idle_status(i2c->idle_ip_index, 1); +#endif - clk_disable(i2c->clk); +#if defined(CONFIG_CPU_IDLE) + list_add_tail(&i2c->node, &drvdata_list); +#endif return 0; err_clk: clk_disable_unprepare(i2c->clk); + exynos_update_ip_idle_status(i2c->idle_ip_index, 1); return ret; } @@ -795,49 +1098,79 @@ static int exynos5_i2c_remove(struct platform_device *pdev) i2c_del_adapter(&i2c->adap); - clk_unprepare(i2c->clk); - return 0; } -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM static int exynos5_i2c_suspend_noirq(struct device *dev) { struct exynos5_i2c *i2c = dev_get_drvdata(dev); + i2c_lock_adapter(&i2c->adap); i2c->suspended = 1; - - clk_unprepare(i2c->clk); + i2c_unlock_adapter(&i2c->adap); return 0; } static int exynos5_i2c_resume_noirq(struct device *dev) { - struct exynos5_i2c *i2c = dev_get_drvdata(dev); - int ret = 0; + struct platform_device *pdev = to_platform_device(dev); + struct exynos5_i2c *i2c = platform_get_drvdata(pdev); - ret = clk_prepare_enable(i2c->clk); - if (ret) - return ret; + i2c_lock_adapter(&i2c->adap); + exynos_update_ip_idle_status(i2c->idle_ip_index, 0); + clk_prepare_enable(i2c->clk); + exynos5_i2c_reset(i2c); + clk_disable_unprepare(i2c->clk); + exynos_update_ip_idle_status(i2c->idle_ip_index, 1); + i2c->suspended = 0; + i2c_unlock_adapter(&i2c->adap); - ret = exynos5_hsi2c_clock_setup(i2c); - if (ret) { - clk_disable_unprepare(i2c->clk); - return ret; - } + return 0; +} - exynos5_i2c_init(i2c); - clk_disable(i2c->clk); - i2c->suspended = 0; +#else +static int exynos5_i2c_suspend_noirq(struct device *dev) +{ + return 0; +} +static int exynos5_i2c_resume_noirq(struct device *dev) +{ return 0; } #endif -static const struct dev_pm_ops exynos5_i2c_dev_pm_ops = { +#ifdef CONFIG_PM_RUNTIME +static int exynos5_i2c_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct exynos5_i2c *i2c = platform_get_drvdata(pdev); + + clk_disable_unprepare(i2c->clk); + exynos_update_ip_idle_status(i2c->idle_ip_index, 1); + + return 0; +} + +static int exynos5_i2c_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct exynos5_i2c *i2c = platform_get_drvdata(pdev); + + exynos_update_ip_idle_status(i2c->idle_ip_index, 0); + clk_prepare_enable(i2c->clk); + + return 0; +} +#endif /* CONFIG_PM_RUNTIME */ + +static const struct dev_pm_ops exynos5_i2c_pm = { SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(exynos5_i2c_suspend_noirq, exynos5_i2c_resume_noirq) + SET_RUNTIME_PM_OPS(exynos5_i2c_runtime_suspend, + exynos5_i2c_runtime_resume, NULL) }; static struct platform_driver exynos5_i2c_driver = { @@ -845,12 +1178,25 @@ static struct platform_driver exynos5_i2c_driver = { .remove = exynos5_i2c_remove, .driver = { .name = "exynos5-hsi2c", - .pm = &exynos5_i2c_dev_pm_ops, + .pm = &exynos5_i2c_pm, .of_match_table = exynos5_i2c_match, }, }; -module_platform_driver(exynos5_i2c_driver); +static int __init i2c_adap_exynos5_init(void) +{ +#ifdef CONFIG_CPU_IDLE + exynos_pm_register_notifier(&exynos5_i2c_notifier_block); +#endif + return platform_driver_register(&exynos5_i2c_driver); +} +subsys_initcall(i2c_adap_exynos5_init); + +static void __exit i2c_adap_exynos5_exit(void) +{ + platform_driver_unregister(&exynos5_i2c_driver); +} +module_exit(i2c_adap_exynos5_exit); MODULE_DESCRIPTION("Exynos5 HS-I2C Bus driver"); MODULE_AUTHOR("Naveen Krishna Chatradhi, ");