From mboxrd@z Thu Jan 1 00:00:00 1970 From: Heiko Schocher Date: Wed, 27 Aug 2014 07:26:12 +0200 Subject: [U-Boot] [PATCH v2 15/40] power: Add AMS AS3722 PMIC support In-Reply-To: <1409067268-956-16-git-send-email-thierry.reding@gmail.com> References: <1409067268-956-1-git-send-email-thierry.reding@gmail.com> <1409067268-956-16-git-send-email-thierry.reding@gmail.com> Message-ID: <53FD6BF4.9000601@denx.de> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de Hello Thierry, Am 26.08.2014 17:34, schrieb Thierry Reding: > From: Thierry Reding > > The AS3722 provides a number of DC/DC converters and LDOs as well as 8 > GPIOs. > > Signed-off-by: Thierry Reding > --- > drivers/power/Makefile | 1 + > drivers/power/as3722.c | 300 +++++++++++++++++++++++++++++++++++++++++++++++++ > include/fdtdec.h | 1 + > include/power/as3722.h | 27 +++++ > lib/fdtdec.c | 1 + > 5 files changed, 330 insertions(+) > create mode 100644 drivers/power/as3722.c > create mode 100644 include/power/as3722.h > > diff --git a/drivers/power/Makefile b/drivers/power/Makefile > index dc64e4d32bff..b3097316e27e 100644 > --- a/drivers/power/Makefile > +++ b/drivers/power/Makefile > @@ -5,6 +5,7 @@ > # SPDX-License-Identifier: GPL-2.0+ > # > > +obj-$(CONFIG_AS3722_POWER) += as3722.o > obj-$(CONFIG_AXP152_POWER) += axp152.o > obj-$(CONFIG_AXP209_POWER) += axp209.o > obj-$(CONFIG_EXYNOS_TMU) += exynos-tmu.o > diff --git a/drivers/power/as3722.c b/drivers/power/as3722.c > new file mode 100644 > index 000000000000..59d1bf1b50b0 > --- /dev/null > +++ b/drivers/power/as3722.c > @@ -0,0 +1,300 @@ > +/* > + * Copyright (C) 2014 NVIDIA Corporation > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#define pr_fmt(fmt) "as3722: " fmt > + > +#include > +#include > +#include > +#include > + > +#include > + > +#define AS3722_SD_VOLTAGE(n) (0x00 + (n)) > +#define AS3722_GPIO_CONTROL(n) (0x08 + (n)) > +#define AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDH (1<< 0) > +#define AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDL (7<< 0) > +#define AS3722_GPIO_CONTROL_INVERT (1<< 7) > +#define AS3722_LDO_VOLTAGE(n) (0x10 + (n)) > +#define AS3722_GPIO_SIGNAL_OUT 0x20 > +#define AS3722_SD_CONTROL 0x4d > +#define AS3722_LDO_CONTROL 0x4e > +#define AS3722_ASIC_ID1 0x90 > +#define AS3722_DEVICE_ID 0x0c > +#define AS3722_ASIC_ID2 0x91 > + > +struct as3722 { > + struct i2c_client client; > + u8 address; > +}; > + > +static struct as3722 as3722_pmic; > + > +static int as3722_read(struct as3722 *pmic, u8 reg, u8 *value) > +{ > + int err; > + > + err = i2c_client_read(&pmic->client, reg, 1, value, 1); > + if (err< 0) > + return err; > + > + return 0; > +} > + > +static int as3722_write(struct as3722 *pmic, u8 reg, u8 value) > +{ > + int err; > + > + err = i2c_client_write(&pmic->client, reg, 1,&value, 1); > + if (err< 0) > + return err; > + > + return 0; > +} > + > +static int as3722_read_id(struct as3722 *pmic, u8 *id, u8 *revision) > +{ > + int err; > + > + err = as3722_read(pmic, AS3722_ASIC_ID1, id); > + if (err) { > + error("failed to read ID1 register: %d", err); > + return err; > + } > + > + err = as3722_read(pmic, AS3722_ASIC_ID2, revision); > + if (err) { > + error("failed to read ID2 register: %d", err); > + return err; > + } > + > + return 0; > +} > + > +int as3722_sd_enable(struct as3722 *pmic, unsigned int sd) > +{ > + u8 value; > + int err; > + > + if (sd> 6) > + return -EINVAL; > + > + err = as3722_read(pmic, AS3722_SD_CONTROL,&value); > + if (err) { > + error("failed to read SD control register: %d", err); > + return err; > + } > + > + value |= 1<< sd; > + > + err = as3722_write(pmic, AS3722_SD_CONTROL, value); > + if (err< 0) { > + error("failed to write SD control register: %d", err); > + return err; > + } > + > + return 0; > +} > + > +int as3722_sd_set_voltage(struct as3722 *pmic, unsigned int sd, u8 value) > +{ > + int err; > + > + if (sd> 6) > + return -EINVAL; > + > + err = as3722_write(pmic, AS3722_SD_VOLTAGE(sd), value); > + if (err< 0) { > + error("failed to write SD%u voltage register: %d", sd, err); > + return err; > + } > + > + return 0; > +} > + > +int as3722_ldo_enable(struct as3722 *pmic, unsigned int ldo) > +{ > + u8 value; > + int err; > + > + if (ldo> 11) > + return -EINVAL; > + > + err = as3722_read(pmic, AS3722_LDO_CONTROL,&value); > + if (err) { > + error("failed to read LDO control register: %d", err); > + return err; > + } > + > + value |= 1<< ldo; > + > + err = as3722_write(pmic, AS3722_LDO_CONTROL, value); > + if (err< 0) { > + error("failed to write LDO control register: %d", err); > + return err; > + } > + > + return 0; > +} > + > +int as3722_ldo_set_voltage(struct as3722 *pmic, unsigned int ldo, u8 value) > +{ > + int err; > + > + if (ldo> 11) > + return -EINVAL; > + > + err = as3722_write(pmic, AS3722_LDO_VOLTAGE(ldo), value); > + if (err< 0) { > + error("failed to write LDO%u voltage register: %d", ldo, > + err); > + return err; > + } > + > + return 0; > +} > + > +int as3722_gpio_configure(struct as3722 *pmic, unsigned int gpio, > + unsigned long flags) > +{ > + u8 value = 0; > + int err; > + > + if (flags& AS3722_GPIO_OUTPUT_VDDH) > + value |= AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDH; > + > + if (flags& AS3722_GPIO_INVERT) > + value |= AS3722_GPIO_CONTROL_INVERT; > + > + err = as3722_write(pmic, AS3722_GPIO_CONTROL(gpio), value); > + if (err) { > + error("failed to configure GPIO#%u: %d", gpio, err); > + return err; > + } > + > + return 0; > +} > + > +static int as3722_gpio_set(struct as3722 *pmic, unsigned int gpio, > + unsigned int level) > +{ > + const char *l; > + u8 value; > + int err; > + > + if (gpio> 7) > + return -EINVAL; > + > + err = as3722_read(pmic, AS3722_GPIO_SIGNAL_OUT,&value); > + if (err< 0) { > + error("failed to read GPIO signal out register: %d", err); > + return err; > + } > + > + if (level == 0) { > + value&= ~(1<< gpio); > + l = "low"; > + } else { > + value |= 1<< gpio; > + l = "high"; > + } > + > + err = as3722_write(pmic, AS3722_GPIO_SIGNAL_OUT, value); > + if (err) { > + error("failed to set GPIO#%u %s: %d", gpio, l, err); > + return err; > + } > + > + return 0; > +} > + > +int as3722_gpio_direction_output(struct as3722 *pmic, unsigned int gpio, > + unsigned int level) > +{ > + u8 value; > + int err; > + > + if (gpio> 7) > + return -EINVAL; > + > + if (level == 0) > + value = AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDL; > + else > + value = AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDH; > + > + err = as3722_write(pmic, AS3722_GPIO_CONTROL(gpio), value); > + if (err) { > + error("failed to configure GPIO#%u as output: %d", gpio, err); > + return err; > + } > + > + err = as3722_gpio_set(pmic, gpio, level); > + if (err< 0) { > + error("failed to set GPIO#%u high: %d", gpio, err); > + return err; > + } > + > + return 0; > +} > + > +int as3722_init(struct as3722 **pmicp, const void *fdt) > +{ > + struct as3722 *pmic =&as3722_pmic; > + int count, nodes[1], i; > + int err; > + > + count = fdtdec_find_aliases_for_id(fdt, NULL, COMPAT_AMS_AS3722, > + nodes, ARRAY_SIZE(nodes)); > + for (i = 0; i< count; i++) { > + int parent = fdt_parent_offset(fdt, nodes[i]), bus; > + struct i2c_adapter *adapter; > + fdt_addr_t address; > + u8 id, revision; > + > + bus = i2c_get_bus_num_fdt(parent); > + if (bus< 0) { > + error("invalid bus %d", bus); > + continue; > + } > + > + address = fdtdec_get_addr(fdt, nodes[i], "reg"); > + if (address == FDT_ADDR_T_NONE) { > + error("slave address not found"); > + continue; > + } > + > + adapter = i2c_adapter_get(bus); > + if (!adapter) { > + error("I2C adapter for bus %d not found", bus); > + continue; > + } Why is i2c_set_bus_num() not enough for you? Why introducing a new API? If another uses this PMIC behind a i2c mux, this driver maybe not work correctly. bye, Heiko > + > + err = i2c_client_init(&pmic->client, adapter, address); > + if (err< 0) { > + error("failed to initialize I2C slave: %d", err); > + continue; > + } > + > + err = as3722_read_id(pmic,&id,&revision); > + if (err< 0) { > + error("failed to read ID: %d", err); > + continue; > + } > + > + if (id != AS3722_DEVICE_ID) { > + error("unknown device"); > + continue; > + } > + > + debug("AS3722 revision %#x found on I2C bus %u, address %#x\n", > + revision, bus, address); > + > + *pmicp = pmic; > + return 0; > + } > + > + return -ENODEV; > +} > diff --git a/include/fdtdec.h b/include/fdtdec.h > index 5c669a5c8e03..3c38375397df 100644 > --- a/include/fdtdec.h > +++ b/include/fdtdec.h > @@ -115,6 +115,7 @@ enum fdt_compat_id { > COMPAT_SANDBOX_LCD_SDL, /* Sandbox LCD emulation with SDL */ > COMPAT_TI_TPS65090, /* Texas Instrument TPS65090 */ > COMPAT_NXP_PTN3460, /* NXP PTN3460 DP/LVDS bridge */ > + COMPAT_AMS_AS3722, /* AMS AS3722 PMIC */ > > COMPAT_COUNT, > }; > diff --git a/include/power/as3722.h b/include/power/as3722.h > new file mode 100644 > index 000000000000..2c2e45a44bab > --- /dev/null > +++ b/include/power/as3722.h > @@ -0,0 +1,27 @@ > +/* > + * Copyright (C) 2014 NVIDIA Corporation > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#ifndef __POWER_AS3722_H__ > +#define __POWER_AS3722_H__ > + > +#include > + > +#define AS3722_GPIO_OUTPUT_VDDH (1<< 0) > +#define AS3722_GPIO_INVERT (1<< 1) > + > +struct as3722; > + > +int as3722_init(struct as3722 **pmic, const void *fdt); > +int as3722_sd_enable(struct as3722 *pmic, unsigned int sd); > +int as3722_sd_set_voltage(struct as3722 *pmic, unsigned int sd, u8 value); > +int as3722_ldo_enable(struct as3722 *pmic, unsigned int ldo); > +int as3722_ldo_set_voltage(struct as3722 *pmic, unsigned int ldo, u8 value); > +int as3722_gpio_configure(struct as3722 *pmic, unsigned int gpio, > + unsigned long flags); > +int as3722_gpio_direction_output(struct as3722 *pmic, unsigned int gpio, > + unsigned int level); > + > +#endif /* __POWER_AS3722_H__ */ > diff --git a/lib/fdtdec.c b/lib/fdtdec.c > index fa5da0c0147d..cddd86c3186f 100644 > --- a/lib/fdtdec.c > +++ b/lib/fdtdec.c > @@ -70,6 +70,7 @@ static const char * const compat_names[COMPAT_COUNT] = { > COMPAT(SANDBOX_LCD_SDL, "sandbox,lcd-sdl"), > COMPAT(TI_TPS65090, "ti,tps65090"), > COMPAT(COMPAT_NXP_PTN3460, "nxp,ptn3460"), > + COMPAT(AMS_AS3722, "ams,as3722"), > }; > > const char *fdtdec_get_compatible(enum fdt_compat_id id) -- DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany