Loading drivers/i2c/busses/i2c-qup.c +55 −121 Original line number Diff line number Diff line Loading @@ -65,6 +65,7 @@ enum { QUP_IN_FIFO_BASE = 0x218, QUP_I2C_CLK_CTL = 0x400, QUP_I2C_STATUS = 0x404, QUP_I2C_MASTER_BUS_CLR = 0x40C, }; /* QUP States and reset values */ Loading Loading @@ -150,12 +151,6 @@ enum msm_i2c_state { (((reg_val) & ~(0x3 << 26)) | (((noise_rej_val) & 0x3) << 26)) static char const * const i2c_rsrcs[] = {"i2c_clk", "i2c_sda"}; static struct gpiomux_setting recovery_config = { .func = GPIOMUX_FUNC_GPIO, .drv = GPIOMUX_DRV_8MA, .pull = GPIOMUX_PULL_NONE, }; /** * qup_i2c_clk_path_vote: data to use bus scaling driver for clock path vote * Loading Loading @@ -896,113 +891,75 @@ qup_set_wr_mode(struct qup_i2c_dev *dev, int rem) return ret; } static void qup_i2c_recover_bus_busy(struct qup_i2c_dev *dev) static int qup_i2c_reset(struct qup_i2c_dev *dev) { int i; int gpio_clk; int gpio_dat; bool gpio_clk_status = false; uint32_t status = readl_relaxed(dev->base + QUP_I2C_STATUS); struct gpiomux_setting old_gpio_setting[ARRAY_SIZE(i2c_rsrcs)]; int ret; if (dev->pdata->msm_i2c_config_gpio) return; /* sw reset */ writel_relaxed(1, dev->base + QUP_SW_RESET); ret = qup_i2c_poll_state(dev, QUP_RESET_STATE, false); if (ret) { dev_err(dev->dev, "QUP Busy:Trying to recover\n"); return ret; } if (!(status & (I2C_STATUS_BUS_ACTIVE)) || (status & (I2C_STATUS_BUS_MASTER))) return; /* Initialize QUP registers */ writel_relaxed(0, dev->base + QUP_CONFIG); writel_relaxed(QUP_OPERATIONAL_RESET, dev->base + QUP_OPERATIONAL); writel_relaxed(QUP_STATUS_ERROR_FLAGS, dev->base + QUP_ERROR_FLAGS_EN); gpio_clk = dev->i2c_gpios[0]; gpio_dat = dev->i2c_gpios[1]; writel_relaxed(I2C_MINI_CORE | I2C_N_VAL, dev->base + QUP_CONFIG); if ((gpio_clk == -1) && (gpio_dat == -1)) { dev_err(dev->dev, "Recovery failed due to undefined GPIO's\n"); return; } /* Initialize I2C mini core registers */ writel_relaxed(0, dev->base + QUP_I2C_CLK_CTL); writel_relaxed(QUP_I2C_STATUS_RESET, dev->base + QUP_I2C_STATUS); disable_irq(dev->err_irq); for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) { if (msm_gpiomux_write(dev->i2c_gpios[i], GPIOMUX_ACTIVE, &recovery_config, &old_gpio_setting[i])) { dev_err(dev->dev, "GPIO pins have no active setting\n"); goto recovery_end; } /* Make sure QUP I2C core reset registers are written */ wmb(); return ret; } dev_warn(dev->dev, "i2c_scl: %d, i2c_sda: %d\n", gpio_get_value(gpio_clk), gpio_get_value(gpio_dat)); static int qup_i2c_recover_bus_busy(struct qup_i2c_dev *dev) { int ret; u32 status; ulong min_sleep_usec; for (i = 0; i < 9; i++) { if (gpio_get_value(gpio_dat) && gpio_clk_status) break; gpio_direction_output(gpio_clk, 0); udelay(5); gpio_direction_output(gpio_dat, 0); udelay(5); gpio_direction_input(gpio_clk); udelay(5); if (!gpio_get_value(gpio_clk)) udelay(20); if (!gpio_get_value(gpio_clk)) usleep_range(10000, 10000); gpio_clk_status = gpio_get_value(gpio_clk); gpio_direction_input(gpio_dat); udelay(5); } /* Configure ALT funciton to QUP I2C*/ for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) { msm_gpiomux_write(dev->i2c_gpios[i], GPIOMUX_ACTIVE, &old_gpio_setting[i], NULL); } disable_irq(dev->err_irq); dev_info(dev->dev, "Executing bus recovery procedure (9 clk pulse)\n"); udelay(10); qup_i2c_reset(dev); status = readl_relaxed(dev->base + QUP_I2C_STATUS); if (!(status & I2C_STATUS_BUS_ACTIVE)) { dev_info(dev->dev, "Bus busy cleared after %d clock cycles, " "status %x\n", i, status); ret = qup_update_state(dev, QUP_RUN_STATE); if (ret < 0) { dev_err(dev->dev, "error: bus clear fail to set run state\n"); goto recovery_end; } dev_warn(dev->dev, "Bus still busy, status %x\n", status); writel_relaxed(dev->clk_ctl, dev->base + QUP_I2C_CLK_CTL); recovery_end: enable_irq(dev->err_irq); } /* Make sure QUP I2C core reset registers are written */ wmb(); static void i2c_qup_dump_gpio_conf(struct qup_i2c_dev *dev, int gpio_num, const char *gpio_name, enum msm_gpiomux_setting setting, const char *setting_name) { struct gpiomux_setting cur_conf; if (msm_gpiomux_write(gpio_num, setting, NULL, &cur_conf)) { dev_err(dev->dev, "error reading %s-gpio:#%d %s setting\n", gpio_name, gpio_num, setting_name); return; } dev_info(dev->dev, "dump %s-gpio:#%d state:%s func:%d drv:%d pull:%d dir:%d\n", gpio_name, gpio_num, setting_name, cur_conf.func, cur_conf.drv, cur_conf.pull, cur_conf.dir); writel_relaxed(0x1, dev->base + QUP_I2C_MASTER_BUS_CLR); if (msm_gpiomux_write(gpio_num, setting, &cur_conf, NULL)) dev_err(dev->dev, "error restoring %s-gpio:#%d %s setting\n", gpio_name, gpio_num, setting_name); } /* * wait for bus clear (9 clock pulse cycles) to complete. * min_time = 9 clock *10 (1000% margin) * max_time = 10* min_time */ min_sleep_usec = max_t(ulong, (9 * 10 * USEC_PER_SEC) / dev->pdata->clk_freq, 100); usleep_range(min_sleep_usec, min_sleep_usec * 10); static void i2c_qup_dump_gpios(struct qup_i2c_dev *dev) { i2c_qup_dump_gpio_conf(dev, dev->i2c_gpios[0], i2c_rsrcs[0], GPIOMUX_ACTIVE, "active"); i2c_qup_dump_gpio_conf(dev, dev->i2c_gpios[0], i2c_rsrcs[0], GPIOMUX_SUSPENDED, "suspended"); i2c_qup_dump_gpio_conf(dev, dev->i2c_gpios[1], i2c_rsrcs[1], GPIOMUX_ACTIVE, "active"); i2c_qup_dump_gpio_conf(dev, dev->i2c_gpios[1], i2c_rsrcs[1], GPIOMUX_SUSPENDED, "suspended"); status = readl_relaxed(dev->base + QUP_I2C_STATUS); dev_info(dev->dev, "Bus recovery %s\n", (status & I2C_STATUS_BUS_ACTIVE) ? "fail" : "success"); recovery_end: enable_irq(dev->err_irq); return ret; } static int Loading Loading @@ -1094,12 +1051,8 @@ qup_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) dev->out_blk_sz, dev->out_fifo_sz); } writel_relaxed(1, dev->base + QUP_SW_RESET); ret = qup_i2c_poll_state(dev, QUP_RESET_STATE, false); if (ret) { dev_err(dev->dev, "QUP Busy:Trying to recover\n"); goto out_err; } if (qup_i2c_reset(dev)) dev_err(dev->dev, "warning: QUP reset before a xfer failed\n"); if (dev->num_irqs == 3) { enable_irq(dev->in_irq); Loading @@ -1107,17 +1060,6 @@ qup_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) } enable_irq(dev->err_irq); /* Initialize QUP registers */ writel_relaxed(0, dev->base + QUP_CONFIG); writel_relaxed(QUP_OPERATIONAL_RESET, dev->base + QUP_OPERATIONAL); writel_relaxed(QUP_STATUS_ERROR_FLAGS, dev->base + QUP_ERROR_FLAGS_EN); writel_relaxed(I2C_MINI_CORE | I2C_N_VAL, dev->base + QUP_CONFIG); /* Initialize I2C mini core registers */ writel_relaxed(0, dev->base + QUP_I2C_CLK_CTL); writel_relaxed(QUP_I2C_STATUS_RESET, dev->base + QUP_I2C_STATUS); while (rem) { bool filled = false; Loading Loading @@ -1246,16 +1188,10 @@ qup_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) dev_err(dev->dev, "Transaction timed out, SL-AD = 0x%x\n", dev->msg->addr); i2c_qup_dump_gpios(dev); dev_err(dev->dev, "I2C Status: %x\n", istatus); dev_err(dev->dev, "QUP Status: %x\n", qstatus); dev_err(dev->dev, "OP Flags: %x\n", op_flgs); writel_relaxed(1, dev->base + QUP_SW_RESET); /* Make sure that the write has gone through * before returning from the function */ mb(); ret = -ETIMEDOUT; goto out_err; } Loading @@ -1270,11 +1206,9 @@ timeout_err: } else if (dev->err < 0) { dev_err(dev->dev, "QUP data xfer error %d\n", dev->err); i2c_qup_dump_gpios(dev); ret = dev->err; goto out_err; } else if (dev->err > 0) { i2c_qup_dump_gpios(dev); /* * ISR returns +ve error if error code * is I2C related, e.g. unexpected start Loading Loading
drivers/i2c/busses/i2c-qup.c +55 −121 Original line number Diff line number Diff line Loading @@ -65,6 +65,7 @@ enum { QUP_IN_FIFO_BASE = 0x218, QUP_I2C_CLK_CTL = 0x400, QUP_I2C_STATUS = 0x404, QUP_I2C_MASTER_BUS_CLR = 0x40C, }; /* QUP States and reset values */ Loading Loading @@ -150,12 +151,6 @@ enum msm_i2c_state { (((reg_val) & ~(0x3 << 26)) | (((noise_rej_val) & 0x3) << 26)) static char const * const i2c_rsrcs[] = {"i2c_clk", "i2c_sda"}; static struct gpiomux_setting recovery_config = { .func = GPIOMUX_FUNC_GPIO, .drv = GPIOMUX_DRV_8MA, .pull = GPIOMUX_PULL_NONE, }; /** * qup_i2c_clk_path_vote: data to use bus scaling driver for clock path vote * Loading Loading @@ -896,113 +891,75 @@ qup_set_wr_mode(struct qup_i2c_dev *dev, int rem) return ret; } static void qup_i2c_recover_bus_busy(struct qup_i2c_dev *dev) static int qup_i2c_reset(struct qup_i2c_dev *dev) { int i; int gpio_clk; int gpio_dat; bool gpio_clk_status = false; uint32_t status = readl_relaxed(dev->base + QUP_I2C_STATUS); struct gpiomux_setting old_gpio_setting[ARRAY_SIZE(i2c_rsrcs)]; int ret; if (dev->pdata->msm_i2c_config_gpio) return; /* sw reset */ writel_relaxed(1, dev->base + QUP_SW_RESET); ret = qup_i2c_poll_state(dev, QUP_RESET_STATE, false); if (ret) { dev_err(dev->dev, "QUP Busy:Trying to recover\n"); return ret; } if (!(status & (I2C_STATUS_BUS_ACTIVE)) || (status & (I2C_STATUS_BUS_MASTER))) return; /* Initialize QUP registers */ writel_relaxed(0, dev->base + QUP_CONFIG); writel_relaxed(QUP_OPERATIONAL_RESET, dev->base + QUP_OPERATIONAL); writel_relaxed(QUP_STATUS_ERROR_FLAGS, dev->base + QUP_ERROR_FLAGS_EN); gpio_clk = dev->i2c_gpios[0]; gpio_dat = dev->i2c_gpios[1]; writel_relaxed(I2C_MINI_CORE | I2C_N_VAL, dev->base + QUP_CONFIG); if ((gpio_clk == -1) && (gpio_dat == -1)) { dev_err(dev->dev, "Recovery failed due to undefined GPIO's\n"); return; } /* Initialize I2C mini core registers */ writel_relaxed(0, dev->base + QUP_I2C_CLK_CTL); writel_relaxed(QUP_I2C_STATUS_RESET, dev->base + QUP_I2C_STATUS); disable_irq(dev->err_irq); for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) { if (msm_gpiomux_write(dev->i2c_gpios[i], GPIOMUX_ACTIVE, &recovery_config, &old_gpio_setting[i])) { dev_err(dev->dev, "GPIO pins have no active setting\n"); goto recovery_end; } /* Make sure QUP I2C core reset registers are written */ wmb(); return ret; } dev_warn(dev->dev, "i2c_scl: %d, i2c_sda: %d\n", gpio_get_value(gpio_clk), gpio_get_value(gpio_dat)); static int qup_i2c_recover_bus_busy(struct qup_i2c_dev *dev) { int ret; u32 status; ulong min_sleep_usec; for (i = 0; i < 9; i++) { if (gpio_get_value(gpio_dat) && gpio_clk_status) break; gpio_direction_output(gpio_clk, 0); udelay(5); gpio_direction_output(gpio_dat, 0); udelay(5); gpio_direction_input(gpio_clk); udelay(5); if (!gpio_get_value(gpio_clk)) udelay(20); if (!gpio_get_value(gpio_clk)) usleep_range(10000, 10000); gpio_clk_status = gpio_get_value(gpio_clk); gpio_direction_input(gpio_dat); udelay(5); } /* Configure ALT funciton to QUP I2C*/ for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) { msm_gpiomux_write(dev->i2c_gpios[i], GPIOMUX_ACTIVE, &old_gpio_setting[i], NULL); } disable_irq(dev->err_irq); dev_info(dev->dev, "Executing bus recovery procedure (9 clk pulse)\n"); udelay(10); qup_i2c_reset(dev); status = readl_relaxed(dev->base + QUP_I2C_STATUS); if (!(status & I2C_STATUS_BUS_ACTIVE)) { dev_info(dev->dev, "Bus busy cleared after %d clock cycles, " "status %x\n", i, status); ret = qup_update_state(dev, QUP_RUN_STATE); if (ret < 0) { dev_err(dev->dev, "error: bus clear fail to set run state\n"); goto recovery_end; } dev_warn(dev->dev, "Bus still busy, status %x\n", status); writel_relaxed(dev->clk_ctl, dev->base + QUP_I2C_CLK_CTL); recovery_end: enable_irq(dev->err_irq); } /* Make sure QUP I2C core reset registers are written */ wmb(); static void i2c_qup_dump_gpio_conf(struct qup_i2c_dev *dev, int gpio_num, const char *gpio_name, enum msm_gpiomux_setting setting, const char *setting_name) { struct gpiomux_setting cur_conf; if (msm_gpiomux_write(gpio_num, setting, NULL, &cur_conf)) { dev_err(dev->dev, "error reading %s-gpio:#%d %s setting\n", gpio_name, gpio_num, setting_name); return; } dev_info(dev->dev, "dump %s-gpio:#%d state:%s func:%d drv:%d pull:%d dir:%d\n", gpio_name, gpio_num, setting_name, cur_conf.func, cur_conf.drv, cur_conf.pull, cur_conf.dir); writel_relaxed(0x1, dev->base + QUP_I2C_MASTER_BUS_CLR); if (msm_gpiomux_write(gpio_num, setting, &cur_conf, NULL)) dev_err(dev->dev, "error restoring %s-gpio:#%d %s setting\n", gpio_name, gpio_num, setting_name); } /* * wait for bus clear (9 clock pulse cycles) to complete. * min_time = 9 clock *10 (1000% margin) * max_time = 10* min_time */ min_sleep_usec = max_t(ulong, (9 * 10 * USEC_PER_SEC) / dev->pdata->clk_freq, 100); usleep_range(min_sleep_usec, min_sleep_usec * 10); static void i2c_qup_dump_gpios(struct qup_i2c_dev *dev) { i2c_qup_dump_gpio_conf(dev, dev->i2c_gpios[0], i2c_rsrcs[0], GPIOMUX_ACTIVE, "active"); i2c_qup_dump_gpio_conf(dev, dev->i2c_gpios[0], i2c_rsrcs[0], GPIOMUX_SUSPENDED, "suspended"); i2c_qup_dump_gpio_conf(dev, dev->i2c_gpios[1], i2c_rsrcs[1], GPIOMUX_ACTIVE, "active"); i2c_qup_dump_gpio_conf(dev, dev->i2c_gpios[1], i2c_rsrcs[1], GPIOMUX_SUSPENDED, "suspended"); status = readl_relaxed(dev->base + QUP_I2C_STATUS); dev_info(dev->dev, "Bus recovery %s\n", (status & I2C_STATUS_BUS_ACTIVE) ? "fail" : "success"); recovery_end: enable_irq(dev->err_irq); return ret; } static int Loading Loading @@ -1094,12 +1051,8 @@ qup_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) dev->out_blk_sz, dev->out_fifo_sz); } writel_relaxed(1, dev->base + QUP_SW_RESET); ret = qup_i2c_poll_state(dev, QUP_RESET_STATE, false); if (ret) { dev_err(dev->dev, "QUP Busy:Trying to recover\n"); goto out_err; } if (qup_i2c_reset(dev)) dev_err(dev->dev, "warning: QUP reset before a xfer failed\n"); if (dev->num_irqs == 3) { enable_irq(dev->in_irq); Loading @@ -1107,17 +1060,6 @@ qup_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) } enable_irq(dev->err_irq); /* Initialize QUP registers */ writel_relaxed(0, dev->base + QUP_CONFIG); writel_relaxed(QUP_OPERATIONAL_RESET, dev->base + QUP_OPERATIONAL); writel_relaxed(QUP_STATUS_ERROR_FLAGS, dev->base + QUP_ERROR_FLAGS_EN); writel_relaxed(I2C_MINI_CORE | I2C_N_VAL, dev->base + QUP_CONFIG); /* Initialize I2C mini core registers */ writel_relaxed(0, dev->base + QUP_I2C_CLK_CTL); writel_relaxed(QUP_I2C_STATUS_RESET, dev->base + QUP_I2C_STATUS); while (rem) { bool filled = false; Loading Loading @@ -1246,16 +1188,10 @@ qup_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) dev_err(dev->dev, "Transaction timed out, SL-AD = 0x%x\n", dev->msg->addr); i2c_qup_dump_gpios(dev); dev_err(dev->dev, "I2C Status: %x\n", istatus); dev_err(dev->dev, "QUP Status: %x\n", qstatus); dev_err(dev->dev, "OP Flags: %x\n", op_flgs); writel_relaxed(1, dev->base + QUP_SW_RESET); /* Make sure that the write has gone through * before returning from the function */ mb(); ret = -ETIMEDOUT; goto out_err; } Loading @@ -1270,11 +1206,9 @@ timeout_err: } else if (dev->err < 0) { dev_err(dev->dev, "QUP data xfer error %d\n", dev->err); i2c_qup_dump_gpios(dev); ret = dev->err; goto out_err; } else if (dev->err > 0) { i2c_qup_dump_gpios(dev); /* * ISR returns +ve error if error code * is I2C related, e.g. unexpected start Loading