summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2021-01-26 03:46:02 +0300
committerTom Rini <trini@konsulko.com>2021-01-26 03:46:02 +0300
commite262b2973e22174da666038514d17f0f7171466b (patch)
tree0b0b605f1192d4d2e18c956b6e8ef340b2799bfd /drivers
parentc99be953e787cfb2414de67390427e00b6812240 (diff)
parent38be6b838780e8ad0ee80e716752c8843cd87e05 (diff)
downloadu-boot-e262b2973e22174da666038514d17f0f7171466b.tar.xz
Merge https://gitlab.denx.de/u-boot/custodians/u-boot-sunxi
- New Allwinner H616 SoC support (sans Ethernet & USB) - H6 DT update - Tanix TX6 TV box support - OrangePi 3 support - OrangePi Zero2 (H616) support
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/sunxi/Kconfig7
-rw-r--r--drivers/clk/sunxi/Makefile1
-rw-r--r--drivers/clk/sunxi/clk_h616.c120
-rw-r--r--drivers/gpio/sunxi_gpio.c2
-rw-r--r--drivers/i2c/mvtwsi.c2
-rw-r--r--drivers/mmc/sunxi_mmc.c96
-rw-r--r--drivers/net/sun8i_emac.c3
-rw-r--r--drivers/power/Kconfig14
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/axp305.c83
10 files changed, 260 insertions, 69 deletions
diff --git a/drivers/clk/sunxi/Kconfig b/drivers/clk/sunxi/Kconfig
index 5ff101b993..bf084fa7a8 100644
--- a/drivers/clk/sunxi/Kconfig
+++ b/drivers/clk/sunxi/Kconfig
@@ -79,6 +79,13 @@ config CLK_SUN50I_H6
This enables common clock driver support for platforms based
on Allwinner H6 SoC.
+config CLK_SUN50I_H616
+ bool "Clock driver for Allwinner H616"
+ default MACH_SUN50I_H616
+ help
+ This enables common clock driver support for platforms based
+ on Allwinner H616 SoC.
+
config CLK_SUN50I_A64
bool "Clock driver for Allwinner A64"
default MACH_SUN50I
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
index 36fb2aeb56..0dfc0593fb 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -16,4 +16,5 @@ obj-$(CONFIG_CLK_SUN8I_V3S) += clk_v3s.o
obj-$(CONFIG_CLK_SUN9I_A80) += clk_a80.o
obj-$(CONFIG_CLK_SUN8I_H3) += clk_h3.o
obj-$(CONFIG_CLK_SUN50I_H6) += clk_h6.o
+obj-$(CONFIG_CLK_SUN50I_H616) += clk_h616.o
obj-$(CONFIG_CLK_SUN50I_A64) += clk_a64.o
diff --git a/drivers/clk/sunxi/clk_h616.c b/drivers/clk/sunxi/clk_h616.c
new file mode 100644
index 0000000000..553d7c6e55
--- /dev/null
+++ b/drivers/clk/sunxi/clk_h616.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (C) 2021 Jernej Skrabec <jernej.skrabec@siol.net>
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/arch/ccu.h>
+#include <dt-bindings/clock/sun50i-h616-ccu.h>
+#include <dt-bindings/reset/sun50i-h616-ccu.h>
+#include <linux/bitops.h>
+
+static struct ccu_clk_gate h616_gates[] = {
+ [CLK_BUS_MMC0] = GATE(0x84c, BIT(0)),
+ [CLK_BUS_MMC1] = GATE(0x84c, BIT(1)),
+ [CLK_BUS_MMC2] = GATE(0x84c, BIT(2)),
+
+ [CLK_BUS_UART0] = GATE(0x90c, BIT(0)),
+ [CLK_BUS_UART1] = GATE(0x90c, BIT(1)),
+ [CLK_BUS_UART2] = GATE(0x90c, BIT(2)),
+ [CLK_BUS_UART3] = GATE(0x90c, BIT(3)),
+ [CLK_BUS_UART4] = GATE(0x90c, BIT(4)),
+ [CLK_BUS_UART5] = GATE(0x90c, BIT(5)),
+
+ [CLK_SPI0] = GATE(0x940, BIT(31)),
+ [CLK_SPI1] = GATE(0x944, BIT(31)),
+
+ [CLK_BUS_SPI0] = GATE(0x96c, BIT(0)),
+ [CLK_BUS_SPI1] = GATE(0x96c, BIT(1)),
+
+ [CLK_BUS_EMAC0] = GATE(0x97c, BIT(0)),
+ [CLK_BUS_EMAC1] = GATE(0x97c, BIT(1)),
+
+ [CLK_USB_PHY0] = GATE(0xa70, BIT(29)),
+ [CLK_USB_OHCI0] = GATE(0xa70, BIT(31)),
+
+ [CLK_USB_PHY1] = GATE(0xa74, BIT(29)),
+ [CLK_USB_OHCI1] = GATE(0xa74, BIT(31)),
+
+ [CLK_USB_PHY2] = GATE(0xa78, BIT(29)),
+ [CLK_USB_OHCI2] = GATE(0xa78, BIT(31)),
+
+ [CLK_USB_PHY3] = GATE(0xa7c, BIT(29)),
+ [CLK_USB_OHCI3] = GATE(0xa7c, BIT(31)),
+
+ [CLK_BUS_OHCI0] = GATE(0xa8c, BIT(0)),
+ [CLK_BUS_OHCI1] = GATE(0xa8c, BIT(1)),
+ [CLK_BUS_OHCI2] = GATE(0xa8c, BIT(2)),
+ [CLK_BUS_OHCI3] = GATE(0xa8c, BIT(3)),
+ [CLK_BUS_EHCI0] = GATE(0xa8c, BIT(4)),
+ [CLK_BUS_EHCI1] = GATE(0xa8c, BIT(5)),
+ [CLK_BUS_EHCI2] = GATE(0xa8c, BIT(6)),
+ [CLK_BUS_EHCI3] = GATE(0xa8c, BIT(7)),
+ [CLK_BUS_OTG] = GATE(0xa8c, BIT(8)),
+};
+
+static struct ccu_reset h616_resets[] = {
+ [RST_BUS_MMC0] = RESET(0x84c, BIT(16)),
+ [RST_BUS_MMC1] = RESET(0x84c, BIT(17)),
+ [RST_BUS_MMC2] = RESET(0x84c, BIT(18)),
+
+ [RST_BUS_UART0] = RESET(0x90c, BIT(16)),
+ [RST_BUS_UART1] = RESET(0x90c, BIT(17)),
+ [RST_BUS_UART2] = RESET(0x90c, BIT(18)),
+ [RST_BUS_UART3] = RESET(0x90c, BIT(19)),
+ [RST_BUS_UART4] = RESET(0x90c, BIT(20)),
+ [RST_BUS_UART5] = RESET(0x90c, BIT(21)),
+
+ [RST_BUS_SPI0] = RESET(0x96c, BIT(16)),
+ [RST_BUS_SPI1] = RESET(0x96c, BIT(17)),
+
+ [RST_BUS_EMAC0] = RESET(0x97c, BIT(16)),
+ [RST_BUS_EMAC1] = RESET(0x97c, BIT(17)),
+
+ [RST_USB_PHY0] = RESET(0xa70, BIT(30)),
+
+ [RST_USB_PHY1] = RESET(0xa74, BIT(30)),
+
+ [RST_USB_PHY2] = RESET(0xa78, BIT(30)),
+
+ [RST_USB_PHY3] = RESET(0xa7c, BIT(30)),
+
+ [RST_BUS_OHCI0] = RESET(0xa8c, BIT(16)),
+ [RST_BUS_OHCI1] = RESET(0xa8c, BIT(17)),
+ [RST_BUS_OHCI2] = RESET(0xa8c, BIT(18)),
+ [RST_BUS_OHCI3] = RESET(0xa8c, BIT(19)),
+ [RST_BUS_EHCI0] = RESET(0xa8c, BIT(20)),
+ [RST_BUS_EHCI1] = RESET(0xa8c, BIT(21)),
+ [RST_BUS_EHCI2] = RESET(0xa8c, BIT(22)),
+ [RST_BUS_EHCI3] = RESET(0xa8c, BIT(23)),
+ [RST_BUS_OTG] = RESET(0xa8c, BIT(24)),
+};
+
+static const struct ccu_desc h616_ccu_desc = {
+ .gates = h616_gates,
+ .resets = h616_resets,
+};
+
+static int h616_clk_bind(struct udevice *dev)
+{
+ return sunxi_reset_bind(dev, ARRAY_SIZE(h616_resets));
+}
+
+static const struct udevice_id h616_ccu_ids[] = {
+ { .compatible = "allwinner,sun50i-h616-ccu",
+ .data = (ulong)&h616_ccu_desc },
+ { }
+};
+
+U_BOOT_DRIVER(clk_sun50i_h616) = {
+ .name = "sun50i_h616_ccu",
+ .id = UCLASS_CLK,
+ .of_match = h616_ccu_ids,
+ .priv_auto = sizeof(struct ccu_priv),
+ .ops = &sunxi_clk_ops,
+ .probe = sunxi_clk_probe,
+ .bind = h616_clk_bind,
+};
diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c
index 7633422b0b..24cb604e3e 100644
--- a/drivers/gpio/sunxi_gpio.c
+++ b/drivers/gpio/sunxi_gpio.c
@@ -355,6 +355,7 @@ static const struct udevice_id sunxi_gpio_ids[] = {
ID("allwinner,sun9i-a80-pinctrl", a_all),
ID("allwinner,sun50i-a64-pinctrl", a_all),
ID("allwinner,sun50i-h6-pinctrl", a_all),
+ ID("allwinner,sun50i-h616-pinctrl", a_all),
ID("allwinner,sun6i-a31-r-pinctrl", l_2),
ID("allwinner,sun8i-a23-r-pinctrl", l_1),
ID("allwinner,sun8i-a83t-r-pinctrl", l_1),
@@ -362,6 +363,7 @@ static const struct udevice_id sunxi_gpio_ids[] = {
ID("allwinner,sun9i-a80-r-pinctrl", l_3),
ID("allwinner,sun50i-a64-r-pinctrl", l_1),
ID("allwinner,sun50i-h6-r-pinctrl", l_2),
+ ID("allwinner,sun50i-h616-r-pinctrl", l_1),
{ }
};
diff --git a/drivers/i2c/mvtwsi.c b/drivers/i2c/mvtwsi.c
index a4d59b67a2..37b1a06ee0 100644
--- a/drivers/i2c/mvtwsi.c
+++ b/drivers/i2c/mvtwsi.c
@@ -121,7 +121,7 @@ enum mvtwsi_ctrl_register_fields {
* on other platforms, it is a normal r/w bit, which is cleared by writing 0.
*/
-#ifdef CONFIG_SUNXI_GEN_SUN6I
+#if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6)
#define MVTWSI_CONTROL_CLEAR_IFLG 0x00000008
#else
#define MVTWSI_CONTROL_CLEAR_IFLG 0x00000000
diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c
index b33f80b9da..3503ccdb2e 100644
--- a/drivers/mmc/sunxi_mmc.c
+++ b/drivers/mmc/sunxi_mmc.c
@@ -23,12 +23,6 @@
#include <asm-generic/gpio.h>
#include <linux/delay.h>
-#ifdef CONFIG_DM_MMC
-struct sunxi_mmc_variant {
- u16 mclk_offset;
-};
-#endif
-
struct sunxi_mmc_plat {
struct mmc_config cfg;
struct mmc mmc;
@@ -42,9 +36,6 @@ struct sunxi_mmc_priv {
int cd_inverted; /* Inverted Card Detect */
struct sunxi_mmc *reg;
struct mmc_config cfg;
-#ifdef CONFIG_DM_MMC
- const struct sunxi_mmc_variant *variant;
-#endif
};
#if !CONFIG_IS_ENABLED(DM_MMC)
@@ -122,7 +113,7 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
if (IS_ENABLED(CONFIG_MACH_SUN8I_A83T) && priv->mmc_no != 2)
new_mode = false;
-#if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN50I_H6)
+#if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_SUN50I_GEN_H6)
calibrate = true;
#endif
@@ -133,7 +124,7 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
#ifdef CONFIG_MACH_SUN9I
pll = CCM_MMC_CTRL_PLL_PERIPH0;
pll_hz = clock_get_pll4_periph0();
-#elif defined(CONFIG_MACH_SUN50I_H6)
+#elif defined(CONFIG_SUN50I_GEN_H6)
pll = CCM_MMC_CTRL_PLL6X2;
pll_hz = clock_get_pll6() * 2;
#else
@@ -249,7 +240,7 @@ static int mmc_config_clock(struct sunxi_mmc_priv *priv, struct mmc *mmc)
rval &= ~SUNXI_MMC_CLK_DIVIDER_MASK;
writel(rval, &priv->reg->clkcr);
-#if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN50I_H6)
+#if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_SUN50I_GEN_H6)
/* A64 supports calibration of delays on MMC controller and we
* have to set delay of zero before starting calibration.
* Allwinner BSP driver sets a delay only in the case of
@@ -530,7 +521,7 @@ struct mmc *sunxi_mmc_init(int sdc_no)
cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
cfg->host_caps = MMC_MODE_4BIT;
-#if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN8I) || defined(CONFIG_MACH_SUN50I_H6)
+#if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN8I) || defined(CONFIG_SUN50I_GEN_H6)
if (sdc_no == 2)
cfg->host_caps = MMC_MODE_8BIT;
#endif
@@ -545,7 +536,7 @@ struct mmc *sunxi_mmc_init(int sdc_no)
/* config ahb clock */
debug("init mmc %d clock and io\n", sdc_no);
-#if !defined(CONFIG_MACH_SUN50I_H6)
+#if !defined(CONFIG_SUN50I_GEN_H6)
setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MMC(sdc_no));
#ifdef CONFIG_SUNXI_GEN_SUN6I
@@ -557,7 +548,7 @@ struct mmc *sunxi_mmc_init(int sdc_no)
writel(SUNXI_MMC_COMMON_CLK_GATE | SUNXI_MMC_COMMON_RESET,
SUNXI_MMC_COMMON_BASE + 4 * sdc_no);
#endif
-#else /* CONFIG_MACH_SUN50I_H6 */
+#else /* CONFIG_SUN50I_GEN_H6 */
setbits_le32(&ccm->sd_gate_reset, 1 << sdc_no);
/* unassert reset */
setbits_le32(&ccm->sd_gate_reset, 1 << (RESET_SHIFT + sdc_no));
@@ -605,6 +596,17 @@ static const struct dm_mmc_ops sunxi_mmc_ops = {
.get_cd = sunxi_mmc_getcd,
};
+static unsigned get_mclk_offset(void)
+{
+ if (IS_ENABLED(CONFIG_MACH_SUN9I_A80))
+ return 0x410;
+
+ if (IS_ENABLED(CONFIG_SUN50I_GEN_H6))
+ return 0x830;
+
+ return 0x88;
+};
+
static int sunxi_mmc_probe(struct udevice *dev)
{
struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
@@ -633,8 +635,6 @@ static int sunxi_mmc_probe(struct udevice *dev)
cfg->f_max = 52000000;
priv->reg = (void *)dev_read_addr(dev);
- priv->variant =
- (const struct sunxi_mmc_variant *)dev_get_driver_data(dev);
/* We don't have a sunxi clock driver so find the clock address here */
ret = dev_read_phandle_with_args(dev, "clocks", "#clock-cells", 0,
@@ -644,8 +644,7 @@ static int sunxi_mmc_probe(struct udevice *dev)
ccu_reg = (u32 *)ofnode_get_addr(args.node);
priv->mmc_no = ((uintptr_t)priv->reg - SUNXI_MMC0_BASE) / 0x1000;
- priv->mclkreg = (void *)ccu_reg +
- (priv->variant->mclk_offset + (priv->mmc_no * 4));
+ priv->mclkreg = (void *)ccu_reg + get_mclk_offset() + priv->mmc_no * 4;
ret = clk_get_by_name(dev, "ahb", &gate_clk);
if (!ret)
@@ -687,55 +686,18 @@ static int sunxi_mmc_bind(struct udevice *dev)
return mmc_bind(dev, &plat->mmc, &plat->cfg);
}
-static const struct sunxi_mmc_variant sun4i_a10_variant = {
- .mclk_offset = 0x88,
-};
-
-static const struct sunxi_mmc_variant sun9i_a80_variant = {
- .mclk_offset = 0x410,
-};
-
-static const struct sunxi_mmc_variant sun50i_h6_variant = {
- .mclk_offset = 0x830,
-};
-
static const struct udevice_id sunxi_mmc_ids[] = {
- {
- .compatible = "allwinner,sun4i-a10-mmc",
- .data = (ulong)&sun4i_a10_variant,
- },
- {
- .compatible = "allwinner,sun5i-a13-mmc",
- .data = (ulong)&sun4i_a10_variant,
- },
- {
- .compatible = "allwinner,sun7i-a20-mmc",
- .data = (ulong)&sun4i_a10_variant,
- },
- {
- .compatible = "allwinner,sun8i-a83t-emmc",
- .data = (ulong)&sun4i_a10_variant,
- },
- {
- .compatible = "allwinner,sun9i-a80-mmc",
- .data = (ulong)&sun9i_a80_variant,
- },
- {
- .compatible = "allwinner,sun50i-a64-mmc",
- .data = (ulong)&sun4i_a10_variant,
- },
- {
- .compatible = "allwinner,sun50i-a64-emmc",
- .data = (ulong)&sun4i_a10_variant,
- },
- {
- .compatible = "allwinner,sun50i-h6-mmc",
- .data = (ulong)&sun50i_h6_variant,
- },
- {
- .compatible = "allwinner,sun50i-h6-emmc",
- .data = (ulong)&sun50i_h6_variant,
- },
+ { .compatible = "allwinner,sun4i-a10-mmc" },
+ { .compatible = "allwinner,sun5i-a13-mmc" },
+ { .compatible = "allwinner,sun7i-a20-mmc" },
+ { .compatible = "allwinner,sun8i-a83t-emmc" },
+ { .compatible = "allwinner,sun9i-a80-mmc" },
+ { .compatible = "allwinner,sun50i-a64-mmc" },
+ { .compatible = "allwinner,sun50i-a64-emmc" },
+ { .compatible = "allwinner,sun50i-h6-mmc" },
+ { .compatible = "allwinner,sun50i-h6-emmc" },
+ { .compatible = "allwinner,sun50i-a100-mmc" },
+ { .compatible = "allwinner,sun50i-a100-emmc" },
{ /* sentinel */ }
};
diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c
index cf15e9f475..9f91a20d1d 100644
--- a/drivers/net/sun8i_emac.c
+++ b/drivers/net/sun8i_emac.c
@@ -353,6 +353,9 @@ static int sun8i_emac_set_syscon(struct sun8i_eth_pdata *pdata,
/* default */
break;
case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
reg |= SC_EPIT | SC_ETCS_INT_GMII;
break;
case PHY_INTERFACE_MODE_RMII:
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 02050f6f35..c5fbf1f832 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -13,6 +13,7 @@ choice
depends on ARCH_SUNXI
default AXP209_POWER if MACH_SUN4I || MACH_SUN5I || MACH_SUN7I
default AXP221_POWER if MACH_SUN6I || MACH_SUN8I_A23 || MACH_SUN8I_A33 || MACH_SUN8I_R40
+ default AXP305_POWER if MACH_SUN50I_H616
default AXP818_POWER if MACH_SUN8I_A83T
default SUNXI_NO_PMIC if MACH_SUNXI_H3_H5 || MACH_SUN50I || MACH_SUN8I_V3S
@@ -48,6 +49,15 @@ config AXP221_POWER
Select this to enable support for the axp221/axp223 pmic found on most
A23 and A31 boards.
+config AXP305_POWER
+ bool "axp305 pmic support"
+ depends on MACH_SUN50I_H616
+ select AXP_PMIC_BUS
+ select CMD_POWEROFF
+ ---help---
+ Select this to enable support for the axp305 pmic found on most
+ H616 boards.
+
config AXP809_POWER
bool "axp809 pmic support"
depends on MACH_SUN9I
@@ -127,11 +137,12 @@ config AXP_DCDC3_VOLT
config AXP_DCDC4_VOLT
int "axp pmic dcdc4 voltage"
- depends on AXP152_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER
+ depends on AXP152_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP305_POWER
default 1250 if AXP152_POWER
default 1200 if MACH_SUN6I
default 0 if MACH_SUN8I
default 900 if MACH_SUN9I
+ default 1500 if AXP305_POWER
---help---
Set the voltage (mV) to program the axp pmic dcdc4 at, set to 0 to
disable dcdc4.
@@ -140,6 +151,7 @@ config AXP_DCDC4_VOLT
On A23 / A33 boards dcdc4 is unused and should be disabled.
On A80 boards dcdc4 powers VDD-SYS, HDMI, USB OTG and should be 0.9V.
On A83T boards dcdc4 is used for VDD-GPU.
+ On H616 boards dcdcd is used for VCC-DRAM.
config AXP_DCDC5_VOLT
int "axp pmic dcdc5 voltage"
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 2dcc7bb99d..0bef06920a 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -6,6 +6,7 @@
obj-$(CONFIG_AXP152_POWER) += axp152.o
obj-$(CONFIG_AXP209_POWER) += axp209.o
obj-$(CONFIG_AXP221_POWER) += axp221.o
+obj-$(CONFIG_AXP305_POWER) += axp305.o
obj-$(CONFIG_AXP809_POWER) += axp809.o
obj-$(CONFIG_AXP818_POWER) += axp818.o
obj-$(CONFIG_EXYNOS_TMU) += exynos-tmu.o
diff --git a/drivers/power/axp305.c b/drivers/power/axp305.c
new file mode 100644
index 0000000000..0191e4d427
--- /dev/null
+++ b/drivers/power/axp305.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AXP305 driver
+ *
+ * (C) Copyright 2020 Jernej Skrabec <jernej.skrabec@siol.net>
+ *
+ * Based on axp221.c
+ * (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com>
+ * (C) Copyright 2013 Oliver Schinagl <oliver@schinagl.nl>
+ */
+
+#include <common.h>
+#include <command.h>
+#include <errno.h>
+#include <asm/arch/pmic_bus.h>
+#include <axp_pmic.h>
+
+#define AXP305_DCDC4_1600MV_OFFSET 46
+
+static u8 axp305_mvolt_to_cfg(int mvolt, int min, int max, int div)
+{
+ if (mvolt < min)
+ mvolt = min;
+ else if (mvolt > max)
+ mvolt = max;
+
+ return (mvolt - min) / div;
+}
+
+int axp_set_dcdc4(unsigned int mvolt)
+{
+ int ret;
+ u8 cfg;
+
+ if (mvolt >= 1600)
+ cfg = AXP305_DCDC4_1600MV_OFFSET +
+ axp305_mvolt_to_cfg(mvolt, 1600, 3300, 100);
+ else
+ cfg = axp305_mvolt_to_cfg(mvolt, 600, 1500, 20);
+
+ if (mvolt == 0)
+ return pmic_bus_clrbits(AXP305_OUTPUT_CTRL1,
+ AXP305_OUTPUT_CTRL1_DCDCD_EN);
+
+ ret = pmic_bus_write(AXP305_DCDCD_VOLTAGE, cfg);
+ if (ret)
+ return ret;
+
+ return pmic_bus_setbits(AXP305_OUTPUT_CTRL1,
+ AXP305_OUTPUT_CTRL1_DCDCD_EN);
+}
+
+int axp_init(void)
+{
+ u8 axp_chip_id;
+ int ret;
+
+ ret = pmic_bus_init();
+ if (ret)
+ return ret;
+
+ ret = pmic_bus_read(AXP305_CHIP_VERSION, &axp_chip_id);
+ if (ret)
+ return ret;
+
+ if ((axp_chip_id & AXP305_CHIP_VERSION_MASK) != 0x40)
+ return -ENODEV;
+
+ return ret;
+}
+
+#ifndef CONFIG_PSCI_RESET
+int do_poweroff(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ pmic_bus_write(AXP305_SHUTDOWN, AXP305_POWEROFF);
+
+ /* infinite loop during shutdown */
+ while (1) {}
+
+ /* not reached */
+ return 0;
+}
+#endif