Merge "ipq806x: Added support for I2C diagnostics" into eggplant

This commit is contained in:
Linux Build Service Account 2016-11-21 05:30:47 -08:00 committed by Gerrit - the friendly Code Review server
commit 46a3cf234e
8 changed files with 227 additions and 48 deletions

View file

@ -23,6 +23,7 @@
console = "/serial@16340000";
nand = "/nand@1A600000";
gmac_gpio = "/gmac1_gpio";
i2c0 = "/i2c@16380000";
};
serial@16340000 {

View file

@ -401,7 +401,12 @@
oe = <GPIO_OE_ENABLE>;
};
};
};
i2c: i2c@16380000 {
compatible = "qcom,i2c-qup-v1.1.1";
reg = <0x16380000 0x500>;
clock-frequency = <24000>;
};
};

View file

@ -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);

View file

@ -23,6 +23,15 @@
#include <asm/arch-ipq40xx/clk.h>
#endif
#ifdef CONFIG_IPQ806x
#include <asm/arch-ipq806x/clock.h>
#endif
struct ipq_i2c_platdata {
int type;
};
typedef struct {
uint base;
uint clk_mode;

View file

@ -13,8 +13,38 @@
#include <common.h>
#include <asm/io.h>
#include <asm/arch-ipq806x/clk.h>
#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

View file

@ -24,10 +24,12 @@
#include <asm/arch-qcom-common/qca_common.h>
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,
};

View file

@ -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

View file

@ -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
/*