diff options
Diffstat (limited to 'drivers/i2c/busses')
-rw-r--r-- | drivers/i2c/busses/Kconfig | 35 | ||||
-rw-r--r-- | drivers/i2c/busses/Makefile | 6 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-at91.c | 158 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-bcm283x.c | 335 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-cadence.c | 454 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-designware.c | 35 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-efi.c | 292 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-gpio.c | 20 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-imx-early.c | 80 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-imx-lpi2c.c | 555 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-imx.c | 55 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-imx.h | 1 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-mv64xxx.c | 19 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-omap.c | 28 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-rockchip.c | 462 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-stm32.c | 465 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-tegra.c | 26 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-versatile.c | 20 |
18 files changed, 2707 insertions, 339 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 21d2cb21cf..093b12b2ef 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,28 @@ 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. + +config I2C_EFI + bool "EFI I2C Master driver" + depends on EFI_PAYLOAD + help + Say Y here to include support for the EFI I2C Master driver. endmenu diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 7e450ead27..30005c2bf8 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -1,10 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_I2C_AT91) += i2c-at91.o +obj-$(CONFIG_I2C_BCM283X) += i2c-bcm283x.o +obj-$(CONFIG_I2C_EFI) += i2c-efi.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, ®s->interrupt_disable); + + regval = readl(®s->control); + regval &= ~CDNS_I2C_CONTROL_HOLD; + regval |= CDNS_I2C_CONTROL_CLR_FIFO; + writel(regval, ®s->control); + + writel(0xFF, ®s->time_out); + + writel(0, ®s->transfer_size); + + regval = readl(®s->interrupt_status); + writel(regval, ®s->interrupt_status); + + regval = readl(®s->status); + writel(regval, ®s->status); + + writel(0, ®s->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(®s->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, ®s->control); + + do { + uint bytes_to_receive; + u32 isr_status; + u64 start_time; + + isr_status = readl(®s->interrupt_status); + writel(isr_status, ®s->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, ®s->transfer_size); + writel(chip & CDNS_I2C_ADDRESS_MASK, ®s->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(®s->status) & CDNS_I2C_STATUS_RXDV) { + *buf++ = readl(®s->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(®s->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, ®s->control); + + isr_status = readl(®s->interrupt_status); + writel(isr_status, ®s->interrupt_status); + + start_transfer = true; + do { + uint bytes_to_send; + + bytes_to_send = + CDNS_I2C_FIFO_DEPTH - readl(®s->transfer_size); + + if (buf_len < bytes_to_send) + bytes_to_send = buf_len; + + buf_len -= bytes_to_send; + + while (bytes_to_send--) + writel(*buf++, ®s->data); + + if (start_transfer) { + writel(chip & CDNS_I2C_ADDRESS_MASK, ®s->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-efi.c b/drivers/i2c/busses/i2c-efi.c new file mode 100644 index 0000000000..5f6cc0eed2 --- /dev/null +++ b/drivers/i2c/busses/i2c-efi.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * EFI I2C master driver + * + * Copyright (C) 2024 Elrest Solutions Company s.r.o. + * Author: Tomas Marek <tomas.marek@elrest.cz> + */ + +#include <common.h> +#include <i2c/i2c.h> +#include <driver.h> +#include <efi.h> +#include <efi/efi-device.h> +#include <efi/efi-util.h> +#include <linux/kernel.h> + +/* Define EFI I2C transfer control flags */ +#define EFI_I2C_FLAG_READ 0x00000001 + +#define EFI_I2C_ADDRESSING_10_BIT 0x80000000 + +/* The set_bus_frequency() EFI call doesn't work (doesn't alter SPI clock + * frequency) if it's parameter is defined on the stack (observed with + * American Megatrends EFI Revision 5.19) - let's define it globaly. + */ +static unsigned int bus_clock; + +struct efi_i2c_capabilities { + u32 StructureSizeInBytes; + u32 MaximumReceiveBytes; + u32 MaximumTransmitBytes; + u32 MaximumTotalBytes; +}; + +struct efi_i2c_operation { + u32 Flags; + u32 LengthInBytes; + u8 *Buffer; +}; + +struct efi_i2c_request_packet { + unsigned int OperationCount; + struct efi_i2c_operation Operation[]; +}; + +struct efi_i2c_master_protocol { + efi_status_t(EFIAPI * set_bus_frequency)( + struct efi_i2c_master_protocol *this, + unsigned int *bus_clock + ); + efi_status_t(EFIAPI * reset)( + struct efi_i2c_master_protocol *this + ); + efi_status_t(EFIAPI * start_request)( + struct efi_i2c_master_protocol *this, + unsigned int slave_address, + struct efi_i2c_request_packet *request_packet, + void *event, + efi_status_t *status + ); + struct efi_i2c_capabilities *capabilities; +}; + +struct efi_i2c_priv { + struct efi_i2c_master_protocol *efi_protocol; + struct i2c_adapter adapter; +}; + +static inline struct efi_i2c_priv * +adapter_to_efi_i2c_priv(struct i2c_adapter *a) +{ + return container_of(a, struct efi_i2c_priv, adapter); +} + +static efi_status_t efi_i2c_request( + struct efi_i2c_request_packet *request, + const struct efi_i2c_priv *i2c_priv, + const unsigned int slave_address) +{ + const struct device *dev = &i2c_priv->adapter.dev; + efi_status_t efiret; + + efiret = i2c_priv->efi_protocol->start_request( + i2c_priv->efi_protocol, + slave_address, + request, + NULL, + NULL + ); + + if (EFI_ERROR(efiret)) + dev_err(dev, "I2C operation failed - %s (%lx)\n", + efi_strerror(efiret), -efiret); + + return efiret; +} + +static u32 efi_i2c_max_len(const struct efi_i2c_priv *i2c_priv, + const struct i2c_msg *msg) +{ + const struct efi_i2c_capabilities *capabilities = + i2c_priv->efi_protocol->capabilities; + + if (msg->flags & I2C_M_RD) + return capabilities->MaximumReceiveBytes; + else + return capabilities->MaximumTransmitBytes; +} + +static unsigned int efi_i2c_msg_op_cnt(const struct efi_i2c_priv *i2c_priv, + const struct i2c_msg *msg) +{ + unsigned int max_len; + + max_len = efi_i2c_max_len(i2c_priv, msg); + + return ((u64)msg->len + max_len - 1) / max_len; +} + +static unsigned int efi_i2c_req_op_cnt( + const struct efi_i2c_priv *i2c_priv, + const struct i2c_msg *msg, + const int nmsgs) +{ + unsigned int op_cnt = 0; + int i; + + for (i = nmsgs; i > 0; i--, msg++) + op_cnt += efi_i2c_msg_op_cnt(i2c_priv, msg); + + return op_cnt; +} + +static void i2c_msg_to_efi_op( + const struct efi_i2c_priv *i2c_priv, + const struct i2c_msg *msg, + struct efi_i2c_operation **op) +{ + unsigned int max_len = efi_i2c_max_len(i2c_priv, msg); + unsigned int remaining = msg->len; + u32 flags; + + flags = (msg->flags & I2C_M_RD) ? EFI_I2C_FLAG_READ : 0; + + do { + unsigned int len = min(remaining, max_len); + + (*op)->Flags = flags; + (*op)->LengthInBytes = len; + (*op)->Buffer = msg->buf + (msg->len - remaining); + (*op)++; + + remaining -= len; + } while (remaining > 0); +} + +static int i2c_msgs_to_efi_transaction(struct i2c_adapter *adapter, + struct efi_i2c_operation *op, + const struct i2c_msg *msg, + const int nmsgs) +{ + struct efi_i2c_priv *i2c_priv = adapter_to_efi_i2c_priv(adapter); + const struct i2c_msg *msg_tmp; + int ret = 0; + int i; + + msg_tmp = msg; + for (i = nmsgs; i > 0; i--, msg_tmp++) { + if (msg_tmp->flags & I2C_M_DATA_ONLY) { + ret = -ENOTSUPP; + break; + } + + if (i > 0 && msg_tmp->addr != msg->addr) { + dev_err(&adapter->dev, "Different I2C addresses in one request not supported!\n"); + ret = -ENOTSUPP; + break; + } + + i2c_msg_to_efi_op(i2c_priv, msg_tmp, &op); + } + + return ret; +} + +static int efi_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg *msg, int nmsgs) +{ + struct efi_i2c_priv *i2c_priv = adapter_to_efi_i2c_priv(adapter); + struct efi_i2c_request_packet *request_packet; + unsigned int slave_address; + efi_status_t efiret; + unsigned int op_cnt; + int ret = nmsgs; + int len; + + op_cnt = efi_i2c_req_op_cnt(i2c_priv, msg, nmsgs); + + len = sizeof(*request_packet) + op_cnt * sizeof(struct efi_i2c_operation); + request_packet = malloc(len); + if (!request_packet) + return -ENOMEM; + + request_packet->OperationCount = op_cnt; + ret = i2c_msgs_to_efi_transaction(adapter, request_packet->Operation, + msg, nmsgs); + if (ret) + goto out_free; + + slave_address = msg->addr; + if (msg->flags & I2C_M_TEN) + slave_address |= EFI_I2C_ADDRESSING_10_BIT; + + efiret = efi_i2c_request(request_packet, i2c_priv, slave_address); + if (EFI_ERROR(efiret)) { + ret = -efi_errno(efiret); + goto out_free; + } + + ret = nmsgs; + +out_free: + free(request_packet); + + return ret; +} + +static int efi_i2c_probe(struct efi_device *efidev) +{ + struct i2c_platform_data *pdata; + struct efi_i2c_priv *efi_i2c; + struct i2c_timings timings; + efi_status_t efiret; + int ret; + + efi_i2c = xzalloc(sizeof(*efi_i2c)); + + efi_i2c->efi_protocol = efidev->protocol; + + efi_i2c->adapter.master_xfer = efi_i2c_xfer; + efi_i2c->adapter.nr = efidev->dev.id; + efi_i2c->adapter.dev.parent = &efidev->dev; + efi_i2c->adapter.dev.of_node = efidev->dev.of_node; + + i2c_parse_fw_timings(&efidev->dev, &timings, true); + + pdata = efidev->dev.platform_data; + if (pdata && pdata->bitrate) + timings.bus_freq_hz = pdata->bitrate; + + efiret = efi_i2c->efi_protocol->reset(efi_i2c->efi_protocol); + if (EFI_ERROR(efiret)) { + dev_err(&efidev->dev, "controller reset failed - %ld\n", + efiret); + ret = -efi_errno(efiret); + goto out_free; + } + + bus_clock = timings.bus_freq_hz; + efiret = efi_i2c->efi_protocol->set_bus_frequency( + efi_i2c->efi_protocol, + &bus_clock); + if (EFI_ERROR(efiret)) { + dev_err(&efidev->dev, "I2C clock frequency %u update failed - %s (%lx)\n", + timings.bus_freq_hz, efi_strerror(efiret), -efiret); + ret = -efi_errno(efiret); + goto out_free; + } + + dev_dbg(&efidev->dev, "I2C clock frequency %u\n", bus_clock); + + ret = i2c_add_numbered_adapter(&efi_i2c->adapter); + +out_free: + if (ret < 0) + kfree(efi_i2c); + + return ret; +} + +static struct efi_driver efi_i2c_driver = { + .driver = { + .name = "efi-i2c", + }, + .probe = efi_i2c_probe, + .guid = EFI_I2C_MASTER_PROTOCOL_GUID +}; +device_efi_driver(efi_i2c_driver); + +MODULE_AUTHOR("Tomas Marek <tomas.marek@elrest.cz>"); +MODULE_DESCRIPTION("EFI I2C master driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index 5ab43fe935..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> @@ -94,8 +91,8 @@ static int of_i2c_gpio_probe(struct device_node *np, if (!IS_ENABLED(CONFIG_OFDEVICE)) return -ENODEV; - pdata->sda_pin = of_get_named_gpio_flags(np, "sda-gpios", 0, NULL); - pdata->scl_pin = of_get_named_gpio_flags(np, "scl-gpios", 0, NULL); + pdata->sda_pin = of_get_named_gpio(np, "sda-gpios", 0); + pdata->scl_pin = of_get_named_gpio(np, "scl-gpios", 0); if ((!gpio_is_valid(pdata->sda_pin) || !gpio_is_valid(pdata->scl_pin)) && (of_gpio_count(np) >= 2)) { @@ -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 d67226441e..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; @@ -282,7 +294,7 @@ fail0: i2c_fsl_stop(fsl_i2c); /* Disable I2C controller, and force our state to stopped */ - temp = fsl_i2c->i2cr_ien_opcode ^ I2CR_IEN, + temp = fsl_i2c->i2cr_ien_opcode ^ I2CR_IEN; fsl_i2c_write_reg(temp, fsl_i2c, FSL_I2C_I2CR); return (ret < 0) ? ret : num; @@ -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,5 +318,35 @@ 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; +} + +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; + 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 = 0x0f; + + 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 6911f803b2..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 @@ -511,7 +503,7 @@ fail0: i2c_fsl_stop(adapter); /* Disable I2C controller, and force our state to stopped */ - temp = i2c_fsl->hwdata->i2cr_ien_opcode ^ I2CR_IEN, + temp = i2c_fsl->hwdata->i2cr_ien_opcode ^ I2CR_IEN; fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR); return (result < 0) ? result : num; @@ -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 bdb34ca1b4..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; } @@ -1176,10 +1167,10 @@ i2c_omap_probe(struct device_d *pdev) omap_i2c_idle(i2c_omap); - i2c_omap->adapter.master_xfer = omap_i2c_xfer, + 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(®s->con)); + dev_dbg(dev, "i2c_clkdiv: 0x%08x\n", readl(®s->clkdiv)); + dev_dbg(dev, "i2c_mrxaddr: 0x%08x\n", readl(®s->mrxaddr)); + dev_dbg(dev, "i2c_mrxraddR: 0x%08x\n", readl(®s->mrxraddr)); + dev_dbg(dev, "i2c_mtxcnt: 0x%08x\n", readl(®s->mtxcnt)); + dev_dbg(dev, "i2c_mrxcnt: 0x%08x\n", readl(®s->mrxcnt)); + dev_dbg(dev, "i2c_ien: 0x%08x\n", readl(®s->ien)); + dev_dbg(dev, "i2c_ipd: 0x%08x\n", readl(®s->ipd)); + dev_dbg(dev, "i2c_fcnt: 0x%08x\n", readl(®s->fcnt)); + + for (i = 0; i < 8; i++) + dev_dbg(dev, "i2c_txdata%d: 0x%08x\n", i, readl(®s->txdata[i])); + for (i = 0; i < 8; i++) + dev_dbg(dev, "i2c_rxdata%d: 0x%08x\n", i, readl(®s->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, ®s->ipd); + + writel(I2C_CON_EN | I2C_CON_START, ®s->con); + writel(I2C_STARTIEN, ®s->ien); + + err = readl_poll_timeout(®s->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, ®s->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, ®s->ipd); + + writel(I2C_CON_EN | I2C_CON_STOP, ®s->con); + writel(I2C_CON_STOP, ®s->ien); + + err = readl_poll_timeout(®s->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, ®s->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), ®s->mrxaddr); + if (r_len == 0) { + writel(0, ®s->mrxraddr); + } else if (r_len < 4) { + writel(I2C_MRXRADDR_SET(r_len, reg), ®s->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, ®s->con); + writel(bytes_xferred, ®s->mrxcnt); + writel(I2C_MBRFIEN | I2C_NAKRCVIEN, ®s->ien); + + err = readl_poll_timeout(®s->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, ®s->ipd); + err = -EREMOTEIO; + goto i2c_exit; + } + + writel(I2C_MBRFIPD, ®s->ipd); + + for (i = 0; i < words_xferred; i++) { + rxdata = readl(®s->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, ®s->txdata[i]); + dev_dbg(dev, "I2c Write TXDATA[%d] = 0x%08x\n", i, txdata); + } + + writel(I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TX), ®s->con); + writel(bytes_xferred, ®s->mtxcnt); + writel(I2C_MBTFIEN | I2C_NAKRCVIEN, ®s->ien); + + err = readl_poll_timeout(®s->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, ®s->ipd); + err = -EREMOTEIO; + goto i2c_exit; + } + + writel(I2C_MBTFIPD, ®s->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 6af55fb3ff..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, +}; + +static const struct stm32_i2c_data stm32mp15_data = { + .fmp_clr_offset = 0x40, +}; + +static const struct stm32_i2c_data stm32mp13_data = { + .fmp_clr_offset = 0x4, }; -#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 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(®s->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(®s->cr2); @@ -302,7 +338,7 @@ static void stm32_i2c_handle_reload(struct stm32_i2c *i2c_priv, writel(cr2, ®s->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(®s->icr, STM32_I2C_ICR_STOPCF); /* Clear control register 2 */ - setbits_le32(®s->cr2, STM32_I2C_CR2_RESET_MASK); + clrbits_le32(®s->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(®s->rxdr); - len--; + *msg->buf++ = readb(®s->rxdr); + msg->len--; bytes_to_rw--; } if (status & STM32_I2C_ISR_TXIS) { - writeb(*buf++, ®s->txdr); - len--; + writeb(*msg->buf++, ®s->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(®s->cr2, mask); + /* End of transfer, send stop condition if appropriate */ + if (!ret && !(status & (STM32_I2C_ISR_NACKF | STM32_I2C_ISR_ERRORS))) + setbits_le32(®s->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(®s->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,45 +838,74 @@ static int stm32_i2c_hw_config(struct stm32_i2c *i2c_priv, clrbits_le32(®s->cr1, STM32_I2C_CR1_ANFOFF); else setbits_le32(®s->cr1, STM32_I2C_CR1_ANFOFF); + + /* Program the Digital Filter */ + clrsetbits_le32(®s->cr1, STM32_I2C_CR1_DNF_MASK, + STM32_I2C_CR1_DNF(i2c_priv->setup.dnf)); + setbits_le32(®s->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; - struct reset_control *rst; - 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); @@ -799,19 +913,20 @@ static int __init stm32_i2c_probe(struct device_d *dev) return PTR_ERR(stm32_i2c->clk); clk_enable(stm32_i2c->clk); - rst = reset_control_get(dev, NULL); - if (IS_ERR(rst)) - return PTR_ERR(rst); + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); - reset_control_assert(rst); - udelay(2); - reset_control_deassert(rst); + stm32_i2c->regs = IOMEM(iores->start); - ret = dev_get_drvdata(dev, (const void **)&setup); + ret = device_reset_us(dev, 2); + if (ret) + return ret; + + 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 */ @@ -828,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; @@ -845,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 d56c0def65..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; @@ -632,18 +623,18 @@ static int tegra_i2c_probe(struct device_d *dev) i2c_dev->rst = reset_control_get(dev, "i2c"); if (IS_ERR(i2c_dev->rst)) { - dev_err(dev, "missing controller reset"); + dev_err(dev, "invalid controller reset"); 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), |