diff options
-rw-r--r-- | plat/sun50iw1p1/bl31_sunxi_setup.c | 3 | ||||
-rw-r--r-- | plat/sun50iw1p1/platform.mk | 1 | ||||
-rw-r--r-- | plat/sun50iw1p1/sunxi_power.c | 287 | ||||
-rw-r--r-- | plat/sun50iw1p1/sunxi_private.h | 5 |
4 files changed, 296 insertions, 0 deletions
diff --git a/plat/sun50iw1p1/bl31_sunxi_setup.c b/plat/sun50iw1p1/bl31_sunxi_setup.c index ab40114..adb1b50 100644 --- a/plat/sun50iw1p1/bl31_sunxi_setup.c +++ b/plat/sun50iw1p1/bl31_sunxi_setup.c @@ -178,6 +178,7 @@ void bl31_early_platform_setup(bl31_params_t *from_bl2, * present. */ sunxi_security_setup(); + /* * Tell BL31 where the non-trusted software image * is located and the entry state information @@ -202,6 +203,8 @@ void bl31_platform_setup(void) /* Detect if this SoC is a multi-cluster one. */ plat_setup_topology(); + + sunxi_pmic_setup(); } /******************************************************************************* diff --git a/plat/sun50iw1p1/platform.mk b/plat/sun50iw1p1/platform.mk index 0123a04..4fa5098 100644 --- a/plat/sun50iw1p1/platform.mk +++ b/plat/sun50iw1p1/platform.mk @@ -47,6 +47,7 @@ BL31_SOURCES += drivers/arm/gic/arm_gic.c \ plat/sun50iw1p1/bl31_sunxi_setup.c \ plat/sun50iw1p1/plat_pm.c \ plat/sun50iw1p1/sunxi_security.c \ + plat/sun50iw1p1/sunxi_power.c \ plat/sun50iw1p1/sunxi_cpu_ops.c \ plat/sun50iw1p1/plat_topology.c \ plat/sun50iw1p1/aarch64/plat_helpers.S \ diff --git a/plat/sun50iw1p1/sunxi_power.c b/plat/sun50iw1p1/sunxi_power.c new file mode 100644 index 0000000..8507f7c --- /dev/null +++ b/plat/sun50iw1p1/sunxi_power.c @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <debug.h> +#include <plat_config.h> +#include <mmio.h> +#include <sys/errno.h> +#include "sunxi_def.h" +#include "sunxi_private.h" + +#define R_PRCM_BASE 0x1f01400ULL +#define R_TWI_BASE 0x1f02400ULL +#define R_PIO_BASE 0x1f02c00ULL + +#define RSB_BASE 0x1f03400ULL +#define RSB_CTRL 0x00 +#define RSB_CCR 0x04 +#define RSB_INTE 0x08 +#define RSB_STAT 0x0c +#define RSB_DADDR0 0x10 +#define RSB_DLEN 0x18 +#define RSB_DATA0 0x1c +#define RSB_LCR 0x24 +#define RSB_PMCR 0x28 +#define RSB_CMD 0x2c +#define RSB_SADDR 0x30 + +#define RSBCMD_SRTA 0xE8 +#define RSBCMD_RD8 0x8B +#define RSBCMD_RD16 0x9C +#define RSBCMD_RD32 0xA6 +#define RSBCMD_WR8 0x4E +#define RSBCMD_WR16 0x59 +#define RSBCMD_WR32 0x63 + +#define BIT(n) (1U << (n)) + +#define RUNTIME_ADDR 0x2d +#define AXP803_HW_ADDR 0x3a3 + +/* Initialize the RSB controller and its pins. */ +static int init_rsb(void) +{ + uint32_t reg; + + /* un-gate PIO clock */ + reg = mmio_read_32(R_PRCM_BASE + 0x28); + mmio_write_32(R_PRCM_BASE + 0x28, reg | 0x01); + + /* get currently configured function for pins PL0 and PL1 */ + reg = mmio_read_32(R_PIO_BASE + 0x00); + if ((reg & 0xff) == 0x33) { + NOTICE("already configured for TWI\n"); + return -EBUSY; + } + + if ((reg & 0xff) == 0x22) { + NOTICE("PMIC: already configured for RSB\n"); + return -EEXIST; /* configured for RSB mode already */ + } + + /* switch pins PL0 and PL1 to RSB */ + mmio_write_32(R_PIO_BASE + 0, (reg & ~0xff) | 0x22); + + /* level 2 drive strength */ + reg = mmio_read_32(R_PIO_BASE + 0x14); + mmio_write_32(R_PIO_BASE + 0x14, (reg & ~0x0f) | 0xa); + + /* set both ports to pull-up */ + reg = mmio_read_32(R_PIO_BASE + 0x1c); + mmio_write_32(R_PIO_BASE + 0x1c, (reg & ~0x0f) | 0x5); + + /* assert & de-assert reset of RSB */ + reg = mmio_read_32(R_PRCM_BASE + 0xb0); + mmio_write_32(R_PRCM_BASE + 0xb0, reg & ~0x08); + reg = mmio_read_32(R_PRCM_BASE + 0xb0); + mmio_write_32(R_PRCM_BASE + 0xb0, reg | 0x08); + + /* un-gate RSB clock */ + reg = mmio_read_32(R_PRCM_BASE + 0x28); + mmio_write_32(R_PRCM_BASE + 0x28, reg | 0x08); + + mmio_write_32(RSB_BASE + RSB_CTRL, 0x01); /* soft reset */ + + mmio_write_32(RSB_BASE + RSB_CCR, 0x11d); /* clock to 400 KHz */ + + do { + reg = mmio_read_32(RSB_BASE + RSB_CTRL); + } while (reg & 1); /* transaction in progress */ + + return 0; +} + +int sunxi_pmic_read(uint8_t address) +{ + uint32_t reg; + + mmio_write_32(RSB_BASE + RSB_DLEN, 0x10); /* read a byte, snake oil? */ + mmio_write_32(RSB_BASE + RSB_CMD, RSBCMD_RD8); /* read a byte */ + mmio_write_32(RSB_BASE + RSB_DADDR0, address); + mmio_write_32(RSB_BASE + RSB_CTRL, 0x80); /* start transaction */ + do { + reg = mmio_read_32(RSB_BASE + RSB_CTRL); + } while (reg & 0x80); /* transaction in progress */ + + reg = mmio_read_32(RSB_BASE + RSB_STAT); + if (reg == 0x01) { /* transaction complete */ + reg = mmio_read_32(RSB_BASE + RSB_DATA0); /* result register */ + return reg & 0xff; + } + + return -reg; +} + +int sunxi_pmic_write(uint8_t address, uint8_t value) +{ + uint32_t reg; + + mmio_write_32(RSB_BASE + RSB_DLEN, 0x00); /* write a byte, snake oil? */ + mmio_write_32(RSB_BASE + RSB_CMD, RSBCMD_WR8); /* write a byte */ + mmio_write_32(RSB_BASE + RSB_DADDR0, address); + mmio_write_32(RSB_BASE + RSB_DATA0, value); + mmio_write_32(RSB_BASE + RSB_CTRL, 0x80); /* start transaction */ + do { + reg = mmio_read_32(RSB_BASE + RSB_CTRL); + } while (reg & 0x80); /* transaction in progress */ + + reg = mmio_read_32(RSB_BASE + RSB_STAT); + if (reg == 0x01) /* transaction complete */ + return 0; + + return -reg; +} + +static void rsb_wait(const char *desc) +{ + uint32_t reg; + int cnt = 0; + + do { + reg = mmio_read_32(RSB_BASE + RSB_CTRL); + cnt++; + } while (reg & 0x80); /* transaction in progress */ + + reg = mmio_read_32(RSB_BASE + RSB_STAT); + if (reg == 0x01) + return; + + ERROR("%s: 0x%x\n", desc, reg); +} + +/* Initialize the RSB PMIC connection. */ +static int pmic_init(uint16_t hw_addr, uint8_t rt_addr) +{ + int ret; + + /* Switch PMIC to RSB mode */ + mmio_write_32(RSB_BASE + RSB_PMCR, + 0x00 | (0x3e << 8) | (0x7c << 16) | BIT(31)); + do { + ret = mmio_read_32(RSB_BASE + RSB_PMCR); + } while (ret & (1U << 31)); /* transaction in progress */ + + mmio_write_32(RSB_BASE + RSB_CCR, 0x103); /* 3 MHz */ + + mmio_write_32(RSB_BASE + RSB_SADDR, hw_addr | (rt_addr << 16)); + mmio_write_32(RSB_BASE + RSB_CMD, RSBCMD_SRTA); + mmio_write_32(RSB_BASE + RSB_CTRL, 0x80); + rsb_wait("set run-time address"); + + /* Set slave runtime address */ + mmio_write_32(RSB_BASE + RSB_SADDR, rt_addr << 16); + + ret = sunxi_pmic_read(0x03); + if (ret < 0) { + ERROR("PMIC: error %d reading PMIC type\n", ret); + return -2; + } + + if ((ret & 0xcf) != 0x41) { + ERROR("PMIC: unknown PMIC type number 0x%x\n", ret); + return -3; + } + + return 0; +} + +/* Setup the PMIC: DCDC1 to 3.3V, enable DC1SW */ +static int pmic_setup(void) +{ + int ret; + + ret = sunxi_pmic_read(0x20); + if (ret != 0x0e && ret != 0x11) { + int voltage = (ret & 0x1f) * 10 + 16; + + NOTICE("PMIC: DCDC1 voltage is an unexpected %d.%dV\n", + voltage / 10, voltage % 10); + return -1; + } + + if (ret != 0x11) { + /* Set DCDC1 voltage to 3.3 Volts */ + ret = sunxi_pmic_write(0x20, 0x11); + if (ret < 0) { + NOTICE("PMIC: error %d writing DCDC1 voltage\n", ret); + return -2; + } + } + + ret = sunxi_pmic_read(0x12); + if (ret != 0x01 && ret != 0x81) { + NOTICE("PMIC: Output power control 2 is an unexpected 0x%x\n", + ret); + return -3; + } + + if (ret != 0x81) { + /* Enable DC1SW to power PHY */ + ret = sunxi_pmic_write(0x12, ret | 0x80); + if (ret < 0) { + NOTICE("PMIC: error %d enabling DC1SW\n", ret); + return -4; + } + } + + return 0; +} + +/* + * Program the AXP803 via the RSB bus. + */ +int sunxi_pmic_setup(void) +{ + int ret; + + NOTICE("Configuring AXP PMIC\n"); + + ret = init_rsb(); + if (ret && ret != -EEXIST) { + ERROR("Could not init RSB controller.\n"); + return -1; + } + + if (ret != -EEXIST) { + ret = pmic_init(AXP803_HW_ADDR, RUNTIME_ADDR); + if (ret) { + ERROR("Could not connect to AXP PMIC.\n"); + return -2; + } + } + + ret = pmic_setup(); + if (!ret) + NOTICE("PMIC: setup successful\n"); + else + ERROR("PMIC: setup failed: %d\n", ret); + + return ret; +} diff --git a/plat/sun50iw1p1/sunxi_private.h b/plat/sun50iw1p1/sunxi_private.h index afb5943..cfc7ff8 100644 --- a/plat/sun50iw1p1/sunxi_private.h +++ b/plat/sun50iw1p1/sunxi_private.h @@ -65,6 +65,11 @@ void sunxi_io_setup(void); /* Declarations for sunxi_security.c */ void sunxi_security_setup(void); +/* Declarations for sunxi_power.c */ +int sunxi_pmic_setup(void); +int sunxi_pmic_read(uint8_t address); +int sunxi_pmic_write(uint8_t address, uint8_t value); + /* Gets the SPSR for BL33 entry */ uint32_t sunxi_get_spsr_for_bl33_entry(int aarch); |