On Fri, 8 Oct 2021 00:17:20 -0500 Samuel Holland <sam...@sholland.org> wrote:
> This bus controller is used to communicate with an X-Powers AXP PMIC. > Currently, various drivers access PMIC registers through a platform- > specific non-DM "pmic_bus" interface, which depends on the legacy I2C > framework. In order to convert those drivers to use DM_PMIC, this bus > needs a DM_I2C driver. > > Refactor the p2wi functions to take the base address as a parameter, > and implement both the existing interface (which is still needed in > SPL) and the DM_I2C interface on top of them. I compared the old and new file (with some tweaks), and can indeed confirm that it's a conversion that preserves the actual functionality. The new DM wrappers look good as well. > The register for switching between I2C/P2WI/RSB mode is the same across > all PMIC variants. Move that to the common header, so it can be used by > both interface implementations. > > Signed-off-by: Samuel Holland <sam...@sholland.org> Reviewed-by: Andre Przywara <andre.przyw...@arm.com> Thanks! Andre > --- > > Changes in v2: > - Renamed Kconfig symbol to SYS_I2C_SUN6I_P2WI > - Moved entire P2WI driver source to drivers/i2c > > arch/arm/mach-sunxi/Kconfig | 14 +-- > arch/arm/mach-sunxi/Makefile | 1 - > arch/arm/mach-sunxi/p2wi.c | 117 ------------------ > arch/arm/mach-sunxi/pmic_bus.c | 7 +- > drivers/i2c/Kconfig | 8 ++ > drivers/i2c/Makefile | 1 + > drivers/i2c/sun6i_p2wi.c | 220 +++++++++++++++++++++++++++++++++ > include/axp_pmic.h | 6 + > 8 files changed, 240 insertions(+), 134 deletions(-) > delete mode 100644 arch/arm/mach-sunxi/p2wi.c > create mode 100644 drivers/i2c/sun6i_p2wi.c > > diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig > index 4d8f0e17d40..ae3b7974f09 100644 > --- a/arch/arm/mach-sunxi/Kconfig > +++ b/arch/arm/mach-sunxi/Kconfig > @@ -88,17 +88,6 @@ config DRAM_SUN50I_H616_UNKNOWN_FEATURE > feature. > endif > > -config SUN6I_P2WI > - bool "Allwinner sun6i internal P2WI controller" > - help > - If you say yes to this option, support will be included for > the > - P2WI (Push/Pull 2 Wire Interface) controller embedded in some > sunxi > - SOCs. > - The P2WI looks like an SMBus controller (which supports only > byte > - accesses), except that it only supports one slave device. > - This interface is used to connect to specific PMIC devices > (like the > - AXP221). > - > config SUN6I_PRCM > bool > help > @@ -232,10 +221,11 @@ config MACH_SUN6I > select ARCH_SUPPORT_PSCI > select DRAM_SUN6I > select PHY_SUN4I_USB > - select SUN6I_P2WI > + select SPL_I2C > select SUN6I_PRCM > select SUNXI_GEN_SUN6I > select SUPPORT_SPL > + select SYS_I2C_SUN6I_P2WI > select ARMV7_BOOT_SEC_DEFAULT if OLD_SUNXI_KERNEL_COMPAT > > config MACH_SUN7I > diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile > index 3f081d92f37..c9312bb3934 100644 > --- a/arch/arm/mach-sunxi/Makefile > +++ b/arch/arm/mach-sunxi/Makefile > @@ -11,7 +11,6 @@ obj-y += clock.o > obj-y += cpu_info.o > obj-y += dram_helpers.o > obj-y += pinmux.o > -obj-$(CONFIG_SUN6I_P2WI) += p2wi.o > obj-$(CONFIG_SUN6I_PRCM) += prcm.o > obj-$(CONFIG_AXP_PMIC_BUS) += pmic_bus.o > obj-$(CONFIG_SUN8I_RSB) += rsb.o > diff --git a/arch/arm/mach-sunxi/p2wi.c b/arch/arm/mach-sunxi/p2wi.c > deleted file mode 100644 > index 7c5c12254ea..00000000000 > --- a/arch/arm/mach-sunxi/p2wi.c > +++ /dev/null > @@ -1,117 +0,0 @@ > -// SPDX-License-Identifier: GPL-2.0+ > -/* > - * Sunxi A31 Power Management Unit > - * > - * (C) Copyright 2013 Oliver Schinagl <oli...@schinagl.nl> > - * http://linux-sunxi.org > - * > - * Based on sun6i sources and earlier U-Boot Allwinner A10 SPL work > - * > - * (C) Copyright 2006-2013 > - * Allwinner Technology Co., Ltd. <www.allwinnertech.com> > - * Berg Xing <bergx...@allwinnertech.com> > - * Tom Cubie <tangli...@allwinnertech.com> > - */ > - > -#include <common.h> > -#include <errno.h> > -#include <time.h> > -#include <asm/io.h> > -#include <asm/arch/cpu.h> > -#include <asm/arch/gpio.h> > -#include <asm/arch/p2wi.h> > -#include <asm/arch/prcm.h> > -#include <asm/arch/clock.h> > -#include <asm/arch/sys_proto.h> > - > -void p2wi_init(void) > -{ > - struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg > *)SUN6I_P2WI_BASE; - > - /* Enable p2wi and PIO clk, and de-assert their resets */ > - prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_P2WI); > - > - sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN6I_GPL0_R_P2WI_SCK); > - sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN6I_GPL1_R_P2WI_SDA); > - > - /* Reset p2wi controller and set clock to CLKIN(12)/8 = 1.5 MHz > */ > - writel(P2WI_CTRL_RESET, &p2wi->ctrl); > - sdelay(0x100); > - writel(P2WI_CC_SDA_OUT_DELAY(1) | P2WI_CC_CLK_DIV(8), > - &p2wi->cc); > -} > - > -int p2wi_change_to_p2wi_mode(u8 slave_addr, u8 ctrl_reg, u8 init_data) > -{ > - struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg > *)SUN6I_P2WI_BASE; > - unsigned long tmo = timer_get_us() + 1000000; > - > - writel(P2WI_PM_DEV_ADDR(slave_addr) | > - P2WI_PM_CTRL_ADDR(ctrl_reg) | > - P2WI_PM_INIT_DATA(init_data) | > - P2WI_PM_INIT_SEND, > - &p2wi->pm); > - > - while ((readl(&p2wi->pm) & P2WI_PM_INIT_SEND)) { > - if (timer_get_us() > tmo) > - return -ETIME; > - } > - > - return 0; > -} > - > -static int p2wi_await_trans(void) > -{ > - struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg > *)SUN6I_P2WI_BASE; > - unsigned long tmo = timer_get_us() + 1000000; > - int ret; > - u8 reg; > - > - while (1) { > - reg = readl(&p2wi->status); > - if (reg & P2WI_STAT_TRANS_ERR) { > - ret = -EIO; > - break; > - } > - if (reg & P2WI_STAT_TRANS_DONE) { > - ret = 0; > - break; > - } > - if (timer_get_us() > tmo) { > - ret = -ETIME; > - break; > - } > - } > - writel(reg, &p2wi->status); /* Clear status bits */ > - return ret; > -} > - > -int p2wi_read(const u8 addr, u8 *data) > -{ > - struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg > *)SUN6I_P2WI_BASE; > - int ret; > - > - writel(P2WI_DATADDR_BYTE_1(addr), &p2wi->dataddr0); > - writel(P2WI_DATA_NUM_BYTES(1) | > - P2WI_DATA_NUM_BYTES_READ, &p2wi->numbytes); > - writel(P2WI_STAT_TRANS_DONE, &p2wi->status); > - writel(P2WI_CTRL_TRANS_START, &p2wi->ctrl); > - > - ret = p2wi_await_trans(); > - > - *data = readl(&p2wi->data0) & P2WI_DATA_BYTE_1_MASK; > - return ret; > -} > - > -int p2wi_write(const u8 addr, u8 data) > -{ > - struct sunxi_p2wi_reg *p2wi = (struct sunxi_p2wi_reg > *)SUN6I_P2WI_BASE; - > - writel(P2WI_DATADDR_BYTE_1(addr), &p2wi->dataddr0); > - writel(P2WI_DATA_BYTE_1(data), &p2wi->data0); > - writel(P2WI_DATA_NUM_BYTES(1), &p2wi->numbytes); > - writel(P2WI_STAT_TRANS_DONE, &p2wi->status); > - writel(P2WI_CTRL_TRANS_START, &p2wi->ctrl); > - > - return p2wi_await_trans(); > -} > diff --git a/arch/arm/mach-sunxi/pmic_bus.c > b/arch/arm/mach-sunxi/pmic_bus.c index 0394ce85644..673a05fdd16 100644 > --- a/arch/arm/mach-sunxi/pmic_bus.c > +++ b/arch/arm/mach-sunxi/pmic_bus.c > @@ -8,6 +8,7 @@ > * axp223 uses the rsb bus, these functions abstract this. > */ > > +#include <axp_pmic.h> > #include <common.h> > #include <asm/arch/p2wi.h> > #include <asm/arch/rsb.h> > @@ -21,8 +22,6 @@ > #define AXP305_I2C_ADDR 0x36 > > #define AXP221_CHIP_ADDR 0x68 > -#define AXP221_CTRL_ADDR 0x3e > -#define AXP221_INIT_DATA 0x3e > > /* AXP818 device and runtime addresses are same as AXP223 */ > #define AXP223_DEVICE_ADDR 0x3a3 > @@ -40,8 +39,8 @@ int pmic_bus_init(void) > #if defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || > defined CONFIG_AXP818_POWER # ifdef CONFIG_MACH_SUN6I > p2wi_init(); > - ret = p2wi_change_to_p2wi_mode(AXP221_CHIP_ADDR, > AXP221_CTRL_ADDR, > - AXP221_INIT_DATA); > + ret = p2wi_change_to_p2wi_mode(AXP221_CHIP_ADDR, > AXP_PMIC_MODE_REG, > + AXP_PMIC_MODE_P2WI); > # elif defined CONFIG_MACH_SUN8I_R40 > /* Nothing. R40 uses the AXP221s in I2C mode */ > ret = 0; > diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig > index 57cac4483f0..0adf143abfd 100644 > --- a/drivers/i2c/Kconfig > +++ b/drivers/i2c/Kconfig > @@ -575,6 +575,14 @@ config SYS_I2C_STM32F7 > _ Optional clock stretching > _ Software reset > > +config SYS_I2C_SUN6I_P2WI > + bool "Allwinner sun6i P2WI controller" > + depends on ARCH_SUNXI > + help > + Support for the P2WI (Push/Pull 2 Wire Interface) controller > embedded > + in the Allwinner A31 and A31s SOCs. This interface is used to > connect > + to specific devices like the X-Powers AXP221 PMIC. > + > config SYS_I2C_SYNQUACER > bool "Socionext SynQuacer I2C controller" > depends on ARCH_SYNQUACER && DM_I2C > diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile > index 67841bf3e02..0a2cc641930 100644 > --- a/drivers/i2c/Makefile > +++ b/drivers/i2c/Makefile > @@ -43,6 +43,7 @@ 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_STM32F7) += stm32f7_i2c.o > +obj-$(CONFIG_SYS_I2C_SUN6I_P2WI) += sun6i_p2wi.o > obj-$(CONFIG_SYS_I2C_SYNQUACER) += synquacer_i2c.o > obj-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o > obj-$(CONFIG_SYS_I2C_UNIPHIER) += i2c-uniphier.o > diff --git a/drivers/i2c/sun6i_p2wi.c b/drivers/i2c/sun6i_p2wi.c > new file mode 100644 > index 00000000000..c9e1b3fcd5f > --- /dev/null > +++ b/drivers/i2c/sun6i_p2wi.c > @@ -0,0 +1,220 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Sunxi A31 Power Management Unit > + * > + * (C) Copyright 2013 Oliver Schinagl <oli...@schinagl.nl> > + * http://linux-sunxi.org > + * > + * Based on sun6i sources and earlier U-Boot Allwinner A10 SPL work > + * > + * (C) Copyright 2006-2013 > + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> > + * Berg Xing <bergx...@allwinnertech.com> > + * Tom Cubie <tangli...@allwinnertech.com> > + */ > + > +#include <axp_pmic.h> > +#include <common.h> > +#include <dm.h> > +#include <errno.h> > +#include <i2c.h> > +#include <time.h> > +#include <asm/io.h> > +#include <asm/arch/cpu.h> > +#include <asm/arch/gpio.h> > +#include <asm/arch/p2wi.h> > +#include <asm/arch/prcm.h> > +#include <asm/arch/sys_proto.h> > + > +static int sun6i_p2wi_await_trans(struct sunxi_p2wi_reg *base) > +{ > + unsigned long tmo = timer_get_us() + 1000000; > + int ret; > + u8 reg; > + > + while (1) { > + reg = readl(&base->status); > + if (reg & P2WI_STAT_TRANS_ERR) { > + ret = -EIO; > + break; > + } > + if (reg & P2WI_STAT_TRANS_DONE) { > + ret = 0; > + break; > + } > + if (timer_get_us() > tmo) { > + ret = -ETIME; > + break; > + } > + } > + writel(reg, &base->status); /* Clear status bits */ > + > + return ret; > +} > + > +static int sun6i_p2wi_read(struct sunxi_p2wi_reg *base, const u8 addr, > u8 *data) +{ > + int ret; > + > + writel(P2WI_DATADDR_BYTE_1(addr), &base->dataddr0); > + writel(P2WI_DATA_NUM_BYTES(1) | > + P2WI_DATA_NUM_BYTES_READ, &base->numbytes); > + writel(P2WI_STAT_TRANS_DONE, &base->status); > + writel(P2WI_CTRL_TRANS_START, &base->ctrl); > + > + ret = sun6i_p2wi_await_trans(base); > + > + *data = readl(&base->data0) & P2WI_DATA_BYTE_1_MASK; > + > + return ret; > +} > + > +static int sun6i_p2wi_write(struct sunxi_p2wi_reg *base, const u8 addr, > u8 data) +{ > + writel(P2WI_DATADDR_BYTE_1(addr), &base->dataddr0); > + writel(P2WI_DATA_BYTE_1(data), &base->data0); > + writel(P2WI_DATA_NUM_BYTES(1), &base->numbytes); > + writel(P2WI_STAT_TRANS_DONE, &base->status); > + writel(P2WI_CTRL_TRANS_START, &base->ctrl); > + > + return sun6i_p2wi_await_trans(base); > +} > + > +static int sun6i_p2wi_change_to_p2wi_mode(struct sunxi_p2wi_reg *base, > + u8 slave_addr, u8 ctrl_reg, > + u8 init_data) > +{ > + unsigned long tmo = timer_get_us() + 1000000; > + > + writel(P2WI_PM_DEV_ADDR(slave_addr) | > + P2WI_PM_CTRL_ADDR(ctrl_reg) | > + P2WI_PM_INIT_DATA(init_data) | > + P2WI_PM_INIT_SEND, > + &base->pm); > + > + while ((readl(&base->pm) & P2WI_PM_INIT_SEND)) { > + if (timer_get_us() > tmo) > + return -ETIME; > + } > + > + return 0; > +} > + > +static void sun6i_p2wi_init(struct sunxi_p2wi_reg *base) > +{ > + /* Enable p2wi and PIO clk, and de-assert their resets */ > + prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_P2WI); > + > + sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN6I_GPL0_R_P2WI_SCK); > + sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN6I_GPL1_R_P2WI_SDA); > + > + /* Reset p2wi controller and set clock to CLKIN(12)/8 = 1.5 MHz > */ > + writel(P2WI_CTRL_RESET, &base->ctrl); > + sdelay(0x100); > + writel(P2WI_CC_SDA_OUT_DELAY(1) | P2WI_CC_CLK_DIV(8), > + &base->cc); > +} > + > +#if IS_ENABLED(CONFIG_AXP_PMIC_BUS) > +int p2wi_read(const u8 addr, u8 *data) > +{ > + struct sunxi_p2wi_reg *base = (struct sunxi_p2wi_reg > *)SUN6I_P2WI_BASE; + > + return sun6i_p2wi_read(base, addr, data); > +} > + > +int p2wi_write(const u8 addr, u8 data) > +{ > + struct sunxi_p2wi_reg *base = (struct sunxi_p2wi_reg > *)SUN6I_P2WI_BASE; + > + return sun6i_p2wi_write(base, addr, data); > +} > + > +int p2wi_change_to_p2wi_mode(u8 slave_addr, u8 ctrl_reg, u8 init_data) > +{ > + struct sunxi_p2wi_reg *base = (struct sunxi_p2wi_reg > *)SUN6I_P2WI_BASE; + > + return sun6i_p2wi_change_to_p2wi_mode(base, slave_addr, > ctrl_reg, > + init_data); > +} > + > +void p2wi_init(void) > +{ > + struct sunxi_p2wi_reg *base = (struct sunxi_p2wi_reg > *)SUN6I_P2WI_BASE; + > + sun6i_p2wi_init(base); > +} > +#endif > + > +#if CONFIG_IS_ENABLED(DM_I2C) > +struct sun6i_p2wi_priv { > + struct sunxi_p2wi_reg *base; > +}; > + > +static int sun6i_p2wi_xfer(struct udevice *bus, struct i2c_msg *msg, > int nmsgs) +{ > + struct sun6i_p2wi_priv *priv = dev_get_priv(bus); > + > + /* The hardware only supports SMBus-style transfers. */ > + if (nmsgs == 2 && msg[1].flags == I2C_M_RD && msg[1].len == 1) > + return sun6i_p2wi_read(priv->base, > + msg[0].buf[0], &msg[1].buf[0]); > + > + if (nmsgs == 1 && msg[0].len == 2) > + return sun6i_p2wi_write(priv->base, > + msg[0].buf[0], msg[0].buf[1]); > + > + return -EINVAL; > +} > + > +static int sun6i_p2wi_probe_chip(struct udevice *bus, uint chip_addr, > + uint chip_flags) > +{ > + struct sun6i_p2wi_priv *priv = dev_get_priv(bus); > + > + return sun6i_p2wi_change_to_p2wi_mode(priv->base, chip_addr, > + AXP_PMIC_MODE_REG, > + AXP_PMIC_MODE_P2WI); > +} > + > +static int sun6i_p2wi_probe(struct udevice *bus) > +{ > + struct sun6i_p2wi_priv *priv = dev_get_priv(bus); > + > + priv->base = dev_read_addr_ptr(bus); > + > + sun6i_p2wi_init(priv->base); > + > + return 0; > +} > + > +static int sun6i_p2wi_child_pre_probe(struct udevice *child) > +{ > + struct dm_i2c_chip *chip = dev_get_parent_plat(child); > + > + /* Ensure each transfer is for a single register. */ > + chip->flags |= DM_I2C_CHIP_RD_ADDRESS | DM_I2C_CHIP_WR_ADDRESS; > + > + return 0; > +} > + > +static const struct dm_i2c_ops sun6i_p2wi_ops = { > + .xfer = sun6i_p2wi_xfer, > + .probe_chip = sun6i_p2wi_probe_chip, > +}; > + > +static const struct udevice_id sun6i_p2wi_ids[] = { > + { .compatible = "allwinner,sun6i-a31-p2wi" }, > + { /* sentinel */ } > +}; > + > +U_BOOT_DRIVER(sun6i_p2wi) = { > + .name = "sun6i_p2wi", > + .id = UCLASS_I2C, > + .of_match = sun6i_p2wi_ids, > + .probe = sun6i_p2wi_probe, > + .child_pre_probe = sun6i_p2wi_child_pre_probe, > + .priv_auto = sizeof(struct sun6i_p2wi_priv), > + .ops = &sun6i_p2wi_ops, > +}; > +#endif /* CONFIG_IS_ENABLED(DM_I2C) */ > diff --git a/include/axp_pmic.h b/include/axp_pmic.h > index 405044c3a32..0db3e143eda 100644 > --- a/include/axp_pmic.h > +++ b/include/axp_pmic.h > @@ -6,6 +6,8 @@ > */ > #ifndef _AXP_PMIC_H_ > > +#include <stdbool.h> > + > #ifdef CONFIG_AXP152_POWER > #include <axp152.h> > #endif > @@ -25,6 +27,10 @@ > #include <axp818.h> > #endif > > +#define AXP_PMIC_MODE_REG 0x3e > +#define AXP_PMIC_MODE_I2C 0x00 > +#define AXP_PMIC_MODE_P2WI 0x3e > + > int axp_set_dcdc1(unsigned int mvolt); > int axp_set_dcdc2(unsigned int mvolt); > int axp_set_dcdc3(unsigned int mvolt);