summaryrefslogtreecommitdiff
path: root/drivers/i2c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/Kconfig175
-rw-r--r--drivers/i2c/Makefile2
-rw-r--r--drivers/i2c/designware_i2c.c44
-rw-r--r--drivers/i2c/designware_i2c_pci.c2
-rw-r--r--drivers/i2c/imx_lpi2c.c4
-rw-r--r--drivers/i2c/k1_i2c.c523
-rw-r--r--drivers/i2c/k1_i2c.h69
-rw-r--r--drivers/i2c/muxes/Kconfig6
-rw-r--r--drivers/i2c/nx_i2c.c9
-rw-r--r--drivers/i2c/soft_i2c.c418
10 files changed, 726 insertions, 526 deletions
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 55465dc1d46..0e8b2408376 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -98,7 +98,7 @@ config SYS_I2C_EARLY_INIT
board_early_init_f.
config I2C_CROS_EC_TUNNEL
- tristate "Chrome OS EC tunnel I2C bus"
+ bool "Chrome OS EC tunnel I2C bus"
depends on CROS_EC
help
This provides an I2C bus that will tunnel i2c commands through to
@@ -110,16 +110,16 @@ config I2C_CROS_EC_TUNNEL
config I2C_CROS_EC_LDO
bool "Provide access to LDOs on the Chrome OS EC"
depends on CROS_EC
- ---help---
- On many Chromebooks the main PMIC is inaccessible to the AP. This is
- often dealt with by using an I2C pass-through interface provided by
- the EC. On some unfortunate models (e.g. Spring) the pass-through
- is not available, and an LDO message is available instead. This
- option enables a driver which provides very basic access to those
- regulators, via the EC. We implement this as an I2C bus which
- emulates just the TPS65090 messages we know about. This is done to
- avoid duplicating the logic in the TPS65090 regulator driver for
- enabling/disabling an LDO.
+ help
+ On many Chromebooks the main PMIC is inaccessible to the AP. This is
+ often dealt with by using an I2C pass-through interface provided by
+ the EC. On some unfortunate models (e.g. Spring) the pass-through
+ is not available, and an LDO message is available instead. This
+ option enables a driver which provides very basic access to those
+ regulators, via the EC. We implement this as an I2C bus which
+ emulates just the TPS65090 messages we know about. This is done to
+ avoid duplicating the logic in the TPS65090 regulator driver for
+ enabling/disabling an LDO.
config I2C_SET_DEFAULT_BUS_NUM
bool "Set default I2C bus number"
@@ -180,9 +180,9 @@ config SYS_I2C_IPROC
Say yes here to to enable the Broadco I2C driver.
config SYS_I2C_FSL
- bool "Freescale I2C bus driver"
- depends on M68K || PPC
- help
+ bool "Freescale I2C bus driver"
+ depends on M68K || PPC
+ help
Add support for Freescale I2C busses as used on MPC8240, MPC8245, and
MPC85xx processors.
@@ -213,14 +213,14 @@ config SYS_FSL_I2C4_OFFSET
endif
config SYS_I2C_CADENCE
- tristate "Cadence I2C Controller"
+ bool "Cadence I2C Controller"
depends on DM_I2C
help
Say yes here to select Cadence I2C Host Controller. This controller is
e.g. used by Xilinx Zynq.
config SYS_I2C_CA
- tristate "Cortina-Access I2C Controller"
+ bool "Cortina-Access I2C Controller"
depends on DM_I2C && CORTINA_PLATFORM
help
Add support for the Cortina Access I2C host controller.
@@ -249,14 +249,14 @@ config SYS_I2C_DW_PCI
controller.
config SYS_I2C_AST2600
- bool "AST2600 I2C Controller"
- depends on DM_I2C && ARCH_ASPEED
- help
- Say yes here to select AST2600 I2C Host Controller. The driver
- support AST2600 I2C new mode register. This I2C controller supports:
- _Standard-mode (up to 100 kHz)
- _Fast-mode (up to 400 kHz)
- _Fast-mode Plus (up to 1 MHz)
+ bool "AST2600 I2C Controller"
+ depends on DM_I2C && ARCH_ASPEED
+ help
+ Say yes here to select AST2600 I2C Host Controller. The driver
+ support AST2600 I2C new mode register. This I2C controller supports:
+ _Standard-mode (up to 100 kHz)
+ _Fast-mode (up to 400 kHz)
+ _Fast-mode Plus (up to 1 MHz)
config SYS_I2C_ASPEED
bool "Aspeed I2C Controller"
@@ -333,50 +333,50 @@ if SYS_I2C_MXC && (SYS_I2C_LEGACY || SPL_SYS_I2C_LEGACY)
config SYS_I2C_MXC_I2C1
bool "NXP MXC I2C1"
help
- Add support for NXP MXC I2C Controller 1.
- Required for SoCs which have I2C MXC controller 1 eg LS1088A, LS2080A
+ Add support for NXP MXC I2C Controller 1.
+ Required for SoCs which have I2C MXC controller 1 eg LS1088A, LS2080A
config SYS_I2C_MXC_I2C2
bool "NXP MXC I2C2"
help
- Add support for NXP MXC I2C Controller 2.
- Required for SoCs which have I2C MXC controller 2 eg LS1088A, LS2080A
+ Add support for NXP MXC I2C Controller 2.
+ Required for SoCs which have I2C MXC controller 2 eg LS1088A, LS2080A
config SYS_I2C_MXC_I2C3
bool "NXP MXC I2C3"
help
- Add support for NXP MXC I2C Controller 3.
- Required for SoCs which have I2C MXC controller 3 eg LS1088A, LS2080A
+ Add support for NXP MXC I2C Controller 3.
+ Required for SoCs which have I2C MXC controller 3 eg LS1088A, LS2080A
config SYS_I2C_MXC_I2C4
bool "NXP MXC I2C4"
help
- Add support for NXP MXC I2C Controller 4.
- Required for SoCs which have I2C MXC controller 4 eg LS1088A, LS2080A
+ Add support for NXP MXC I2C Controller 4.
+ Required for SoCs which have I2C MXC controller 4 eg LS1088A, LS2080A
config SYS_I2C_MXC_I2C5
bool "NXP MXC I2C5"
help
- Add support for NXP MXC I2C Controller 5.
- Required for SoCs which have I2C MXC controller 5 eg LX2160A
+ Add support for NXP MXC I2C Controller 5.
+ Required for SoCs which have I2C MXC controller 5 eg LX2160A
config SYS_I2C_MXC_I2C6
bool "NXP MXC I2C6"
help
- Add support for NXP MXC I2C Controller 6.
- Required for SoCs which have I2C MXC controller 6 eg LX2160A
+ Add support for NXP MXC I2C Controller 6.
+ Required for SoCs which have I2C MXC controller 6 eg LX2160A
config SYS_I2C_MXC_I2C7
bool "NXP MXC I2C7"
help
- Add support for NXP MXC I2C Controller 7.
- Required for SoCs which have I2C MXC controller 7 eg LX2160A
+ Add support for NXP MXC I2C Controller 7.
+ Required for SoCs which have I2C MXC controller 7 eg LX2160A
config SYS_I2C_MXC_I2C8
bool "NXP MXC I2C8"
help
- Add support for NXP MXC I2C Controller 8.
- Required for SoCs which have I2C MXC controller 8 eg LX2160A
+ Add support for NXP MXC I2C Controller 8.
+ Required for SoCs which have I2C MXC controller 8 eg LX2160A
endif
if SYS_I2C_MXC_I2C1
@@ -385,13 +385,13 @@ config SYS_MXC_I2C1_SPEED
default 40000000 if TARGET_LS2080A_EMU
default 100000
help
- MXC I2C Channel 1 speed
+ MXC I2C Channel 1 speed
config SYS_MXC_I2C1_SLAVE
hex "I2C1 Slave"
default 0x0
help
- MXC I2C1 Slave
+ MXC I2C1 Slave
endif
if SYS_I2C_MXC_I2C2
@@ -400,13 +400,13 @@ config SYS_MXC_I2C2_SPEED
default 40000000 if TARGET_LS2080A_EMU
default 100000
help
- MXC I2C Channel 2 speed
+ MXC I2C Channel 2 speed
config SYS_MXC_I2C2_SLAVE
hex "I2C2 Slave"
default 0x0
help
- MXC I2C2 Slave
+ MXC I2C2 Slave
endif
if SYS_I2C_MXC_I2C3
@@ -414,13 +414,13 @@ config SYS_MXC_I2C3_SPEED
int "I2C Channel 3 speed"
default 100000
help
- MXC I2C Channel 3 speed
+ MXC I2C Channel 3 speed
config SYS_MXC_I2C3_SLAVE
hex "I2C3 Slave"
default 0x0
help
- MXC I2C3 Slave
+ MXC I2C3 Slave
endif
if SYS_I2C_MXC_I2C4
@@ -428,13 +428,13 @@ config SYS_MXC_I2C4_SPEED
int "I2C Channel 4 speed"
default 100000
help
- MXC I2C Channel 4 speed
+ MXC I2C Channel 4 speed
config SYS_MXC_I2C4_SLAVE
hex "I2C4 Slave"
default 0x0
help
- MXC I2C4 Slave
+ MXC I2C4 Slave
endif
if SYS_I2C_MXC_I2C5
@@ -442,13 +442,13 @@ config SYS_MXC_I2C5_SPEED
int "I2C Channel 5 speed"
default 100000
help
- MXC I2C Channel 5 speed
+ MXC I2C Channel 5 speed
config SYS_MXC_I2C5_SLAVE
hex "I2C5 Slave"
default 0x0
help
- MXC I2C5 Slave
+ MXC I2C5 Slave
endif
if SYS_I2C_MXC_I2C6
@@ -456,13 +456,13 @@ config SYS_MXC_I2C6_SPEED
int "I2C Channel 6 speed"
default 100000
help
- MXC I2C Channel 6 speed
+ MXC I2C Channel 6 speed
config SYS_MXC_I2C6_SLAVE
hex "I2C6 Slave"
default 0x0
help
- MXC I2C6 Slave
+ MXC I2C6 Slave
endif
if SYS_I2C_MXC_I2C7
@@ -470,13 +470,13 @@ config SYS_MXC_I2C7_SPEED
int "I2C Channel 7 speed"
default 100000
help
- MXC I2C Channel 7 speed
+ MXC I2C Channel 7 speed
config SYS_MXC_I2C7_SLAVE
hex "I2C7 Slave"
default 0x0
help
- MXC I2C7 Slave
+ MXC I2C7 Slave
endif
if SYS_I2C_MXC_I2C8
@@ -484,13 +484,13 @@ config SYS_MXC_I2C8_SPEED
int "I2C Channel 8 speed"
default 100000
help
- MXC I2C Channel 8 speed
+ MXC I2C Channel 8 speed
config SYS_MXC_I2C8_SLAVE
hex "I2C8 Slave"
default 0x0
help
- MXC I2C8 Slave
+ MXC I2C8 Slave
endif
config SYS_I2C_NEXELL
@@ -624,26 +624,6 @@ config SH_I2C_CLOCK
default 104000000
endif
-config SYS_I2C_SOFT
- bool "Legacy software I2C interface"
- depends on !COMPILE_TEST
- help
- Enable the legacy software defined I2C interface
-
-config SYS_I2C_SOFT_SPEED
- int "Software I2C bus speed"
- depends on SYS_I2C_SOFT
- default 100000
- help
- Speed of the software I2C bus
-
-config SYS_I2C_SOFT_SLAVE
- hex "Software I2C slave address"
- depends on SYS_I2C_SOFT
- default 0xfe
- help
- Slave address of the software I2C bus
-
config SYS_I2C_OCTEON
bool "Octeon II/III/TX/TX2 I2C driver"
depends on (ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2) && DM_I2C
@@ -688,19 +668,19 @@ config SYS_I2C_STM32F7
help
Enable this option to add support for STM32 I2C controller
introduced with STM32F7/H7 SoCs. This I2C controller supports :
- _ Slave and master modes
- _ Multimaster capability
- _ Standard-mode (up to 100 kHz)
- _ Fast-mode (up to 400 kHz)
- _ Fast-mode Plus (up to 1 MHz)
- _ 7-bit and 10-bit addressing mode
- _ Multiple 7-bit slave addresses (2 addresses, 1 with configurable mask)
- _ All 7-bit addresses acknowledge mode
- _ General call
- _ Programmable setup and hold times
- _ Easy to use event management
- _ Optional clock stretching
- _ Software reset
+ _ Slave and master modes
+ _ Multimaster capability
+ _ Standard-mode (up to 100 kHz)
+ _ Fast-mode (up to 400 kHz)
+ _ Fast-mode Plus (up to 1 MHz)
+ _ 7-bit and 10-bit addressing mode
+ _ Multiple 7-bit slave addresses (2 addresses, 1 with configurable mask)
+ _ All 7-bit addresses acknowledge mode
+ _ General call
+ _ Programmable setup and hold times
+ _ Easy to use event management
+ _ Optional clock stretching
+ _ Software reset
config SYS_I2C_SUN6I_P2WI
bool "Allwinner sun6i P2WI controller"
@@ -800,7 +780,7 @@ config SYS_I2C_BUS_MAX
int "Max I2C busses"
depends on ARCH_OMAP2PLUS || ARCH_SOCFPGA
default 3 if OMAP34XX || AM33XX || AM43XX
- default 4 if ARCH_SOCFPGA
+ default 4 if ARCH_SOCFPGA || OMAP44XX
default 5 if OMAP54XX
help
Define the maximum number of available I2C buses.
@@ -812,10 +792,17 @@ config SYS_I2C_XILINX_XIIC
Support for Xilinx AXI I2C controller.
config SYS_I2C_IHS
- bool "gdsys IHS I2C driver"
- depends on DM_I2C
- help
- Support for gdsys IHS I2C driver on FPGA bus.
+ bool "gdsys IHS I2C driver"
+ depends on DM_I2C
+ help
+ Support for gdsys IHS I2C driver on FPGA bus.
+
+config SYS_I2C_SPACEMIT_K1
+ bool "Spacemit K1 I2C driver"
+ depends on DM_I2C
+ help
+ Support for Spacemit I2C controller. It's based on
+ Driver Model.
source "drivers/i2c/muxes/Kconfig"
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index 5fe30d0df4f..0e3a0f9fb5f 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -47,7 +47,7 @@ obj-$(CONFIG_SYS_I2C_RZ_RIIC) += rz_riic.o
obj-$(CONFIG_SYS_I2C_S3C24X0) += s3c24x0_i2c.o exynos_hs_i2c.o
obj-$(CONFIG_SYS_I2C_SANDBOX) += sandbox_i2c.o i2c-emul-uclass.o
obj-$(CONFIG_SYS_I2C_SH) += sh_i2c.o
-obj-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o
+obj-$(CONFIG_SYS_I2C_SPACEMIT_K1) += k1_i2c.o
obj-$(CONFIG_SYS_I2C_STM32F7) += stm32f7_i2c.o
obj-$(CONFIG_SYS_I2C_SUN6I_P2WI) += sun6i_p2wi.o
obj-$(CONFIG_SYS_I2C_SUN8I_RSB) += sun8i_rsb.o
diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c
index 8ad716f410e..8d290acefda 100644
--- a/drivers/i2c/designware_i2c.c
+++ b/drivers/i2c/designware_i2c.c
@@ -740,13 +740,47 @@ static int designware_i2c_probe_chip(struct udevice *bus, uint chip_addr,
{
struct dw_i2c *i2c = dev_get_priv(bus);
struct i2c_regs *i2c_base = i2c->regs;
- u32 tmp;
- int ret;
+ u32 start_time, ic_status;
+ int ret = 0;
- /* Try to read the first location of the chip */
- ret = __dw_i2c_read(i2c_base, chip_addr, 0, 1, (uchar *)&tmp, 1);
+ ret = i2c_wait_for_bb(i2c_base);
if (ret)
- __dw_i2c_init(i2c_base, 0, 0);
+ return ret;
+
+ dw_i2c_enable(i2c_base, false);
+ writel(chip_addr, &i2c_base->ic_tar);
+ dw_i2c_enable(i2c_base, true);
+
+ writel(IC_STOP, &i2c_base->ic_cmd_data);
+
+ start_time = get_timer(0);
+ while (1) {
+ ic_status = readl(&i2c_base->ic_status);
+
+ if ((ic_status & IC_STATUS_TFE) && !(ic_status & IC_STATUS_MA))
+ break;
+
+ if (readl(&i2c_base->ic_raw_intr_stat) & IC_TX_ABRT) {
+ readl(&i2c_base->ic_clr_tx_abrt);
+ ret = -EREMOTEIO;
+ break;
+ }
+
+ if (get_timer(start_time) > I2C_BYTE_TO) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+ }
+
+ start_time = get_timer(0);
+ while (1) {
+ if ((readl(&i2c_base->ic_raw_intr_stat) & IC_STOP_DET)) {
+ readl(&i2c_base->ic_clr_stop_det);
+ break;
+ } else if (get_timer(start_time) > I2C_STOPDET_TO) {
+ break;
+ }
+ }
return ret;
}
diff --git a/drivers/i2c/designware_i2c_pci.c b/drivers/i2c/designware_i2c_pci.c
index c21c412231c..db2706fdb6e 100644
--- a/drivers/i2c/designware_i2c_pci.c
+++ b/drivers/i2c/designware_i2c_pci.c
@@ -168,7 +168,7 @@ static int dw_i2c_acpi_fill_ssdt(const struct udevice *dev,
return 0;
}
-struct acpi_ops dw_i2c_acpi_ops = {
+static const struct acpi_ops dw_i2c_acpi_ops = {
.fill_ssdt = dw_i2c_acpi_fill_ssdt,
};
diff --git a/drivers/i2c/imx_lpi2c.c b/drivers/i2c/imx_lpi2c.c
index a309fd6f07c..e2b4fd334ec 100644
--- a/drivers/i2c/imx_lpi2c.c
+++ b/drivers/i2c/imx_lpi2c.c
@@ -239,7 +239,6 @@ static int bus_i2c_stop(struct udevice *bus)
start_time = get_timer(0);
while (1) {
status = readl(&regs->msr);
- result = imx_lpci2c_check_clear_error(regs);
/* stop detect flag */
if (status & LPI2C_MSR_SDF_MASK) {
/* clear stop flag */
@@ -250,10 +249,13 @@ static int bus_i2c_stop(struct udevice *bus)
if (get_timer(start_time) > LPI2C_NACK_TOUT_MS) {
debug("stop timeout\n");
+ result = imx_lpci2c_check_clear_error(regs);
return -ETIMEDOUT;
}
}
+ result = imx_lpci2c_check_clear_error(regs);
+
return result;
}
diff --git a/drivers/i2c/k1_i2c.c b/drivers/i2c/k1_i2c.c
new file mode 100644
index 00000000000..a08ff15803d
--- /dev/null
+++ b/drivers/i2c/k1_i2c.c
@@ -0,0 +1,523 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023-2026 Spacemit, Inc
+ * Copyright (C) 2025-2026 RISCstar Ltd.
+ */
+
+#include <asm/io.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <i2c.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <reset.h>
+#include "k1_i2c.h"
+
+#define ICR_OFFSET 0x00
+#define ISR_OFFSET 0x04
+#define ISAR_OFFSET 0x08
+#define IDBR_OFFSET 0x0c
+#define ILCR_OFFSET 0x10
+#define IWCR_OFFSET 0x14
+#define IRCR_OFFSET 0x18
+#define IBMR_OFFSET 0x1c
+#define WFIFO_OFFSET 0x20
+#define WFIFO_WPTR_OFFSET 0x24
+#define WFIFO_RPTR_OFFSET 0x28
+#define RFIFO_OFFSET 0x2c
+#define RFIFO_WPTR_OFFSET 0x30
+#define RFIFO_RPTR_OFFSET 0x34
+
+/* All transfers are described by this data structure */
+struct k1_i2c_msg {
+ u8 condition;
+ u8 acknack;
+ u8 direction;
+ u8 data;
+};
+
+struct k1_i2c {
+ u32 icr;
+ u32 isr;
+ u32 isar;
+ u32 idbr;
+ u32 ilcr;
+ u32 iwcr;
+ u32 irst_cyc;
+ u32 ibmr;
+};
+
+struct k1_i2c_priv {
+ int id;
+ void __iomem *base;
+ struct clk clk;
+ u32 clk_rate;
+};
+
+/*
+ * i2c_reset: - reset the host controller
+ *
+ */
+static void i2c_reset(void __iomem *base)
+{
+ u32 icr_mode;
+ u32 val;
+
+ /* Save bus mode (standard or fast speed) for later use */
+ icr_mode = readl(base + ICR_OFFSET) & ICR_MODE_MASK;
+ /* disable unit */
+ val = readl(base + ICR_OFFSET);
+ writel(val & ~ICR_IUE, base + ICR_OFFSET);
+ udelay(10);
+ /* reset the unit */
+ val = readl(base + ICR_OFFSET);
+ val |= ICR_UR;
+ writel(val, base + ICR_OFFSET);
+ udelay(100);
+ /* disable unit */
+ val = readl(base + ICR_OFFSET);
+ writel(val & ~ICR_IUE, base + ICR_OFFSET);
+
+ /* set slave address */
+ writel(0x00, base + ISR_OFFSET);
+ /* set control reg values */
+ writel(I2C_ICR_INIT | icr_mode, base + ICR_OFFSET);
+ writel(I2C_ISR_INIT, base + ISR_OFFSET); /* set clear interrupt bits */
+ val = readl(base + ICR_OFFSET);
+ val |= ICR_IUE;
+ writel(val, base + ICR_OFFSET); /* enable unit */
+ udelay(100);
+}
+
+static inline bool is_isr_set_or_clr(unsigned long isr, unsigned long set_mask,
+ unsigned long clr_mask)
+{
+ return ((isr & set_mask) == set_mask) && ((isr & clr_mask) == 0);
+}
+
+/*
+ * i2c_isr_set_cleared: - wait until certain bits of the I2C status register
+ * are set and cleared
+ *
+ * @return: 0 on success or -ETIMEDOUT.
+ */
+static int i2c_isr_set_cleared(void __iomem *base, unsigned long set_mask,
+ unsigned long clr_mask)
+{
+ int cnt = 1000, delay = 10, isr, ret;
+
+ ret = read_poll_timeout(readl, isr,
+ is_isr_set_or_clr(isr, set_mask, clr_mask),
+ delay, delay * cnt, base + ISR_OFFSET);
+ return ret;
+}
+
+/*
+ * i2c_transfer: - Transfer one byte over the i2c bus
+ *
+ * This function can transfer a byte over the i2c bus in both directions.
+ * It is used by the public API functions.
+ *
+ * @return: 0: transfer successful or error code
+ */
+static int i2c_transfer(void __iomem *base, struct k1_i2c_msg *msg)
+{
+ int ret;
+ u32 val;
+
+ if (!msg)
+ goto transfer_error_msg_empty;
+
+ switch (msg->direction) {
+ case I2C_WRITE:
+ /* check if bus is not busy */
+ if (i2c_isr_set_cleared(base, 0, ISR_IBB))
+ goto transfer_error_bus_busy;
+
+ /* start transmission */
+ val = readl(base + ICR_OFFSET);
+ val &= ~ICR_START;
+ writel(val, base + ICR_OFFSET);
+ val = readl(base + ICR_OFFSET);
+ val &= ~ICR_STOP;
+ writel(val, base + ICR_OFFSET);
+ writel(msg->data, base + IDBR_OFFSET);
+ if (msg->condition == I2C_COND_START) {
+ val = readl(base + ICR_OFFSET);
+ val |= ICR_START;
+ writel(val, base + ICR_OFFSET);
+ }
+ if (msg->condition == I2C_COND_STOP) {
+ val = readl(base + ICR_OFFSET);
+ val |= ICR_STOP;
+ writel(val, base + ICR_OFFSET);
+ }
+ if (msg->acknack == I2C_ACKNAK_SENDNAK) {
+ val = readl(base + ICR_OFFSET);
+ val |= ICR_ACKNAK;
+ writel(val, base + ICR_OFFSET);
+ }
+ if (msg->acknack == I2C_ACKNAK_SENDACK) {
+ val = readl(base + ICR_OFFSET);
+ val &= ~ICR_ACKNAK;
+ writel(val, base + ICR_OFFSET);
+ }
+ val = readl(base + ICR_OFFSET);
+ val &= ~ICR_ALDIE;
+ writel(val, base + ICR_OFFSET);
+ val = readl(base + ICR_OFFSET);
+ val |= ICR_TB;
+ writel(val, base + ICR_OFFSET);
+
+ /* transmit register empty? */
+ if (i2c_isr_set_cleared(base, ISR_ITE, 0))
+ goto transfer_error_transmit_timeout;
+
+ /* clear 'transmit empty' state */
+ val = readl(base + ISR_OFFSET);
+ val |= ISR_ITE;
+ writel(val, base + ISR_OFFSET);
+
+ /* wait for ACK from slave */
+ if (msg->acknack == I2C_ACKNAK_WAITACK)
+ if (i2c_isr_set_cleared(base, 0, ISR_ACKNAK))
+ goto transfer_error_ack_missing;
+ break;
+
+ case I2C_READ:
+ /* check if bus is not busy */
+ if (i2c_isr_set_cleared(base, 0, ISR_IBB))
+ goto transfer_error_bus_busy;
+
+ /* start receive */
+ val = readl(base + ICR_OFFSET);
+ val &= ~ICR_START;
+ writel(val, base + ICR_OFFSET);
+ val = readl(base + ICR_OFFSET);
+ val &= ~ICR_STOP;
+ writel(val, base + ICR_OFFSET);
+ if (msg->condition == I2C_COND_START) {
+ val = readl(base + ICR_OFFSET);
+ val |= ICR_START;
+ writel(val, base + ICR_OFFSET);
+ }
+ if (msg->condition == I2C_COND_STOP) {
+ val = readl(base + ICR_OFFSET);
+ val |= ICR_STOP;
+ writel(val, base + ICR_OFFSET);
+ }
+ if (msg->acknack == I2C_ACKNAK_SENDNAK) {
+ val = readl(base + ICR_OFFSET);
+ val |= ICR_ACKNAK;
+ writel(val, base + ICR_OFFSET);
+ }
+ if (msg->acknack == I2C_ACKNAK_SENDACK) {
+ val = readl(base + ICR_OFFSET);
+ val &= ~ICR_ACKNAK;
+ writel(val, base + ICR_OFFSET);
+ }
+ val = readl(base + ICR_OFFSET);
+ val &= ~ICR_ALDIE;
+ writel(val, base + ICR_OFFSET);
+ val = readl(base + ICR_OFFSET);
+ val |= ICR_TB;
+ writel(val, base + ICR_OFFSET);
+
+ /* receive register full? */
+ if (i2c_isr_set_cleared(base, ISR_IRF, 0))
+ goto transfer_error_receive_timeout;
+
+ msg->data = readl(base + IDBR_OFFSET);
+
+ /* clear 'receive empty' state */
+ val = readl(base + ISR_OFFSET);
+ val |= ISR_IRF;
+ writel(val, base + ISR_OFFSET);
+ break;
+ default:
+ goto transfer_error_illegal_param;
+ }
+
+ return 0;
+
+transfer_error_msg_empty:
+ debug("%s: error: 'msg' is empty\n", __func__);
+ ret = -EINVAL;
+ goto i2c_transfer_finish;
+
+transfer_error_transmit_timeout:
+ debug("%s: error: transmit timeout\n", __func__);
+ ret = -ETIMEDOUT;
+ goto i2c_transfer_finish;
+
+transfer_error_ack_missing:
+ debug("%s: error: ACK missing\n", __func__);
+ ret = -EREMOTEIO;
+ goto i2c_transfer_finish;
+
+transfer_error_receive_timeout:
+ debug("%s: error: receive timeout\n", __func__);
+ ret = -ETIMEDOUT;
+ goto i2c_transfer_finish;
+
+transfer_error_illegal_param:
+ debug("%s: error: illegal parameters\n", __func__);
+ ret = -EINVAL;
+ goto i2c_transfer_finish;
+
+transfer_error_bus_busy:
+ debug("%s: error: bus is busy\n", __func__);
+ ret = -EIO;
+ goto i2c_transfer_finish;
+
+i2c_transfer_finish:
+ debug("%s: ISR: 0x%04x\n", __func__, readl(base + ISR_OFFSET));
+ i2c_reset(base);
+ return ret;
+}
+
+static int __i2c_read(void __iomem *base, uchar chip, u8 *addr, int alen,
+ uchar *buffer, int len)
+{
+ struct k1_i2c_msg msg;
+ int ret;
+
+ debug("%s(chip=0x%02x, addr=0x%02x, alen=0x%02x, len=0x%02x)\n",
+ __func__, chip, *addr, alen, len);
+
+ if (len == 0) {
+ pr_err("reading zero byte is invalid\n");
+ return -EINVAL;
+ }
+
+ i2c_reset(base);
+
+ /* dummy chip address write */
+ debug("%s: dummy chip address write\n", __func__);
+ msg.condition = I2C_COND_START;
+ msg.acknack = I2C_ACKNAK_WAITACK;
+ msg.direction = I2C_WRITE;
+ msg.data = (chip << 1);
+ msg.data &= 0xFE;
+ ret = i2c_transfer(base, &msg);
+ if (ret)
+ return ret;
+
+ /*
+ * send memory address bytes;
+ * alen defines how much bytes we have to send.
+ */
+ while (--alen >= 0) {
+ debug("%s: send address byte %02x (alen=%d)\n",
+ __func__, *addr, alen);
+ msg.condition = I2C_COND_NORMAL;
+ msg.acknack = I2C_ACKNAK_WAITACK;
+ msg.direction = I2C_WRITE;
+ msg.data = addr[alen];
+ ret = i2c_transfer(base, &msg);
+ if (ret)
+ return ret;
+ }
+
+ /* start read sequence */
+ debug("%s: start read sequence\n", __func__);
+ msg.condition = I2C_COND_START;
+ msg.acknack = I2C_ACKNAK_WAITACK;
+ msg.direction = I2C_WRITE;
+ msg.data = (chip << 1);
+ msg.data |= 0x01;
+ ret = i2c_transfer(base, &msg);
+ if (ret)
+ return ret;
+
+ /* read bytes; send NACK at last byte */
+ while (len--) {
+ if (len == 0) {
+ msg.condition = I2C_COND_STOP;
+ msg.acknack = I2C_ACKNAK_SENDNAK;
+ } else {
+ msg.condition = I2C_COND_NORMAL;
+ msg.acknack = I2C_ACKNAK_SENDACK;
+ }
+
+ msg.direction = I2C_READ;
+ msg.data = 0x00;
+ ret = i2c_transfer(base, &msg);
+ if (ret)
+ return ret;
+
+ *buffer = msg.data;
+ debug("%s: reading byte (%p)=0x%02x\n",
+ __func__, buffer, *buffer);
+ buffer++;
+ }
+
+ i2c_reset(base);
+
+ return 0;
+}
+
+static int __i2c_write(struct k1_i2c *base, uchar chip, u8 *addr, int alen,
+ uchar *buffer, int len)
+{
+ struct k1_i2c_msg msg;
+ int ret;
+
+ debug("%s(chip=0x%02x, addr=0x%02x, alen=0x%02x, len=0x%02x)\n",
+ __func__, chip, *addr, alen, len);
+
+ i2c_reset(base);
+
+ /* chip address write */
+ debug("%s: chip address write\n", __func__);
+ msg.condition = I2C_COND_START;
+ msg.acknack = I2C_ACKNAK_WAITACK;
+ msg.direction = I2C_WRITE;
+ msg.data = (chip << 1);
+ msg.data &= 0xFE;
+ ret = i2c_transfer(base, &msg);
+ if (ret)
+ return ret;
+
+ /*
+ * send memory address bytes;
+ * alen defines how much bytes we have to send.
+ */
+ while (--alen >= 0) {
+ debug("%s: send address byte %02x (alen=%d)\n",
+ __func__, *addr, alen);
+ msg.condition = I2C_COND_NORMAL;
+ msg.acknack = I2C_ACKNAK_WAITACK;
+ msg.direction = I2C_WRITE;
+ msg.data = addr[alen];
+ ret = i2c_transfer(base, &msg);
+ if (ret)
+ return ret;
+ }
+
+ /* write bytes; send NACK at last byte */
+ while (len--) {
+ debug("%s: writing byte (%p)=0x%02x\n",
+ __func__, buffer, *buffer);
+
+ if (len == 0)
+ msg.condition = I2C_COND_STOP;
+ else
+ msg.condition = I2C_COND_NORMAL;
+
+ msg.acknack = I2C_ACKNAK_WAITACK;
+ msg.direction = I2C_WRITE;
+ msg.data = *(buffer++);
+
+ ret = i2c_transfer(base, &msg);
+ if (ret)
+ return ret;
+ }
+
+ i2c_reset(base);
+
+ return 0;
+}
+
+static int k1_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
+{
+ struct k1_i2c_priv *i2c = dev_get_priv(bus);
+ struct i2c_msg *dmsg, *omsg, dummy;
+
+ memset(&dummy, 0, sizeof(struct i2c_msg));
+
+ /*
+ * We expect either two messages (one with an offset and one with the
+ * actual data) or one message (just data or offset/data combined)
+ */
+ if (nmsgs > 2 || nmsgs == 0) {
+ debug("%s: Only one or two messages are supported.", __func__);
+ return -EINVAL;
+ }
+
+ omsg = nmsgs == 1 ? &dummy : msg;
+ dmsg = nmsgs == 1 ? msg : msg + 1;
+
+ if (dmsg->flags & I2C_M_RD)
+ return __i2c_read(i2c->base, dmsg->addr, omsg->buf,
+ omsg->len, dmsg->buf, dmsg->len);
+ else
+ return __i2c_write(i2c->base, dmsg->addr, omsg->buf,
+ omsg->len, dmsg->buf, dmsg->len);
+}
+
+static int k1_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
+{
+ struct k1_i2c_priv *priv = dev_get_priv(bus);
+ void __iomem *base = priv->base;
+ u32 val;
+
+ if (speed > I2C_SPEED_STANDARD_RATE)
+ val = ICR_FM;
+ else
+ val = ICR_SM;
+ clrsetbits_le32(base + ICR_OFFSET, ICR_MODE_MASK, val);
+
+ return 0;
+}
+
+static int k1_i2c_probe(struct udevice *bus)
+{
+ struct k1_i2c_priv *priv = dev_get_priv(bus);
+ struct reset_ctl reset;
+ int ret;
+
+ priv->id = dev_seq(bus);
+
+ /*
+ * The upstream K1 dts intentionally omits the 'resets' property
+ * on i2c nodes — the apbc clock-enable path performs the
+ * controller reset internally as part of clock gating. Treat the
+ * reset lookup as optional so we work on the kernel-mainline DT.
+ */
+ ret = reset_get_by_index(bus, 0, &reset);
+ if (!ret) {
+ reset_assert(&reset);
+ udelay(10);
+ reset_deassert(&reset);
+ udelay(10);
+ } else if (ret != -ENOENT && ret != -ENODATA) {
+ dev_err(bus, "%s: reset lookup failed (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ ret = clk_get_by_index(bus, 0, &priv->clk);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(&priv->clk);
+ if (ret && ret != -ENOSYS && ret != -EOPNOTSUPP) {
+ debug("%s: failed to enable clock\n", __func__);
+ return ret;
+ }
+ priv->clk_rate = clk_get_rate(&priv->clk);
+
+ priv->base = (void *)devfdt_get_addr_ptr(bus);
+ k1_i2c_set_bus_speed(bus, priv->clk_rate);
+ return 0;
+}
+
+static const struct dm_i2c_ops k1_i2c_ops = {
+ .xfer = k1_i2c_xfer,
+ .set_bus_speed = k1_i2c_set_bus_speed,
+};
+
+static const struct udevice_id k1_i2c_ids[] = {
+ { .compatible = "spacemit,k1-i2c" },
+ { }
+};
+
+U_BOOT_DRIVER(i2c_spacemit) = {
+ .name = "i2c_spacemit",
+ .id = UCLASS_I2C,
+ .of_match = k1_i2c_ids,
+ .probe = k1_i2c_probe,
+ .priv_auto = sizeof(struct k1_i2c_priv),
+ .ops = &k1_i2c_ops,
+};
diff --git a/drivers/i2c/k1_i2c.h b/drivers/i2c/k1_i2c.h
new file mode 100644
index 00000000000..856947aa43b
--- /dev/null
+++ b/drivers/i2c/k1_i2c.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2023-2026 Spacemit Ltd.
+ * Copyright (C) 2025-2026 RISCstar Ltd.
+ */
+
+#ifndef __SPACEMIT_I2C_H
+#define __SPACEMIT_I2C_H
+
+/* Shall the current transfer have a start/stop condition? */
+#define I2C_COND_NORMAL 0
+#define I2C_COND_START 1
+#define I2C_COND_STOP 2
+
+/* Shall the current transfer be ack/nacked or being waited for it? */
+#define I2C_ACKNAK_WAITACK 1
+#define I2C_ACKNAK_SENDACK 2
+#define I2C_ACKNAK_SENDNAK 4
+
+/* Specify who shall transfer the data (master or slave) */
+#define I2C_READ 0
+#define I2C_WRITE 1
+
+#if (CONFIG_SYS_I2C_SPEED == 400000)
+#define I2C_ICR_INIT (ICR_FM | ICR_BEIE | ICR_IRFIE | ICR_ITEIE | \
+ ICR_GCD | ICR_SCLE)
+#else
+#define I2C_ICR_INIT (ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD | \
+ ICR_SCLE)
+#endif
+
+/* ----- Control register bits ---------------------------------------- */
+
+#define ICR_START 0x1 /* start bit */
+#define ICR_STOP 0x2 /* stop bit */
+#define ICR_ACKNAK 0x4 /* send ACK(0) or NAK(1) */
+#define ICR_TB 0x8 /* transfer byte bit */
+#define ICR_MA BIT(12) /* master abort */
+#define ICR_SCLE BIT(13) /* master clock enable, mona SCLEA */
+#define ICR_IUE BIT(14) /* unit enable */
+#define ICR_GCD BIT(21) /* general call disable */
+#define ICR_ITEIE BIT(19) /* enable tx interrupts */
+#define ICR_IRFIE BIT(20) /* enable rx interrupts, mona: DRFIE */
+#define ICR_BEIE BIT(22) /* enable bus error ints */
+#define ICR_SSDIE BIT(24) /* slave STOP detected int enable */
+#define ICR_ALDIE BIT(18) /* enable arbitration interrupt */
+#define ICR_SADIE BIT(23) /* slave address detected int enable */
+#define ICR_UR BIT(10) /* unit reset */
+#define ICR_SM (0x0) /* Standard Mode */
+#define ICR_FM BIT(8) /* Fast Mode */
+#define ICR_MODE_MASK (0x300) /* Mode mask */
+
+/* ----- Status register bits ----------------------------------------- */
+
+#define ISR_RWM BIT(13) /* read/write mode */
+#define ISR_ACKNAK BIT(14) /* ack/nak status */
+#define ISR_UB BIT(15) /* unit busy */
+#define ISR_IBB BIT(16) /* bus busy */
+#define ISR_SSD BIT(24) /* slave stop detected */
+#define ISR_ALD BIT(18) /* arbitration loss detected */
+#define ISR_ITE BIT(19) /* tx buffer empty */
+#define ISR_IRF BIT(20) /* rx buffer full */
+#define ISR_GCAD BIT(21) /* general call address detected */
+#define ISR_SAD BIT(23) /* slave address detected */
+#define ISR_BED BIT(22) /* bus error no ACK/NAK */
+
+#define I2C_ISR_INIT 0x1FDE000
+
+#endif
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
index 3b1220b2105..89a4b82458a 100644
--- a/drivers/i2c/muxes/Kconfig
+++ b/drivers/i2c/muxes/Kconfig
@@ -26,14 +26,14 @@ config I2C_ARB_GPIO_CHALLENGE
a GPIO.
config I2C_MUX_PCA9541
- tristate "NXP PCA9541 I2C Master Selector"
+ bool "NXP PCA9541 I2C Master Selector"
depends on I2C_MUX
help
If you say yes here you get support for the NXP PCA9541
I2C Master Selector.
config I2C_MUX_PCA954x
- tristate "TI PCA954x I2C Mux/switches"
+ bool "TI PCA954x I2C Mux/switches"
depends on I2C_MUX
help
If you say yes here you get support for the TI PCA954x I2C mux/switch
@@ -49,7 +49,7 @@ config I2C_MUX_PCA954x
MAX7356, MAX7357, MAX7358, MAX7367, MAX7368 and MAX7369
config I2C_MUX_GPIO
- tristate "GPIO-based I2C multiplexer"
+ bool "GPIO-based I2C multiplexer"
depends on I2C_MUX && DM_GPIO
select DEVRES
help
diff --git a/drivers/i2c/nx_i2c.c b/drivers/i2c/nx_i2c.c
index 706b7adefe8..5800e79ab70 100644
--- a/drivers/i2c/nx_i2c.c
+++ b/drivers/i2c/nx_i2c.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0+
+
#include <errno.h>
#include <dm.h>
#include <i2c.h>
@@ -230,12 +232,13 @@ static void i2c_process_node(struct udevice *dev)
static int nx_i2c_probe(struct udevice *dev)
{
struct nx_i2c_bus *bus = dev_get_priv(dev);
- fdt_addr_t addr;
+ void __iomem *addr;
/* get regs = i2c base address */
- addr = devfdt_get_addr(dev);
- if (addr == FDT_ADDR_T_NONE)
+ addr = dev_read_addr_ptr(dev);
+ if (!addr)
return -EINVAL;
+
bus->regs = (struct nx_i2c_regs *)addr;
bus->bus_num = dev_seq(dev);
diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c
index 4102375e5b7..e69de29bb2d 100644
--- a/drivers/i2c/soft_i2c.c
+++ b/drivers/i2c/soft_i2c.c
@@ -1,418 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * (C) Copyright 2009
- * Heiko Schocher, DENX Software Engineering, [email protected].
- * Changes for multibus/multiadapter I2C support.
- *
- * (C) Copyright 2001, 2002
- * Wolfgang Denk, DENX Software Engineering, [email protected].
- *
- * This has been changed substantially by Gerald Van Baren, Custom IDEAS,
- * [email protected]. It was heavily influenced by LiMon, written by
- * Neil Russell.
- *
- * NOTE: This driver should be converted to driver model before June 2017.
- * Please see doc/driver-model/i2c-howto.rst for instructions.
- */
-
-#include <config.h>
-#if defined(CONFIG_AT91FAMILY)
-#include <asm/io.h>
-#include <asm/arch/hardware.h>
-#include <asm/arch/at91_pio.h>
-#ifdef CONFIG_ATMEL_LEGACY
-#include <asm/arch/gpio.h>
-#endif
-#endif
-#include <i2c.h>
-#include <linux/delay.h>
-
-#if defined(CONFIG_SOFT_I2C_GPIO_SCL)
-# include <asm/gpio.h>
-
-# ifndef I2C_GPIO_SYNC
-# define I2C_GPIO_SYNC
-# endif
-
-# ifndef I2C_INIT
-# define I2C_INIT \
- do { \
- gpio_request(CONFIG_SOFT_I2C_GPIO_SCL, "soft_i2c"); \
- gpio_request(CONFIG_SOFT_I2C_GPIO_SDA, "soft_i2c"); \
- } while (0)
-# endif
-
-# ifndef I2C_ACTIVE
-# define I2C_ACTIVE do { } while (0)
-# endif
-
-# ifndef I2C_TRISTATE
-# define I2C_TRISTATE do { } while (0)
-# endif
-
-# ifndef I2C_READ
-# define I2C_READ gpio_get_value(CONFIG_SOFT_I2C_GPIO_SDA)
-# endif
-
-# ifndef I2C_SDA
-# define I2C_SDA(bit) \
- do { \
- if (bit) \
- gpio_direction_input(CONFIG_SOFT_I2C_GPIO_SDA); \
- else \
- gpio_direction_output(CONFIG_SOFT_I2C_GPIO_SDA, 0); \
- I2C_GPIO_SYNC; \
- } while (0)
-# endif
-
-# ifndef I2C_SCL
-# define I2C_SCL(bit) \
- do { \
- gpio_direction_output(CONFIG_SOFT_I2C_GPIO_SCL, bit); \
- I2C_GPIO_SYNC; \
- } while (0)
-# endif
-
-# ifndef I2C_DELAY
-# define I2C_DELAY udelay(5) /* 1/4 I2C clock duration */
-# endif
-
-#endif
-
-/* #define DEBUG_I2C */
-
-#ifndef I2C_SOFT_DECLARATIONS
-# define I2C_SOFT_DECLARATIONS
-#endif
-
-/*-----------------------------------------------------------------------
- * Definitions
- */
-#define RETRIES 0
-
-#define I2C_ACK 0 /* PD_SDA level to ack a byte */
-#define I2C_NOACK 1 /* PD_SDA level to noack a byte */
-
-#ifdef DEBUG_I2C
-#define PRINTD(fmt,args...) do { \
- printf (fmt ,##args); \
- } while (0)
-#else
-#define PRINTD(fmt,args...)
-#endif
-
-/*-----------------------------------------------------------------------
- * Local functions
- */
-static void send_reset (void);
-static void send_start (void);
-static void send_stop (void);
-static void send_ack (int);
-static int write_byte (uchar byte);
-static uchar read_byte (int);
-
-/*-----------------------------------------------------------------------
- * Send a reset sequence consisting of 9 clocks with the data signal high
- * to clock any confused device back into an idle state. Also send a
- * <stop> at the end of the sequence for belts & suspenders.
- */
-static void send_reset(void)
-{
- I2C_SOFT_DECLARATIONS /* intentional without ';' */
- int j;
-
- I2C_SCL(1);
- I2C_SDA(1);
-#ifdef I2C_INIT
- I2C_INIT;
-#endif
- I2C_TRISTATE;
- for(j = 0; j < 9; j++) {
- I2C_SCL(0);
- I2C_DELAY;
- I2C_DELAY;
- I2C_SCL(1);
- I2C_DELAY;
- I2C_DELAY;
- }
- send_stop();
- I2C_TRISTATE;
-}
-
-/*-----------------------------------------------------------------------
- * START: High -> Low on SDA while SCL is High
- */
-static void send_start(void)
-{
- I2C_SOFT_DECLARATIONS /* intentional without ';' */
-
- I2C_DELAY;
- I2C_SDA(1);
- I2C_ACTIVE;
- I2C_DELAY;
- I2C_SCL(1);
- I2C_DELAY;
- I2C_SDA(0);
- I2C_DELAY;
-}
-
-/*-----------------------------------------------------------------------
- * STOP: Low -> High on SDA while SCL is High
- */
-static void send_stop(void)
-{
- I2C_SOFT_DECLARATIONS /* intentional without ';' */
-
- I2C_SCL(0);
- I2C_DELAY;
- I2C_SDA(0);
- I2C_ACTIVE;
- I2C_DELAY;
- I2C_SCL(1);
- I2C_DELAY;
- I2C_SDA(1);
- I2C_DELAY;
- I2C_TRISTATE;
-}
-
-/*-----------------------------------------------------------------------
- * ack should be I2C_ACK or I2C_NOACK
- */
-static void send_ack(int ack)
-{
- I2C_SOFT_DECLARATIONS /* intentional without ';' */
-
- I2C_SCL(0);
- I2C_DELAY;
- I2C_ACTIVE;
- I2C_SDA(ack);
- I2C_DELAY;
- I2C_SCL(1);
- I2C_DELAY;
- I2C_DELAY;
- I2C_SCL(0);
- I2C_DELAY;
-}
-
-/*-----------------------------------------------------------------------
- * Send 8 bits and look for an acknowledgement.
- */
-static int write_byte(uchar data)
-{
- I2C_SOFT_DECLARATIONS /* intentional without ';' */
- int j;
- int nack;
-
- I2C_ACTIVE;
- for(j = 0; j < 8; j++) {
- I2C_SCL(0);
- I2C_DELAY;
- I2C_SDA(data & 0x80);
- I2C_DELAY;
- I2C_SCL(1);
- I2C_DELAY;
- I2C_DELAY;
-
- data <<= 1;
- }
-
- /*
- * Look for an <ACK>(negative logic) and return it.
- */
- I2C_SCL(0);
- I2C_DELAY;
- I2C_SDA(1);
- I2C_TRISTATE;
- I2C_DELAY;
- I2C_SCL(1);
- I2C_DELAY;
- I2C_DELAY;
- nack = I2C_READ;
- I2C_SCL(0);
- I2C_DELAY;
- I2C_ACTIVE;
-
- return(nack); /* not a nack is an ack */
-}
-
-/*-----------------------------------------------------------------------
- * if ack == I2C_ACK, ACK the byte so can continue reading, else
- * send I2C_NOACK to end the read.
- */
-static uchar read_byte(int ack)
-{
- I2C_SOFT_DECLARATIONS /* intentional without ';' */
- int data;
- int j;
-
- /*
- * Read 8 bits, MSB first.
- */
- I2C_TRISTATE;
- I2C_SDA(1);
- data = 0;
- for(j = 0; j < 8; j++) {
- I2C_SCL(0);
- I2C_DELAY;
- I2C_SCL(1);
- I2C_DELAY;
- data <<= 1;
- data |= I2C_READ;
- I2C_DELAY;
- }
- send_ack(ack);
-
- return(data);
-}
-
-/*-----------------------------------------------------------------------
- * Initialization
- */
-static void soft_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
-{
- /*
- * WARNING: Do NOT save speed in a static variable: if the
- * I2C routines are called before RAM is initialized (to read
- * the DIMM SPD, for instance), RAM won't be usable and your
- * system will crash.
- */
- send_reset ();
-}
-
-/*-----------------------------------------------------------------------
- * Probe to see if a chip is present. Also good for checking for the
- * completion of EEPROM writes since the chip stops responding until
- * the write completes (typically 10mSec).
- */
-static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t addr)
-{
- int rc;
-
- /*
- * perform 1 byte write transaction with just address byte
- * (fake write)
- */
- send_start();
- rc = write_byte ((addr << 1) | 0);
- send_stop();
-
- return (rc ? 1 : 0);
-}
-
-/*-----------------------------------------------------------------------
- * Read bytes
- */
-static int soft_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
- int alen, uchar *buffer, int len)
-{
- int shift;
- PRINTD("i2c_read: chip %02X addr %02X alen %d buffer %p len %d\n",
- chip, addr, alen, buffer, len);
-
-#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
- /*
- * EEPROM chips that implement "address overflow" are ones
- * like Catalyst 24WC04/08/16 which has 9/10/11 bits of
- * address and the extra bits end up in the "chip address"
- * bit slots. This makes a 24WC08 (1Kbyte) chip look like
- * four 256 byte chips.
- *
- * Note that we consider the length of the address field to
- * still be one byte because the extra address bits are
- * hidden in the chip address.
- */
- chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
-
- PRINTD("i2c_read: fix addr_overflow: chip %02X addr %02X\n",
- chip, addr);
-#endif
-
- /*
- * Do the addressing portion of a write cycle to set the
- * chip's address pointer. If the address length is zero,
- * don't do the normal write cycle to set the address pointer,
- * there is no address pointer in this chip.
- */
- send_start();
- if(alen > 0) {
- if(write_byte(chip << 1)) { /* write cycle */
- send_stop();
- PRINTD("i2c_read, no chip responded %02X\n", chip);
- return(1);
- }
- shift = (alen-1) * 8;
- while(alen-- > 0) {
- if(write_byte(addr >> shift)) {
- PRINTD("i2c_read, address not <ACK>ed\n");
- return(1);
- }
- shift -= 8;
- }
-
- /* Some I2C chips need a stop/start sequence here,
- * other chips don't work with a full stop and need
- * only a start. Default behaviour is to send the
- * stop/start sequence.
- */
-#ifdef CONFIG_SOFT_I2C_READ_REPEATED_START
- send_start();
-#else
- send_stop();
- send_start();
-#endif
- }
- /*
- * Send the chip address again, this time for a read cycle.
- * Then read the data. On the last byte, we do a NACK instead
- * of an ACK(len == 0) to terminate the read.
- */
- write_byte((chip << 1) | 1); /* read cycle */
- while(len-- > 0) {
- *buffer++ = read_byte(len == 0);
- }
- send_stop();
- return(0);
-}
-
-/*-----------------------------------------------------------------------
- * Write bytes
- */
-static int soft_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
- int alen, uchar *buffer, int len)
-{
- int shift, failures = 0;
-
- PRINTD("i2c_write: chip %02X addr %02X alen %d buffer %p len %d\n",
- chip, addr, alen, buffer, len);
-
- send_start();
- if(write_byte(chip << 1)) { /* write cycle */
- send_stop();
- PRINTD("i2c_write, no chip responded %02X\n", chip);
- return(1);
- }
- shift = (alen-1) * 8;
- while(alen-- > 0) {
- if(write_byte(addr >> shift)) {
- PRINTD("i2c_write, address not <ACK>ed\n");
- return(1);
- }
- shift -= 8;
- }
-
- while(len-- > 0) {
- if(write_byte(*buffer++)) {
- failures++;
- }
- }
- send_stop();
- return(failures);
-}
-
-/*
- * Register soft i2c adapters
- */
-U_BOOT_I2C_ADAP_COMPLETE(soft00, soft_i2c_init, soft_i2c_probe,
- soft_i2c_read, soft_i2c_write, NULL,
- CONFIG_SYS_I2C_SOFT_SPEED, CONFIG_SYS_I2C_SOFT_SLAVE,
- 0)