X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=drivers%2Fi2c%2Frk_i2c.c;h=cdd94bb05a9009c07e278667f086163e97597516;hb=15f09a1a834b125ed4a6102eac96186da0641541;hp=63b141838b6f14be71e0d38c708681d043ac6276;hpb=2313d48445e59f063ec9a3b4940fe8252737db76;p=oweals%2Fu-boot.git diff --git a/drivers/i2c/rk_i2c.c b/drivers/i2c/rk_i2c.c index 63b141838b..cdd94bb05a 100644 --- a/drivers/i2c/rk_i2c.c +++ b/drivers/i2c/rk_i2c.c @@ -1,10 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2015 Google, Inc * * (C) Copyright 2008-2014 Rockchip Electronics * Peter, Software Engineering, . - * - * SPDX-License-Identifier: GPL-2.0+ */ #include @@ -13,14 +12,12 @@ #include #include #include -#include -#include -#include +#include +#include +#include #include #include -DECLARE_GLOBAL_DATA_PTR; - /* i2c timerout */ #define I2C_TIMEOUT_MS 100 #define I2C_RETRY_COUNT 3 @@ -34,6 +31,18 @@ struct rk_i2c { unsigned int speed; }; +enum { + RK_I2C_LEGACY, + RK_I2C_NEW, +}; + +/** + * @controller_type: i2c controller type + */ +struct rk_i2c_soc_data { + int controller_type; +}; + static inline void rk_i2c_get_div(int div, int *divh, int *divl) { *divl = div / 2; @@ -164,6 +173,7 @@ static int rk_i2c_read(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len, uint rxdata; uint i, j; int err; + bool snd_chunk = false; debug("rk_i2c_read: chip = %d, reg = %d, r_len = %d, b_len = %d\n", chip, reg, r_len, b_len); @@ -184,15 +194,26 @@ static int rk_i2c_read(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len, while (bytes_remain_len) { if (bytes_remain_len > RK_I2C_FIFO_SIZE) { - con = I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TRX); + con = I2C_CON_EN; bytes_xferred = 32; } else { - con = I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TRX) | - I2C_CON_LASTACK; + /* + * The hw can read up to 32 bytes at a time. If we need + * more than one chunk, send an ACK after the last byte. + */ + con = I2C_CON_EN | I2C_CON_LASTACK; bytes_xferred = bytes_remain_len; } words_xferred = DIV_ROUND_UP(bytes_xferred, 4); + /* + * make sure we are in plain RX mode if we read a second chunk + */ + if (snd_chunk) + con |= I2C_CON_MOD(I2C_MODE_RX); + else + con |= I2C_CON_MOD(I2C_MODE_TRX); + writel(con, ®s->con); writel(bytes_xferred, ®s->mrxcnt); writel(I2C_MBRFIEN | I2C_NAKRCVIEN, ®s->ien); @@ -227,6 +248,7 @@ static int rk_i2c_read(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len, } bytes_remain_len -= bytes_xferred; + snd_chunk = true; debug("I2C Read bytes_remain_len %d\n", bytes_remain_len); } @@ -258,7 +280,7 @@ static int rk_i2c_write(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len, while (bytes_remain_len) { if (bytes_remain_len > RK_I2C_FIFO_SIZE) - bytes_xferred = 32; + bytes_xferred = RK_I2C_FIFO_SIZE; else bytes_xferred = bytes_remain_len; words_xferred = DIV_ROUND_UP(bytes_xferred, 4); @@ -269,17 +291,17 @@ static int rk_i2c_write(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len, if ((i * 4 + j) == bytes_xferred) break; - if (i == 0 && j == 0) { + if (i == 0 && j == 0 && pbuf == buf) { txdata |= (chip << 1); - } else if (i == 0 && j <= r_len) { + } else if (i == 0 && j <= r_len && pbuf == buf) { txdata |= (reg & (0xff << ((j - 1) * 8))) << 8; } else { txdata |= (*pbuf++)<<(j * 8); } - writel(txdata, ®s->txdata[i]); } - debug("I2c Write TXDATA[%d] = 0x%x\n", i, txdata); + writel(txdata, ®s->txdata[i]); + debug("I2c Write TXDATA[%d] = 0x%08x\n", i, txdata); } writel(I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TX), ®s->con); @@ -368,8 +390,37 @@ static int rockchip_i2c_ofdata_to_platdata(struct udevice *bus) static int rockchip_i2c_probe(struct udevice *bus) { struct rk_i2c *priv = dev_get_priv(bus); + struct rk_i2c_soc_data *soc_data; + struct udevice *pinctrl; + int bus_nr; + int ret; + + priv->regs = dev_read_addr_ptr(bus); + + soc_data = (struct rk_i2c_soc_data*)dev_get_driver_data(bus); - priv->regs = (void *)dev_get_addr(bus); + if (soc_data->controller_type == RK_I2C_LEGACY) { + ret = dev_read_alias_seq(bus, &bus_nr); + if (ret < 0) { + debug("%s: Could not get alias for %s: %d\n", + __func__, bus->name, ret); + return ret; + } + + ret = uclass_get_device(UCLASS_PINCTRL, 0, &pinctrl); + if (ret) { + debug("%s: Cannot find pinctrl device\n", __func__); + return ret; + } + + /* pinctrl will switch I2C to new type */ + ret = pinctrl_request_noflags(pinctrl, PERIPH_ID_I2C0 + bus_nr); + if (ret) { + debug("%s: Failed to switch I2C to new type %s: %d\n", + __func__, bus->name, ret); + return ret; + } + } return 0; } @@ -379,8 +430,55 @@ static const struct dm_i2c_ops rockchip_i2c_ops = { .set_bus_speed = rockchip_i2c_set_bus_speed, }; +static const struct rk_i2c_soc_data rk3066_soc_data = { + .controller_type = RK_I2C_LEGACY, +}; + +static const struct rk_i2c_soc_data rk3188_soc_data = { + .controller_type = RK_I2C_LEGACY, +}; + +static const struct rk_i2c_soc_data rk3228_soc_data = { + .controller_type = RK_I2C_NEW, +}; + +static const struct rk_i2c_soc_data rk3288_soc_data = { + .controller_type = RK_I2C_NEW, +}; + +static const struct rk_i2c_soc_data rk3328_soc_data = { + .controller_type = RK_I2C_NEW, +}; + +static const struct rk_i2c_soc_data rk3399_soc_data = { + .controller_type = RK_I2C_NEW, +}; + static const struct udevice_id rockchip_i2c_ids[] = { - { .compatible = "rockchip,rk3288-i2c" }, + { + .compatible = "rockchip,rk3066-i2c", + .data = (ulong)&rk3066_soc_data, + }, + { + .compatible = "rockchip,rk3188-i2c", + .data = (ulong)&rk3188_soc_data, + }, + { + .compatible = "rockchip,rk3228-i2c", + .data = (ulong)&rk3228_soc_data, + }, + { + .compatible = "rockchip,rk3288-i2c", + .data = (ulong)&rk3288_soc_data, + }, + { + .compatible = "rockchip,rk3328-i2c", + .data = (ulong)&rk3328_soc_data, + }, + { + .compatible = "rockchip,rk3399-i2c", + .data = (ulong)&rk3399_soc_data, + }, { } };