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
<https://www.waveshare.com/wiki/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?

Looking forward to your test results.

Best regards,
Giau Nguyen
--------------------------------------------------------
Phone: (+84) 387 915 996 <+84%20387%20915%20996>


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(&regs->s) & BCM2835_I2C_S_TXD))
> > +                       break;
> > +
> > +               writel(*i2c_dev->msg_buf, &regs->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(&regs->s) & BCM2835_I2C_S_RXD))
> > +                       break;
> > +
> > +               *i2c_dev->msg_buf = readl(&regs->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, &regs->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), &regs->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, &regs->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, &regs->a);
> > +       writel(msg->len, &regs->dlen);
> > +       writel(c, &regs->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, &regs->c);
> > +       writel(BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR |
> BCM2835_I2C_S_DONE,
> > +              &regs->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(&regs->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(&regs->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(&regs->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, &regs->c);
> > +       writel(BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR |
> BCM2835_I2C_S_DONE,
> > +              &regs->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, &regs->c);
> > +       writel(BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR |
> BCM2835_I2C_S_DONE,
> > +              &regs->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
> >
>

Reply via email to