diff --git a/arch/arm/dts/ipq806x-ap148.dts b/arch/arm/dts/ipq806x-ap148.dts index ad64b0db02..a930f48690 100644 --- a/arch/arm/dts/ipq806x-ap148.dts +++ b/arch/arm/dts/ipq806x-ap148.dts @@ -23,6 +23,7 @@ console = "/serial@16340000"; nand = "/nand@1A600000"; gmac_gpio = "/gmac1_gpio"; + i2c0 = "/i2c@16380000"; }; serial@16340000 { diff --git a/arch/arm/dts/ipq806x-soc.dtsi b/arch/arm/dts/ipq806x-soc.dtsi index 0f290612ba..d64566f44c 100644 --- a/arch/arm/dts/ipq806x-soc.dtsi +++ b/arch/arm/dts/ipq806x-soc.dtsi @@ -401,7 +401,12 @@ oe = ; }; }; + }; + i2c: i2c@16380000 { + compatible = "qcom,i2c-qup-v1.1.1"; + reg = <0x16380000 0x500>; + clock-frequency = <24000>; }; }; diff --git a/arch/arm/include/asm/arch-ipq806x/clk.h b/arch/arm/include/asm/arch-ipq806x/clk.h index a084dd795b..9b9c7cf531 100644 --- a/arch/arm/include/asm/arch-ipq806x/clk.h +++ b/arch/arm/include/asm/arch-ipq806x/clk.h @@ -48,6 +48,21 @@ #define ALWAYS_ON_CLK_BRANCH_ENA(i) ((i) << 8) #define CLK_BRANCH_ENA(i) ((i) << 4) +#define MSM_CLK_CTL_BASE 0x00900000 +#define REG(off) (MSM_CLK_CTL_BASE + (off)) + +#define BIT_POS_23 23 +#define BIT_POS_16 16 +#define BIT_POS_6 6 +#define BIT_POS_0 0 +#define I2C_en_mask BIT(11) +#define I2C_clk_ns_mask (BM(BIT_POS_23, BIT_POS_16) | BM(BIT_POS_6, BIT_POS_0)) + +#define GSBIn_QUP_APPS_MD_REG(n) REG(0x29C8+(0x20*((n)-1))) +#define GSBIn_QUP_APPS_NS_REG(n) REG(0x29CC+(0x20*((n)-1))) + +void i2c_clock_config(void); + /* Uart specific clock settings */ void uart_pll_vote_clk_enable(void); diff --git a/arch/arm/include/asm/arch-qcom-common/qca_common.h b/arch/arm/include/asm/arch-qcom-common/qca_common.h index 926712111b..94c0d8cd42 100644 --- a/arch/arm/include/asm/arch-qcom-common/qca_common.h +++ b/arch/arm/include/asm/arch-qcom-common/qca_common.h @@ -23,6 +23,15 @@ #include #endif + +#ifdef CONFIG_IPQ806x +#include +#endif + +struct ipq_i2c_platdata { + int type; +}; + typedef struct { uint base; uint clk_mode; diff --git a/drivers/clk/ipq806x_clk.c b/drivers/clk/ipq806x_clk.c index 8df93d9447..52535ab0db 100644 --- a/drivers/clk/ipq806x_clk.c +++ b/drivers/clk/ipq806x_clk.c @@ -13,8 +13,38 @@ #include #include + #include +#define MSM_CLK_CTL_BASE 0x00900000 +#define GSBIn_UART_APPS_MD_REG(n) (MSM_CLK_CTL_BASE + 0x29D0 + (0x20*((n)-1))) +#define GSBIn_UART_APPS_NS_REG(n) (MSM_CLK_CTL_BASE + 0x29D4 + (0x20*((n)-1))) +#define GSBIn_HCLK_CTL_REG(n) (MSM_CLK_CTL_BASE + 0x29C0 + (0x20*((n)-1))) +#define BB_PLL_ENA_SC0_REG (MSM_CLK_CTL_BASE + 0x34C0) +#define PLL_LOCK_DET_STATUS_REG (MSM_CLK_CTL_BASE + 0x03420) + +#define MN_MODE_DUAL_EDGE 0x2 + +#define BM(m, l) (((((unsigned int)-1) << (31-m)) >> (31-m+l)) << l) +#define BVAL(m, l, val) (((val) << l) & BM(m, l)) + +#define Uart_clk_ns_mask (BM(31, 16) | BM(6, 0)) +#define Uart_en_mask BIT(11) +#define MD16(m, n) (BVAL(31, 16, m) | BVAL(15, 0, ~(n))) + +/* NS Registers */ +#define NS(n_msb, n_lsb, n, m, mde_lsb, d_msb, d_lsb, d, s_msb, s_lsb, s) \ + (BVAL(n_msb, n_lsb, ~(n-m)) \ + | (BVAL((mde_lsb+1), mde_lsb, MN_MODE_DUAL_EDGE) * !!(n)) \ + | BVAL(d_msb, d_lsb, (d-1)) | BVAL(s_msb, s_lsb, s)) + +#ifdef CONFIG_IPQ806X_I2C +unsigned int gsbi_port = 4; +unsigned int GSBI_I2C_CLK_M = 1; +unsigned int GSBI_I2C_CLK_N = 4; +unsigned int GSBI_I2C_CLK_D = 2; +#endif + /** * uart_pll_vote_clk_enable - enables PLL8 */ @@ -298,13 +328,12 @@ void uart_clock_config(unsigned int gsbi_port, unsigned int m, * Sets the M, D parameters of the divider to generate the GSBI QUP * apps clock. */ -static void i2c_set_rate_mnd(unsigned int gsbi_port, unsigned int m, - unsigned int n) +static void i2c_set_rate_mnd(void) { /* Assert MND reset. */ setbits_le32(GSBIn_QUP_APPS_NS_REG(gsbi_port), BIT(7)); /* Program M and D values. */ - writel(MD16(m, n), GSBIn_QUP_APPS_MD_REG(gsbi_port)); + writel(MD16(GSBI_I2C_CLK_M, GSBI_I2C_CLK_D), GSBIn_QUP_APPS_MD_REG(gsbi_port)); /* Deassert MND reset. */ clrbits_le32(GSBIn_QUP_APPS_NS_REG(gsbi_port), BIT(7)); } @@ -324,7 +353,7 @@ void i2c_pll_vote_clk_enable(void) * * Enables branch clock for GSBI I2C port. */ -static void i2c_branch_clk_enable_reg(unsigned int gsbi_port) +static void i2c_branch_clk_enable_reg(void) { setbits_le32(GSBIn_QUP_APPS_NS_REG(gsbi_port), BIT(9)); } @@ -335,8 +364,7 @@ static void i2c_branch_clk_enable_reg(unsigned int gsbi_port) * Sets the N parameter of the divider and enables root clock and * branch clocks for GSBI I2C port. */ -static void i2c_local_clock_enable(unsigned int gsbi_port, unsigned int n, - unsigned int m) +static void i2c_local_clock_enable(void) { unsigned int reg_val, i2c_ns_val; void *const reg = (void *)GSBIn_QUP_APPS_NS_REG(gsbi_port); @@ -348,7 +376,7 @@ static void i2c_local_clock_enable(unsigned int gsbi_port, unsigned int n, */ reg_val = readl(reg); reg_val &= ~(I2C_clk_ns_mask); - i2c_ns_val = NS(23,16,n,m, 5, 4, 3, 4, 2, 0, 3); + i2c_ns_val = NS(23,16,GSBI_I2C_CLK_N, GSBI_I2C_CLK_M, 5, 4, 3, 4, 2, 0, 3); reg_val |= (i2c_ns_val & I2C_clk_ns_mask); writel(reg_val,reg); @@ -365,13 +393,13 @@ static void i2c_local_clock_enable(unsigned int gsbi_port, unsigned int n, /* Enable root. */ reg_val |= I2C_en_mask; writel(reg_val, reg); - i2c_branch_clk_enable_reg(gsbi_port); + i2c_branch_clk_enable_reg(); } /** * i2c_set_gsbi_clk - enables HCLK for I2C GSBI port */ -static void i2c_set_gsbi_clk(unsigned int gsbi_port) +static void i2c_set_gsbi_clk(void) { setbits_le32(GSBIn_HCLK_CTL_REG(gsbi_port), BIT(4)); } @@ -381,13 +409,12 @@ static void i2c_set_gsbi_clk(unsigned int gsbi_port) * * Configures GSBI I2C dividers, enable root and branch clocks. */ -void i2c_clock_config(unsigned int gsbi_port, unsigned int m, - unsigned int n, unsigned int d) +void i2c_clock_config(void) { - i2c_set_rate_mnd(gsbi_port, m, d); + i2c_set_rate_mnd(); i2c_pll_vote_clk_enable(); - i2c_local_clock_enable(gsbi_port, n, m); - i2c_set_gsbi_clk(gsbi_port); + i2c_local_clock_enable(); + i2c_set_gsbi_clk(); } #endif diff --git a/drivers/i2c/qup_i2c.c b/drivers/i2c/qup_i2c.c index 01d79894cc..304aa80695 100644 --- a/drivers/i2c/qup_i2c.c +++ b/drivers/i2c/qup_i2c.c @@ -24,10 +24,12 @@ #include DECLARE_GLOBAL_DATA_PTR; - +static int src_clk_freq; static int i2c_base_addr; static int i2c_hw_initialized; static int i2c_board_initialized; +static int io_mode; +static int clk_en; struct i2c_qup_bus { int *i2c_base_addr; @@ -99,8 +101,7 @@ void config_i2c_mode(void) int cfg; cfg = readl(i2c_base_addr + QUP_IO_MODES_OFFSET); - cfg |= (INPUT_FIFO_MODE | OUTPUT_FIFO_MODE | PACK_EN | UNPACK_EN); - writel(cfg, i2c_base_addr + QUP_IO_MODES_OFFSET); + writel(cfg | io_mode, i2c_base_addr + QUP_IO_MODES_OFFSET); } void i2c_qca_board_init(struct i2c_qup_bus *i2c_bus) @@ -108,7 +109,6 @@ void i2c_qca_board_init(struct i2c_qup_bus *i2c_bus) /*Need to configure GPIO*/ /* Configure the I2C clock */ i2c_clock_config(); - i2c_hw_initialized = 0; i2c_board_initialized = 1; } @@ -136,16 +136,14 @@ static int i2c_hw_init(void) /* QUP configuration */ i2c_reset(); - /* Set the BLSP QUP state */ + /* Set the QUP state */ ret = check_qup_state_valid(); if (ret) return ret; writel(0,(i2c_base_addr + QUP_CONFIG_OFFSET)); - writel(QUP_APP_CLK_ON_EN | QUP_CORE_CLK_ON_EN | - QUP_FIFO_CLK_GATE_EN, (i2c_base_addr + QUP_CONFIG_OFFSET)); - + writel( clk_en, i2c_base_addr + QUP_CONFIG_OFFSET); writel(0, i2c_base_addr + QUP_I2C_MASTER_CLK_CTL_OFFSET); writel(0, i2c_base_addr + QUP_TEST_CTRL_OFFSET); writel(0, i2c_base_addr + QUP_IO_MODES_OFFSET); @@ -203,6 +201,14 @@ static int check_fifo_status(uint dir) status_flag = val & OUTPUT_FIFO_FULL; udelay(10); } while (status_flag); + /* Clear the flag and Acknowledge that the + * software has or will write the data. + */ + if (readl(i2c_base_addr + QUP_OPERATIONAL_OFFSET) + & OUTPUT_SERVICE_FLAG) { + writel(OUTPUT_SERVICE_FLAG, i2c_base_addr + + QUP_OPERATIONAL_OFFSET); + } } return SUCCESS; } @@ -344,7 +350,7 @@ int i2c_read_data(struct i2c_qup_bus *i2c_bus, uchar chip, uint addr, int alen, i2c_base_addr + QUP_MX_WRITE_COUNT_OFFSET); writel((IN_FIFO_TAG_BYTE_CNT + data_len), - i2c_base_addr + QUP_MX_READ_COUNT_OFFSET); + i2c_base_addr + QUP_MX_READ_COUNT_OFFSET); /* Set to RUN state */ ret = config_i2c_state(QUP_STATE_RUN); @@ -354,8 +360,9 @@ int i2c_read_data(struct i2c_qup_bus *i2c_bus, uchar chip, uint addr, int alen, } /* Configure the I2C Master clock */ - cfg = (QUP_INPUT_CLK / (I2C_CLK_100KHZ * 2)) - 3; + cfg = ((src_clk_freq / (I2C_CLK_100KHZ * 2)) - 3) & 0xff; writel(cfg, i2c_base_addr + QUP_I2C_MASTER_CLK_CTL_OFFSET); + /* Write to FIFO in Pause State */ /* Set to PAUSE state */ ret = config_i2c_state(QUP_STATE_PAUSE); @@ -377,7 +384,6 @@ int i2c_read_data(struct i2c_qup_bus *i2c_bus, uchar chip, uint addr, int alen, debug("State run failed\n"); goto out; } - mdelay(2); ret = check_write_done(); if (ret != SUCCESS) { @@ -392,12 +398,6 @@ int i2c_read_data(struct i2c_qup_bus *i2c_bus, uchar chip, uint addr, int alen, return -ENACK; } - if (readl(i2c_base_addr + QUP_OPERATIONAL_OFFSET) - & OUTPUT_SERVICE_FLAG) { - writel(OUTPUT_SERVICE_FLAG, - i2c_base_addr + QUP_OPERATIONAL_OFFSET); - } - fifo = (uint32_t *)(i2c_base_addr + QUP_INPUT_FIFO_OFFSET); mdelay(2); @@ -417,11 +417,99 @@ int i2c_read_data(struct i2c_qup_bus *i2c_bus, uchar chip, uint addr, int alen, } } - if (readl(i2c_base_addr + QUP_OPERATIONAL_OFFSET) - & INPUT_SERVICE_FLAG) { - writel(INPUT_SERVICE_FLAG, - i2c_base_addr + QUP_OPERATIONAL_OFFSET); + (void)config_i2c_state(QUP_STATE_RESET); + return SUCCESS; +out: + /* + * Put the I2C Core back in the Reset State to end the transfer. + */ + (void)config_i2c_state(QUP_STATE_RESET); + writel(QUP_MX_READ_COUNT, i2c_base_addr + QUP_MX_READ_COUNT_OFFSET); + return ret; +} + +int i2c_read_data_qup_v1(struct i2c_qup_bus *i2c_bus, uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int ret = 0; + uint32_t data = 0; + uint32_t *fifo; + int idx = 0; + int cfg; + + config_i2c_state(QUP_STATE_RESET); + + if (!i2c_board_initialized) { + i2c_qca_board_init(i2c_bus); } + + if (!i2c_hw_initialized) { + i2c_hw_init(); + } + + writel(0x3C, i2c_base_addr + QUP_ERROR_FLAGS_OFFSET); + writel(0x3C, i2c_base_addr + QUP_ERROR_FLAGS_EN_OFFSET); + writel(0, i2c_base_addr + QUP_I2C_MASTER_STATUS_OFFSET); + + /* Set to RUN state */ + ret = config_i2c_state(QUP_STATE_RUN); + if (ret != SUCCESS) { + debug("State run failed\n"); + goto out; + } + + /* Configure the I2C Master clock */ + cfg = ((src_clk_freq / (I2C_CLK_100KHZ * 2)) - 3) & 0xff; + writel(cfg, i2c_base_addr + QUP_I2C_MASTER_CLK_CTL_OFFSET); + + /* Send a write request to the chip */ + writel((QUP_I2C_START_SEQ | QUP_I2C_ADDR(chip)), + i2c_base_addr + QUP_OUTPUT_FIFO_OFFSET); + + writel((QUP_I2C_DATA_SEQ | QUP_I2C_DATA(addr)), + i2c_base_addr + QUP_OUTPUT_FIFO_OFFSET); + + mdelay(2); + ret = check_write_done(); + if (ret != SUCCESS) { + debug("Write done failed\n"); + goto out; + } + + ret = check_fifo_status(WRITE); + if (ret != SUCCESS) + goto out; + + /* Send read request */ + writel((QUP_I2C_START_SEQ | (QUP_I2C_ADDR(chip)| QUP_I2C_SLAVE_READ)), + i2c_base_addr + QUP_OUTPUT_FIFO_OFFSET); + + writel((QUP_I2C_RECV_SEQ | len), + i2c_base_addr + QUP_OUTPUT_FIFO_OFFSET); + + fifo = (uint32_t *)(i2c_base_addr + QUP_INPUT_FIFO_OFFSET); + + /* + * Wait some more to be sure that write operation is done + * in the QUP_INPUT_FIFO_OFFSET registeri before checking the + * fifo status + */ + mdelay(2); + ret = check_fifo_status(READ); + if (ret != SUCCESS) { + debug("Read status failed\n"); + goto out; + } + while (len) { + /* Read the data from the FIFO */ + data = readl(fifo); + + ret = i2c_process_read_data(data, buffer + idx, len); + if (ret) { + idx += ret; + len -= ret; + } + } + (void)config_i2c_state(QUP_STATE_RESET); return SUCCESS; out: @@ -505,7 +593,7 @@ int i2c_write_data(struct i2c_qup_bus *i2c_bus, uchar chip, uint addr, int alen, } /* Configure the I2C Master clock */ - cfg = (QUP_INPUT_CLK / (I2C_CLK_100KHZ * 2)) - 3; + cfg = ((src_clk_freq / (I2C_CLK_100KHZ * 2)) - 3) & 0xff; writel(cfg, i2c_base_addr + QUP_I2C_MASTER_CLK_CTL_OFFSET); @@ -594,15 +682,20 @@ out: static int qup_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) { - struct i2c_qup_bus *i2c_bus = dev_get_priv(bus); int ret; + struct i2c_qup_bus *i2c_bus = dev_get_priv(bus); + struct ipq_i2c_platdata *plat = bus->platdata; + plat->type = dev_get_driver_data(bus); + debug("i2c_xfer: %d messages\n", nmsgs); for (; nmsgs > 0; nmsgs--, msg++) { debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len); if (msg->flags & I2C_M_RD) { - ret = i2c_read_data(i2c_bus, msg->addr, 0, 0, msg->buf, - msg->len); + if (plat->type == qup_v1) + ret = i2c_read_data_qup_v1(i2c_bus, msg->addr, 0, 0, msg->buf, msg->len); + else + ret = i2c_read_data(i2c_bus, msg->addr, 0, 0, msg->buf, msg->len); } else { ret = i2c_write_data(i2c_bus, msg->addr, 0, 0, msg->buf, msg->len); @@ -615,6 +708,21 @@ static int qup_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, return 0; } +void qca_i2c_plat_data(struct udevice *bus) +{ + struct ipq_i2c_platdata *plat = bus->platdata; + plat->type = dev_get_driver_data(bus); + + if (plat-> type == qup_v1) { + io_mode = (INPUT_FIFO_MODE | OUTPUT_FIFO_MODE | OUTPUT_BIT_SHIFT_EN); + clk_en = (QUP_APP_CLK_ON_EN | QUP_CORE_CLK_ON_EN); + } + else { + io_mode = (INPUT_FIFO_MODE | OUTPUT_FIFO_MODE | PACK_EN | UNPACK_EN); + clk_en = (QUP_APP_CLK_ON_EN | QUP_CORE_CLK_ON_EN | QUP_FIFO_CLK_GATE_EN); + } +} + /* Probe to see if a chip is present. */ static int qup_i2c_probe_chip(struct udevice *bus, uint chip_addr, uint chip_flags) @@ -622,11 +730,17 @@ static int qup_i2c_probe_chip(struct udevice *bus, uint chip_addr, uchar buf[1]; struct i2c_qup_bus *i2c_bus = dev_get_priv(bus); + struct ipq_i2c_platdata *plat = bus->platdata; + plat->type = dev_get_driver_data(bus); buf[0] = 0; i2c_bus->i2c_base_addr = (int *)dev_get_addr(bus); i2c_base_addr = (int)i2c_bus->i2c_base_addr; - - return i2c_read_data(i2c_bus, chip_addr , 0x0, 0x0, buf, 0x1); + src_clk_freq = fdtdec_get_int(gd->fdt_blob, bus->of_offset, "clock-frequency", -1); + qca_i2c_plat_data(bus); + if (plat-> type == qup_v1) + return i2c_read_data_qup_v1(i2c_bus, chip_addr , 0x0, 0x0, buf, 0x1); + else + return i2c_read_data(i2c_bus, chip_addr , 0x0, 0x0, buf, 0x1); } static const struct dm_i2c_ops qup_i2c_ops = { @@ -635,7 +749,8 @@ static const struct dm_i2c_ops qup_i2c_ops = { }; static const struct udevice_id qpic_ver_ids[] = { - { .compatible = "qcom,qup-i2c"}, + { .compatible = "qcom,i2c-qup-v1.1.1", .data = qup_v1}, + { .compatible = "qcom,qup-i2c", .data = qup_v2}, { }, }; @@ -643,6 +758,7 @@ U_BOOT_DRIVER(i2c_qup) = { .name = "i2c_qup", .id = UCLASS_I2C, .of_match = qpic_ver_ids, + .platdata_auto_alloc_size = sizeof(struct ipq_i2c_platdata), .priv_auto_alloc_size = sizeof(struct i2c_qup_bus), .ops = &qup_i2c_ops, }; diff --git a/drivers/i2c/qup_i2c.h b/drivers/i2c/qup_i2c.h index c468ad4f53..d7d9419c40 100644 --- a/drivers/i2c/qup_i2c.h +++ b/drivers/i2c/qup_i2c.h @@ -63,7 +63,7 @@ #define NACK_BIT_SHIFT 3 #define QUP_CONFIG_MINI_CORE_I2C (2 << 8) -#define I2C_BIT_WORD 0x7 +#define I2C_BIT_WORD 0xF #define INPUT_FIFO_MODE (0x0 << 12) #define OUTPUT_FIFO_MODE (0x0 << 10) #define INPUT_BLOCK_MODE (0x01 << 12) @@ -71,7 +71,7 @@ #define PACK_EN (0x01 << 15) #define UNPACK_EN (0x01 << 14) #define OUTPUT_BIT_SHIFT_EN (0x01 << 16) -#define ERROR_FLAGS_EN 0x3C +#define ERROR_FLAGS_EN 0x7C #define I2C_MASTER_STATUS_CLEAR 0xFFFFFC #define QUP_DATA_AVAILABLE_FOR_READ (1 << 5) #define OUTPUT_SERVICE_FLAG (1 << 8) @@ -89,7 +89,10 @@ #define I2C_WRITE 0x0 #define I2C_READ 0x1 -#define QUP_I2C_START_SEQ 0x81 +#define QUP_I2C_DATA_SEQ (0x2 << 8) +#define QUP_I2C_RECV_SEQ (0x4 << 8) +#define QUP_I2C_SLAVE_READ (0x1) +#define QUP_I2C_START_SEQ (0x1 << 8) #define QUP_I2C_DATA_WRITE_SEQ 0x82 #define QUP_I2C_DATA_WRITE_AND_STOP_SEQ 0x83 #define QUP_I2C_DATA_READ_SEQ 0x85 @@ -119,3 +122,6 @@ enum dir { #define QUP_INPUT_CLK_TCXO 19200 #define QUP_INPUT_CLK QUP_INPUT_CLK_TCXO #define I2C_INPUT_CLK_TCXO_DIV4 ((I2C_INPUT_CLK_TCXO)/4) + +#define qup_v1 0 +#define qup_v2 1 diff --git a/include/configs/ipq806x.h b/include/configs/ipq806x.h index 6337a06f70..8cb4d87df0 100644 --- a/include/configs/ipq806x.h +++ b/include/configs/ipq806x.h @@ -130,11 +130,11 @@ /* * I2C Configs */ -#undef CONFIG_IPQ806X_I2C - +#define CONFIG_IPQ806X_I2C 1 #ifdef CONFIG_IPQ806X_I2C +#define CONFIG_SYS_I2C_QUP #define CONFIG_CMD_I2C -#define CONFIG_SYS_I2C_SPEED 0 +#define CONFIG_DM_I2C #endif /*