On Sat, 31 Oct 2026 at 11:30, Giàu Nguyễn Anh <[email protected]> wrote: > > Hi Peter, > > Thanks for taking a look and for the heads-up on the defconfig! > > To answer your question: I am testing this exclusively on a Raspberry Pi 4 > Model B (BCM2711), as it is the only hardware generation I currently have on > hand. > The specific I2C device I am using for testing is a UPS HAT equipped with an > INA219 power monitor/current sensor. I use it to query battery voltage and > current. > The new I2C driver handles the read/write transactions with the INA219 sensor > flawlessly. > > Here is a quick log of the testing procedure from the U-Boot shell: > => i2c dev 1 > => i2c probe 0x42 > # Prepare calibration and config data in RAM > => mw.b 0x01000000 0x39 1; mw.b 0x01000001 0x9f 1 > => mw.b 0x01000002 0x10 1; mw.b 0x01000003 0x00 1 > # Write configurations to INA219 > => i2c write 0x01000000 0x42 0.1 2 -s > => i2c write 0x01000002 0x42 5.1 2 -s > # Read current register (0x04) > => i2c read 0x42 4.1 2 0x01000000 > > Regarding `rpi_arm64_defconfig`: Understood. Would you like me to send a v4 > to enable CONFIG_SYS_I2C_BCM2835 in `rpi_arm64_defconfig` (and potentially > others), or should I wait for you to finish your testing across other Pi > generations so I can incorporate all feedback into v4 at once?
Please add it in for the next version, but also split the change to the configs out to a second patch. The typical rule of thumb is wait a few days to allow for further feedback before sending the next revision so we don't end up with lots of revs. > Looking forward to your test results. > > Best regards, > Giau Nguyen > -------------------------------------------------------- > Phone: (+84) 387 915 996 > > > On Sat, Oct 31, 2026 at 4:32 PM Peter Robinson <[email protected]> wrote: >> >> Hi Giau, >> >> > Mainline U-Boot currently lacks an I2C controller driver for the >> > Broadcom BCM2711 SoC (Raspberry Pi 4). >> >> May I ask what i2c devices you're using to test this on U-Boot and on >> which generation of device? >> >> > This commit adds a new DM-compliant I2C driver for Broadcom >> > BCM2835/BCM2711 SoCs based on the Linux kernel's i2c-bcm2835.c. >> > (based on Linux commit 32a0a94aa09e ("i2c: Switch back to struct >> > platform_driver::remove()")). >> > >> > The driver dynamically queries the actual VPU core clock rate from >> > the VideoCore firmware via mailbox property messages to calculate >> > the CDIV clock divider, ensuring accurate I2C timing and baud rates >> > on BCM2711. >> > >> > Enable CONFIG_SYS_I2C_BCM2835 in rpi_4_defconfig. >> >> Typically the main config used is rpi_arm64_defconfig which covers all >> 64bit capable generations (3/4/5/Zero2). >> >> I'll test/review in the next few days for a RP >> >> > Reviewed-by: Heiko Schocher <[email protected]> >> > Signed-off-by: Giau Nguyen <[email protected]> >> > --- >> > v3: >> > - Added the Linux commit ID to the commit message. >> > - Fixed alphabetical sorting of CONFIG_SYS_I2C_BCM2835 in >> > drivers/i2c/Makefile. >> > - Added Reviewed-by tag from Heiko. >> > >> > v2: >> > - Clarified commit message: U-Boot mainline currently lacks BCM2711 I2C >> > driver. >> > - Fixed line endings and code formatting per checkpatch.pl. >> > - Converted register offsets to 'struct bcm2835_i2c_regs' abstraction. >> > - Removed 'bcm2835_i2c_readl/writel' wrappers in favor of standard >> > readl/writel. >> > - Moved mailbox clock querying to bcm2835_get_clock_rate() in >> > mach-bcm283x. >> > - Enabled CONFIG_SYS_I2C_BCM2835 in rpi_4_defconfig. >> > - Sorted drivers/i2c/Kconfig and Makefile alphabetically. >> > - Removed 'Assisted by AI' tag. >> > >> > MAINTAINERS | 1 + >> > arch/arm/mach-bcm283x/include/mach/msg.h | 9 + >> > arch/arm/mach-bcm283x/msg.c | 22 ++ >> > configs/rpi_4_defconfig | 4 + >> > drivers/i2c/Kconfig | 10 + >> > drivers/i2c/Makefile | 1 + >> > drivers/i2c/i2c-bcm2835.c | 370 +++++++++++++++++++++++ >> > 7 files changed, 417 insertions(+) >> > create mode 100644 drivers/i2c/i2c-bcm2835.c >> > >> > diff --git a/MAINTAINERS b/MAINTAINERS >> > index 8c6c0c2a4bc..3260024a736 100644 >> > --- a/MAINTAINERS >> > +++ b/MAINTAINERS >> > @@ -215,6 +215,7 @@ F: arch/arm/dts/bcm283* >> > F: arch/arm/mach-bcm283x/ >> > F: board/raspberrypi/ >> > F: drivers/gpio/bcm2835_gpio.c >> > +F: drivers/i2c/i2c-bcm2835.c >> > F: drivers/mmc/bcm2835_sdhci.c >> > F: drivers/mmc/bcm2835_sdhost.c >> > F: drivers/serial/serial_bcm283x_mu.c >> > diff --git a/arch/arm/mach-bcm283x/include/mach/msg.h >> > b/arch/arm/mach-bcm283x/include/mach/msg.h >> > index e54da86e353..d6adec07ae2 100644 >> > --- a/arch/arm/mach-bcm283x/include/mach/msg.h >> > +++ b/arch/arm/mach-bcm283x/include/mach/msg.h >> > @@ -22,6 +22,15 @@ int bcm2835_power_on_module(u32 module); >> > */ >> > int bcm2835_get_mmc_clock(u32 clock_id); >> > >> > +/** >> > + * bcm2835_get_clock_rate() - get the current rate of a clock >> > + * >> > + * @clock_id: ID of clock to get frequency for (BCM2835_MBOX_CLOCK_ID_...) >> > + * @rate: Returns the clock rate in Hz >> > + * Return: 0 if OK, -EIO or -EINVAL on error >> > + */ >> > +int bcm2835_get_clock_rate(u32 clock_id, ulong *rate); >> > + >> > /** >> > * bcm2835_set_sdhost_clock() - determine if firmware controls sdhost cdiv >> > * >> > diff --git a/arch/arm/mach-bcm283x/msg.c b/arch/arm/mach-bcm283x/msg.c >> > index 4993c0bdb81..74f847c5e54 100644 >> > --- a/arch/arm/mach-bcm283x/msg.c >> > +++ b/arch/arm/mach-bcm283x/msg.c >> > @@ -115,6 +115,28 @@ int bcm2835_get_mmc_clock(u32 clock_id) >> > return clock_rate; >> > } >> > >> > +int bcm2835_get_clock_rate(u32 clock_id, ulong *rate) >> > +{ >> > + ALLOC_CACHE_ALIGN_BUFFER(struct msg_get_clock_rate, msg_clk, 1); >> > + int ret; >> > + >> > + BCM2835_MBOX_INIT_HDR(msg_clk); >> > + BCM2835_MBOX_INIT_TAG(&msg_clk->get_clock_rate, GET_CLOCK_RATE); >> > + msg_clk->get_clock_rate.body.req.clock_id = clock_id; >> > + >> > + ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, >> > &msg_clk->hdr); >> > + if (ret) { >> > + printf("bcm2835: Could not query clock rate for %u\n", >> > clock_id); >> > + return -EIO; >> > + } >> > + >> > + *rate = msg_clk->get_clock_rate.body.resp.rate_hz; >> > + if (!*rate) >> > + return -EINVAL; >> > + >> > + return 0; >> > +} >> > + >> > int bcm2835_set_sdhost_clock(u32 rate_hz, u32 *rate_1, u32 *rate_2) >> > { >> > ALLOC_CACHE_ALIGN_BUFFER(struct msg_set_sdhost_clock, >> > msg_sdhost_clk, 1); >> > diff --git a/configs/rpi_4_defconfig b/configs/rpi_4_defconfig >> > index f5fb322aa8f..2ddaf003558 100644 >> > --- a/configs/rpi_4_defconfig >> > +++ b/configs/rpi_4_defconfig >> > @@ -22,6 +22,7 @@ CONFIG_MISC_INIT_R=y >> > CONFIG_SYS_PROMPT="U-Boot> " >> > CONFIG_CMD_DFU=y >> > CONFIG_CMD_GPIO=y >> > +CONFIG_CMD_I2C=y >> > CONFIG_CMD_MMC=y >> > CONFIG_CMD_PCI=y >> > CONFIG_CMD_USB=y >> > @@ -34,6 +35,9 @@ CONFIG_DM_DMA=y >> > CONFIG_DFU_MMC=y >> > CONFIG_SYS_DFU_DATA_BUF_SIZE=0x100000 >> > CONFIG_SYS_DFU_MAX_FILE_SIZE=0x200000 >> > +CONFIG_I2C=y >> > +CONFIG_DM_I2C=y >> > +CONFIG_SYS_I2C_BCM2835=y >> > CONFIG_BCM2835_GPIO=y >> > CONFIG_MMC_SDHCI=y >> > CONFIG_MMC_SDHCI_SDMA=y >> > diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig >> > index 52067fa7c1f..1b26906e403 100644 >> > --- a/drivers/i2c/Kconfig >> > +++ b/drivers/i2c/Kconfig >> > @@ -164,6 +164,16 @@ config SYS_I2C_AT91 >> > i2c-gpio driver unless your system can cope with this limitation. >> > Binding info: doc/device-tree-bindings/i2c/i2c-at91.txt >> > >> > +config SYS_I2C_BCM2835 >> > + bool "BCM2835 I2C controller" >> > + depends on DM_I2C >> > + help >> > + Enable support for the Broadcom BCM2835/BCM2711 I2C controller. >> > + This driver provides standard I2C support for Raspberry Pi >> > + devices, including the newer Raspberry Pi 4. It dynamically >> > + calculates the clock divider based on the VPU core clock >> > + queried via the firmware mailbox. >> > + >> > config SYS_I2C_IPROC >> > bool "Broadcom I2C driver" >> > depends on DM_I2C >> > diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile >> > index bebd728e7da..aac6ce4b6e0 100644 >> > --- a/drivers/i2c/Makefile >> > +++ b/drivers/i2c/Makefile >> > @@ -14,6 +14,7 @@ obj-$(CONFIG_$(XPL_)SYS_I2C_LEGACY) += i2c_core.o >> > obj-$(CONFIG_SYS_I2C_ASPEED) += ast_i2c.o >> > obj-$(CONFIG_SYS_I2C_AST2600) += ast2600_i2c.o >> > obj-$(CONFIG_SYS_I2C_AT91) += at91_i2c.o >> > +obj-$(CONFIG_SYS_I2C_BCM2835) += i2c-bcm2835.o >> > obj-$(CONFIG_SYS_I2C_CADENCE) += i2c-cdns.o >> > obj-$(CONFIG_SYS_I2C_CA) += i2c-cortina.o >> > obj-$(CONFIG_SYS_I2C_DAVINCI) += davinci_i2c.o >> > diff --git a/drivers/i2c/i2c-bcm2835.c b/drivers/i2c/i2c-bcm2835.c >> > new file mode 100644 >> > index 00000000000..e6abb076bdb >> > --- /dev/null >> > +++ b/drivers/i2c/i2c-bcm2835.c >> > @@ -0,0 +1,370 @@ >> > +// SPDX-License-Identifier: GPL-2.0 >> > +/* >> > + * BCM2835 I2C controller driver >> > + * (C) Copyright 2026 >> > + * >> > + * Author: Giau Nguyen <[email protected]> >> > + * Based on Linux driver: drivers/i2c/busses/i2c-bcm2835.c >> > + */ >> > + >> > +#include <log.h> >> > +#include <asm/io.h> >> > +#include <asm/arch/mbox.h> >> > +#include <asm/arch/msg.h> >> > +#include <dm.h> >> > +#include <dm/device_compat.h> >> > +#include <i2c.h> >> > +#include <time.h> >> > +#include <linux/bitops.h> >> > +#include <linux/delay.h> >> > +#include <linux/err.h> >> > +#include <linux/kernel.h> >> > +#include <linux/types.h> >> > + >> > +#define BCM2835_I2C_C_READ BIT(0) >> > +#define BCM2835_I2C_C_CLEAR BIT(4) /* bits 4 and 5 both clear */ >> > +#define BCM2835_I2C_C_ST BIT(7) >> > +#define BCM2835_I2C_C_INTD BIT(8) >> > +#define BCM2835_I2C_C_INTT BIT(9) >> > +#define BCM2835_I2C_C_INTR BIT(10) >> > +#define BCM2835_I2C_C_I2CEN BIT(15) >> > + >> > +#define BCM2835_I2C_S_TA BIT(0) >> > +#define BCM2835_I2C_S_DONE BIT(1) >> > +#define BCM2835_I2C_S_TXW BIT(2) >> > +#define BCM2835_I2C_S_RXR BIT(3) >> > +#define BCM2835_I2C_S_TXD BIT(4) >> > +#define BCM2835_I2C_S_RXD BIT(5) >> > +#define BCM2835_I2C_S_TXE BIT(6) >> > +#define BCM2835_I2C_S_RXF BIT(7) >> > +#define BCM2835_I2C_S_ERR BIT(8) >> > +#define BCM2835_I2C_S_CLKT BIT(9) >> > +#define BCM2835_I2C_S_LEN BIT(10) /* Fake bit for SW error reporting >> > */ >> > + >> > +#define BCM2835_I2C_FEDL_SHIFT 16 >> > +#define BCM2835_I2C_REDL_SHIFT 0 >> > + >> > +#define BCM2835_I2C_CDIV_MIN 0x0002 >> > +#define BCM2835_I2C_CDIV_MAX 0xFFFE >> > + >> > +#define BCM2835_I2C_CLKT_TOUT_MS 35 >> > +#define BCM2835_I2C_XFER_TIMEOUT_MS 1000 >> > + >> > +struct bcm2835_i2c_regs { >> > + u32 c; >> > + u32 s; >> > + u32 dlen; >> > + u32 a; >> > + u32 fifo; >> > + u32 div; >> > + u32 del; >> > + u32 clkt; >> > +}; >> > + >> > +struct bcm2835_i2c_priv { >> > + struct bcm2835_i2c_regs __iomem *regs; >> > + ulong input_rate; >> > + size_t msg_buf_remaining; >> > + u8 *msg_buf; >> > + struct i2c_msg *curr_msg; >> > + int msg_err; >> > + int num_msgs; >> > +}; >> > + >> > +static void bcm2835_fill_txfifo(struct bcm2835_i2c_priv *i2c_dev) >> > +{ >> > + struct bcm2835_i2c_regs __iomem *regs = i2c_dev->regs; >> > + >> > + while (i2c_dev->msg_buf_remaining) { >> > + if (!(readl(®s->s) & BCM2835_I2C_S_TXD)) >> > + break; >> > + >> > + writel(*i2c_dev->msg_buf, ®s->fifo); >> > + i2c_dev->msg_buf++; >> > + i2c_dev->msg_buf_remaining--; >> > + } >> > +} >> > + >> > +static void bcm2835_drain_rxfifo(struct bcm2835_i2c_priv *i2c_dev) >> > +{ >> > + struct bcm2835_i2c_regs __iomem *regs = i2c_dev->regs; >> > + >> > + while (i2c_dev->msg_buf_remaining) { >> > + if (!(readl(®s->s) & BCM2835_I2C_S_RXD)) >> > + break; >> > + >> > + *i2c_dev->msg_buf = readl(®s->fifo); >> > + i2c_dev->msg_buf++; >> > + i2c_dev->msg_buf_remaining--; >> > + } >> > +} >> > + >> > +static int bcm2835_i2c_calc_divider(unsigned long rate, >> > + unsigned long parent_rate) >> > +{ >> > + int divider = DIV_ROUND_UP(parent_rate, rate); >> > + >> > + /* >> > + * 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 (divider & 1) >> > + divider++; >> > + >> > + if (divider < BCM2835_I2C_CDIV_MIN || >> > + divider > BCM2835_I2C_CDIV_MAX) >> > + return -EINVAL; >> > + >> > + return divider; >> > +} >> > + >> > +static int bcm2835_i2c_set_rate(struct udevice *bus, unsigned int rate) >> > +{ >> > + struct bcm2835_i2c_priv *i2c_dev = dev_get_priv(bus); >> > + struct bcm2835_i2c_regs __iomem *regs = i2c_dev->regs; >> > + ulong parent_rate = i2c_dev->input_rate; >> > + u32 redl, fedl; >> > + u32 clk_tout; >> > + int divider; >> > + >> > + if (!rate || !parent_rate) >> > + return -EINVAL; >> > + >> > + divider = bcm2835_i2c_calc_divider(rate, parent_rate); >> > + if (divider < 0) >> > + return divider; >> > + >> > + writel(divider, ®s->div); >> > + >> > + /* >> > + * Number of core clocks to wait after falling edge before >> > + * outputting the next data bit. Note that both FEDL and REDL >> > + * can't be greater than CDIV/2. >> > + */ >> > + fedl = max_t(u32, divider / 16, 1); >> > + >> > + /* >> > + * Number of core clocks to wait after rising edge before >> > + * sampling the next incoming data bit. >> > + */ >> > + redl = max_t(u32, divider / 4, 1); >> > + >> > + writel((fedl << BCM2835_I2C_FEDL_SHIFT) | >> > + (redl << BCM2835_I2C_REDL_SHIFT), ®s->del); >> > + >> > + /* >> > + * Set the clock stretch timeout. >> > + */ >> > + if (rate > 0xffff * 1000 / BCM2835_I2C_CLKT_TOUT_MS) >> > + clk_tout = 0xffff; >> > + else >> > + clk_tout = BCM2835_I2C_CLKT_TOUT_MS * rate / 1000; >> > + >> > + writel(clk_tout, ®s->clkt); >> > + >> > + return 0; >> > +} >> > + >> > +static void bcm2835_i2c_start_transfer(struct bcm2835_i2c_priv *i2c_dev) >> > +{ >> > + struct bcm2835_i2c_regs __iomem *regs = i2c_dev->regs; >> > + u32 c = BCM2835_I2C_C_ST | BCM2835_I2C_C_I2CEN; >> > + struct i2c_msg *msg = i2c_dev->curr_msg; >> > + bool last_msg = (i2c_dev->num_msgs == 1); >> > + >> > + if (!i2c_dev->num_msgs) >> > + return; >> > + >> > + i2c_dev->num_msgs--; >> > + i2c_dev->msg_buf = msg->buf; >> > + i2c_dev->msg_buf_remaining = msg->len; >> > + >> > + if (msg->flags & I2C_M_RD) >> > + c |= BCM2835_I2C_C_READ; >> > + else >> > + c |= BCM2835_I2C_C_INTT; >> > + >> > + if (last_msg) >> > + c |= BCM2835_I2C_C_INTD; >> > + >> > + writel(msg->addr, ®s->a); >> > + writel(msg->len, ®s->dlen); >> > + writel(c, ®s->c); >> > +} >> > + >> > +static void bcm2835_i2c_finish_transfer(struct bcm2835_i2c_priv *i2c_dev) >> > +{ >> > + i2c_dev->curr_msg = NULL; >> > + i2c_dev->num_msgs = 0; >> > + i2c_dev->msg_buf = NULL; >> > + i2c_dev->msg_buf_remaining = 0; >> > +} >> > + >> > +static int bcm2835_i2c_xfer(struct udevice *dev, struct i2c_msg *msgs, >> > int count) >> > +{ >> > + struct bcm2835_i2c_priv *i2c_dev = dev_get_priv(dev); >> > + struct bcm2835_i2c_regs __iomem *regs = i2c_dev->regs; >> > + bool ignore_nak = false; >> > + bool done = false; >> > + ulong start; >> > + int i; >> > + u32 val, err; >> > + int ret = 0; >> > + >> > + if (!msgs || count <= 0) >> > + return -EINVAL; >> > + >> > + for (i = 0; i < count; i++) { >> > + if (msgs[i].flags & I2C_M_TEN) >> > + return -EOPNOTSUPP; >> > + if (msgs[i].flags & I2C_M_RD) { >> > + if (i < count - 1) { >> > + dev_err(dev, "read message must be >> > last\n"); >> > + return -EOPNOTSUPP; >> > + } >> > + } >> > + if (msgs[i].len > 0xffff) >> > + return -EINVAL; >> > + if (msgs[i].flags & I2C_M_IGNORE_NAK) >> > + ignore_nak = true; >> > + } >> > + >> > + /* Clear stale status and FIFO state before starting a new >> > transaction. */ >> > + writel(BCM2835_I2C_C_CLEAR, ®s->c); >> > + writel(BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR | BCM2835_I2C_S_DONE, >> > + ®s->s); >> > + >> > + i2c_dev->curr_msg = msgs; >> > + i2c_dev->num_msgs = count; >> > + i2c_dev->msg_err = 0; >> > + >> > + bcm2835_i2c_start_transfer(i2c_dev); >> > + start = get_timer(0); >> > + while (get_timer(start) < BCM2835_I2C_XFER_TIMEOUT_MS) { >> > + val = readl(®s->s); >> > + >> > + err = val & (BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR); >> > + if (err && !(val & BCM2835_I2C_S_TA)) >> > + i2c_dev->msg_err = err; >> > + >> > + if (val & BCM2835_I2C_S_DONE) { >> > + if (!i2c_dev->curr_msg) { >> > + dev_err(dev, "unexpected completion >> > status\n"); >> > + } else if (i2c_dev->curr_msg->flags & I2C_M_RD) { >> > + bcm2835_drain_rxfifo(i2c_dev); >> > + val = readl(®s->s); >> > + } >> > + >> > + if ((val & BCM2835_I2C_S_RXD) || >> > + i2c_dev->msg_buf_remaining) >> > + i2c_dev->msg_err = BCM2835_I2C_S_LEN; >> > + done = true; >> > + break; >> > + } >> > + >> > + if (val & BCM2835_I2C_S_TXW) { >> > + if (!i2c_dev->msg_buf_remaining) { >> > + i2c_dev->msg_err = val | BCM2835_I2C_S_LEN; >> > + break; >> > + } >> > + >> > + bcm2835_fill_txfifo(i2c_dev); >> > + >> > + if (i2c_dev->num_msgs > 0 && >> > !i2c_dev->msg_buf_remaining) { >> > + i2c_dev->curr_msg++; >> > + bcm2835_i2c_start_transfer(i2c_dev); >> > + } >> > + } >> > + >> > + if (val & BCM2835_I2C_S_RXR) { >> > + if (!i2c_dev->msg_buf_remaining) { >> > + i2c_dev->msg_err = val | BCM2835_I2C_S_LEN; >> > + break; >> > + } >> > + >> > + bcm2835_drain_rxfifo(i2c_dev); >> > + } >> > + udelay(1); >> > + } >> > + >> > + if (!done) { >> > + val = readl(®s->s); >> > + dev_err(dev, "transfer timeout: status=%08x >> > remaining=%zu\n", >> > + val, i2c_dev->msg_buf_remaining); >> > + ret = -ETIMEDOUT; >> > + } >> > + >> > + /* Always abort/reset the controller and clear W1C status bits. */ >> > + writel(BCM2835_I2C_C_CLEAR, ®s->c); >> > + writel(BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR | BCM2835_I2C_S_DONE, >> > + ®s->s); >> > + >> > + bcm2835_i2c_finish_transfer(i2c_dev); >> > + if (ret) >> > + return ret; >> > + >> > + if (ignore_nak) >> > + i2c_dev->msg_err &= ~BCM2835_I2C_S_ERR; >> > + >> > + if (!i2c_dev->msg_err) >> > + return 0; >> > + >> > + if (i2c_dev->msg_err & BCM2835_I2C_S_ERR) >> > + return -EREMOTEIO; >> > + >> > + return -EIO; >> > +} >> > + >> > +static const struct dm_i2c_ops bcm2835_i2c_ops = { >> > + .xfer = bcm2835_i2c_xfer, >> > + .set_bus_speed = bcm2835_i2c_set_rate, >> > +}; >> > + >> > +static const struct udevice_id bcm2835_i2c_ids[] = { >> > + {.compatible = "brcm,bcm2711-i2c"}, >> > + {.compatible = "brcm,bcm2835-i2c"}, >> > + {} >> > +}; >> > + >> > +static int bcm2835_i2c_probe(struct udevice *bus) >> > +{ >> > + struct bcm2835_i2c_priv *priv = dev_get_priv(bus); >> > + struct bcm2835_i2c_regs __iomem *regs; >> > + unsigned int bus_clk_rate; >> > + int ret; >> > + >> > + priv->regs = dev_read_addr_ptr(bus); >> > + if (!priv->regs) { >> > + printf("bcm2835_i2c: cannot get base address\n"); >> > + return -EINVAL; >> > + } >> > + >> > + regs = priv->regs; >> > + >> > + ret = bcm2835_get_clock_rate(BCM2835_MBOX_CLOCK_ID_CORE, >> > &priv->input_rate); >> > + if (ret) >> > + return ret; >> > + >> > + bus_clk_rate = dev_read_u32_default(bus, "clock-frequency", >> > + I2C_SPEED_STANDARD_RATE); >> > + ret = bcm2835_i2c_set_rate(bus, bus_clk_rate); >> > + if (ret) >> > + return ret; >> > + >> > + /* clear FIFO + status */ >> > + writel(BCM2835_I2C_C_CLEAR, ®s->c); >> > + writel(BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR | BCM2835_I2C_S_DONE, >> > + ®s->s); >> > + >> > + return 0; >> > +} >> > + >> > +U_BOOT_DRIVER(bcm2835_i2c) = { >> > + .name = "bcm2835_i2c", >> > + .id = UCLASS_I2C, >> > + .of_match = bcm2835_i2c_ids, >> > + .probe = bcm2835_i2c_probe, >> > + .priv_auto = sizeof(struct bcm2835_i2c_priv), >> > + .ops = &bcm2835_i2c_ops, >> > +}; >> > -- >> > 2.34.1 >> >
