summaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/Kconfig1
-rw-r--r--drivers/i2c/Makefile1
-rw-r--r--drivers/i2c/algos/Kconfig1
-rw-r--r--drivers/i2c/algos/Makefile1
-rw-r--r--drivers/i2c/algos/i2c-algo-bit.c26
-rw-r--r--drivers/i2c/busses/Kconfig29
-rw-r--r--drivers/i2c/busses/Makefile5
-rw-r--r--drivers/i2c/busses/i2c-at91.c158
-rw-r--r--drivers/i2c/busses/i2c-bcm283x.c335
-rw-r--r--drivers/i2c/busses/i2c-cadence.c454
-rw-r--r--drivers/i2c/busses/i2c-designware.c35
-rw-r--r--drivers/i2c/busses/i2c-gpio.c16
-rw-r--r--drivers/i2c/busses/i2c-imx-early.c70
-rw-r--r--drivers/i2c/busses/i2c-imx-lpi2c.c555
-rw-r--r--drivers/i2c/busses/i2c-imx.c53
-rw-r--r--drivers/i2c/busses/i2c-imx.h1
-rw-r--r--drivers/i2c/busses/i2c-mv64xxx.c19
-rw-r--r--drivers/i2c/busses/i2c-omap.c26
-rw-r--r--drivers/i2c/busses/i2c-rockchip.c462
-rw-r--r--drivers/i2c/busses/i2c-stm32.c456
-rw-r--r--drivers/i2c/busses/i2c-tegra.c24
-rw-r--r--drivers/i2c/busses/i2c-versatile.c20
-rw-r--r--drivers/i2c/i2c-mux.c15
-rw-r--r--drivers/i2c/i2c-smbus.c2
-rw-r--r--drivers/i2c/i2c.c194
-rw-r--r--drivers/i2c/muxes/Kconfig1
-rw-r--r--drivers/i2c/muxes/Makefile1
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca954x.c17
28 files changed, 2539 insertions, 439 deletions
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 0bd8be04e1..a8c91e3db9 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
menuconfig I2C
bool "I2C drivers"
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index b887e75573..f8124bab2b 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-y += busses/
obj-$(CONFIG_I2C) += i2c.o i2c-smbus.o algos/ muxes/
obj-$(CONFIG_I2C_MUX) += i2c-mux.o
diff --git a/drivers/i2c/algos/Kconfig b/drivers/i2c/algos/Kconfig
index c74b148f5e..0ebd8b7268 100644
--- a/drivers/i2c/algos/Kconfig
+++ b/drivers/i2c/algos/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# I2C algorithm drivers configuration
#
diff --git a/drivers/i2c/algos/Makefile b/drivers/i2c/algos/Makefile
index e0a03995b5..65b4a0a339 100644
--- a/drivers/i2c/algos/Makefile
+++ b/drivers/i2c/algos/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for the i2c algorithms
#
diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c
index 2563c0d25a..9b7e5d96f0 100644
--- a/drivers/i2c/algos/i2c-algo-bit.c
+++ b/drivers/i2c/algos/i2c-algo-bit.c
@@ -1,26 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* -------------------------------------------------------------------------
* i2c-algo-bit.c i2c driver algorithms for bit-shift adapters
* -------------------------------------------------------------------------
* Copyright (C) 1995-2000 Simon G. Vogl
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- MA 02110-1301 USA.
- * ------------------------------------------------------------------------- */
-
-/* With some changes from Frodo Looijaard <frodol@dds.nl>, Kyösti Mälkki
- <kmalkki@cc.hut.fi> and Jean Delvare <khali@linux-fr.org> */
+ *
+ * With some changes from Frodo Looijaard <frodol@dds.nl>, Kyösti Mälkki
+ * <kmalkki@cc.hut.fi> and Jean Delvare <khali@linux-fr.org>
+ */
#include <common.h>
#include <init.h>
@@ -259,7 +245,7 @@ static int i2c_inb(struct i2c_adapter *i2c_adap)
static int test_bus(struct i2c_adapter *i2c_adap)
{
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
- struct device_d *dev = &i2c_adap->dev;
+ struct device *dev = &i2c_adap->dev;
int scl, sda, ret;
if (adap->pre_xfer) {
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 21d2cb21cf..a274baf4b6 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Sensor device configuration
#
@@ -16,6 +17,10 @@ config I2C_AT91
bool "AT91 I2C Master driver"
depends on ARCH_AT91
+config I2C_BCM283X
+ bool "BCM283X I2C Master driver"
+ depends on ARCH_BCM283X || COMPILE_TEST
+
config I2C_IMX
bool "MPC85xx/MPC5200/i.MX I2C Master driver"
depends on ARCH_IMX || ARCH_MPC85XX || ARCH_MPC5200 || ARCH_LAYERSCAPE
@@ -25,6 +30,13 @@ config I2C_IMX
for many i.MX ARM based SoCs, for MPC85xx and MPC5200 PowerPC based
SoCs.
+config I2C_IMX_LPI2C
+ tristate "IMX Low Power I2C interface"
+ depends on ARCH_IMX || COMPILE_TEST
+ help
+ Say Y here if you want to use the Low Power IIC bus controller
+ on the Freescale i.MX processors.
+
config I2C_DESIGNWARE
bool "Synopsys DesignWare I2C Master driver"
help
@@ -59,5 +71,22 @@ config I2C_STM32
bool "STM32 I2C master driver"
select RESET_CONTROLLER
depends on HAVE_CLK
+ depends on ARCH_STM32 || COMPILE_TEST
+
+config I2C_RK3X
+ tristate "Rockchip RK3xxx I2C adapter"
+ depends on HAVE_CLK
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ help
+ Say Y here to include support for the I2C adapter in Rockchip RK3xxx
+ SoCs.
+
+config I2C_CADENCE
+ bool "Cadence I2C adapter"
+ depends on HAVE_CLK
+ depends on ARCH_ZYNQMP || COMPILE_TEST
+ help
+ Say Y here to include support for the Cadence I2C host controller found
+ in Zynq UltraScale+ MPSoCs.
endmenu
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 7e450ead27..b4225995c0 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -1,10 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
+obj-$(CONFIG_I2C_BCM283X) += i2c-bcm283x.o
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_I2C_IMX) += i2c-imx.o
lwl-$(CONFIG_I2C_IMX_EARLY) += i2c-imx-early.o
+obj-pbl-$(CONFIG_I2C_IMX_LPI2C) += i2c-imx-lpi2c.o
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
obj-$(CONFIG_I2C_DESIGNWARE) += i2c-designware.o
obj-$(CONFIG_I2C_STM32) += i2c-stm32.o
+obj-$(CONFIG_I2C_RK3X) += i2c-rockchip.o
+obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o
diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c
index 76bb51bf30..5b1f456187 100644
--- a/drivers/i2c/busses/i2c-at91.c
+++ b/drivers/i2c/busses/i2c-at91.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* i2c Support for Atmel's AT91 Two-Wire Interface (TWI)
*
@@ -10,11 +11,6 @@
*
* Borrowed heavily from original work by:
* Copyright (C) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
*/
#include <common.h>
@@ -48,6 +44,8 @@
#define AT91_TWI_IADR 0x000c /* Internal Address Register */
#define AT91_TWI_CWGR 0x0010 /* Clock Waveform Generator Reg */
+#define AT91_TWI_CWGR_HOLD_MAX 0x1f
+#define AT91_TWI_CWGR_HOLD(x) (((x) & AT91_TWI_CWGR_HOLD_MAX) << 24)
#define AT91_TWI_SR 0x0020 /* Status Register */
#define AT91_TWI_TXCOMP 0x0001 /* Transmission Complete */
@@ -64,10 +62,22 @@
#define AT91_TWI_RHR 0x0030 /* Receive Holding Register */
#define AT91_TWI_THR 0x0034 /* Transmit Holding Register */
+#define AT91_TWI_FILTR 0x0044
+#define AT91_TWI_FILTR_FILT BIT(0)
+#define AT91_TWI_FILTR_PADFEN BIT(1)
+#define AT91_TWI_FILTR_THRES(v) ((v) << 8)
+#define AT91_TWI_FILTR_THRES_MAX 7
+#define AT91_TWI_FILTR_THRES_MASK GENMASK(10, 8)
+
struct at91_twi_pdata {
unsigned clk_max_div;
unsigned clk_offset;
bool has_unre_flag;
+ bool has_alt_cmd;
+ bool has_hold_field;
+ bool has_dig_filtr;
+ bool has_adv_dig_filtr;
+ bool has_ana_filtr;
};
struct at91_twi_dev {
@@ -82,6 +92,10 @@ struct at91_twi_dev {
struct i2c_adapter adapter;
unsigned twi_cwgr_reg;
struct at91_twi_pdata *pdata;
+ u32 filter_width;
+
+ bool enable_dig_filt;
+ bool enable_ana_filt;
};
#define to_at91_twi_dev(a) container_of(a, struct at91_twi_dev, adapter)
@@ -104,11 +118,31 @@ static void at91_disable_twi_interrupts(struct at91_twi_dev *dev)
static void at91_init_twi_bus(struct at91_twi_dev *dev)
{
+ struct at91_twi_pdata *pdata = dev->pdata;
+ u32 filtr = 0;
+
at91_disable_twi_interrupts(dev);
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SWRST);
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSEN);
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SVDIS);
at91_twi_write(dev, AT91_TWI_CWGR, dev->twi_cwgr_reg);
+
+ /* enable digital filter */
+ if (pdata->has_dig_filtr && dev->enable_dig_filt)
+ filtr |= AT91_TWI_FILTR_FILT;
+
+ /* enable advanced digital filter */
+ if (pdata->has_adv_dig_filtr && dev->enable_dig_filt)
+ filtr |= AT91_TWI_FILTR_FILT |
+ (AT91_TWI_FILTR_THRES(dev->filter_width) &
+ AT91_TWI_FILTR_THRES_MASK);
+
+ /* enable analog filter */
+ if (pdata->has_ana_filtr && dev->enable_ana_filt)
+ filtr |= AT91_TWI_FILTR_PADFEN;
+
+ if (filtr)
+ at91_twi_write(dev, AT91_TWI_FILTR, filtr);
}
/*
@@ -117,10 +151,13 @@ static void at91_init_twi_bus(struct at91_twi_dev *dev)
*/
static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk)
{
- int ckdiv, cdiv, div;
+ int ckdiv, cdiv, div, hold = 0, filter_width = 0;
struct at91_twi_pdata *pdata = dev->pdata;
int offset = pdata->clk_offset;
int max_ckdiv = pdata->clk_max_div;
+ struct i2c_timings timings, *t = &timings;
+
+ i2c_parse_fw_timings(dev->dev, t, true);
div = max(0, (int)DIV_ROUND_UP(clk_get_rate(dev->clk),
2 * twi_clk) - offset);
@@ -128,14 +165,54 @@ static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk)
cdiv = div >> ckdiv;
if (ckdiv > max_ckdiv) {
- dev_warn(&dev->adapter.dev, "%d exceeds ckdiv max value which is %d.\n",
+ dev_warn(dev->dev, "%d exceeds ckdiv max value which is %d.\n",
ckdiv, max_ckdiv);
ckdiv = max_ckdiv;
cdiv = 255;
}
- dev->twi_cwgr_reg = (ckdiv << 16) | (cdiv << 8) | cdiv;
- dev_dbg(&dev->adapter.dev, "cdiv %d ckdiv %d\n", cdiv, ckdiv);
+ if (pdata->has_hold_field) {
+ /*
+ * hold time = HOLD + 3 x T_peripheral_clock
+ * Use clk rate in kHz to prevent overflows when computing
+ * hold.
+ */
+ hold = DIV_ROUND_UP(t->sda_hold_ns
+ * (clk_get_rate(dev->clk) / 1000), 1000000);
+ hold -= 3;
+ if (hold < 0)
+ hold = 0;
+ if (hold > AT91_TWI_CWGR_HOLD_MAX) {
+ dev_warn(dev->dev,
+ "HOLD field set to its maximum value (%d instead of %d)\n",
+ AT91_TWI_CWGR_HOLD_MAX, hold);
+ hold = AT91_TWI_CWGR_HOLD_MAX;
+ }
+ }
+
+ if (pdata->has_adv_dig_filtr) {
+ /*
+ * filter width = 0 to AT91_TWI_FILTR_THRES_MAX
+ * peripheral clocks
+ */
+ filter_width = DIV_ROUND_UP(t->digital_filter_width_ns
+ * (clk_get_rate(dev->clk) / 1000), 1000000);
+ if (filter_width > AT91_TWI_FILTR_THRES_MAX) {
+ dev_warn(dev->dev,
+ "Filter threshold set to its maximum value (%d instead of %d)\n",
+ AT91_TWI_FILTR_THRES_MAX, filter_width);
+ filter_width = AT91_TWI_FILTR_THRES_MAX;
+ }
+ }
+
+ dev->twi_cwgr_reg = (ckdiv << 16) | (cdiv << 8) | cdiv
+ | AT91_TWI_CWGR_HOLD(hold);
+
+ dev->filter_width = filter_width;
+
+ dev_dbg(dev->dev, "cdiv %d ckdiv %d hold %d (%d ns), filter_width %d (%d ns)\n",
+ cdiv, ckdiv, hold, t->sda_hold_ns, filter_width,
+ t->digital_filter_width_ns);
}
static void at91_twi_write_next_byte(struct at91_twi_dev *dev)
@@ -149,7 +226,7 @@ static void at91_twi_write_next_byte(struct at91_twi_dev *dev)
if (--dev->buf_len == 0)
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP);
- dev_dbg(&dev->adapter.dev, "wrote 0x%x, to go %d\n", *dev->buf, dev->buf_len);
+ dev_dbg(dev->dev, "wrote 0x%x, to go %d\n", *dev->buf, dev->buf_len);
++dev->buf;
}
@@ -166,7 +243,7 @@ static void at91_twi_read_next_byte(struct at91_twi_dev *dev)
if (dev->buf_len == 1)
at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP);
- dev_dbg(&dev->adapter.dev, "read 0x%x, to go %d\n", *dev->buf, dev->buf_len);
+ dev_dbg(dev->dev, "read 0x%x, to go %d\n", *dev->buf, dev->buf_len);
++dev->buf;
}
@@ -183,7 +260,7 @@ static int at91_twi_wait_completion(struct at91_twi_dev *dev)
if (!(status & irqstatus)) {
if (is_timeout(start, AT91_I2C_TIMEOUT)) {
- dev_warn(&dev->adapter.dev, "timeout waiting for bus ready\n");
+ dev_warn(dev->dev, "timeout waiting for bus ready\n");
return -ETIMEDOUT;
} else {
continue;
@@ -195,7 +272,7 @@ static int at91_twi_wait_completion(struct at91_twi_dev *dev)
else if (irqstatus & AT91_TWI_TXRDY)
at91_twi_write_next_byte(dev);
else
- dev_warn(&dev->adapter.dev, "neither rx and tx are ready\n");
+ dev_warn(dev->dev, "neither rx and tx are ready\n");
dev->transfer_status |= status;
@@ -211,7 +288,7 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
int ret;
bool has_unre_flag = dev->pdata->has_unre_flag;
- dev_dbg(&dev->adapter.dev, "transfer: %s %d bytes.\n",
+ dev_dbg(dev->dev, "transfer: %s %d bytes.\n",
(dev->msg->flags & I2C_M_RD) ? "read" : "write", dev->buf_len);
dev->transfer_status = 0;
@@ -223,7 +300,7 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
unsigned start_flags = AT91_TWI_START;
if (at91_twi_read(dev, AT91_TWI_SR) & AT91_TWI_RXRDY) {
- dev_err(&dev->adapter.dev, "RXRDY still set!");
+ dev_err(dev->dev, "RXRDY still set!");
at91_twi_read(dev, AT91_TWI_RHR);
}
@@ -243,27 +320,27 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
ret = at91_twi_wait_completion(dev);
if (ret < 0) {
- dev_err(&dev->adapter.dev, "controller timed out\n");
+ dev_err(dev->dev, "controller timed out\n");
at91_init_twi_bus(dev);
ret = -ETIMEDOUT;
goto error;
}
if (dev->transfer_status & AT91_TWI_NACK) {
- dev_dbg(&dev->adapter.dev, "received nack\n");
+ dev_dbg(dev->dev, "received nack\n");
ret = -EREMOTEIO;
goto error;
}
if (dev->transfer_status & AT91_TWI_OVRE) {
- dev_err(&dev->adapter.dev, "overrun while reading\n");
+ dev_err(dev->dev, "overrun while reading\n");
ret = -EIO;
goto error;
}
if (has_unre_flag && dev->transfer_status & AT91_TWI_UNRE) {
- dev_err(&dev->adapter.dev, "underrun while writing\n");
+ dev_err(dev->dev, "underrun while writing\n");
ret = -EIO;
goto error;
}
- dev_dbg(&dev->adapter.dev, "transfer complete\n");
+ dev_dbg(dev->dev, "transfer complete\n");
return 0;
@@ -285,7 +362,7 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num)
* repeated start via it's internal address feature.
*/
if (num > 2) {
- dev_err(&dev->adapter.dev,
+ dev_err(dev->dev,
"cannot handle more than two concatenated messages.\n");
return 0;
} else if (num == 2) {
@@ -293,11 +370,11 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num)
int i;
if (msg->flags & I2C_M_RD) {
- dev_err(&dev->adapter.dev, "first transfer must be write.\n");
+ dev_err(dev->dev, "first transfer must be write.\n");
return -EINVAL;
}
if (msg->len > 3) {
- dev_err(&dev->adapter.dev, "first message size must be <= 3.\n");
+ dev_err(dev->dev, "first message size must be <= 3.\n");
return -EINVAL;
}
@@ -360,6 +437,24 @@ static struct at91_twi_pdata at91sam9x5_config = {
.has_unre_flag = false,
};
+static struct at91_twi_pdata sama5d4_config = {
+ .clk_max_div = 7,
+ .clk_offset = 4,
+ .has_hold_field = true,
+ .has_dig_filtr = true,
+};
+
+static struct at91_twi_pdata sama5d2_config = {
+ .clk_max_div = 7,
+ .clk_offset = 3,
+ .has_unre_flag = true,
+ .has_alt_cmd = true,
+ .has_hold_field = true,
+ .has_dig_filtr = true,
+ .has_adv_dig_filtr = true,
+ .has_ana_filtr = true,
+};
+
static struct platform_device_id at91_twi_devtypes[] = {
{
.name = "at91rm9200-i2c",
@@ -404,11 +499,21 @@ static struct of_device_id at91_twi_dt_ids[] = {
.compatible = "atmel,at91sam9x5-i2c",
.data = &at91sam9x5_config,
}, {
+ .compatible = "atmel,sama5d4-i2c",
+ .data = &sama5d4_config,
+ }, {
+ .compatible = "atmel,sama5d2-i2c",
+ .data = &sama5d2_config,
+ }, {
+ .compatible = "microchip,sam9x60-i2c",
+ .data = &sama5d2_config,
+ }, {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, at91_twi_dt_ids);
-static int at91_twi_probe(struct device_d *dev)
+static int at91_twi_probe(struct device *dev)
{
struct resource *iores;
struct at91_twi_dev *i2c_at91;
@@ -417,6 +522,7 @@ static int at91_twi_probe(struct device_d *dev)
u32 bus_clk_rate;
i2c_at91 = xzalloc(sizeof(struct at91_twi_dev));
+ i2c_at91->dev = dev;
rc = dev_get_drvdata(dev, (const void **)&i2c_data);
if (rc < 0) {
@@ -453,7 +559,7 @@ static int at91_twi_probe(struct device_d *dev)
i2c_at91->adapter.master_xfer = at91_twi_xfer;
i2c_at91->adapter.dev.parent = dev;
i2c_at91->adapter.nr = dev->id;
- i2c_at91->adapter.dev.device_node = dev->device_node;
+ i2c_at91->adapter.dev.of_node = dev->of_node;
rc = i2c_add_numbered_adapter(&i2c_at91->adapter);
if (rc) {
@@ -472,7 +578,7 @@ out_free:
return rc;
}
-static struct driver_d at91_twi_driver = {
+static struct driver at91_twi_driver = {
.name = "at91-twi",
.probe = at91_twi_probe,
.id_table = at91_twi_devtypes,
diff --git a/drivers/i2c/busses/i2c-bcm283x.c b/drivers/i2c/busses/i2c-bcm283x.c
new file mode 100644
index 0000000000..b40918932f
--- /dev/null
+++ b/drivers/i2c/busses/i2c-bcm283x.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * I2C bus driver for the BSC peripheral on Broadcom's bcm283x family of SoCs
+ *
+ * Based on documentation published by Raspberry Pi foundation and the kernel
+ * driver written by Stephen Warren.
+ *
+ * Copyright (C) Stephen Warren
+ * Copyright (C) 2022 Daniel Brát
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <malloc.h>
+#include <i2c/i2c.h>
+#include <i2c/i2c-algo-bit.h>
+#include <linux/iopoll.h>
+#include <linux/clk.h>
+#include <init.h>
+#include <of_address.h>
+
+// BSC C (Control) register
+#define BSC_C_READ BIT(0)
+#define BSC_C_CLEAR1 BIT(4)
+#define BSC_C_CLEAR2 BIT(5)
+#define BSC_C_ST BIT(7)
+#define BSC_C_INTD BIT(8)
+#define BSC_C_INTT BIT(9)
+#define BSC_C_INTR BIT(10)
+#define BSC_C_I2CEN BIT(15)
+
+// BSC S (Status) register
+#define BSC_S_TA BIT(0)
+#define BSC_S_DONE BIT(1)
+#define BSC_S_TXW BIT(2)
+#define BSC_S_RXR BIT(3)
+#define BSC_S_TXD BIT(4)
+#define BSC_S_RXD BIT(5)
+#define BSC_S_TXE BIT(6)
+#define BSC_S_RXF BIT(7)
+#define BSC_S_ERR BIT(8)
+#define BSC_S_CLKT BIT(9)
+
+// BSC A (Address) register
+#define BSC_A_MASK 0x7f
+
+// Constants
+#define BSC_CDIV_MIN 0x0002
+#define BSC_CDIV_MAX 0xfffe
+#define BSC_FIFO_SIZE 16U
+
+struct __packed bcm283x_i2c_regs {
+ u32 c;
+ u32 s;
+ u32 dlen;
+ u32 a;
+ u32 fifo;
+ u32 div;
+ u32 del;
+ u32 clkt;
+};
+
+struct bcm283x_i2c {
+ struct i2c_adapter adapter;
+ struct clk *mclk;
+ struct bcm283x_i2c_regs __iomem *regs;
+ u32 bitrate;
+};
+
+static inline struct bcm283x_i2c *to_bcm283x_i2c(struct i2c_adapter *adapter)
+{
+ return container_of(adapter, struct bcm283x_i2c, adapter);
+}
+
+static inline int bcm283x_i2c_init(struct bcm283x_i2c *bcm_i2c)
+{
+ struct device *dev = bcm_i2c->adapter.dev.parent;
+ u32 mclk_rate, cdiv, redl, fedl;
+
+ /*
+ * Reset control reg, flush FIFO, clear all flags and disable
+ * clock stretching
+ */
+ writel(0UL, &bcm_i2c->regs->c);
+ writel(BSC_C_CLEAR1, &bcm_i2c->regs->c);
+ writel(BSC_S_DONE | BSC_S_ERR | BSC_S_CLKT, &bcm_i2c->regs->s);
+ writel(0UL, &bcm_i2c->regs->clkt);
+
+ /*
+ * Set the divider based on the master clock frequency and the
+ * requested i2c bitrate
+ */
+ mclk_rate = clk_get_rate(bcm_i2c->mclk);
+ cdiv = DIV_ROUND_UP(mclk_rate, bcm_i2c->bitrate);
+ dev_dbg(dev, "bcm283x_i2c_init: mclk_rate=%u, cdiv=%08x\n",
+ mclk_rate, cdiv);
+ /* Note from kernel driver:
+ * Per the datasheet, the register is always interpreted as an even
+ * number, by rounding down. In other words, the LSB is ignored. So,
+ * if the LSB is set, increment the divider to avoid any issue.
+ */
+ if (cdiv & 1)
+ cdiv++;
+ if ((cdiv < BSC_CDIV_MIN) || (cdiv > BSC_CDIV_MAX)) {
+ dev_err(dev, "failed to calculate valid clock divider value\n");
+ return -EINVAL;
+ }
+ dev_dbg(dev, "bcm283x_i2c_init: cdiv adjusted to %04x\n", cdiv);
+ fedl = max(cdiv / 16, 1U);
+ redl = max(cdiv / 4, 1U);
+ dev_dbg(dev, "bcm283x_i2c_init: fedl=%04x, redl=%04x\n", fedl, redl);
+ writel(cdiv & 0xffff, &bcm_i2c->regs->div);
+ writel((fedl << 16) | redl, &bcm_i2c->regs->del);
+ dev_dbg(dev, "bcm283x_i2c_init: regs->div=%08x, regs->del=%08x\n",
+ readl(&bcm_i2c->regs->div), readl(&bcm_i2c->regs->del));
+
+ return 0;
+}
+
+/*
+ * Macro to calculate generous timeout for given bitrate and number of bytes
+ */
+#define calc_byte_timeout_us(bitrate) \
+ (3 * 9 * DIV_ROUND_UP(1000000, bitrate))
+#define calc_msg_timeout_us(bitrate, bytes) \
+ ((bytes + 1) * calc_byte_timeout_us(bitrate))
+
+static int bcm283x_i2c_msg_xfer(struct bcm283x_i2c *bcm_i2c,
+ struct i2c_msg *msg)
+{
+ int ret;
+ u32 reg_c, reg_s, reg_dlen, timeout;
+ struct device *dev = &bcm_i2c->adapter.dev;
+ bool msg_read = (msg->flags & I2C_M_RD) > 0;
+ bool msg_10bit = (msg->flags & I2C_M_TEN) > 0;
+ u16 buf_pos = 0;
+ u32 bytes_left = reg_dlen = msg->len;
+
+ if (msg_10bit && msg_read) {
+ timeout = calc_byte_timeout_us(bcm_i2c->bitrate);
+ writel(1UL, &bcm_i2c->regs->dlen);
+ writel(msg->addr & 0xff, &bcm_i2c->regs->fifo);
+ writel(((msg->addr >> 8) | 0x78) & BSC_A_MASK, &bcm_i2c->regs->a);
+ writel(BSC_C_ST | BSC_C_I2CEN, &bcm_i2c->regs->c);
+ ret = readl_poll_timeout(&bcm_i2c->regs->s, reg_s,
+ reg_s & (BSC_S_TA | BSC_S_ERR), timeout);
+
+ if (ret) {
+ dev_err(dev, "timeout: 10bit read initilization\n");
+ goto out;
+ }
+ if (reg_s & BSC_S_ERR)
+ goto nack;
+
+ } else if (msg_10bit) {
+ reg_dlen++;
+ writel(msg->addr & 0xff, &bcm_i2c->regs->fifo);
+ writel(((msg->addr >> 8) | 0x78) & BSC_A_MASK, &bcm_i2c->regs->a);
+ } else {
+ writel(msg->addr & BSC_A_MASK, &bcm_i2c->regs->a);
+ }
+
+ writel(reg_dlen, &bcm_i2c->regs->dlen);
+ reg_c = BSC_C_ST | BSC_C_I2CEN;
+ if (msg_read)
+ reg_c |= BSC_C_READ;
+ writel(reg_c, &bcm_i2c->regs->c);
+
+ if (msg_read) {
+ /*
+ * Read out data from FIFO as soon as it is available
+ */
+ timeout = calc_byte_timeout_us(bcm_i2c->bitrate);
+ for (; bytes_left; bytes_left--) {
+ ret = readl_poll_timeout(&bcm_i2c->regs->s, reg_s,
+ reg_s & (BSC_S_RXD | BSC_S_ERR),
+ timeout);
+
+ if (ret) {
+ dev_err(dev, "timeout: waiting for data in FIFO\n");
+ goto out;
+ }
+ if (reg_s & BSC_S_ERR)
+ goto nack;
+
+ msg->buf[buf_pos++] = (u8) readl(&bcm_i2c->regs->fifo);
+ }
+ } else {
+ timeout = calc_byte_timeout_us(bcm_i2c->bitrate);
+ /*
+ * Feed data to FIFO as soon as there is space for them
+ */
+ for (; bytes_left; bytes_left--) {
+ ret = readl_poll_timeout(&bcm_i2c->regs->s, reg_s,
+ reg_s & (BSC_S_TXD | BSC_S_ERR),
+ timeout);
+
+ if (ret) {
+ dev_err(dev, "timeout: waiting for space in FIFO\n");
+ goto out;
+ }
+ if (reg_s & BSC_S_ERR)
+ goto nack;
+
+ writel(msg->buf[buf_pos++], &bcm_i2c->regs->fifo);
+ }
+ }
+
+ /*
+ * Wait for the current transfer to finish and then flush FIFO
+ * and clear any flags so that we are ready for next msg
+ */
+ timeout = calc_msg_timeout_us(bcm_i2c->bitrate, reg_dlen);
+ ret = readl_poll_timeout(&bcm_i2c->regs->s, reg_s,
+ reg_s & (BSC_S_DONE | BSC_S_ERR), timeout);
+
+ if (ret) {
+ dev_err(dev, "timeout: waiting for transfer to end\n");
+ goto out;
+ }
+ if (reg_s & BSC_S_ERR)
+ goto nack;
+ goto out;
+nack:
+ dev_dbg(dev, "device with addr %x didn't ACK\n", msg->addr);
+ writel(BSC_S_ERR, &bcm_i2c->regs->s);
+ timeout = calc_byte_timeout_us(bcm_i2c->bitrate);
+ // Wait for end of transfer so BSC has time to send STOP condition
+ readl_poll_timeout(&bcm_i2c->regs->s, reg_s, ~reg_s & BSC_S_TA, timeout);
+ ret = -EREMOTEIO;
+out:
+ // Return to default state for next xfer
+ writel(BSC_S_DONE | BSC_S_ERR | BSC_S_CLKT, &bcm_i2c->regs->s);
+ writel(BSC_C_CLEAR1 | BSC_C_I2CEN, &bcm_i2c->regs->c);
+ return ret;
+}
+
+static int bcm283x_i2c_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int count)
+{
+ int ret, i;
+ struct i2c_msg *msg;
+ struct bcm283x_i2c *bcm_i2c = to_bcm283x_i2c(adapter);
+
+ /*
+ * Reset control reg, flush FIFO, clear flags and enable the BSC
+ */
+ writel(0UL, &bcm_i2c->regs->c);
+ writel(BSC_C_CLEAR1, &bcm_i2c->regs->c);
+ writel(BSC_S_DONE | BSC_S_ERR | BSC_S_CLKT, &bcm_i2c->regs->s);
+ writel(BSC_C_I2CEN, &bcm_i2c->regs->c);
+
+ for (i = 0; i < count; i++) {
+ msg = &msgs[i];
+ ret = bcm283x_i2c_msg_xfer(bcm_i2c, msg);
+ if (ret)
+ goto out;
+ }
+
+ writel(0UL, &bcm_i2c->regs->c);
+ return count;
+out:
+ writel(0UL, &bcm_i2c->regs->c);
+ writel(BSC_C_CLEAR1, &bcm_i2c->regs->c);
+ writel(BSC_S_DONE | BSC_S_ERR | BSC_S_CLKT, &bcm_i2c->regs->s);
+ return ret;
+}
+
+static int bcm283x_i2c_probe(struct device *dev)
+{
+ int ret;
+ struct resource *iores;
+ struct bcm283x_i2c *bcm_i2c;
+ struct device_node *np = dev->of_node;
+
+ bcm_i2c = xzalloc(sizeof(*bcm_i2c));
+
+ if (!np) {
+ ret = -ENXIO;
+ goto err;
+ }
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores)) {
+ dev_err(dev, "could not get iomem region\n");
+ ret = PTR_ERR(iores);
+ goto err;
+ }
+ bcm_i2c->regs = IOMEM(iores->start);
+
+ bcm_i2c->mclk = clk_get(dev, NULL);
+ if (IS_ERR(bcm_i2c->mclk)) {
+ dev_err(dev, "could not acquire clock\n");
+ ret = PTR_ERR(bcm_i2c->mclk);
+ goto err;
+ }
+ clk_enable(bcm_i2c->mclk);
+
+ bcm_i2c->bitrate = I2C_MAX_STANDARD_MODE_FREQ;
+ of_property_read_u32(np, "clock-frequency", &bcm_i2c->bitrate);
+ if (bcm_i2c->bitrate > I2C_MAX_FAST_MODE_FREQ) {
+ dev_err(dev, "clock frequency of %u is not supported\n",
+ bcm_i2c->bitrate);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ bcm_i2c->adapter.master_xfer = bcm283x_i2c_xfer;
+ bcm_i2c->adapter.nr = dev->id;
+ bcm_i2c->adapter.dev.parent = dev;
+ bcm_i2c->adapter.dev.of_node = np;
+
+ ret = bcm283x_i2c_init(bcm_i2c);
+ if (ret)
+ goto err;
+
+ return i2c_add_numbered_adapter(&bcm_i2c->adapter);
+err:
+ free(bcm_i2c);
+ return ret;
+}
+
+static struct of_device_id bcm283x_i2c_dt_ids[] = {
+ { .compatible = "brcm,bcm2835-i2c", },
+ { .compatible = "brcm,bcm2711-i2c", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, bcm283x_i2c_dt_ids);
+
+static struct driver bcm283x_i2c_driver = {
+ .name = "i2c-bcm283x",
+ .probe = bcm283x_i2c_probe,
+ .of_compatible = DRV_OF_COMPAT(bcm283x_i2c_dt_ids),
+};
+device_platform_driver(bcm283x_i2c_driver);
diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c
new file mode 100644
index 0000000000..bf9ec30994
--- /dev/null
+++ b/drivers/i2c/busses/i2c-cadence.c
@@ -0,0 +1,454 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * I2C bus driver for the Cadence I2C host controller (master only).
+ *
+ * Partly based on the driver in the Linux kernel
+ * Copyright (C) 2009 - 2014 Xilinx, Inc.
+ *
+ * Copyright (C) 2022 Matthias Fend <matthias.fend@emfend.at>
+ */
+
+#include <common.h>
+#include <i2c/i2c.h>
+#include <linux/iopoll.h>
+#include <errno.h>
+#include <linux/err.h>
+#include <driver.h>
+#include <io.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+struct __packed i2c_regs {
+ u32 control;
+ u32 status;
+ u32 address;
+ u32 data;
+ u32 interrupt_status;
+ u32 transfer_size;
+ u32 slave_mon_pause;
+ u32 time_out;
+ u32 interrupt_mask;
+ u32 interrupt_enable;
+ u32 interrupt_disable;
+ u32 glitch_filter;
+};
+
+/* Control register fields */
+#define CDNS_I2C_CONTROL_RW BIT(0)
+#define CDNS_I2C_CONTROL_MS BIT(1)
+#define CDNS_I2C_CONTROL_NEA BIT(2)
+#define CDNS_I2C_CONTROL_ACKEN BIT(3)
+#define CDNS_I2C_CONTROL_HOLD BIT(4)
+#define CDNS_I2C_CONTROL_SLVMON BIT(5)
+#define CDNS_I2C_CONTROL_CLR_FIFO BIT(6)
+#define CDNS_I2C_CONTROL_DIV_B_SHIFT 8
+#define CDNS_I2C_CONTROL_DIV_B_MASK (0x3F << CDNS_I2C_CONTROL_DIV_B_SHIFT)
+#define CDNS_I2C_CONTROL_DIV_A_SHIFT 14
+#define CDNS_I2C_CONTROL_DIV_A_MASK (0x03 << CDNS_I2C_CONTROL_DIV_A_SHIFT)
+
+#define CDNS_I2C_CONTROL_DIV_B_MAX 64
+#define CDNS_I2C_CONTROL_DIV_A_MAX 4
+
+/* Status register fields */
+#define CDNS_I2C_STATUS_RXRW BIT(3)
+#define CDNS_I2C_STATUS_RXDV BIT(5)
+#define CDNS_I2C_STATUS_TXDV BIT(6)
+#define CDNS_I2C_STATUS_RXOVF BIT(7)
+#define CDNS_I2C_STATUS_BA BIT(8)
+
+/* Address register fields */
+#define CDNS_I2C_ADDRESS_MASK 0x3FF
+
+/* Interrupt register fields */
+#define CDNS_I2C_INTERRUPT_COMP BIT(0)
+#define CDNS_I2C_INTERRUPT_DATA BIT(1)
+#define CDNS_I2C_INTERRUPT_NACK BIT(2)
+#define CDNS_I2C_INTERRUPT_TO BIT(3)
+#define CDNS_I2C_INTERRUPT_SLVRDY BIT(4)
+#define CDNS_I2C_INTERRUPT_RXOVF BIT(5)
+#define CDNS_I2C_INTERRUPT_TXOVF BIT(6)
+#define CDNS_I2C_INTERRUPT_RXUNF BIT(7)
+#define CDNS_I2C_INTERRUPT_ARBLOST BIT(9)
+
+#define CDNS_I2C_INTERRUPTS_MASK_MASTER (CDNS_I2C_INTERRUPT_COMP | \
+ CDNS_I2C_INTERRUPT_DATA | \
+ CDNS_I2C_INTERRUPT_NACK | \
+ CDNS_I2C_INTERRUPT_RXOVF | \
+ CDNS_I2C_INTERRUPT_TXOVF | \
+ CDNS_I2C_INTERRUPT_RXUNF | \
+ CDNS_I2C_INTERRUPT_ARBLOST)
+
+#define CDNS_I2C_INTERRUPTS_MASK_ALL (CDNS_I2C_INTERRUPT_COMP | \
+ CDNS_I2C_INTERRUPT_DATA | \
+ CDNS_I2C_INTERRUPT_NACK | \
+ CDNS_I2C_INTERRUPT_TO | \
+ CDNS_I2C_INTERRUPT_SLVRDY | \
+ CDNS_I2C_INTERRUPT_RXOVF | \
+ CDNS_I2C_INTERRUPT_TXOVF | \
+ CDNS_I2C_INTERRUPT_RXUNF | \
+ CDNS_I2C_INTERRUPT_ARBLOST)
+
+#define CDNS_I2C_FIFO_DEPTH 16
+#define CDNS_I2C_TRANSFER_SIZE_MAX 255
+#define CDNS_I2C_TRANSFER_SIZE (CDNS_I2C_TRANSFER_SIZE_MAX - 3)
+
+#define I2C_TIMEOUT_US (100 * USEC_PER_MSEC)
+
+struct cdns_i2c {
+ struct i2c_adapter adapter;
+ struct clk *clk;
+ struct i2c_regs *regs;
+ bool bus_hold_flag;
+};
+
+static void cdns_i2c_reset_hardware(struct cdns_i2c *i2c)
+{
+ struct i2c_regs *regs = i2c->regs;
+ u32 regval;
+
+ writel(CDNS_I2C_INTERRUPTS_MASK_ALL, &regs->interrupt_disable);
+
+ regval = readl(&regs->control);
+ regval &= ~CDNS_I2C_CONTROL_HOLD;
+ regval |= CDNS_I2C_CONTROL_CLR_FIFO;
+ writel(regval, &regs->control);
+
+ writel(0xFF, &regs->time_out);
+
+ writel(0, &regs->transfer_size);
+
+ regval = readl(&regs->interrupt_status);
+ writel(regval, &regs->interrupt_status);
+
+ regval = readl(&regs->status);
+ writel(regval, &regs->status);
+
+ writel(0, &regs->control);
+}
+
+static void cdns_i2c_setup_master(struct cdns_i2c *i2c)
+{
+ u32 control;
+
+ control = readl(&i2c->regs->control);
+ control |= CDNS_I2C_CONTROL_MS | CDNS_I2C_CONTROL_ACKEN |
+ CDNS_I2C_CONTROL_NEA;
+ writel(control, &i2c->regs->control);
+
+ writel(CDNS_I2C_INTERRUPTS_MASK_MASTER, &i2c->regs->interrupt_enable);
+}
+
+static void cdns_i2c_clear_hold_flag(struct cdns_i2c *i2c)
+{
+ u32 control;
+
+ control = readl(&i2c->regs->control);
+ if (control & CDNS_I2C_CONTROL_HOLD)
+ writel(control & ~CDNS_I2C_CONTROL_HOLD, &i2c->regs->control);
+}
+
+static bool cdns_i2c_is_busy(struct cdns_i2c *i2c)
+{
+ return readl(&i2c->regs->status) & CDNS_I2C_STATUS_BA;
+}
+
+static int cdns_i2c_hw_error(struct cdns_i2c *i2c)
+{
+ u32 isr_status;
+
+ isr_status = readl(&i2c->regs->interrupt_status);
+
+ if (isr_status & CDNS_I2C_INTERRUPT_NACK)
+ return -EREMOTEIO;
+
+ if (isr_status &
+ (CDNS_I2C_INTERRUPT_ARBLOST | CDNS_I2C_INTERRUPT_RXOVF))
+ return -EAGAIN;
+
+ return 0;
+}
+
+static int cdns_i2c_wait_for_completion(struct cdns_i2c *i2c)
+{
+ int err;
+ u32 isr_status;
+ const u32 isr_mask =
+ (CDNS_I2C_INTERRUPT_COMP | CDNS_I2C_INTERRUPT_NACK |
+ CDNS_I2C_INTERRUPT_ARBLOST);
+
+ err = readl_poll_timeout(&i2c->regs->interrupt_status, isr_status,
+ isr_status & isr_mask, I2C_TIMEOUT_US);
+
+ if (err)
+ return -ETIMEDOUT;
+
+ return cdns_i2c_hw_error(i2c);
+}
+
+/*
+ * Find best clock divisors
+ *
+ * f = finput / (22 x (div_a + 1) x (div_b + 1))
+ */
+static int cdns_i2c_calc_divs(u32 *f, u32 input_clk, u32 *a, u32 *b)
+{
+ ulong fscl = *f, best_fscl = *f, actual_fscl, temp;
+ uint div_a, div_b, calc_div_a = 0, calc_div_b = 0;
+ uint last_error, current_error;
+
+ temp = input_clk / (22 * fscl);
+
+ if (!temp ||
+ (temp > (CDNS_I2C_CONTROL_DIV_A_MAX * CDNS_I2C_CONTROL_DIV_B_MAX)))
+ return -EINVAL;
+
+ last_error = -1;
+ for (div_a = 0; div_a < CDNS_I2C_CONTROL_DIV_A_MAX; div_a++) {
+ div_b = DIV_ROUND_UP(input_clk, 22 * fscl * (div_a + 1));
+
+ if ((div_b < 1) || (div_b > CDNS_I2C_CONTROL_DIV_B_MAX))
+ continue;
+ div_b--;
+
+ actual_fscl = input_clk / (22 * (div_a + 1) * (div_b + 1));
+
+ if (actual_fscl > fscl)
+ continue;
+
+ current_error = ((actual_fscl > fscl) ? (actual_fscl - fscl) :
+ (fscl - actual_fscl));
+
+ if (last_error > current_error) {
+ calc_div_a = div_a;
+ calc_div_b = div_b;
+ best_fscl = actual_fscl;
+ last_error = current_error;
+ }
+ }
+
+ *a = calc_div_a;
+ *b = calc_div_b;
+ *f = best_fscl;
+
+ return 0;
+}
+
+static int cdns_i2c_set_clk(struct cdns_i2c *i2c, u32 scl_rate)
+{
+ u32 i2c_rate;
+ u32 control;
+ u32 div_a, div_b;
+ int err;
+
+ i2c_rate = clk_get_rate(i2c->clk);
+
+ err = cdns_i2c_calc_divs(&scl_rate, i2c_rate, &div_a, &div_b);
+ if (err)
+ return err;
+
+ control = readl(&i2c->regs->control);
+ control &= ~(CDNS_I2C_CONTROL_DIV_B_MASK | CDNS_I2C_CONTROL_DIV_A_MASK);
+ control |= (div_b << CDNS_I2C_CONTROL_DIV_B_SHIFT) |
+ (div_a << CDNS_I2C_CONTROL_DIV_A_SHIFT);
+ writel(control, &i2c->regs->control);
+
+ return err;
+}
+
+static int cdns_i2c_read(struct cdns_i2c *i2c, uchar chip, uchar *buf,
+ uint buf_len)
+{
+ struct i2c_regs *regs = i2c->regs;
+ u32 control;
+ int err;
+
+ control = readl(&regs->control);
+ control |= CDNS_I2C_CONTROL_RW | CDNS_I2C_CONTROL_CLR_FIFO;
+ if (i2c->bus_hold_flag || (buf_len > CDNS_I2C_FIFO_DEPTH))
+ control |= CDNS_I2C_CONTROL_HOLD;
+ writel(control, &regs->control);
+
+ do {
+ uint bytes_to_receive;
+ u32 isr_status;
+ u64 start_time;
+
+ isr_status = readl(&regs->interrupt_status);
+ writel(isr_status, &regs->interrupt_status);
+
+ if (buf_len > CDNS_I2C_TRANSFER_SIZE)
+ bytes_to_receive = CDNS_I2C_TRANSFER_SIZE;
+ else
+ bytes_to_receive = buf_len;
+
+ buf_len -= bytes_to_receive;
+
+ writel(bytes_to_receive, &regs->transfer_size);
+ writel(chip & CDNS_I2C_ADDRESS_MASK, &regs->address);
+
+ start_time = get_time_ns();
+ while (bytes_to_receive) {
+ err = cdns_i2c_hw_error(i2c);
+ if (err)
+ goto i2c_exit;
+
+ if (is_timeout(start_time,
+ (I2C_TIMEOUT_US * USECOND))) {
+ err = -ETIMEDOUT;
+ goto i2c_exit;
+ }
+
+ if (readl(&regs->status) & CDNS_I2C_STATUS_RXDV) {
+ *buf++ = readl(&regs->data);
+ bytes_to_receive--;
+ }
+ }
+
+ } while (buf_len);
+
+ err = cdns_i2c_wait_for_completion(i2c);
+
+i2c_exit:
+ if (!i2c->bus_hold_flag)
+ cdns_i2c_clear_hold_flag(i2c);
+
+ return err;
+}
+
+static int cdns_i2c_write(struct cdns_i2c *i2c, uchar chip, uchar *buf,
+ uint buf_len)
+{
+ struct i2c_regs *regs = i2c->regs;
+ u32 control;
+ u32 isr_status;
+ bool start_transfer;
+ int err;
+
+ control = readl(&regs->control);
+ control &= ~CDNS_I2C_CONTROL_RW;
+ control |= CDNS_I2C_CONTROL_CLR_FIFO;
+ if (i2c->bus_hold_flag || (buf_len > CDNS_I2C_FIFO_DEPTH))
+ control |= CDNS_I2C_CONTROL_HOLD;
+ writel(control, &regs->control);
+
+ isr_status = readl(&regs->interrupt_status);
+ writel(isr_status, &regs->interrupt_status);
+
+ start_transfer = true;
+ do {
+ uint bytes_to_send;
+
+ bytes_to_send =
+ CDNS_I2C_FIFO_DEPTH - readl(&regs->transfer_size);
+
+ if (buf_len < bytes_to_send)
+ bytes_to_send = buf_len;
+
+ buf_len -= bytes_to_send;
+
+ while (bytes_to_send--)
+ writel(*buf++, &regs->data);
+
+ if (start_transfer) {
+ writel(chip & CDNS_I2C_ADDRESS_MASK, &regs->address);
+ start_transfer = false;
+ }
+
+ err = cdns_i2c_wait_for_completion(i2c);
+ if (err)
+ goto i2c_exit;
+
+ } while (buf_len);
+
+i2c_exit:
+ if (!i2c->bus_hold_flag)
+ cdns_i2c_clear_hold_flag(i2c);
+
+ return err;
+}
+
+static int cdns_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msg,
+ int nmsgs)
+{
+ struct cdns_i2c *i2c = container_of(adapter, struct cdns_i2c, adapter);
+ int i;
+ int err;
+
+ if (cdns_i2c_is_busy(i2c))
+ return -EBUSY;
+
+ for (i = 0; i < nmsgs; i++) {
+ i2c->bus_hold_flag = i < (nmsgs - 1);
+
+ if (msg->flags & I2C_M_RD) {
+ err = cdns_i2c_read(i2c, msg->addr, msg->buf, msg->len);
+ } else {
+ err = cdns_i2c_write(i2c, msg->addr, msg->buf,
+ msg->len);
+ }
+
+ if (err)
+ return err;
+
+ msg++;
+ }
+
+ return nmsgs;
+}
+
+static int cdns_i2c_probe(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct resource *iores;
+ struct cdns_i2c *i2c;
+ u32 bitrate;
+ int err;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ i2c = xzalloc(sizeof(*i2c));
+
+ dev->priv = i2c;
+ i2c->regs = IOMEM(iores->start);
+
+ i2c->clk = clk_get(dev, NULL);
+ if (IS_ERR(i2c->clk))
+ return PTR_ERR(i2c->clk);
+
+ err = clk_enable(i2c->clk);
+ if (err)
+ return err;
+
+ i2c->adapter.master_xfer = cdns_i2c_xfer;
+ i2c->adapter.nr = dev->id;
+ i2c->adapter.dev.parent = dev;
+ i2c->adapter.dev.of_node = np;
+
+ cdns_i2c_reset_hardware(i2c);
+
+ bitrate = 100000;
+ of_property_read_u32(np, "clock-frequency", &bitrate);
+
+ err = cdns_i2c_set_clk(i2c, bitrate);
+ if (err)
+ return err;
+
+ cdns_i2c_setup_master(i2c);
+
+ return i2c_add_numbered_adapter(&i2c->adapter);
+}
+
+static const struct of_device_id cdns_i2c_match[] = {
+ { .compatible = "cdns,i2c-r1p14" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cdns_i2c_match);
+
+static struct driver cdns_i2c_driver = {
+ .name = "cdns-i2c",
+ .of_compatible = cdns_i2c_match,
+ .probe = cdns_i2c_probe,
+};
+coredevice_platform_driver(cdns_i2c_driver);
diff --git a/drivers/i2c/busses/i2c-designware.c b/drivers/i2c/busses/i2c-designware.c
index 33f89148f0..152b795c37 100644
--- a/drivers/i2c/busses/i2c-designware.c
+++ b/drivers/i2c/busses/i2c-designware.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Synopsys DesignWare I2C adapter driver (master only).
*
@@ -10,16 +11,6 @@
* Copyright (C) 2009 Provigent Ltd.
*
* Copyright (C) 2015 Andrey Smirnov <andrew.smirnov@gmail.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <clock.h>
@@ -121,6 +112,8 @@ static inline struct dw_i2c_dev *to_dw_i2c_dev(struct i2c_adapter *a)
static void i2c_dw_enable(struct dw_i2c_dev *dw, bool enable)
{
+ u32 reg = 0;
+
/*
* This subrotine is an implementation of an algorithm
* described in "Cyclone V Hard Processor System Technical
@@ -128,12 +121,13 @@ static void i2c_dw_enable(struct dw_i2c_dev *dw, bool enable)
*/
int timeout = MAX_T_POLL_COUNT;
- enable = enable ? DW_IC_ENABLE_ENABLE : 0;
+ if (enable)
+ reg |= DW_IC_ENABLE_ENABLE;
do {
uint32_t ic_enable_status;
- writel(enable, dw->base + DW_IC_ENABLE);
+ writel(reg, dw->base + DW_IC_ENABLE);
ic_enable_status = readl(dw->base + DW_IC_ENABLE_STATUS);
if ((ic_enable_status & DW_IC_ENABLE_STATUS_IC_EN) == enable)
@@ -251,7 +245,7 @@ static void i2c_dw_setup_timings(struct dw_i2c_dev *dw)
u32 ht;
int ret;
- ret = of_property_read_u32(dw->adapter.dev.device_node,
+ ret = of_property_read_u32(dw->adapter.dev.of_node,
"i2c-sda-hold-time-ns", &ht);
if (ret) {
/* Keep previous hold time setting if no one set it */
@@ -271,7 +265,7 @@ static void i2c_dw_setup_timings(struct dw_i2c_dev *dw)
if (!(dw->sda_hold_time & DW_IC_SDA_HOLD_RX_MASK))
dw->sda_hold_time |= 1 << DW_IC_SDA_HOLD_RX_SHIFT;
- dev_dbg(&dw->adapter.dev, "adjust SDA hold time.\n");
+ dev_dbg(dw->adapter.dev.parent, "adjust SDA hold time.\n");
writel(dw->sda_hold_time, dw->base + DW_IC_SDA_HOLD);
}
}
@@ -510,7 +504,7 @@ static int i2c_dw_xfer(struct i2c_adapter *adapter,
}
-static int i2c_dw_probe(struct device_d *pdev)
+static int i2c_dw_probe(struct device *pdev)
{
struct resource *iores;
struct dw_i2c_dev *dw;
@@ -533,7 +527,7 @@ static int i2c_dw_probe(struct device_d *pdev)
dw->adapter.master_xfer = i2c_dw_xfer;
dw->adapter.nr = pdev->id;
dw->adapter.dev.parent = pdev;
- dw->adapter.dev.device_node = pdev->device_node;
+ dw->adapter.dev.of_node = pdev->of_node;
iores = dev_request_mem_resource(pdev, 0);
if (IS_ERR(iores)) {
@@ -544,9 +538,7 @@ static int i2c_dw_probe(struct device_d *pdev)
ic_comp_type_value = readl(dw->base + DW_IC_COMP_TYPE);
if (ic_comp_type_value != DW_IC_COMP_TYPE_VALUE) {
- dev_err(pdev,
- "unknown DesignWare IP block 0x%08x",
- ic_comp_type_value);
+ dev_err(pdev, "unknown DesignWare IP block 0x%08x\n", ic_comp_type_value);
ret = -ENODEV;
goto fail;
}
@@ -571,7 +563,7 @@ static int i2c_dw_probe(struct device_d *pdev)
ic_con = DW_IC_CON_SPEED_FAST;
break;
default:
- dev_warn(pdev, "requested bitrate (%d) is not supported."
+ dev_warn(pdev, "requested bitrate (%d) is not supported.\n"
" Falling back to 100kHz", bitrate);
case 100000: /* FALLTHROUGH */
ic_con = DW_IC_CON_SPEED_STD;
@@ -607,8 +599,9 @@ static __maybe_unused struct of_device_id i2c_dw_dt_ids[] = {
{ .compatible = "snps,designware-i2c", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, i2c_dw_dt_ids);
-static struct driver_d i2c_dw_driver = {
+static struct driver i2c_dw_driver = {
.probe = i2c_dw_probe,
.name = "i2c-designware",
.of_compatible = DRV_OF_COMPAT(i2c_dw_dt_ids),
diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c
index 7ccdb0b222..3649c8b189 100644
--- a/drivers/i2c/busses/i2c-gpio.c
+++ b/drivers/i2c/busses/i2c-gpio.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Bitbanging I2C bus driver using the GPIO API
*
* Copyright (C) 2007 Atmel Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <common.h>
@@ -129,7 +126,7 @@ static int of_i2c_gpio_probe(struct device_node *np,
return 0;
}
-static int i2c_gpio_probe(struct device_d *dev)
+static int i2c_gpio_probe(struct device *dev)
{
struct i2c_gpio_private_data *priv;
struct i2c_gpio_platform_data *pdata;
@@ -143,8 +140,8 @@ static int i2c_gpio_probe(struct device_d *dev)
bit_data = &priv->bit_data;
pdata = &priv->pdata;
- if (dev->device_node) {
- ret = of_i2c_gpio_probe(dev->device_node, pdata);
+ if (dev->of_node) {
+ ret = of_i2c_gpio_probe(dev->of_node, pdata);
if (ret)
return ret;
} else {
@@ -196,7 +193,7 @@ static int i2c_gpio_probe(struct device_d *dev)
adap->algo_data = bit_data;
adap->dev.parent = dev;
- adap->dev.device_node = dev->device_node;
+ adap->dev.of_node = dev->of_node;
adap->bus_recovery_info = xzalloc(sizeof(*adap->bus_recovery_info));
adap->bus_recovery_info->scl_gpio = pdata->scl_pin;
adap->bus_recovery_info->sda_gpio = pdata->sda_pin;
@@ -231,8 +228,9 @@ static struct of_device_id i2c_gpio_dt_ids[] = {
{ .compatible = "i2c-gpio", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, i2c_gpio_dt_ids);
-static struct driver_d i2c_gpio_driver = {
+static struct driver i2c_gpio_driver = {
.name = "i2c-gpio",
.probe = i2c_gpio_probe,
.of_compatible = DRV_OF_COMPAT(i2c_gpio_dt_ids),
diff --git a/drivers/i2c/busses/i2c-imx-early.c b/drivers/i2c/busses/i2c-imx-early.c
index 472e4be83a..1db48a85e5 100644
--- a/drivers/i2c/busses/i2c-imx-early.c
+++ b/drivers/i2c/busses/i2c-imx-early.c
@@ -1,28 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2013 GE Intelligent Platforms, Inc
* Copyright 2006,2009 Freescale Semiconductor, Inc.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* Early I2C support functions to read SPD data or board
* information.
* Based on U-Boot drivers/i2c/fsl_i2c.c
*/
#include <common.h>
#include <i2c/i2c.h>
-#include <i2c/i2c-early.h>
+#include <pbl/i2c.h>
#include "i2c-imx.h"
struct fsl_i2c {
+ struct pbl_i2c i2c;
void __iomem *regs;
unsigned int i2cr_ien_opcode;
unsigned int i2sr_clr_opcode;
@@ -98,6 +90,26 @@ static int i2c_fsl_acked(struct fsl_i2c *fsl_i2c)
return i2c_fsl_poll_status(fsl_i2c, 0, I2SR_RXAK);
}
+static void i2c_fsl_settle(struct fsl_i2c *fsl_i2c)
+{
+#ifdef CPU_ARCH_ARMv8
+ udelay(100);
+#else
+ /*
+ * We lack udelay on the 32bit i.MX SoCs, so delay manually: On an
+ * i.MX6 with a 66Mhz I2C peripheral clock one cycle of this loop
+ * takes 1.30us. Let's be generous and round up to 100 cycles. Other
+ * i.MX SoCs do not have a higher peripheral clock, so we should be
+ * safe here as well.
+ */
+
+ volatile int i = 0;
+
+ for (i = 0; i < 100; i++)
+ fsl_i2c_read_reg(fsl_i2c, FSL_I2C_I2SR);
+#endif
+}
+
static int i2c_fsl_start(struct fsl_i2c *fsl_i2c)
{
unsigned int temp = 0;
@@ -112,7 +124,7 @@ static int i2c_fsl_start(struct fsl_i2c *fsl_i2c)
fsl_i2c, FSL_I2C_I2CR);
/* Wait controller to be stable */
- udelay(100);
+ i2c_fsl_settle(fsl_i2c);
/* Start I2C transaction */
temp = fsl_i2c_read_reg(fsl_i2c, FSL_I2C_I2CR);
@@ -126,7 +138,7 @@ static int i2c_fsl_start(struct fsl_i2c *fsl_i2c)
temp |= I2CR_MTX | I2CR_TXAK;
fsl_i2c_write_reg(temp, fsl_i2c, FSL_I2C_I2CR);
- return ret;
+ return 0;
}
static void i2c_fsl_stop(struct fsl_i2c *fsl_i2c)
@@ -179,7 +191,7 @@ static int i2c_fsl_write(struct fsl_i2c *fsl_i2c, struct i2c_msg *msg)
/* write data */
for (i = 0; i < msg->len; i++) {
ret = i2c_fsl_send(fsl_i2c, msg->buf[i]);
- if (ret)
+ if (ret)
return ret;
}
@@ -238,9 +250,9 @@ static int i2c_fsl_read(struct fsl_i2c *fsl_i2c, struct i2c_msg *msg)
* If successful returns the number of messages transferred, otherwise a negative
* error code is returned.
*/
-int i2c_fsl_xfer(void *ctx, struct i2c_msg *msgs, int num)
+static int i2c_fsl_xfer(struct pbl_i2c *i2c, struct i2c_msg *msgs, int num)
{
- struct fsl_i2c *fsl_i2c = ctx;
+ struct fsl_i2c *fsl_i2c = container_of(i2c, struct fsl_i2c, i2c);
unsigned int i, temp;
int ret;
@@ -297,7 +309,7 @@ static struct fsl_i2c fsl_i2c;
* This function returns a context pointer suitable to transfer I2C messages
* using i2c_fsl_xfer.
*/
-void *ls1046_i2c_init(void __iomem *regs)
+struct pbl_i2c *ls1046_i2c_init(void __iomem *regs)
{
fsl_i2c.regs = regs;
fsl_i2c.regshift = 0;
@@ -306,10 +318,26 @@ void *ls1046_i2c_init(void __iomem *regs)
/* Divider for ~100kHz when coming from the ROM */
fsl_i2c.ifdr = 0x3e;
- return &fsl_i2c;
+ fsl_i2c.i2c.xfer = i2c_fsl_xfer;
+
+ return &fsl_i2c.i2c;
}
-void *imx8m_i2c_early_init(void __iomem *regs)
+struct pbl_i2c *imx6_i2c_early_init(void __iomem *regs)
+{
+ fsl_i2c.regs = regs;
+ fsl_i2c.regshift = 2;
+ fsl_i2c.i2cr_ien_opcode = I2CR_IEN_OPCODE_1;
+ fsl_i2c.i2sr_clr_opcode = I2SR_CLR_OPCODE_W0C;
+ /* Divider for ~100kHz when coming from the ROM */
+ fsl_i2c.ifdr = 0x36;
+
+ fsl_i2c.i2c.xfer = i2c_fsl_xfer;
+
+ return &fsl_i2c.i2c;
+}
+
+struct pbl_i2c *imx8m_i2c_early_init(void __iomem *regs)
{
fsl_i2c.regs = regs;
fsl_i2c.regshift = 2;
@@ -318,5 +346,7 @@ void *imx8m_i2c_early_init(void __iomem *regs)
/* Divider for ~100kHz when coming from the ROM */
fsl_i2c.ifdr = 0x0f;
- return &fsl_i2c;
+ fsl_i2c.i2c.xfer = i2c_fsl_xfer;
+
+ return &fsl_i2c.i2c;
}
diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c
new file mode 100644
index 0000000000..e32bc4fd18
--- /dev/null
+++ b/drivers/i2c/busses/i2c-imx-lpi2c.c
@@ -0,0 +1,555 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This is i.MX low power i2c controller driver.
+ *
+ * Copyright 2016 Freescale Semiconductor, Inc.
+ */
+
+#include <clock.h>
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <of.h>
+#include <gpio.h>
+#include <malloc.h>
+#include <types.h>
+#include <xfuncs.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <pinctrl.h>
+#include <of_gpio.h>
+#include <of_device.h>
+#include <pbl/i2c.h>
+
+#include <io.h>
+#include <i2c/i2c.h>
+
+#define DRIVER_NAME "imx-lpi2c"
+
+#define LPI2C_PARAM 0x04 /* i2c RX/TX FIFO size */
+#define LPI2C_MCR 0x10 /* i2c contrl register */
+#define LPI2C_MSR 0x14 /* i2c status register */
+#define LPI2C_MIER 0x18 /* i2c interrupt enable */
+#define LPI2C_MCFGR0 0x20 /* i2c master configuration */
+#define LPI2C_MCFGR1 0x24 /* i2c master configuration */
+#define LPI2C_MCFGR2 0x28 /* i2c master configuration */
+#define LPI2C_MCFGR3 0x2C /* i2c master configuration */
+#define LPI2C_MCCR0 0x48 /* i2c master clk configuration */
+#define LPI2C_MCCR1 0x50 /* i2c master clk configuration */
+#define LPI2C_MFCR 0x58 /* i2c master FIFO control */
+#define LPI2C_MFSR 0x5C /* i2c master FIFO status */
+#define LPI2C_MTDR 0x60 /* i2c master TX data register */
+#define LPI2C_MRDR 0x70 /* i2c master RX data register */
+
+/* i2c command */
+#define TRAN_DATA 0X00
+#define RECV_DATA 0X01
+#define GEN_STOP 0X02
+#define RECV_DISCARD 0X03
+#define GEN_START 0X04
+#define START_NACK 0X05
+#define START_HIGH 0X06
+#define START_HIGH_NACK 0X07
+
+#define MCR_MEN BIT(0)
+#define MCR_RST BIT(1)
+#define MCR_DOZEN BIT(2)
+#define MCR_DBGEN BIT(3)
+#define MCR_RTF BIT(8)
+#define MCR_RRF BIT(9)
+#define MSR_TDF BIT(0)
+#define MSR_RDF BIT(1)
+#define MSR_SDF BIT(9)
+#define MSR_NDF BIT(10)
+#define MSR_ALF BIT(11)
+#define MSR_MBF BIT(24)
+#define MSR_BBF BIT(25)
+#define MIER_TDIE BIT(0)
+#define MIER_RDIE BIT(1)
+#define MIER_SDIE BIT(9)
+#define MIER_NDIE BIT(10)
+#define MCFGR1_AUTOSTOP BIT(8)
+#define MCFGR1_IGNACK BIT(9)
+#define MRDR_RXEMPTY BIT(14)
+
+#define I2C_CLK_RATIO 2
+#define CHUNK_DATA 256
+
+#define I2C_PM_TIMEOUT 10 /* ms */
+
+enum lpi2c_imx_mode {
+ STANDARD, /* 100+Kbps */
+ FAST, /* 400+Kbps */
+ FAST_PLUS, /* 1.0+Mbps */
+ HS, /* 3.4+Mbps */
+ ULTRA_FAST, /* 5.0+Mbps */
+};
+
+enum lpi2c_imx_pincfg {
+ TWO_PIN_OD,
+ TWO_PIN_OO,
+ TWO_PIN_PP,
+ FOUR_PIN_PP,
+};
+
+struct lpi2c_imx_struct {
+ struct i2c_adapter adapter;
+ struct pbl_i2c pbl_i2c;
+ int num_clks;
+ struct clk_bulk_data *clks;
+ void __iomem *base;
+ __u8 *rx_buf;
+ __u8 *tx_buf;
+ unsigned int msglen;
+ unsigned int delivered;
+ unsigned int bitrate;
+ unsigned int txfifosize;
+ unsigned int rxfifosize;
+ enum lpi2c_imx_mode mode;
+ unsigned long clk_rate;
+};
+
+static void lpi2c_imx_intctrl(struct lpi2c_imx_struct *lpi2c_imx,
+ unsigned int enable)
+{
+ writel(enable, lpi2c_imx->base + LPI2C_MIER);
+}
+
+static int lpi2c_imx_bus_busy(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ unsigned int temp;
+ unsigned int timeout = 500000;
+
+ while (1) {
+ temp = readl(lpi2c_imx->base + LPI2C_MSR);
+
+ /* check for arbitration lost, clear if set */
+ if (temp & (MSR_ALF | MSR_NDF)) {
+ writel(temp, lpi2c_imx->base + LPI2C_MSR);
+ return -EAGAIN;
+ }
+
+ if (temp & (MSR_BBF | MSR_MBF))
+ break;
+
+ udelay(1);
+ if (!timeout--) {
+ dev_dbg(&lpi2c_imx->adapter.dev, "bus not work\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+static void lpi2c_imx_set_mode(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ unsigned int bitrate = lpi2c_imx->bitrate;
+ enum lpi2c_imx_mode mode;
+
+ if (bitrate < I2C_MAX_FAST_MODE_FREQ)
+ mode = STANDARD;
+ else if (bitrate < I2C_MAX_FAST_MODE_PLUS_FREQ)
+ mode = FAST;
+ else if (bitrate < I2C_MAX_HIGH_SPEED_MODE_FREQ)
+ mode = FAST_PLUS;
+ else if (bitrate < I2C_MAX_ULTRA_FAST_MODE_FREQ)
+ mode = HS;
+ else
+ mode = ULTRA_FAST;
+
+ lpi2c_imx->mode = mode;
+}
+
+static int lpi2c_imx_start(struct lpi2c_imx_struct *lpi2c_imx,
+ struct i2c_msg *msgs)
+{
+ unsigned int temp;
+
+ temp = readl(lpi2c_imx->base + LPI2C_MCR);
+ temp |= MCR_RRF | MCR_RTF;
+ writel(temp, lpi2c_imx->base + LPI2C_MCR);
+ writel(0x7f00, lpi2c_imx->base + LPI2C_MSR);
+
+ temp = i2c_8bit_addr_from_msg(msgs) | (GEN_START << 8);
+ writel(temp, lpi2c_imx->base + LPI2C_MTDR);
+
+ return lpi2c_imx_bus_busy(lpi2c_imx);
+}
+
+static void lpi2c_imx_stop(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ unsigned int temp;
+ unsigned int timeout = 500000;
+
+ writel(GEN_STOP << 8, lpi2c_imx->base + LPI2C_MTDR);
+
+ do {
+ temp = readl(lpi2c_imx->base + LPI2C_MSR);
+ if (temp & MSR_SDF)
+ break;
+
+ udelay(1);
+ if (!timeout--) {
+ dev_dbg(&lpi2c_imx->adapter.dev, "stop timeout\n");
+ break;
+ }
+
+ } while (1);
+}
+
+/* CLKLO = I2C_CLK_RATIO * CLKHI, SETHOLD = CLKHI, DATAVD = CLKHI/2 */
+static int lpi2c_imx_config(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ u8 prescale, filt, sethold, datavd;
+ unsigned int clk_cycle, clkhi, clklo;
+ enum lpi2c_imx_pincfg pincfg;
+ unsigned int temp;
+
+ lpi2c_imx_set_mode(lpi2c_imx);
+
+ if (lpi2c_imx->mode == HS || lpi2c_imx->mode == ULTRA_FAST)
+ filt = 0;
+ else
+ filt = 2;
+
+ for (prescale = 0; prescale <= 7; prescale++) {
+ clk_cycle = lpi2c_imx->clk_rate / ((1 << prescale) * lpi2c_imx->bitrate)
+ - 3 - (filt >> 1);
+ clkhi = DIV_ROUND_UP(clk_cycle, I2C_CLK_RATIO + 1);
+ clklo = clk_cycle - clkhi;
+ if (clklo < 64)
+ break;
+ }
+
+ if (prescale > 7)
+ return -EINVAL;
+
+ /* set MCFGR1: PINCFG, PRESCALE, IGNACK */
+ if (lpi2c_imx->mode == ULTRA_FAST)
+ pincfg = TWO_PIN_OO;
+ else
+ pincfg = TWO_PIN_OD;
+ temp = prescale | pincfg << 24;
+
+ if (lpi2c_imx->mode == ULTRA_FAST)
+ temp |= MCFGR1_IGNACK;
+
+ writel(temp, lpi2c_imx->base + LPI2C_MCFGR1);
+
+ /* set MCFGR2: FILTSDA, FILTSCL */
+ temp = (filt << 16) | (filt << 24);
+ writel(temp, lpi2c_imx->base + LPI2C_MCFGR2);
+
+ /* set MCCR: DATAVD, SETHOLD, CLKHI, CLKLO */
+ sethold = clkhi;
+ datavd = clkhi >> 1;
+ temp = datavd << 24 | sethold << 16 | clkhi << 8 | clklo;
+
+ if (lpi2c_imx->mode == HS)
+ writel(temp, lpi2c_imx->base + LPI2C_MCCR1);
+ else
+ writel(temp, lpi2c_imx->base + LPI2C_MCCR0);
+
+ return 0;
+}
+
+static int lpi2c_imx_master_enable(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ unsigned int temp;
+ int ret;
+
+ temp = MCR_RST;
+ writel(temp, lpi2c_imx->base + LPI2C_MCR);
+ writel(0, lpi2c_imx->base + LPI2C_MCR);
+
+ ret = lpi2c_imx_config(lpi2c_imx);
+ if (ret)
+ return ret;
+
+ temp = readl(lpi2c_imx->base + LPI2C_MCR);
+ temp |= MCR_MEN;
+ writel(temp, lpi2c_imx->base + LPI2C_MCR);
+
+ return 0;
+}
+
+static int lpi2c_imx_master_disable(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ u32 temp;
+
+ temp = readl(lpi2c_imx->base + LPI2C_MCR);
+ temp &= ~MCR_MEN;
+ writel(temp, lpi2c_imx->base + LPI2C_MCR);
+
+ return 0;
+}
+
+static int lpi2c_imx_txfifo_empty(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ u32 txcnt;
+ unsigned int timeout = 500000;
+
+ do {
+ txcnt = readl(lpi2c_imx->base + LPI2C_MFSR) & 0xff;
+
+ if (readl(lpi2c_imx->base + LPI2C_MSR) & MSR_NDF) {
+ dev_dbg(&lpi2c_imx->adapter.dev, "NDF detected\n");
+ return -EIO;
+ }
+
+ udelay(1);
+ if (!timeout--) {
+ dev_dbg(&lpi2c_imx->adapter.dev, "txfifo empty timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ } while (txcnt);
+
+ return 0;
+}
+
+static void lpi2c_imx_set_tx_watermark(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ writel(lpi2c_imx->txfifosize >> 1, lpi2c_imx->base + LPI2C_MFCR);
+}
+
+static void lpi2c_imx_set_rx_watermark(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ unsigned int temp, remaining;
+
+ remaining = lpi2c_imx->msglen - lpi2c_imx->delivered;
+
+ if (remaining > (lpi2c_imx->rxfifosize >> 1))
+ temp = lpi2c_imx->rxfifosize >> 1;
+ else
+ temp = 0;
+
+ writel(temp << 16, lpi2c_imx->base + LPI2C_MFCR);
+}
+
+static int lpi2c_imx_write_txfifo(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ unsigned int data, remaining;
+ unsigned int timeout = 100000;;
+
+ do {
+ u32 cnt = readl(lpi2c_imx->base + LPI2C_MFSR) & 0xff;
+ if (cnt == lpi2c_imx->txfifosize) {
+ udelay(1);
+ if (!timeout--)
+ return -EIO;
+ continue;
+ }
+
+ data = lpi2c_imx->tx_buf[lpi2c_imx->delivered++];
+
+ writel(data, lpi2c_imx->base + LPI2C_MTDR);
+ remaining = lpi2c_imx->msglen - lpi2c_imx->delivered;
+ } while (remaining);
+
+ return 0;
+}
+
+static int lpi2c_imx_read_rxfifo(struct lpi2c_imx_struct *lpi2c_imx)
+{
+ unsigned int remaining;
+ unsigned int data;
+ unsigned int timeout = 100000;;
+
+ do {
+ data = readl(lpi2c_imx->base + LPI2C_MRDR);
+ if (data & MRDR_RXEMPTY) {
+ udelay(1);
+ if (!timeout--)
+ return -EIO;
+ continue;
+ }
+
+ lpi2c_imx->rx_buf[lpi2c_imx->delivered++] = data & 0xff;
+
+ remaining = lpi2c_imx->msglen - lpi2c_imx->delivered;
+ } while (remaining);
+
+ return 0;
+}
+
+static int lpi2c_imx_write(struct lpi2c_imx_struct *lpi2c_imx,
+ struct i2c_msg *msgs)
+{
+ lpi2c_imx->tx_buf = msgs->buf;
+ lpi2c_imx_set_tx_watermark(lpi2c_imx);
+
+ return lpi2c_imx_write_txfifo(lpi2c_imx);
+}
+
+static int lpi2c_imx_read(struct lpi2c_imx_struct *lpi2c_imx,
+ struct i2c_msg *msgs)
+{
+ unsigned int temp;
+
+ lpi2c_imx->rx_buf = msgs->buf;
+
+ lpi2c_imx_set_rx_watermark(lpi2c_imx);
+ temp = msgs->len > CHUNK_DATA ? CHUNK_DATA - 1 : msgs->len - 1;
+ temp |= (RECV_DATA << 8);
+ writel(temp, lpi2c_imx->base + LPI2C_MTDR);
+
+ lpi2c_imx_intctrl(lpi2c_imx, MIER_RDIE | MIER_NDIE);
+
+ return lpi2c_imx_read_rxfifo(lpi2c_imx);
+}
+
+static int lpi2c_imx_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num)
+{
+ struct lpi2c_imx_struct *lpi2c_imx = container_of(adapter, struct lpi2c_imx_struct, adapter);
+ unsigned int temp;
+ int i, result;
+
+ result = lpi2c_imx_master_enable(lpi2c_imx);
+ if (result)
+ return result;
+
+ for (i = 0; i < num; i++) {
+ result = lpi2c_imx_start(lpi2c_imx, &msgs[i]);
+ if (result)
+ goto disable;
+
+ /* quick smbus */
+ if (num == 1 && msgs[0].len == 0)
+ goto stop;
+
+ lpi2c_imx->rx_buf = NULL;
+ lpi2c_imx->tx_buf = NULL;
+ lpi2c_imx->delivered = 0;
+ lpi2c_imx->msglen = msgs[i].len;
+
+ if (msgs[i].flags & I2C_M_RD)
+ result = lpi2c_imx_read(lpi2c_imx, &msgs[i]);
+ else
+ result = lpi2c_imx_write(lpi2c_imx, &msgs[i]);
+
+ if (result)
+ goto stop;
+
+ if (!(msgs[i].flags & I2C_M_RD)) {
+ result = lpi2c_imx_txfifo_empty(lpi2c_imx);
+ if (result)
+ goto stop;
+ }
+ }
+
+stop:
+ lpi2c_imx_stop(lpi2c_imx);
+
+ temp = readl(lpi2c_imx->base + LPI2C_MSR);
+ if ((temp & MSR_NDF) && !result)
+ result = -EIO;
+
+disable:
+ lpi2c_imx_master_disable(lpi2c_imx);
+
+ dev_dbg(&lpi2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
+ (result < 0) ? "error" : "success msg",
+ (result < 0) ? result : num);
+
+ return (result < 0) ? result : num;
+}
+
+#ifdef __PBL__
+
+static int lpi2c_pbl_imx_xfer(struct pbl_i2c *lpi2c, struct i2c_msg *msgs, int num)
+{
+ struct lpi2c_imx_struct *lpi2c_imx = container_of(lpi2c, struct lpi2c_imx_struct, pbl_i2c);
+
+ return lpi2c_imx_xfer(&lpi2c_imx->adapter, msgs, num);
+}
+
+struct pbl_i2c *imx93_i2c_early_init(void __iomem *regs)
+{
+ static struct lpi2c_imx_struct lpi2c;
+ u32 temp;
+
+ lpi2c.base = regs;
+
+ temp = readl(lpi2c.base + LPI2C_PARAM);
+ lpi2c.txfifosize = 1 << (temp & 0x0f);
+ lpi2c.rxfifosize = 1 << ((temp >> 8) & 0x0f);
+ lpi2c.bitrate = 100000;
+ lpi2c.clk_rate = 24000000;
+
+ lpi2c.pbl_i2c.xfer = lpi2c_pbl_imx_xfer;
+
+ return &lpi2c.pbl_i2c;
+}
+
+#else
+
+static const struct of_device_id lpi2c_imx_of_match[] = {
+ { .compatible = "fsl,imx7ulp-lpi2c" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, lpi2c_imx_of_match);
+
+static int lpi2c_imx_probe(struct device *dev)
+{
+ struct lpi2c_imx_struct *lpi2c_imx;
+ struct resource *iores;
+ unsigned int temp;
+ int ret;
+
+ lpi2c_imx = xzalloc(sizeof(*lpi2c_imx));
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ lpi2c_imx->base = IOMEM(iores->start);
+
+ lpi2c_imx->adapter.nr = -1;
+ lpi2c_imx->adapter.master_xfer = lpi2c_imx_xfer;
+ lpi2c_imx->adapter.dev.parent = dev;
+ lpi2c_imx->adapter.dev.of_node = dev->of_node;
+
+ ret = clk_bulk_get_all(dev, &lpi2c_imx->clks);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "can't get I2C peripheral clock\n");
+ lpi2c_imx->num_clks = ret;
+
+ ret = of_property_read_u32(dev->of_node,
+ "clock-frequency", &lpi2c_imx->bitrate);
+ if (ret)
+ lpi2c_imx->bitrate = I2C_MAX_STANDARD_MODE_FREQ;
+
+ ret = clk_bulk_enable(lpi2c_imx->num_clks, lpi2c_imx->clks);
+ if (ret)
+ return ret;
+
+ lpi2c_imx->clk_rate = clk_get_rate(lpi2c_imx->clks[0].clk);
+
+ temp = readl(lpi2c_imx->base + LPI2C_PARAM);
+ lpi2c_imx->txfifosize = 1 << (temp & 0x0f);
+ lpi2c_imx->rxfifosize = 1 << ((temp >> 8) & 0x0f);
+
+ ret = i2c_add_numbered_adapter(&lpi2c_imx->adapter);
+ if (ret)
+ return ret;
+
+ dev_dbg(&lpi2c_imx->adapter.dev, "LPI2C adapter registered\n");
+
+ return 0;
+}
+
+static struct driver lpi2c_imx_driver = {
+ .probe = lpi2c_imx_probe,
+ .name = "i2c-fsl",
+ .of_compatible = DRV_OF_COMPAT(lpi2c_imx_of_match),
+};
+coredevice_platform_driver(lpi2c_imx_driver);
+
+#endif
+
+MODULE_AUTHOR("Gao Pan <pandy.gao@nxp.com>");
+MODULE_DESCRIPTION("I2C adapter driver for LPI2C bus");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 7d25ed3af5..f6a67ec067 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -1,18 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2012 GE Intelligent Platforms, Inc
* Copyright (C) 2002 Motorola GSG-China
* 2009 Marc Kleine-Budde, Pengutronix
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* Author:
* Darius Augulis, Teltonika Inc.
*
@@ -111,6 +102,7 @@ struct fsl_i2c_hwdata {
struct fsl_i2c_struct {
void __iomem *base;
struct clk *clk;
+ struct device *dev;
struct i2c_adapter adapter;
unsigned int disable_delay;
unsigned int ifdr; /* FSL_I2C_IFDR */
@@ -210,7 +202,7 @@ static int i2c_fsl_trx_complete(struct i2c_adapter *adapter)
static int i2c_fsl_acked(struct i2c_adapter *adapter)
{
- return i2c_fsl_poll_status(adapter, 1, 0, I2SR_RXAK);
+ return i2c_fsl_poll_status(adapter, 5, 0, I2SR_RXAK);
}
static int i2c_fsl_start(struct i2c_adapter *adapter)
@@ -304,7 +296,7 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl,
* Translate to dfsr = 5 * Frequency / 100,000,000
*/
dfsr = (5 * (i2c_clk / 1000)) / 100000;
- dev_dbg(&i2c_fsl->adapter.dev,
+ dev_dbg(i2c_fsl->dev,
"<%s> requested speed:%d, i2c_clk:%d\n", __func__,
rate, i2c_clk);
if (!dfsr)
@@ -323,12 +315,12 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl,
bin_ga = (ga & 0x3) | ((ga & 0x4) << 3);
fdr = bin_gb | bin_ga;
rate = i2c_clk / est_div;
- dev_dbg(&i2c_fsl->adapter.dev,
+ dev_dbg(i2c_fsl->dev,
"FDR:0x%.2x, div:%ld, ga:0x%x, gb:0x%x,"
" a:%d, b:%d, speed:%d\n", fdr, est_div,
ga, gb, a, b, rate);
/* Condition 2 not accounted for */
- dev_dbg(&i2c_fsl->adapter.dev,
+ dev_dbg(i2c_fsl->dev,
"Tr <= %d ns\n", (b - 3 * dfsr) *
1000000 / (i2c_clk / 1000));
}
@@ -338,9 +330,9 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl,
if (a == 24)
a += 4;
}
- dev_dbg(&i2c_fsl->adapter.dev,
+ dev_dbg(i2c_fsl->dev,
"divider:%d, est_div:%ld, DFSR:%d\n", divider, est_div, dfsr);
- dev_dbg(&i2c_fsl->adapter.dev, "FDR:0x%.2x, speed:%d\n", fdr, rate);
+ dev_dbg(i2c_fsl->dev, "FDR:0x%.2x, speed:%d\n", fdr, rate);
i2c_fsl->ifdr = fdr;
i2c_fsl->dfsrr = dfsr;
}
@@ -377,9 +369,9 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl,
(500000U * i2c_clk_div[i].div + (i2c_clk_rate / 2) - 1) /
(i2c_clk_rate / 2);
- dev_dbg(&i2c_fsl->adapter.dev, "<%s> I2C_CLK=%d, REQ DIV=%d\n",
+ dev_dbg(i2c_fsl->dev, "<%s> I2C_CLK=%d, REQ DIV=%d\n",
__func__, i2c_clk_rate, div);
- dev_dbg(&i2c_fsl->adapter.dev, "<%s> IFDR[IC]=0x%x, REAL DIV=%d\n",
+ dev_dbg(i2c_fsl->dev, "<%s> IFDR[IC]=0x%x, REAL DIV=%d\n",
__func__, i2c_clk_div[i].val, i2c_clk_div[i].div);
}
#endif
@@ -534,15 +526,18 @@ static void i2c_fsl_unprepare_recovery(struct i2c_adapter *adapter)
dev_err(adapter->dev.parent, "pinctrl failed: %s\n", strerror(-ret));
}
-static void i2c_fsl_init_recovery(struct fsl_i2c_struct *i2c_fsl, struct device_d *dev)
+static void i2c_fsl_init_recovery(struct fsl_i2c_struct *i2c_fsl,
+ struct device *dev)
{
- if (!dev->device_node)
+ if (!dev->of_node)
return;
- i2c_fsl->rinfo.sda_gpio = of_get_named_gpio_flags(dev->device_node,
- "sda-gpios", 0, NULL);
- i2c_fsl->rinfo.scl_gpio = of_get_named_gpio_flags(dev->device_node,
- "scl-gpios", 0, NULL);
+ i2c_fsl->rinfo.sda_gpio = of_get_named_gpio_flags(dev->of_node,
+ "sda-gpios", 0,
+ NULL);
+ i2c_fsl->rinfo.scl_gpio = of_get_named_gpio_flags(dev->of_node,
+ "scl-gpios", 0,
+ NULL);
if (!gpio_is_valid(i2c_fsl->rinfo.sda_gpio) ||
!gpio_is_valid(i2c_fsl->rinfo.scl_gpio))
@@ -559,7 +554,7 @@ static void i2c_fsl_init_recovery(struct fsl_i2c_struct *i2c_fsl, struct device_
dev_dbg(dev, "initialized recovery info\n");
}
-static int __init i2c_fsl_probe(struct device_d *pdev)
+static int __init i2c_fsl_probe(struct device *pdev)
{
struct resource *iores;
struct fsl_i2c_struct *i2c_fsl;
@@ -570,6 +565,7 @@ static int __init i2c_fsl_probe(struct device_d *pdev)
pdata = pdev->platform_data;
i2c_fsl = xzalloc(sizeof(*i2c_fsl));
+ i2c_fsl->dev = pdev;
#ifdef CONFIG_COMMON_CLK
i2c_fsl->clk = clk_get(pdev, NULL);
@@ -594,7 +590,7 @@ static int __init i2c_fsl_probe(struct device_d *pdev)
i2c_fsl->adapter.master_xfer = i2c_fsl_xfer;
i2c_fsl->adapter.nr = pdev->id;
i2c_fsl->adapter.dev.parent = pdev;
- i2c_fsl->adapter.dev.device_node = pdev->device_node;
+ i2c_fsl->adapter.dev.of_node = pdev->of_node;
iores = dev_request_mem_resource(pdev, 0);
if (IS_ERR(iores)) {
ret = PTR_ERR(iores);
@@ -608,7 +604,7 @@ static int __init i2c_fsl_probe(struct device_d *pdev)
/* Set up clock divider */
bitrate = 100000;
- of_property_read_u32(pdev->device_node, "clock-frequency", &bitrate);
+ of_property_read_u32(pdev->of_node, "clock-frequency", &bitrate);
if (pdata && pdata->bitrate)
bitrate = pdata->bitrate;
@@ -654,8 +650,9 @@ static __maybe_unused struct of_device_id imx_i2c_dt_ids[] = {
{ .compatible = "fsl,vf610-i2c", .data = &vf610_i2c_hwdata, },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, imx_i2c_dt_ids);
-static struct driver_d i2c_fsl_driver = {
+static struct driver i2c_fsl_driver = {
.probe = i2c_fsl_probe,
.name = "i2c-fsl",
.of_compatible = DRV_OF_COMPAT(imx_i2c_dt_ids),
diff --git a/drivers/i2c/busses/i2c-imx.h b/drivers/i2c/busses/i2c-imx.h
index 3e3e1317f2..f7157f48f2 100644
--- a/drivers/i2c/busses/i2c-imx.h
+++ b/drivers/i2c/busses/i2c-imx.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef I2C_IMX_H
#define I2C_IMX_H
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index f54d81608f..44f1fd4ce6 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the i2c controller on the Marvell line of host bridges
* (e.g, gt642[46]0, mv643[46]0, mv644[46]0, and Orion SoC family).
@@ -6,10 +7,7 @@
*
* Author: Mark A. Greer <mgreer@mvista.com>
*
- * 2005 (c) MontaVista, Software, Inc. This file is licensed under
- * the terms of the GNU General Public License version 2. This program
- * is licensed "as is" without any warranty of any kind, whether express
- * or implied.
+ * 2005 (c) MontaVista, Software, Inc.
*/
#include <common.h>
@@ -497,6 +495,7 @@ static struct of_device_id mv64xxx_i2c_of_match_table[] = {
{ .compatible = "marvell,mv78230-a0-i2c", .data = &mv64xxx_i2c_regs_mv64xxx},
{}
};
+MODULE_DEVICE_TABLE(of, mv64xxx_i2c_of_match_table);
static inline int
mv64xxx_calc_freq(const int tclk, const int n, const int m)
@@ -530,9 +529,9 @@ mv64xxx_find_baud_factors(const u32 req_freq, const u32 tclk, u32 *best_n,
static int
mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
- struct device_d *pd)
+ struct device *pd)
{
- struct device_node *np = pd->device_node;
+ struct device_node *np = pd->of_node;
u32 bus_freq, tclk;
int rc = 0;
u32 prop;
@@ -598,13 +597,13 @@ out:
}
static int
-mv64xxx_i2c_probe(struct device_d *pd)
+mv64xxx_i2c_probe(struct device *pd)
{
struct resource *iores;
struct mv64xxx_i2c_data *drv_data;
int rc;
- if (!pd->device_node)
+ if (!pd->of_node)
return -ENODEV;
drv_data = xzalloc(sizeof(*drv_data));
@@ -627,7 +626,7 @@ mv64xxx_i2c_probe(struct device_d *pd)
drv_data->adapter.master_xfer = mv64xxx_i2c_xfer;
drv_data->adapter.dev.parent = pd;
drv_data->adapter.nr = pd->id;
- drv_data->adapter.dev.device_node = pd->device_node;
+ drv_data->adapter.dev.of_node = pd->of_node;
mv64xxx_i2c_hw_init(drv_data);
@@ -645,7 +644,7 @@ exit_clk:
return rc;
}
-static struct driver_d mv64xxx_i2c_driver = {
+static struct driver mv64xxx_i2c_driver = {
.probe = mv64xxx_i2c_probe,
.name = "mv64xxx_i2c",
.of_compatible = DRV_OF_COMPAT(mv64xxx_i2c_of_match_table),
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index d3f525f333..023c9673ba 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* TI OMAP I2C master mode driver
*
@@ -12,16 +13,6 @@
* Juha Yrjölä <juha.yrjola@solidboot.com>
* Syed Khasim <x0khasim@ti.com>
* Nishant Menon <nm@ti.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <clock.h>
@@ -37,8 +28,8 @@
#include <io.h>
#include <i2c/i2c.h>
-#include <mach/generic.h>
-#include <mach/omap3-clock.h>
+#include <mach/omap/generic.h>
+#include <mach/omap/omap3-clock.h>
/* This will be the driver name */
#define DRIVER_NAME "i2c-omap"
@@ -1068,7 +1059,7 @@ static struct i2c_bus_recovery_info omap_i2c_bus_recovery_info = {
};
static int __init
-i2c_omap_probe(struct device_d *pdev)
+i2c_omap_probe(struct device *pdev)
{
struct resource *iores;
struct omap_i2c_struct *i2c_omap;
@@ -1100,8 +1091,8 @@ i2c_omap_probe(struct device_d *pdev)
if (pdev->platform_data != NULL) {
speed = *(u32 *)pdev->platform_data;
} else {
- of_property_read_u32(pdev->device_node, "clock-frequency",
- &speed);
+ of_property_read_u32(pdev->of_node, "clock-frequency",
+ &speed);
/* convert DT freq value in Hz into kHz for speed */
speed /= 1000;
}
@@ -1179,7 +1170,7 @@ i2c_omap_probe(struct device_d *pdev)
i2c_omap->adapter.master_xfer = omap_i2c_xfer;
i2c_omap->adapter.nr = pdev->id;
i2c_omap->adapter.dev.parent = pdev;
- i2c_omap->adapter.dev.device_node = pdev->device_node;
+ i2c_omap->adapter.dev.of_node = pdev->of_node;
i2c_omap->adapter.bus_recovery_info = &omap_i2c_bus_recovery_info;
/* i2c device drivers may be active on return from add_adapter() */
@@ -1228,8 +1219,9 @@ static __maybe_unused struct of_device_id omap_i2c_dt_ids[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, omap_i2c_dt_ids);
-static struct driver_d omap_i2c_driver = {
+static struct driver omap_i2c_driver = {
.probe = i2c_omap_probe,
.name = DRIVER_NAME,
.id_table = omap_i2c_ids,
diff --git a/drivers/i2c/busses/i2c-rockchip.c b/drivers/i2c/busses/i2c-rockchip.c
new file mode 100644
index 0000000000..ce029d148f
--- /dev/null
+++ b/drivers/i2c/busses/i2c-rockchip.c
@@ -0,0 +1,462 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2015 Google, Inc
+ *
+ * (C) Copyright 2008-2014 Rockchip Electronics
+ * Peter, Software Engineering, <superpeter.cai@gmail.com>.
+ */
+
+#include <common.h>
+#include <i2c/i2c.h>
+#include <linux/iopoll.h>
+#include <errno.h>
+#include <linux/err.h>
+#include <driver.h>
+#include <io.h>
+#include <linux/clk.h>
+#include <mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/sizes.h>
+
+struct i2c_regs {
+ u32 con;
+ u32 clkdiv;
+ u32 mrxaddr;
+ u32 mrxraddr;
+ u32 mtxcnt;
+ u32 mrxcnt;
+ u32 ien;
+ u32 ipd;
+ u32 fcnt;
+ u32 reserved0[0x37];
+ u32 txdata[8];
+ u32 reserved1[0x38];
+ u32 rxdata[8];
+};
+
+/* Control register */
+#define I2C_CON_EN (1 << 0)
+#define I2C_CON_MOD(mod) ((mod) << 1)
+#define I2C_MODE_TX 0x00
+#define I2C_MODE_TRX 0x01
+#define I2C_MODE_RX 0x02
+#define I2C_MODE_RRX 0x03
+#define I2C_CON_MASK (3 << 1)
+
+#define I2C_CON_START (1 << 3)
+#define I2C_CON_STOP (1 << 4)
+#define I2C_CON_LASTACK (1 << 5)
+#define I2C_CON_ACTACK (1 << 6)
+
+/* Clock divider register */
+#define I2C_CLKDIV_VAL(divl, divh) \
+ (((divl) & 0xffff) | (((divh) << 16) & 0xffff0000))
+
+/* the slave address accessed for master rx mode */
+#define I2C_MRXADDR_SET(vld, addr) (((vld) << 24) | (addr))
+
+/* the slave register address accessed for master rx mode */
+#define I2C_MRXRADDR_SET(vld, raddr) (((vld) << 24) | (raddr))
+
+/* interrupt enable register */
+#define I2C_BTFIEN (1 << 0)
+#define I2C_BRFIEN (1 << 1)
+#define I2C_MBTFIEN (1 << 2)
+#define I2C_MBRFIEN (1 << 3)
+#define I2C_STARTIEN (1 << 4)
+#define I2C_STOPIEN (1 << 5)
+#define I2C_NAKRCVIEN (1 << 6)
+
+/* interrupt pending register */
+#define I2C_BTFIPD (1 << 0)
+#define I2C_BRFIPD (1 << 1)
+#define I2C_MBTFIPD (1 << 2)
+#define I2C_MBRFIPD (1 << 3)
+#define I2C_STARTIPD (1 << 4)
+#define I2C_STOPIPD (1 << 5)
+#define I2C_NAKRCVIPD (1 << 6)
+#define I2C_IPD_ALL_CLEAN 0x7f
+
+/* i2c timeout */
+#define I2C_TIMEOUT_US (100 * USEC_PER_MSEC)
+
+/* rk i2c fifo max transfer bytes */
+#define RK_I2C_FIFO_SIZE 32
+
+struct rk_i2c {
+ struct i2c_adapter adapter;
+ struct clk *clk;
+ struct i2c_regs *regs;
+ unsigned int speed;
+};
+
+static inline struct rk_i2c *to_rk_i2c(struct i2c_adapter *adapter)
+{
+ return container_of(adapter, struct rk_i2c, adapter);
+}
+
+static inline void rk_i2c_get_div(int div, int *divh, int *divl)
+{
+ *divl = div / 2;
+ *divh = DIV_ROUND_UP(div, 2);
+}
+
+/*
+ * SCL Divisor = 8 * (CLKDIVL+1 + CLKDIVH+1)
+ * SCL = PCLK / SCLK Divisor
+ * i2c_rate = PCLK
+ */
+static void rk_i2c_set_clk(struct rk_i2c *i2c, uint32_t scl_rate)
+{
+ struct device *dev = i2c->adapter.dev.parent;
+ uint32_t i2c_rate;
+ int div, divl, divh;
+
+ /* First get i2c rate from pclk */
+ i2c_rate = clk_get_rate(i2c->clk);
+
+ div = DIV_ROUND_UP(i2c_rate, scl_rate * 8) - 2;
+ divh = 0;
+ divl = 0;
+ if (div >= 0)
+ rk_i2c_get_div(div, &divh, &divl);
+ writel(I2C_CLKDIV_VAL(divl, divh), &i2c->regs->clkdiv);
+
+ dev_dbg(dev, "rk_i2c_set_clk: i2c rate = %d, scl rate = %d\n", i2c_rate,
+ scl_rate);
+ dev_dbg(dev, "set i2c clk div = %d, divh = %d, divl = %d\n", div, divh, divl);
+ dev_dbg(dev, "set clk(I2C_CLKDIV: 0x%08x)\n", readl(&i2c->regs->clkdiv));
+}
+
+static void rk_i2c_show_regs(struct rk_i2c *i2c)
+{
+ struct device *dev = &i2c->adapter.dev;
+ struct i2c_regs *regs = i2c->regs;
+ int i;
+
+ dev_dbg(dev, "i2c_con: 0x%08x\n", readl(&regs->con));
+ dev_dbg(dev, "i2c_clkdiv: 0x%08x\n", readl(&regs->clkdiv));
+ dev_dbg(dev, "i2c_mrxaddr: 0x%08x\n", readl(&regs->mrxaddr));
+ dev_dbg(dev, "i2c_mrxraddR: 0x%08x\n", readl(&regs->mrxraddr));
+ dev_dbg(dev, "i2c_mtxcnt: 0x%08x\n", readl(&regs->mtxcnt));
+ dev_dbg(dev, "i2c_mrxcnt: 0x%08x\n", readl(&regs->mrxcnt));
+ dev_dbg(dev, "i2c_ien: 0x%08x\n", readl(&regs->ien));
+ dev_dbg(dev, "i2c_ipd: 0x%08x\n", readl(&regs->ipd));
+ dev_dbg(dev, "i2c_fcnt: 0x%08x\n", readl(&regs->fcnt));
+
+ for (i = 0; i < 8; i++)
+ dev_dbg(dev, "i2c_txdata%d: 0x%08x\n", i, readl(&regs->txdata[i]));
+ for (i = 0; i < 8; i++)
+ dev_dbg(dev, "i2c_rxdata%d: 0x%08x\n", i, readl(&regs->rxdata[i]));
+}
+
+static int rk_i2c_send_start_bit(struct rk_i2c *i2c)
+{
+ struct device *dev = &i2c->adapter.dev;
+ struct i2c_regs *regs = i2c->regs;
+ u32 val;
+ int err;
+
+ dev_dbg(dev, "I2c Send Start bit.\n");
+ writel(I2C_IPD_ALL_CLEAN, &regs->ipd);
+
+ writel(I2C_CON_EN | I2C_CON_START, &regs->con);
+ writel(I2C_STARTIEN, &regs->ien);
+
+ err = readl_poll_timeout(&regs->ipd, val, val & I2C_STARTIPD, I2C_TIMEOUT_US);
+ if (err) {
+ dev_dbg(dev, "I2C Send Start Bit Timeout\n");
+ rk_i2c_show_regs(i2c);
+ return err;
+ }
+
+ writel(I2C_STARTIPD, &regs->ipd);
+ return 0;
+}
+
+static int rk_i2c_send_stop_bit(struct rk_i2c *i2c)
+{
+ struct device *dev = &i2c->adapter.dev;
+ struct i2c_regs *regs = i2c->regs;
+ u32 val;
+ int err;
+
+ dev_dbg(dev, "I2c Send Stop bit.\n");
+ writel(I2C_IPD_ALL_CLEAN, &regs->ipd);
+
+ writel(I2C_CON_EN | I2C_CON_STOP, &regs->con);
+ writel(I2C_CON_STOP, &regs->ien);
+
+ err = readl_poll_timeout(&regs->ipd, val, val & I2C_STOPIPD, I2C_TIMEOUT_US);
+ if (err) {
+ dev_dbg(dev, "I2C Send Start Bit Timeout\n");
+ rk_i2c_show_regs(i2c);
+ return err;
+ }
+
+ writel(I2C_STOPIPD, &regs->ipd);
+ return 0;
+}
+
+static inline void rk_i2c_disable(struct rk_i2c *i2c)
+{
+ writel(0, &i2c->regs->con);
+}
+
+static int rk_i2c_read(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len,
+ uchar *buf, uint b_len)
+{
+ struct device *dev = &i2c->adapter.dev;
+ struct i2c_regs *regs = i2c->regs;
+ uchar *pbuf = buf;
+ uint bytes_remain_len = b_len;
+ uint bytes_xferred = 0;
+ uint words_xferred = 0;
+ uint con = 0;
+ uint rxdata;
+ uint i, j;
+ int err;
+ u32 val;
+ bool snd_chunk = false;
+
+ dev_dbg(dev, "rk_i2c_read: chip = %d, reg = %d, r_len = %d, b_len = %d\n",
+ chip, reg, r_len, b_len);
+
+ err = rk_i2c_send_start_bit(i2c);
+ if (err)
+ return err;
+
+ writel(I2C_MRXADDR_SET(1, chip << 1 | 1), &regs->mrxaddr);
+ if (r_len == 0) {
+ writel(0, &regs->mrxraddr);
+ } else if (r_len < 4) {
+ writel(I2C_MRXRADDR_SET(r_len, reg), &regs->mrxraddr);
+ } else {
+ dev_dbg(dev, "I2C Read: addr len %d not supported\n", r_len);
+ return -EIO;
+ }
+
+ while (bytes_remain_len) {
+ if (bytes_remain_len > RK_I2C_FIFO_SIZE) {
+ con = I2C_CON_EN;
+ bytes_xferred = RK_I2C_FIFO_SIZE;
+ } else {
+ /*
+ * 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, &regs->con);
+ writel(bytes_xferred, &regs->mrxcnt);
+ writel(I2C_MBRFIEN | I2C_NAKRCVIEN, &regs->ien);
+
+ err = readl_poll_timeout(&regs->ipd, val, val & (I2C_NAKRCVIPD|I2C_MBRFIPD), I2C_TIMEOUT_US);
+ if (err) {
+ dev_dbg(dev, "I2C Read Data Timeout\n");
+ goto i2c_exit;
+ }
+
+ if (val & I2C_NAKRCVIPD) {
+ writel(I2C_NAKRCVIPD, &regs->ipd);
+ err = -EREMOTEIO;
+ goto i2c_exit;
+ }
+
+ writel(I2C_MBRFIPD, &regs->ipd);
+
+ for (i = 0; i < words_xferred; i++) {
+ rxdata = readl(&regs->rxdata[i]);
+ dev_dbg(dev, "I2c Read RXDATA[%d] = 0x%x\n", i, rxdata);
+ for (j = 0; j < 4; j++) {
+ if ((i * 4 + j) == bytes_xferred)
+ break;
+ *pbuf++ = (rxdata >> (j * 8)) & 0xff;
+ }
+ }
+
+ bytes_remain_len -= bytes_xferred;
+ snd_chunk = true;
+ dev_dbg(dev, "I2C Read bytes_remain_len %d\n", bytes_remain_len);
+ }
+
+i2c_exit:
+ if (err)
+ rk_i2c_show_regs(i2c);
+ rk_i2c_disable(i2c);
+
+ return err;
+}
+
+static int rk_i2c_write(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len,
+ uchar *buf, uint b_len)
+{
+ struct device *dev = &i2c->adapter.dev;
+ struct i2c_regs *regs = i2c->regs;
+ u32 val;
+ int err;
+ uchar *pbuf = buf;
+ uint bytes_remain_len = b_len + r_len + 1;
+ uint bytes_xferred = 0;
+ uint words_xferred = 0;
+ uint txdata;
+ uint i, j;
+
+ dev_dbg(dev, "rk_i2c_write: chip = %d, reg = %d, r_len = %d, b_len = %d\n",
+ chip, reg, r_len, b_len);
+ err = rk_i2c_send_start_bit(i2c);
+ if (err)
+ return err;
+
+ while (bytes_remain_len) {
+ if (bytes_remain_len > RK_I2C_FIFO_SIZE)
+ bytes_xferred = RK_I2C_FIFO_SIZE;
+ else
+ bytes_xferred = bytes_remain_len;
+ words_xferred = DIV_ROUND_UP(bytes_xferred, 4);
+
+ for (i = 0; i < words_xferred; i++) {
+ txdata = 0;
+ for (j = 0; j < 4; j++) {
+ if ((i * 4 + j) == bytes_xferred)
+ break;
+
+ if (i == 0 && j == 0 && pbuf == buf) {
+ txdata |= (chip << 1);
+ } else if (i == 0 && j <= r_len && pbuf == buf) {
+ txdata |= (reg &
+ (0xff << ((j - 1) * 8))) << 8;
+ } else {
+ txdata |= (*pbuf++)<<(j * 8);
+ }
+ }
+ writel(txdata, &regs->txdata[i]);
+ dev_dbg(dev, "I2c Write TXDATA[%d] = 0x%08x\n", i, txdata);
+ }
+
+ writel(I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TX), &regs->con);
+ writel(bytes_xferred, &regs->mtxcnt);
+ writel(I2C_MBTFIEN | I2C_NAKRCVIEN, &regs->ien);
+
+ err = readl_poll_timeout(&regs->ipd, val, val & (I2C_NAKRCVIPD|I2C_MBTFIPD), I2C_TIMEOUT_US);
+ if (err) {
+ dev_dbg(dev, "I2C Write Data Timeout\n");
+ goto i2c_exit;
+ }
+
+ if (val & I2C_NAKRCVIPD) {
+ writel(I2C_NAKRCVIPD, &regs->ipd);
+ err = -EREMOTEIO;
+ goto i2c_exit;
+ }
+
+ writel(I2C_MBTFIPD, &regs->ipd);
+
+ bytes_remain_len -= bytes_xferred;
+ dev_dbg(dev, "I2C Write bytes_remain_len %d\n", bytes_remain_len);
+ }
+
+i2c_exit:
+ if (err)
+ rk_i2c_show_regs(i2c);
+ rk_i2c_disable(i2c);
+
+ return err;
+}
+
+static int rockchip_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
+ int nmsgs)
+{
+ struct rk_i2c *i2c = to_rk_i2c(adapter);
+ struct device *dev = &adapter->dev;
+ int i, ret = 0;
+
+ dev_dbg(dev, "i2c_xfer: %d messages\n", nmsgs);
+ for (i = 0; i < nmsgs; i++) {
+ struct i2c_msg *msg = &msgs[i];
+
+ dev_dbg(dev, "i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
+ if (msg->flags & I2C_M_RD) {
+ ret = rk_i2c_read(i2c, msg->addr, 0, 0, msg->buf,
+ msg->len);
+ } else {
+ ret = rk_i2c_write(i2c, msg->addr, 0, 0, msg->buf,
+ msg->len);
+ }
+ if (ret) {
+ dev_dbg(dev, "i2c_write: error sending: %pe\n",
+ ERR_PTR(ret));
+ ret = -EREMOTEIO;
+ break;
+ }
+ }
+
+ rk_i2c_send_stop_bit(i2c);
+ rk_i2c_disable(i2c);
+
+ return ret < 0 ? ret : nmsgs;
+}
+
+static int rk_i2c_probe(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct resource *iores;
+ struct rk_i2c *i2c;
+ unsigned bitrate;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ i2c = kzalloc(sizeof(struct rk_i2c), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ dev->priv = i2c;
+ i2c->regs = IOMEM(iores->start);
+
+ /* Only one clock to use for bus clock and peripheral clock */
+ i2c->clk = clk_get(dev, NULL);
+ if (IS_ERR(i2c->clk))
+ return dev_err_probe(dev, PTR_ERR(i2c->clk), "Can't get bus clk\n");
+
+ i2c->adapter.master_xfer = rockchip_i2c_xfer;
+ i2c->adapter.nr = dev->id;
+ i2c->adapter.dev.parent = dev;
+ i2c->adapter.dev.of_node = np;
+
+ /* Set up clock divider */
+ bitrate = 100000;
+ of_property_read_u32(np, "clock-frequency", &bitrate);
+
+ rk_i2c_set_clk(i2c, bitrate);
+
+ return i2c_add_numbered_adapter(&i2c->adapter);
+}
+
+static const struct of_device_id rk_i2c_match[] = {
+ { .compatible = "rockchip,rv1108-i2c" },
+ { .compatible = "rockchip,rk3228-i2c" },
+ { .compatible = "rockchip,rk3288-i2c" },
+ { .compatible = "rockchip,rk3399-i2c" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rk_i2c_match);
+
+static struct driver rk_i2c_driver = {
+ .name = "rk3x-i2c",
+ .of_compatible = rk_i2c_match,
+ .probe = rk_i2c_probe,
+};
+coredevice_platform_driver(rk_i2c_driver);
diff --git a/drivers/i2c/busses/i2c-stm32.c b/drivers/i2c/busses/i2c-stm32.c
index 9f34760e3f..1be52b3dd9 100644
--- a/drivers/i2c/busses/i2c-stm32.c
+++ b/drivers/i2c/busses/i2c-stm32.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2017 STMicroelectronics
* Copyright 2019 Ahmad Fatoum, Pengutronix
@@ -9,9 +9,11 @@
#include <common.h>
#include <i2c/i2c.h>
#include <init.h>
+#include <linux/regmap.h>
#include <linux/clk.h>
#include <linux/iopoll.h>
#include <linux/reset.h>
+#include <mfd/syscon.h>
/* STM32 I2C registers */
struct __packed stm32_i2c_regs {
@@ -38,6 +40,8 @@ struct __packed stm32_i2c_regs {
/* STM32 I2C control 1 */
#define STM32_I2C_CR1_ANFOFF BIT(12)
+#define STM32_I2C_CR1_DNF_MASK GENMASK(11, 8)
+#define STM32_I2C_CR1_DNF(n) (((n) & 0xf) << 8)
#define STM32_I2C_CR1_ERRIE BIT(7)
#define STM32_I2C_CR1_TCIE BIT(6)
#define STM32_I2C_CR1_STOPIE BIT(5)
@@ -48,7 +52,6 @@ struct __packed stm32_i2c_regs {
#define STM32_I2C_CR1_PE BIT(0)
/* STM32 I2C control 2 */
-#define STM32_I2C_CR2_AUTOEND BIT(25)
#define STM32_I2C_CR2_RELOAD BIT(24)
#define STM32_I2C_CR2_NBYTES_MASK GENMASK(23, 16)
#define STM32_I2C_CR2_NBYTES(n) ((n & 0xff) << 16)
@@ -98,10 +101,8 @@ struct __packed stm32_i2c_regs {
#define STM32_I2C_MAX_LEN 0xff
-#define STM32_I2C_DNF_DEFAULT 0
-#define STM32_I2C_DNF_MAX 16
+#define STM32_I2C_DNF_MAX 15
-#define STM32_I2C_ANALOG_FILTER_ENABLE 1
#define STM32_I2C_ANALOG_FILTER_DELAY_MIN 50 /* ns */
#define STM32_I2C_ANALOG_FILTER_DELAY_MAX 260 /* ns */
@@ -119,10 +120,9 @@ struct __packed stm32_i2c_regs {
#define FAST_PLUS_RATE 1000000
enum stm32_i2c_speed {
- STM32_I2C_SPEED_STANDARD, /* 100 kHz */
- STM32_I2C_SPEED_FAST, /* 400 kHz */
- STM32_I2C_SPEED_FAST_PLUS, /* 1 MHz */
- STM32_I2C_SPEED_END,
+ IC_SPEED_MODE_STANDARD,
+ IC_SPEED_MODE_FAST,
+ IC_SPEED_MODE_FAST_PLUS,
};
/**
@@ -154,20 +154,23 @@ struct stm32_i2c_spec {
/**
* struct stm32_i2c_setup - private I2C timing setup parameters
- * @speed: I2C speed mode (standard, Fast Plus)
- * @speed_freq: actual I2C speed frequency (Hz)
* @clock_src: I2C clock source frequency (Hz)
- * @dnf: Digital filter coefficient (0-16)
+ * @timings: I2C timing information
* @analog_filter: Analog filter delay (On/Off)
- * @timings: I2C timings parameters
*/
struct stm32_i2c_setup {
- enum stm32_i2c_speed speed;
- u32 speed_freq;
u32 clock_src;
+ struct i2c_timings timings;
u8 dnf;
bool analog_filter;
- struct i2c_timings timings;
+};
+
+/**
+ * struct stm32_i2c_data - driver data for I2C configuration by compatible
+ * @fmp_clr_offset: Fast Mode Plus clear register offset from set register
+ */
+struct stm32_i2c_data {
+ u32 fmp_clr_offset;
};
/**
@@ -187,8 +190,32 @@ struct stm32_i2c_timings {
u8 scll;
};
+/**
+ * struct stm32_i2c_priv - private data of the controller
+ * @regs: I2C registers address
+ * @clk: hw i2c clock
+ * @setup: I2C timing setup parameters
+ * @speed: I2C clock frequency of the controller. Standard, Fast or Fast+
+ * @regmap: holds SYSCFG phandle for Fast Mode Plus bit
+ * @regmap_sreg: register address for setting Fast Mode Plus bits
+ * @regmap_creg: register address for clearing Fast Mode Plus bits
+ * @regmap_mask: mask for Fast Mode Plus bits
+ */
+struct stm32_i2c_priv {
+ struct stm32_i2c_regs __iomem *regs;
+ struct clk *clk;
+ struct i2c_adapter adapter;
+ struct stm32_i2c_setup setup;
+ u32 speed;
+ struct regmap *regmap;
+ u32 regmap_sreg;
+ u32 regmap_creg;
+ u32 regmap_mask;
+};
+
static const struct stm32_i2c_spec i2c_specs[] = {
- [STM32_I2C_SPEED_STANDARD] = {
+ /* Standard speed - 100 KHz */
+ [IC_SPEED_MODE_STANDARD] = {
.rate = STANDARD_RATE,
.rate_min = 8000,
.rate_max = 120000,
@@ -200,7 +227,8 @@ static const struct stm32_i2c_spec i2c_specs[] = {
.l_min = 4700,
.h_min = 4000,
},
- [STM32_I2C_SPEED_FAST] = {
+ /* Fast speed - 400 KHz */
+ [IC_SPEED_MODE_FAST] = {
.rate = FAST_RATE,
.rate_min = 320000,
.rate_max = 480000,
@@ -212,7 +240,8 @@ static const struct stm32_i2c_spec i2c_specs[] = {
.l_min = 1300,
.h_min = 600,
},
- [STM32_I2C_SPEED_FAST_PLUS] = {
+ /* Fast Plus Speed - 1 MHz */
+ [IC_SPEED_MODE_FAST_PLUS] = {
.rate = FAST_PLUS_RATE,
.rate_min = 800000,
.rate_max = 1200000,
@@ -226,22 +255,30 @@ static const struct stm32_i2c_spec i2c_specs[] = {
},
};
-struct stm32_i2c {
- struct stm32_i2c_regs __iomem *regs;
- struct clk *clk;
- struct i2c_adapter adapter;
- struct stm32_i2c_setup setup;
+static const struct stm32_i2c_data stm32f7_data = {
+ .fmp_clr_offset = 0x00,
};
-#define to_stm32_i2c(a) container_of(a, struct stm32_i2c, adapter)
-static inline int stm32_i2c_check_device_busy(struct stm32_i2c *priv)
+static const struct stm32_i2c_data stm32mp15_data = {
+ .fmp_clr_offset = 0x40,
+};
+
+static const struct stm32_i2c_data stm32mp13_data = {
+ .fmp_clr_offset = 0x4,
+};
+
+static inline int stm32_i2c_check_device_busy(struct stm32_i2c_priv *priv)
{
u32 status = readl(&priv->regs->isr);
- return status & STM32_I2C_ISR_BUSY;
+
+ if (status & STM32_I2C_ISR_BUSY)
+ return -EBUSY;
+
+ return 0;
}
-static void stm32_i2c_message_start(struct stm32_i2c *i2c_priv,
- struct i2c_msg *msg, bool stop)
+static void stm32_i2c_message_start(struct stm32_i2c_priv *i2c_priv,
+ struct i2c_msg *msg)
{
struct stm32_i2c_regs *regs = i2c_priv->regs;
u32 cr2 = readl(&regs->cr2);
@@ -262,9 +299,8 @@ static void stm32_i2c_message_start(struct stm32_i2c *i2c_priv,
cr2 |= STM32_I2C_CR2_SADD7(msg->addr);
}
- /* Set nb bytes to transfer and reload or autoend bits */
- cr2 &= ~(STM32_I2C_CR2_NBYTES_MASK | STM32_I2C_CR2_RELOAD |
- STM32_I2C_CR2_AUTOEND);
+ /* Set nb bytes to transfer and reload (if needed) */
+ cr2 &= ~(STM32_I2C_CR2_NBYTES_MASK | STM32_I2C_CR2_RELOAD);
if (msg->len > STM32_I2C_MAX_LEN) {
cr2 |= STM32_I2C_CR2_NBYTES(STM32_I2C_MAX_LEN);
cr2 |= STM32_I2C_CR2_RELOAD;
@@ -284,8 +320,8 @@ static void stm32_i2c_message_start(struct stm32_i2c *i2c_priv,
* sent is greater than MAX_LEN
*/
-static void stm32_i2c_handle_reload(struct stm32_i2c *i2c_priv,
- struct i2c_msg *msg, bool stop)
+static void stm32_i2c_handle_reload(struct stm32_i2c_priv *i2c_priv,
+ struct i2c_msg *msg)
{
struct stm32_i2c_regs *regs = i2c_priv->regs;
u32 cr2 = readl(&regs->cr2);
@@ -302,7 +338,7 @@ static void stm32_i2c_handle_reload(struct stm32_i2c *i2c_priv,
writel(cr2, &regs->cr2);
}
-static int stm32_i2c_wait_flags(struct stm32_i2c *i2c_priv,
+static int stm32_i2c_wait_flags(struct stm32_i2c_priv *i2c_priv,
u32 flags, u32 *status)
{
struct stm32_i2c_regs *regs = i2c_priv->regs;
@@ -311,12 +347,12 @@ static int stm32_i2c_wait_flags(struct stm32_i2c *i2c_priv,
*status & flags, USEC_PER_SEC);
}
-static int stm32_i2c_check_end_of_message(struct stm32_i2c *i2c_priv)
+static int stm32_i2c_check_end_of_message(struct stm32_i2c_priv *i2c_priv)
{
struct stm32_i2c_regs *regs = i2c_priv->regs;
u32 mask = STM32_I2C_ISR_ERRORS | STM32_I2C_ISR_NACKF |
STM32_I2C_ISR_STOPF;
- struct device_d *dev = &i2c_priv->adapter.dev;
+ struct device *dev = &i2c_priv->adapter.dev;
u32 status;
int ret;
@@ -362,30 +398,29 @@ static int stm32_i2c_check_end_of_message(struct stm32_i2c *i2c_priv)
setbits_le32(&regs->icr, STM32_I2C_ICR_STOPCF);
/* Clear control register 2 */
- setbits_le32(&regs->cr2, STM32_I2C_CR2_RESET_MASK);
+ clrbits_le32(&regs->cr2, STM32_I2C_CR2_RESET_MASK);
}
return ret;
}
-static int stm32_i2c_message_xfer(struct stm32_i2c *i2c_priv,
+static int stm32_i2c_message_xfer(struct stm32_i2c_priv *i2c_priv,
struct i2c_msg *msg, bool stop)
{
struct stm32_i2c_regs *regs = i2c_priv->regs;
- int len = msg->len;
- u8 *buf = msg->buf;
u32 status;
u32 mask = msg->flags & I2C_M_RD ? STM32_I2C_ISR_RXNE :
STM32_I2C_ISR_TXIS | STM32_I2C_ISR_NACKF;
- int bytes_to_rw = min(len, STM32_I2C_MAX_LEN);
+ int bytes_to_rw = msg->len > STM32_I2C_MAX_LEN ?
+ STM32_I2C_MAX_LEN : msg->len;
int ret = 0;
/* Add errors */
mask |= STM32_I2C_ISR_ERRORS;
- stm32_i2c_message_start(i2c_priv, msg, stop);
+ stm32_i2c_message_start(i2c_priv, msg);
- while (len) {
+ while (msg->len) {
/*
* Wait until TXIS/NACKF/BERR/ARLO flags or
* RXNE/BERR/ARLO flags are set
@@ -398,29 +433,30 @@ static int stm32_i2c_message_xfer(struct stm32_i2c *i2c_priv,
break;
if (status & STM32_I2C_ISR_RXNE) {
- *buf++ = readb(&regs->rxdr);
- len--;
+ *msg->buf++ = readb(&regs->rxdr);
+ msg->len--;
bytes_to_rw--;
}
if (status & STM32_I2C_ISR_TXIS) {
- writeb(*buf++, &regs->txdr);
- len--;
+ writeb(*msg->buf++, &regs->txdr);
+ msg->len--;
bytes_to_rw--;
}
- if (!bytes_to_rw && len) {
+ if (!bytes_to_rw && msg->len) {
/* Wait until TCR flag is set */
mask = STM32_I2C_ISR_TCR;
ret = stm32_i2c_wait_flags(i2c_priv, mask, &status);
if (ret)
break;
- bytes_to_rw = min(len, STM32_I2C_MAX_LEN);
+ bytes_to_rw = msg->len > STM32_I2C_MAX_LEN ?
+ STM32_I2C_MAX_LEN : msg->len;
mask = msg->flags & I2C_M_RD ? STM32_I2C_ISR_RXNE :
STM32_I2C_ISR_TXIS | STM32_I2C_ISR_NACKF;
- stm32_i2c_handle_reload(i2c_priv, msg, stop);
+ stm32_i2c_handle_reload(i2c_priv, msg);
} else if (!bytes_to_rw) {
/* Wait until TC flag is set */
mask = STM32_I2C_ISR_TC;
@@ -434,9 +470,9 @@ static int stm32_i2c_message_xfer(struct stm32_i2c *i2c_priv,
}
}
- /* End of transfer, send stop condition */
- mask = STM32_I2C_CR2_STOP;
- setbits_le32(&regs->cr2, mask);
+ /* End of transfer, send stop condition if appropriate */
+ if (!ret && !(status & (STM32_I2C_ISR_NACKF | STM32_I2C_ISR_ERRORS)))
+ setbits_le32(&regs->cr2, STM32_I2C_CR2_STOP);
return stm32_i2c_check_end_of_message(i2c_priv);
}
@@ -444,16 +480,16 @@ static int stm32_i2c_message_xfer(struct stm32_i2c *i2c_priv,
static int stm32_i2c_xfer(struct i2c_adapter *adapter,
struct i2c_msg *msg, int nmsgs)
{
- struct stm32_i2c *i2c_priv = to_stm32_i2c(adapter);
- int ret;
- int i;
+ struct stm32_i2c_priv *i2c_priv =
+ container_of(adapter, struct stm32_i2c_priv, adapter);
+ int i, ret;
ret = stm32_i2c_check_device_busy(i2c_priv);
if (ret)
- return -EBUSY;
+ return ret;
- for (i = 0; i < nmsgs; i++) {
- ret = stm32_i2c_message_xfer(i2c_priv, &msg[i], i == nmsgs - 1);
+ for (i = nmsgs; i > 0; i--, msg++) {
+ ret = stm32_i2c_message_xfer(i2c_priv, msg, i == 1);
if (ret)
return ret;
}
@@ -461,30 +497,30 @@ static int stm32_i2c_xfer(struct i2c_adapter *adapter,
return nmsgs;
}
-
-static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup,
+static int stm32_i2c_compute_solutions(u32 i2cclk,
+ struct stm32_i2c_setup *setup,
+ const struct stm32_i2c_spec *specs,
struct list_head *solutions)
{
struct stm32_i2c_timings *v;
u32 p_prev = STM32_PRESC_MAX;
- u32 i2cclk = DIV_ROUND_CLOSEST(NSEC_PER_SEC, setup->clock_src);
- u32 af_delay_min = 0, af_delay_max = 0;
+ u32 af_delay_min, af_delay_max;
u16 p, l, a;
int sdadel_min, sdadel_max, scldel_min;
int ret = 0;
- if (setup->analog_filter) {
- af_delay_min = STM32_I2C_ANALOG_FILTER_DELAY_MIN;
- af_delay_max = STM32_I2C_ANALOG_FILTER_DELAY_MAX;
- }
+ af_delay_min = setup->analog_filter ?
+ STM32_I2C_ANALOG_FILTER_DELAY_MIN : 0;
+ af_delay_max = setup->analog_filter ?
+ STM32_I2C_ANALOG_FILTER_DELAY_MAX : 0;
- sdadel_min = i2c_specs[setup->speed].hddat_min + setup->timings.scl_fall_ns -
+ sdadel_min = specs->hddat_min + setup->timings.scl_fall_ns -
af_delay_min - (setup->dnf + 3) * i2cclk;
- sdadel_max = i2c_specs[setup->speed].vddat_max - setup->timings.scl_rise_ns -
+ sdadel_max = specs->vddat_max - setup->timings.scl_rise_ns -
af_delay_max - (setup->dnf + 4) * i2cclk;
- scldel_min = setup->timings.scl_rise_ns + i2c_specs[setup->speed].sudat_min;
+ scldel_min = setup->timings.scl_rise_ns + specs->sudat_min;
if (sdadel_min < 0)
sdadel_min = 0;
@@ -497,13 +533,13 @@ static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup,
/* Compute possible values for PRESC, SCLDEL and SDADEL */
for (p = 0; p < STM32_PRESC_MAX; p++) {
for (l = 0; l < STM32_SCLDEL_MAX; l++) {
- u32 scldel = (l + 1) * (p + 1) * i2cclk;
+ int scldel = (l + 1) * (p + 1) * i2cclk;
if (scldel < scldel_min)
continue;
for (a = 0; a < STM32_SDADEL_MAX; a++) {
- u32 sdadel = (a * (p + 1) + 1) * i2cclk;
+ int sdadel = (a * (p + 1) + 1) * i2cclk;
if (((sdadel >= sdadel_min) &&
(sdadel <= sdadel_max)) &&
@@ -527,36 +563,40 @@ static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup,
}
}
- if (list_empty(solutions))
+ if (list_empty(solutions)) {
+ pr_err("no Prescaler solution\n");
ret = -EPERM;
+ }
return ret;
}
-static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup,
+static int stm32_i2c_choose_solution(u32 i2cclk,
+ struct stm32_i2c_setup *setup,
+ const struct stm32_i2c_spec *specs,
struct list_head *solutions,
struct stm32_i2c_timings *s)
{
struct stm32_i2c_timings *v;
- u32 i2cbus = DIV_ROUND_CLOSEST(NSEC_PER_SEC, setup->speed_freq);
+ u32 i2cbus = DIV_ROUND_CLOSEST(NSEC_PER_SEC,
+ setup->timings.bus_freq_hz);
u32 clk_error_prev = i2cbus;
- u32 i2cclk = DIV_ROUND_CLOSEST(NSEC_PER_SEC, setup->clock_src);
u32 clk_min, clk_max;
- u32 af_delay_min = 0;
+ u32 af_delay_min;
u32 dnf_delay;
u32 tsync;
u16 l, h;
bool sol_found = false;
int ret = 0;
- if (setup->analog_filter)
- af_delay_min = STM32_I2C_ANALOG_FILTER_DELAY_MIN;
+ af_delay_min = setup->analog_filter ?
+ STM32_I2C_ANALOG_FILTER_DELAY_MIN : 0;
dnf_delay = setup->dnf * i2cclk;
tsync = af_delay_min + dnf_delay + (2 * i2cclk);
- clk_max = NSEC_PER_SEC / i2c_specs[setup->speed].rate_min;
- clk_min = NSEC_PER_SEC / i2c_specs[setup->speed].rate_max;
+ clk_max = NSEC_PER_SEC / specs->rate_min;
+ clk_min = NSEC_PER_SEC / specs->rate_max;
/*
* Among Prescaler possibilities discovered above figures out SCL Low
@@ -574,7 +614,7 @@ static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup,
for (l = 0; l < STM32_SCLL_MAX; l++) {
u32 tscl_l = (l + 1) * prescaler + tsync;
- if ((tscl_l < i2c_specs[setup->speed].l_min) ||
+ if (tscl_l < specs->l_min ||
(i2cclk >=
((tscl_l - af_delay_min - dnf_delay) / 4))) {
continue;
@@ -583,16 +623,17 @@ static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup,
for (h = 0; h < STM32_SCLH_MAX; h++) {
u32 tscl_h = (h + 1) * prescaler + tsync;
u32 tscl = tscl_l + tscl_h +
- setup->timings.scl_rise_ns +
- setup->timings.scl_fall_ns;
+ setup->timings.scl_rise_ns + setup->timings.scl_fall_ns;
if ((tscl >= clk_min) && (tscl <= clk_max) &&
- (tscl_h >= i2c_specs[setup->speed].h_min) &&
+ (tscl_h >= specs->h_min) &&
(i2cclk < tscl_h)) {
- int clk_error = tscl - i2cbus;
+ u32 clk_error;
- if (clk_error < 0)
- clk_error = -clk_error;
+ if (tscl > i2cbus)
+ clk_error = tscl - i2cbus;
+ else
+ clk_error = i2cbus - tscl;
if (clk_error < clk_error_prev) {
clk_error_prev = clk_error;
@@ -606,61 +647,68 @@ static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup,
}
}
- if (!sol_found)
+ if (!sol_found) {
+ pr_err("no solution at all\n");
ret = -EPERM;
+ }
return ret;
}
-static int stm32_i2c_compute_timing(struct stm32_i2c *i2c_priv,
- struct stm32_i2c_setup *setup,
+static const struct stm32_i2c_spec *get_specs(u32 rate)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(i2c_specs); i++)
+ if (rate <= i2c_specs[i].rate)
+ return &i2c_specs[i];
+
+ /* NOT REACHED */
+ return ERR_PTR(-EINVAL);
+}
+
+static int stm32_i2c_compute_timing(struct stm32_i2c_priv *i2c_priv,
struct stm32_i2c_timings *output)
{
- struct device_d *dev = &i2c_priv->adapter.dev;
+ struct device *dev = &i2c_priv->adapter.dev;
+ struct stm32_i2c_setup *setup = &i2c_priv->setup;
+ const struct stm32_i2c_spec *specs;
struct stm32_i2c_timings *v, *_v;
struct list_head solutions;
+ u32 i2cclk = DIV_ROUND_CLOSEST(NSEC_PER_SEC, setup->clock_src);
int ret;
- if (setup->speed >= STM32_I2C_SPEED_END) {
- dev_err(dev, "speed out of bound {%d/%d}\n",
- setup->speed, STM32_I2C_SPEED_END - 1);
+ specs = get_specs(setup->timings.bus_freq_hz);
+ if (specs == ERR_PTR(-EINVAL)) {
+ dev_err(dev, "speed out of bound {%d}\n",
+ setup->timings.bus_freq_hz);
return -EINVAL;
}
- if ((setup->timings.scl_rise_ns > i2c_specs[setup->speed].rise_max) ||
- (setup->timings.scl_fall_ns > i2c_specs[setup->speed].fall_max)) {
+ if (setup->timings.scl_rise_ns > specs->rise_max ||
+ setup->timings.scl_fall_ns > specs->fall_max) {
dev_err(dev, "timings out of bound Rise{%d>%d}/Fall{%d>%d}\n",
- setup->timings.scl_rise_ns, i2c_specs[setup->speed].rise_max,
- setup->timings.scl_fall_ns, i2c_specs[setup->speed].fall_max);
+ setup->timings.scl_rise_ns, specs->rise_max,
+ setup->timings.scl_fall_ns, specs->fall_max);
return -EINVAL;
}
+ /* Analog and Digital Filters */
+ setup->dnf = DIV_ROUND_CLOSEST(i2c_priv->setup.timings.digital_filter_width_ns, i2cclk);
if (setup->dnf > STM32_I2C_DNF_MAX) {
dev_err(dev, "DNF out of bound %d/%d\n",
- setup->dnf, STM32_I2C_DNF_MAX);
- return -EINVAL;
- }
-
- if (setup->speed_freq > i2c_specs[setup->speed].rate) {
- dev_err(dev, "Freq {%d/%d}\n",
- setup->speed_freq, i2c_specs[setup->speed].rate);
+ setup->dnf, STM32_I2C_DNF_MAX);
return -EINVAL;
}
INIT_LIST_HEAD(&solutions);
- ret = stm32_i2c_compute_solutions(setup, &solutions);
- if (ret) {
- if (ret == -EPERM)
- dev_err(dev, "No prescaler solution\n");
+ ret = stm32_i2c_compute_solutions(i2cclk, setup, specs, &solutions);
+ if (ret)
goto exit;
- }
- ret = stm32_i2c_choose_solution(setup, &solutions, output);
- if (ret) {
- if (ret == -EPERM)
- dev_err(dev, "no solution at all\n");
+ ret = stm32_i2c_choose_solution(i2cclk, setup, specs, &solutions, output);
+ if (ret)
goto exit;
- }
dev_dbg(dev, "Presc: %i, scldel: %i, sdadel: %i, scll: %i, sclh: %i\n",
output->presc,
@@ -677,16 +725,25 @@ exit:
return ret;
}
-static int stm32_i2c_setup_timing(struct stm32_i2c *i2c_priv,
- enum stm32_i2c_speed speed,
+static u32 get_lower_rate(u32 rate)
+{
+ int i;
+
+ for (i = ARRAY_SIZE(i2c_specs) - 1; i >= 0; i--)
+ if (rate > i2c_specs[i].rate)
+ return i2c_specs[i].rate;
+
+ return i2c_specs[0].rate;
+}
+
+static int stm32_i2c_setup_timing(struct stm32_i2c_priv *i2c_priv,
struct stm32_i2c_timings *timing)
{
- struct device_d *dev = &i2c_priv->adapter.dev;
+ struct device *dev = &i2c_priv->adapter.dev;
struct stm32_i2c_setup *setup = &i2c_priv->setup;
int ret = 0;
- setup->speed = speed;
- setup->speed_freq = i2c_specs[setup->speed].rate;
+ setup->timings.bus_freq_hz = i2c_priv->speed;
setup->clock_src = clk_get_rate(i2c_priv->clk);
if (!setup->clock_src) {
@@ -695,15 +752,14 @@ static int stm32_i2c_setup_timing(struct stm32_i2c *i2c_priv,
}
do {
- ret = stm32_i2c_compute_timing(i2c_priv, setup, timing);
+ ret = stm32_i2c_compute_timing(i2c_priv, timing);
if (ret) {
dev_dbg(dev, "failed to compute I2C timings.\n");
- if (speed > STM32_I2C_SPEED_STANDARD) {
- speed--;
- setup->speed = speed;
- setup->speed_freq = i2c_specs[setup->speed].rate;
+ if (setup->timings.bus_freq_hz > STANDARD_RATE) {
+ setup->timings.bus_freq_hz =
+ get_lower_rate(setup->timings.bus_freq_hz);
dev_dbg(dev, "downgrade I2C Speed Freq to (%i)\n",
- i2c_specs[setup->speed].rate);
+ setup->timings.bus_freq_hz);
} else {
break;
}
@@ -715,31 +771,60 @@ static int stm32_i2c_setup_timing(struct stm32_i2c *i2c_priv,
return ret;
}
- dev_dbg(dev, "I2C Speed(%i), Freq(%i), Clk Source(%i)\n",
- setup->speed, setup->speed_freq, setup->clock_src);
+ dev_dbg(dev, "I2C Freq(%i), Clk Source(%i)\n",
+ setup->timings.bus_freq_hz, setup->clock_src);
dev_dbg(dev, "I2C Rise(%i) and Fall(%i) Time\n",
- setup->timings.scl_rise_ns, setup->timings.scl_fall_ns);
+ setup->timings.scl_rise_ns, setup->timings.scl_fall_ns);
dev_dbg(dev, "I2C Analog Filter(%s), DNF(%i)\n",
- setup->analog_filter ? "On" : "Off", setup->dnf);
+ setup->analog_filter ? "On" : "Off", setup->dnf);
+
+ i2c_priv->speed = setup->timings.bus_freq_hz;
return 0;
}
-static int stm32_i2c_hw_config(struct stm32_i2c *i2c_priv,
- enum stm32_i2c_speed speed)
+static int stm32_i2c_write_fm_plus_bits(struct stm32_i2c_priv *i2c_priv)
+{
+ int ret;
+ bool enable = i2c_priv->speed > FAST_RATE;
+
+ /* Optional */
+ if (IS_ERR_OR_NULL(i2c_priv->regmap))
+ return 0;
+
+ if (i2c_priv->regmap_sreg == i2c_priv->regmap_creg)
+ ret = regmap_update_bits(i2c_priv->regmap,
+ i2c_priv->regmap_sreg,
+ i2c_priv->regmap_mask,
+ enable ? i2c_priv->regmap_mask : 0);
+ else
+ ret = regmap_write(i2c_priv->regmap,
+ enable ? i2c_priv->regmap_sreg :
+ i2c_priv->regmap_creg,
+ i2c_priv->regmap_mask);
+
+ return ret;
+}
+
+static int stm32_i2c_hw_config(struct stm32_i2c_priv *i2c_priv)
{
struct stm32_i2c_regs *regs = i2c_priv->regs;
struct stm32_i2c_timings t;
int ret;
u32 timing = 0;
- ret = stm32_i2c_setup_timing(i2c_priv, speed, &t);
+ ret = stm32_i2c_setup_timing(i2c_priv, &t);
if (ret)
return ret;
/* Disable I2C */
clrbits_le32(&regs->cr1, STM32_I2C_CR1_PE);
+ /* Setup Fast mode plus if necessary */
+ ret = stm32_i2c_write_fm_plus_bits(i2c_priv);
+ if (ret)
+ return ret;
+
/* Timing settings */
timing |= STM32_I2C_TIMINGR_PRESC(t.presc);
timing |= STM32_I2C_TIMINGR_SCLDEL(t.scldel);
@@ -753,44 +838,74 @@ static int stm32_i2c_hw_config(struct stm32_i2c *i2c_priv,
clrbits_le32(&regs->cr1, STM32_I2C_CR1_ANFOFF);
else
setbits_le32(&regs->cr1, STM32_I2C_CR1_ANFOFF);
+
+ /* Program the Digital Filter */
+ clrsetbits_le32(&regs->cr1, STM32_I2C_CR1_DNF_MASK,
+ STM32_I2C_CR1_DNF(i2c_priv->setup.dnf));
+
setbits_le32(&regs->cr1, STM32_I2C_CR1_PE);
return 0;
}
-static int stm32_i2c_set_bus_speed(struct stm32_i2c *i2c_priv, unsigned speed)
+static int stm32_i2c_set_bus_speed(struct stm32_i2c_priv *i2c_priv, unsigned int speed)
{
- struct device_d *parent_dev = i2c_priv->adapter.dev.parent;
- enum stm32_i2c_speed stm32_speed;
- switch (speed) {
- case STANDARD_RATE:
- stm32_speed = STM32_I2C_SPEED_STANDARD;
- break;
- case FAST_RATE:
- stm32_speed = STM32_I2C_SPEED_FAST;
- break;
- case FAST_PLUS_RATE:
- stm32_speed = STM32_I2C_SPEED_FAST_PLUS;
- break;
- default:
- dev_warn(parent_dev, "Speed %d not supported\n", speed);
+ struct device *dev = &i2c_priv->adapter.dev;
+
+ if (speed > FAST_PLUS_RATE) {
+ dev_dbg(dev, "Speed %d not supported\n", speed);
return -EINVAL;
}
- return stm32_i2c_hw_config(i2c_priv, stm32_speed);
+ i2c_priv->speed = speed;
+
+ return stm32_i2c_hw_config(i2c_priv);
+}
+
+static int stm32_of_to_plat(struct device *dev, struct stm32_i2c_priv *i2c_priv)
+{
+ const struct stm32_i2c_data *data;
+ int ret;
+
+ ret = dev_get_drvdata(dev, (const void **)&data);
+ if (ret)
+ return ret;
+
+ if (of_property_read_u32(dev->of_node, "i2c-digital-filter-width-ns",
+ &i2c_priv->setup.timings.digital_filter_width_ns))
+ i2c_priv->setup.timings.digital_filter_width_ns = 0;
+ if (!of_property_read_bool(dev->of_node, "i2c-digital-filter"))
+ i2c_priv->setup.timings.digital_filter_width_ns = 0;
+
+ i2c_priv->setup.analog_filter =
+ of_property_read_bool(dev->of_node, "i2c-analog-filter");
+
+ /* Optional */
+ i2c_priv->regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "st,syscfg-fmp");
+ if (!IS_ERR(i2c_priv->regmap)) {
+ u32 fmp[3];
+
+ ret = of_property_read_u32_array(dev->of_node, "st,syscfg-fmp", fmp, 3);
+ if (ret)
+ return ret;
+
+ i2c_priv->regmap_sreg = fmp[1];
+ i2c_priv->regmap_creg = fmp[1] + data->fmp_clr_offset;
+ i2c_priv->regmap_mask = fmp[2];
+ }
+
+ return 0;
}
-static int __init stm32_i2c_probe(struct device_d *dev)
+static int __init stm32_i2c_probe(struct device *dev)
{
struct resource *iores;
- struct stm32_i2c *stm32_i2c;
+ struct stm32_i2c_priv *stm32_i2c;
struct i2c_platform_data *pdata;
- const struct stm32_i2c_setup *setup;
struct i2c_timings *timings;
int ret;
- pdata = dev->platform_data;
-
stm32_i2c = xzalloc(sizeof(*stm32_i2c));
stm32_i2c->clk = clk_get(dev, NULL);
@@ -798,15 +913,20 @@ static int __init stm32_i2c_probe(struct device_d *dev)
return PTR_ERR(stm32_i2c->clk);
clk_enable(stm32_i2c->clk);
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ stm32_i2c->regs = IOMEM(iores->start);
+
ret = device_reset_us(dev, 2);
if (ret)
return ret;
- ret = dev_get_drvdata(dev, (const void **)&setup);
+ ret = stm32_of_to_plat(dev, stm32_i2c);
if (ret)
return ret;
- stm32_i2c->setup = *setup;
timings = &stm32_i2c->setup.timings;
/* We've our own defaults, so don't use the i2c_parse_fw_timings ones */
@@ -823,13 +943,9 @@ static int __init stm32_i2c_probe(struct device_d *dev)
stm32_i2c->adapter.master_xfer = stm32_i2c_xfer;
stm32_i2c->adapter.nr = dev->id;
stm32_i2c->adapter.dev.parent = dev;
- stm32_i2c->adapter.dev.device_node = dev->device_node;
- iores = dev_request_mem_resource(dev, 0);
- if (IS_ERR(iores))
- return PTR_ERR(iores);
-
- stm32_i2c->regs = IOMEM(iores->start);
+ stm32_i2c->adapter.dev.of_node = dev->of_node;
+ pdata = dev->platform_data;
if (pdata && pdata->bitrate)
timings->bus_freq_hz = pdata->bitrate;
@@ -840,17 +956,15 @@ static int __init stm32_i2c_probe(struct device_d *dev)
return i2c_add_numbered_adapter(&stm32_i2c->adapter);
}
-static const struct stm32_i2c_setup stm32f7_setup = {
- .dnf = STM32_I2C_DNF_DEFAULT,
- .analog_filter = STM32_I2C_ANALOG_FILTER_ENABLE,
-};
-
static __maybe_unused struct of_device_id stm32_i2c_dt_ids[] = {
- { .compatible = "st,stm32f7-i2c", .data = &stm32f7_setup, },
+ { .compatible = "st,stm32f7-i2c", .data = &stm32f7_data },
+ { .compatible = "st,stm32mp15-i2c", .data = &stm32mp15_data },
+ { .compatible = "st,stm32mp13-i2c", .data = &stm32mp13_data },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, stm32_i2c_dt_ids);
-static struct driver_d stm32_i2c_driver = {
+static struct driver stm32_i2c_driver = {
.probe = stm32_i2c_probe,
.name = "stm32f7-i2c",
.of_compatible = DRV_OF_COMPAT(stm32_i2c_dt_ids),
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 94c982d5c2..f86f64f573 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2014 Lucas Stach <l.stach@pengutronix.de>
*
* Partly based on code Copyright (C) 2010 Google, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <common.h>
#include <clock.h>
@@ -146,7 +137,7 @@ struct tegra_i2c_hw_feature {
* @bus_clk_rate: current i2c bus clock rate
*/
struct tegra_i2c_dev {
- struct device_d *dev;
+ struct device *dev;
const struct tegra_i2c_hw_feature *hw;
struct i2c_adapter adapter;
struct clk *div_clk;
@@ -603,7 +594,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
.clk_divisor_std_fast_mode = 0x19,
};
-static int tegra_i2c_probe(struct device_d *dev)
+static int tegra_i2c_probe(struct device *dev)
{
struct resource *iores;
struct tegra_i2c_dev *i2c_dev;
@@ -636,14 +627,14 @@ static int tegra_i2c_probe(struct device_d *dev)
return PTR_ERR(i2c_dev->rst);
}
- ret = of_property_read_u32(dev->device_node, "clock-frequency",
+ ret = of_property_read_u32(dev->of_node, "clock-frequency",
&i2c_dev->bus_clk_rate);
if (ret)
i2c_dev->bus_clk_rate = 100000; /* default clock rate */
i2c_dev->hw = &tegra20_i2c_hw;
dev_get_drvdata(dev, (const void **)&i2c_dev->hw);
- i2c_dev->is_dvc = of_device_is_compatible(dev->device_node,
+ i2c_dev->is_dvc = of_device_is_compatible(dev->of_node,
"nvidia,tegra20-i2c-dvc");
if (!i2c_dev->hw->has_single_clk_source) {
@@ -664,7 +655,7 @@ static int tegra_i2c_probe(struct device_d *dev)
i2c_dev->adapter.master_xfer = tegra_i2c_xfer;
i2c_dev->adapter.dev.parent = dev;
i2c_dev->adapter.nr = dev->id;
- i2c_dev->adapter.dev.device_node = dev->device_node;
+ i2c_dev->adapter.dev.of_node = dev->of_node;
ret = i2c_add_numbered_adapter(&i2c_dev->adapter);
if (ret) {
@@ -692,8 +683,9 @@ static __maybe_unused struct of_device_id tegra_i2c_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, tegra_i2c_compatible);
-static struct driver_d tegra_i2c_driver = {
+static struct driver tegra_i2c_driver = {
.name = "tegra-i2c",
.probe = tegra_i2c_probe,
.of_compatible = DRV_OF_COMPAT(tegra_i2c_compatible),
diff --git a/drivers/i2c/busses/i2c-versatile.c b/drivers/i2c/busses/i2c-versatile.c
index 6a00c2a2eb..f508cf1506 100644
--- a/drivers/i2c/busses/i2c-versatile.c
+++ b/drivers/i2c/busses/i2c-versatile.c
@@ -1,13 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2006 ARM Ltd.
+
/*
- * i2c-versatile.c
- *
- * Copyright (C) 2006 ARM Ltd.
- * written by Russell King, Deep Blue Solutions Ltd.
+ * i2c-versatile.c
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * written by Russell King, Deep Blue Solutions Ltd.
*/
+
#include <common.h>
#include <driver.h>
#include <i2c/i2c.h>
@@ -64,7 +63,7 @@ static struct i2c_algo_bit_data i2c_versatile_algo = {
.timeout_ms = 100,
};
-static int i2c_versatile_probe(struct device_d *dev)
+static int i2c_versatile_probe(struct device *dev)
{
struct resource *iores;
struct i2c_versatile *i2c;
@@ -87,7 +86,7 @@ static int i2c_versatile_probe(struct device_d *dev)
i2c->adap.algo_data = &i2c->algo;
i2c->adap.dev.parent = dev;
- i2c->adap.dev.device_node = dev->device_node;
+ i2c->adap.dev.of_node = dev->of_node;
i2c->algo = i2c_versatile_algo;
i2c->algo.data = i2c;
@@ -107,8 +106,9 @@ static struct of_device_id i2c_versatile_match[] = {
{ .compatible = "arm,versatile-i2c", },
{},
};
+MODULE_DEVICE_TABLE(of, i2c_versatile_match);
-static struct driver_d i2c_versatile_driver = {
+static struct driver i2c_versatile_driver = {
.name = "versatile-i2c",
.probe = i2c_versatile_probe,
.of_compatible = DRV_OF_COMPAT(i2c_versatile_match),
diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c
index ab31da61d6..6695ec0411 100644
--- a/drivers/i2c/i2c-mux.c
+++ b/drivers/i2c/i2c-mux.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Multiplexed I2C bus driver.
*
@@ -16,10 +17,6 @@
* i2c-virt.c from Kumar Gala <galak@kernel.crashing.org>
* i2c-virtual.c from Ken Harrenstien, Copyright (c) 2004 Google, Inc.
* i2c-virtual.c from Brian Kuschak <bkuschak@yahoo.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
*/
#include <common.h>
@@ -34,7 +31,7 @@ struct i2c_mux_priv {
struct i2c_adapter adap;
struct i2c_adapter *parent;
- struct device_d *mux_dev;
+ struct device *mux_dev;
void *mux_priv;
u32 chan_id;
@@ -61,7 +58,7 @@ static int i2c_mux_master_xfer(struct i2c_adapter *adap,
}
struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
- struct device_d *mux_dev,
+ struct device *mux_dev,
void *mux_priv, u32 force_nr, u32 chan_id,
int (*select) (struct i2c_adapter *,
void *, u32),
@@ -98,16 +95,16 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
* Try to populate the mux adapter's device_node, expands to
* nothing if !CONFIG_OFDEVICE.
*/
- if (mux_dev->device_node) {
+ if (mux_dev->of_node) {
struct device_node *child;
u32 reg;
- for_each_child_of_node(mux_dev->device_node, child) {
+ for_each_child_of_node(mux_dev->of_node, child) {
ret = of_property_read_u32(child, "reg", &reg);
if (ret)
continue;
if (chan_id == reg) {
- priv->adap.dev.device_node = child;
+ priv->adap.dev.of_node = child;
break;
}
}
diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c
index e8aeaa7234..ed90f76412 100644
--- a/drivers/i2c/i2c-smbus.c
+++ b/drivers/i2c/i2c-smbus.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#include <common.h>
#include <errno.h>
#include <i2c/i2c.h>
diff --git a/drivers/i2c/i2c.c b/drivers/i2c/i2c.c
index 2fed624d69..5471519045 100644
--- a/drivers/i2c/i2c.c
+++ b/drivers/i2c/i2c.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2009 Marc Kleine-Budde <mkl@pengutronix.de>
*
- * This file is released under the GPLv2
- *
* Derived from:
* - i2c-core.c - a device driver for the iic-bus interface
* Copyright (C) 1995-99 Simon G. Vogl
@@ -24,6 +23,7 @@
#include <init.h>
#include <of.h>
#include <gpio.h>
+#include <slice.h>
#include <i2c/i2c.h>
@@ -63,6 +63,8 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
uint64_t start;
int ret, try;
+ slice_acquire(&adap->slice);
+
/*
* REVISIT the fault reporting model here is weak:
*
@@ -97,6 +99,8 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
break;
}
+ slice_release(&adap->slice);
+
return ret;
}
EXPORT_SYMBOL(i2c_transfer);
@@ -215,7 +219,8 @@ int i2c_write_reg(struct i2c_client *client, u32 addr, const u8 *buf, u16 count)
msgbuf[i++] = addr;
msg->len += i;
- memcpy(msg->buf + i, buf, count);
+ if (count)
+ memcpy(msg->buf + i, buf, count);
status = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
dev_dbg(&client->adapter->dev, "%s: %u@%u --> %d\n", __func__,
@@ -253,7 +258,7 @@ int i2c_get_sda_gpio_value(struct i2c_adapter *adap)
static int i2c_get_gpios_for_recovery(struct i2c_adapter *adap)
{
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
- struct device_d *dev = &adap->dev;
+ struct device *dev = &adap->dev;
int ret = 0;
ret = gpio_request_one(bri->scl_gpio, GPIOF_IN, "i2c-scl");
@@ -353,13 +358,13 @@ int i2c_generic_gpio_recovery(struct i2c_adapter *adap)
int i2c_recover_bus(struct i2c_adapter *adap)
{
if (!adap->bus_recovery_info)
- return -EOPNOTSUPP;
+ return -EBUSY;
dev_dbg(&adap->dev, "Trying i2c bus recovery\n");
return adap->bus_recovery_info->recover_bus(adap);
}
-static void i2c_info(struct device_d *dev)
+static void i2c_info(struct device *dev)
{
const struct i2c_client *client = to_i2c_client(dev);
@@ -394,7 +399,7 @@ static struct i2c_client *i2c_new_device(struct i2c_adapter *adapter,
client->dev.platform_data = chip->platform_data;
client->dev.id = DEVICE_ID_DYNAMIC;
client->dev.bus = &i2c_bus;
- client->dev.device_node = chip->of_node;
+ client->dev.of_node = chip->of_node;
client->adapter = adapter;
client->addr = chip->addr;
@@ -407,6 +412,12 @@ static struct i2c_client *i2c_new_device(struct i2c_adapter *adapter,
}
client->dev.info = i2c_info;
+ if (chip->of_node)
+ chip->of_node->dev = &client->dev;
+
+ dev_dbg(&client->dev, "registered on bus %d, chip->addr 0x%02x\n",
+ adapter->nr, client->addr);
+
return client;
}
@@ -415,37 +426,68 @@ static void of_i2c_register_devices(struct i2c_adapter *adap)
struct device_node *n;
/* Only register child devices if the adapter has a node pointer set */
- if (!IS_ENABLED(CONFIG_OFDEVICE) || !adap->dev.device_node)
+ if (!IS_ENABLED(CONFIG_OFDEVICE) || !adap->dev.of_node)
return;
- for_each_available_child_of_node(adap->dev.device_node, n) {
+ for_each_available_child_of_node(adap->dev.of_node, n) {
struct i2c_board_info info = {};
struct i2c_client *result;
const __be32 *addr;
int len;
+ if (n->dev) {
+ dev_dbg(&adap->dev, "of_i2c: skipping already registered %s\n",
+ dev_name(n->dev));
+ continue;
+ }
+
of_modalias_node(n, info.type, I2C_NAME_SIZE);
info.of_node = n;
addr = of_get_property(n, "reg", &len);
if (!addr || (len < sizeof(int))) {
- dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
- n->full_name);
+ dev_err(&adap->dev, "of_i2c: invalid reg on %pOF\n", n);
continue;
}
info.addr = be32_to_cpup(addr);
if (info.addr > (1 << 10) - 1) {
- dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
- info.addr, n->full_name);
+ dev_err(&adap->dev, "of_i2c: invalid addr=%x on %pOF\n",
+ info.addr, n);
continue;
}
result = i2c_new_device(adap, &info);
if (!result)
- dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
- n->full_name);
+ dev_err(&adap->dev, "of_i2c: Failure registering %pOF\n",
+ n);
+ }
+}
+
+int of_i2c_register_devices_by_node(struct device_node *node)
+{
+ struct i2c_adapter *adap;
+
+ adap = of_find_i2c_adapter_by_node(node);
+ if (!adap)
+ return -ENODEV;
+ if (IS_ERR(adap))
+ return PTR_ERR(adap);
+
+ of_i2c_register_devices(adap);
+ return 0;
+}
+
+static void i2c_hw_rescan(struct device *dev)
+{
+ struct i2c_adapter *adap;
+
+ list_for_each_entry(adap, &i2c_adapter_list, list) {
+ if (dev != adap->dev.parent)
+ continue;
+ of_i2c_register_devices(adap);
+ break;
}
}
@@ -518,7 +560,6 @@ static void scan_boardinfo(struct i2c_adapter *adapter)
continue;
for (n = bi->n_board_info; n > 0; n--, chip++) {
- debug("%s: bus_num: %d, chip->addr 0x%02x\n", __func__, bi->bus_num, chip->addr);
/*
* NOTE: this relies on i2c_new_device to
* issue diagnostics when given bogus inputs
@@ -548,9 +589,14 @@ struct i2c_adapter *i2c_get_adapter(int busnum)
struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node)
{
struct i2c_adapter *adap;
+ int ret;
+
+ ret = of_device_ensure_probed(node);
+ if (ret)
+ return NULL;
for_each_i2c_adapter(adap)
- if (adap->dev.device_node == node)
+ if (adap->dev.of_node == node)
return adap;
return NULL;
@@ -558,7 +604,7 @@ struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node)
struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
{
- struct device_d *dev = of_find_device_by_node(node);
+ struct device *dev = of_find_device_by_node(node);
if (!dev)
return NULL;
@@ -569,6 +615,32 @@ struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
return to_i2c_client(dev);
}
+int of_i2c_device_enable_and_register_by_alias(const char *alias)
+{
+ struct device_node *np;
+
+ np = of_find_node_by_alias(NULL, alias);
+ if (!np)
+ return -ENODEV;
+
+ of_device_enable(np);
+ return of_i2c_register_devices_by_node(np->parent);
+}
+
+
+static void i2c_parse_timing(struct device *dev, char *prop_name,
+ u32 *cur_val_p,
+ u32 def_val, bool use_def)
+{
+ int ret;
+
+ ret = of_property_read_u32(dev->of_node, prop_name, cur_val_p);
+ if (ret && use_def)
+ *cur_val_p = def_val;
+
+ dev_dbg(dev, "%s: %u\n", prop_name, *cur_val_p);
+}
+
/**
* i2c_parse_fw_timings - get I2C related timing parameters from firmware
* @dev: The device to scan for I2C timing properties
@@ -585,47 +657,31 @@ struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
* to switch to this function. New drivers almost always should use the defaults.
*/
-void i2c_parse_fw_timings(struct device_d *dev, struct i2c_timings *t, bool use_defaults)
+void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t,
+ bool use_defaults)
{
- int ret;
-
- memset(t, 0, sizeof(*t));
-
- ret = of_property_read_u32(dev->device_node, "clock-frequency",
- &t->bus_freq_hz);
- if (ret && use_defaults)
- t->bus_freq_hz = 100000;
-
- ret = of_property_read_u32(dev->device_node, "i2c-scl-rising-time-ns",
- &t->scl_rise_ns);
- if (ret && use_defaults) {
- if (t->bus_freq_hz <= 100000)
- t->scl_rise_ns = 1000;
- else if (t->bus_freq_hz <= 400000)
- t->scl_rise_ns = 300;
- else
- t->scl_rise_ns = 120;
- }
-
- ret = of_property_read_u32(dev->device_node, "i2c-scl-falling-time-ns",
- &t->scl_fall_ns);
- if (ret && use_defaults) {
- if (t->bus_freq_hz <= 400000)
- t->scl_fall_ns = 300;
- else
- t->scl_fall_ns = 120;
- }
-
- of_property_read_u32(dev->device_node, "i2c-scl-internal-delay-ns",
- &t->scl_int_delay_ns);
-
- ret = of_property_read_u32(dev->device_node, "i2c-sda-falling-time-ns",
- &t->sda_fall_ns);
- if (ret && use_defaults)
- t->sda_fall_ns = t->scl_fall_ns;
-
- of_property_read_u32(dev->device_node, "i2c-sda-hold-time-ns",
- &t->sda_hold_ns);
+ bool u = use_defaults;
+ u32 d;
+
+ i2c_parse_timing(dev, "clock-frequency", &t->bus_freq_hz,
+ I2C_MAX_STANDARD_MODE_FREQ, u);
+
+ d = t->bus_freq_hz <= I2C_MAX_STANDARD_MODE_FREQ ? 1000 :
+ t->bus_freq_hz <= I2C_MAX_FAST_MODE_FREQ ? 300 : 120;
+ i2c_parse_timing(dev, "i2c-scl-rising-time-ns", &t->scl_rise_ns, d, u);
+
+ d = t->bus_freq_hz <= I2C_MAX_FAST_MODE_FREQ ? 300 : 120;
+ i2c_parse_timing(dev, "i2c-scl-falling-time-ns", &t->scl_fall_ns, d, u);
+
+ i2c_parse_timing(dev, "i2c-scl-internal-delay-ns",
+ &t->scl_int_delay_ns, 0, u);
+ i2c_parse_timing(dev, "i2c-sda-falling-time-ns", &t->sda_fall_ns,
+ t->scl_fall_ns, u);
+ i2c_parse_timing(dev, "i2c-sda-hold-time-ns", &t->sda_hold_ns, 0, u);
+ i2c_parse_timing(dev, "i2c-digital-filter-width-ns",
+ &t->digital_filter_width_ns, 0, u);
+ i2c_parse_timing(dev, "i2c-analog-filter-cutoff-frequency",
+ &t->analog_filter_cutoff_freq_hz, 0, u);
}
EXPORT_SYMBOL_GPL(i2c_parse_fw_timings);
@@ -646,6 +702,7 @@ EXPORT_SYMBOL_GPL(i2c_parse_fw_timings);
*/
int i2c_add_numbered_adapter(struct i2c_adapter *adapter)
{
+ struct device *hw_dev;
int ret;
if (adapter->nr < 0) {
@@ -669,31 +726,26 @@ int i2c_add_numbered_adapter(struct i2c_adapter *adapter)
list_add_tail(&adapter->list, &i2c_adapter_list);
+ slice_init(&adapter->slice, dev_name(&adapter->dev));
+
/* populate children from any i2c device tables */
scan_boardinfo(adapter);
of_i2c_register_devices(adapter);
+ hw_dev = adapter->dev.parent;
+ if (hw_dev && dev_of_node(hw_dev)) {
+ if (!hw_dev->rescan)
+ hw_dev->rescan = i2c_hw_rescan;
+ }
+
return 0;
}
EXPORT_SYMBOL(i2c_add_numbered_adapter);
-static int i2c_probe(struct device_d *dev)
-{
- return dev->driver->probe(dev);
-}
-
-static void i2c_remove(struct device_d *dev)
-{
- if (dev->driver->remove)
- dev->driver->remove(dev);
-}
-
struct bus_type i2c_bus = {
.name = "i2c",
.match = device_match_of_modalias,
- .probe = i2c_probe,
- .remove = i2c_remove,
};
static int i2c_bus_init(void)
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
index 74b73459aa..035a58f757 100644
--- a/drivers/i2c/muxes/Kconfig
+++ b/drivers/i2c/muxes/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Multiplexer I2C chip drivers configuration
#
diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile
index f35d40d0e3..4266fd89f8 100644
--- a/drivers/i2c/muxes/Makefile
+++ b/drivers/i2c/muxes/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for multiplexer I2C chip drivers.
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index aa7dcb8c31..6c21b92860 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* I2C multiplexer
*
@@ -32,10 +33,6 @@
* i2c-virtual_cb.c from Brian Kuschak <bkuschak@yahoo.com>
* and
* pca9540.c from Jean Delvare <jdelvare@suse.de>.
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
*/
#include <common.h>
@@ -183,7 +180,7 @@ static int pca954x_deselect_chan(struct i2c_adapter *adap,
/*
* I2C init/probing/exit functions
*/
-static int pca954x_probe(struct device_d *dev)
+static int pca954x_probe(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
@@ -202,7 +199,7 @@ static int pca954x_probe(struct device_d *dev)
i2c_set_clientdata(client, data);
- gpio = of_get_named_gpio(dev->device_node, "reset-gpios", 0);
+ gpio = of_get_named_gpio(dev->of_node, "reset-gpios", 0);
if (gpio_is_valid(gpio))
gpio_direction_output(gpio, 1);
@@ -210,17 +207,15 @@ static int pca954x_probe(struct device_d *dev)
* that the mux is in fact present. This also
* initializes the mux to disconnected state.
*/
- if (i2c_smbus_write_byte(client, 0) < 0) {
- dev_warn(&client->dev, "probe failed\n");
+ if (i2c_smbus_write_byte(client, 0) < 0)
goto exit_free;
- }
ret = dev_get_drvdata(dev, (const void **)&tmp);
data->type = tmp;
if (ret)
goto exit_free;
- idle_disconnect = of_property_read_bool(dev->device_node,
+ idle_disconnect = of_property_read_bool(dev->of_node,
"i2c-mux-idle-disconnect");
data->last_chan = 0; /* force the first selection */
@@ -260,7 +255,7 @@ err:
return ret;
}
-static struct driver_d pca954x_driver = {
+static struct driver pca954x_driver = {
.name = "pca954x",
.probe = pca954x_probe,
.id_table = pca954x_id,