diff options
-rw-r--r-- | plat/sun50iw1p1/bl31_sunxi_setup.c | 5 | ||||
-rw-r--r-- | plat/sun50iw1p1/platform.mk | 1 | ||||
-rw-r--r-- | plat/sun50iw1p1/sunxi_power.c | 199 | ||||
-rw-r--r-- | plat/sun50iw1p1/sunxi_private.h | 5 | ||||
-rw-r--r-- | plat/sun50iw1p1/sunxi_rsb.c | 292 |
5 files changed, 323 insertions, 179 deletions
diff --git a/plat/sun50iw1p1/bl31_sunxi_setup.c b/plat/sun50iw1p1/bl31_sunxi_setup.c index 3e30814..78d3edc 100644 --- a/plat/sun50iw1p1/bl31_sunxi_setup.c +++ b/plat/sun50iw1p1/bl31_sunxi_setup.c @@ -235,6 +235,7 @@ void bl31_early_platform_setup(bl31_params_t *from_bl2, ******************************************************************************/ void bl31_platform_setup(void) { + int ret; uint16_t socid; /* Initialize the gic cpu and distributor interfaces */ @@ -248,6 +249,10 @@ void bl31_platform_setup(void) switch (socid) { case 0x1689: + ret = sunxi_rsb_init(); + if (ret) + ERROR("Could not init RSB controller.\n"); + sunxi_pmic_setup(); break; case 0x1718: diff --git a/plat/sun50iw1p1/platform.mk b/plat/sun50iw1p1/platform.mk index b788f81..bc20110 100644 --- a/plat/sun50iw1p1/platform.mk +++ b/plat/sun50iw1p1/platform.mk @@ -45,6 +45,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_rsb.c \ plat/sun50iw1p1/sunxi_power.c \ plat/sun50iw1p1/sunxi_cpu_ops.c \ plat/sun50iw1p1/plat_topology.c \ diff --git a/plat/sun50iw1p1/sunxi_power.c b/plat/sun50iw1p1/sunxi_power.c index 9ab605e..827f0d5 100644 --- a/plat/sun50iw1p1/sunxi_power.c +++ b/plat/sun50iw1p1/sunxi_power.c @@ -35,181 +35,22 @@ #include "sunxi_def.h" #include "sunxi_private.h" -#define R_PRCM_BASE 0x1f01400ULL -#define R_TWI_BASE 0x1f02400ULL -#define R_PIO_BASE 0x1f02c00ULL +#define PMIC_RSB_RT_ADDR 0x2d -#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) +int sunxi_pmic_read(uint8_t reg) { - 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; + uint32_t val; + int ret; - ERROR("%s: 0x%x\n", desc, reg); + ret = rsb_read(PMIC_RSB_RT_ADDR, reg, &val, 1); + if (ret) + return ret; + return val; } -/* Initialize the RSB PMIC connection. */ -static int pmic_init(uint16_t hw_addr, uint8_t rt_addr) +int sunxi_pmic_write(uint8_t reg, uint8_t value) { - 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; + return rsb_write(PMIC_RSB_RT_ADDR, reg, value, 1); } /* Setup the PMIC: DCDC1 to 3.3V, enable DC1SW and DLDO4 */ @@ -270,20 +111,20 @@ int sunxi_pmic_setup(void) NOTICE("Configuring AXP PMIC\n"); - ret = init_rsb(); - if (ret && ret != -EEXIST) { - ERROR("Could not init RSB controller.\n"); - return -1; + /* Test PMIC communication */ + ret = sunxi_pmic_read(0x03); + if (ret < 0) { + ERROR("PMIC: error %d reading PMIC IC type register\n", ret); + return -2; } - if (ret != -EEXIST) { - ret = pmic_init(AXP803_HW_ADDR, RUNTIME_ADDR); - if (ret) { - ERROR("Could not connect to AXP PMIC.\n"); - return -2; - } + /* Check IC type number equals 0b01xx00001 */ + if ((ret & 0xcf) != 0x41) { + ERROR("PMIC: unknown PMIC IC type 0x%x\n", ret); + return -3; } + /* Setup the PMIC */ ret = pmic_setup(); if (!ret) NOTICE("PMIC: setup successful\n"); diff --git a/plat/sun50iw1p1/sunxi_private.h b/plat/sun50iw1p1/sunxi_private.h index 4750056..34e36d8 100644 --- a/plat/sun50iw1p1/sunxi_private.h +++ b/plat/sun50iw1p1/sunxi_private.h @@ -67,6 +67,11 @@ void sunxi_io_setup(void); /* Declarations for sunxi_security.c */ void sunxi_security_setup(void); +/* Declarations for sunxi_rsb.c */ +int rsb_read(uint8_t rt_addr, uint8_t addr, uint32_t *val, size_t size); +int rsb_write(uint8_t rt_addr, uint8_t addr, uint32_t val, size_t size); +int sunxi_rsb_init(void); + /* Declarations for sunxi_power.c */ int sunxi_pmic_setup(void); int sunxi_pmic_read(uint8_t address); diff --git a/plat/sun50iw1p1/sunxi_rsb.c b/plat/sun50iw1p1/sunxi_rsb.c new file mode 100644 index 0000000..7d7210e --- /dev/null +++ b/plat/sun50iw1p1/sunxi_rsb.c @@ -0,0 +1,292 @@ +/* + * 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 ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#define BIT(n) (1U << (n)) + +#define AXP803_HW_ADDR 0x3a3 + +/* + * RSB has 15 valid runtime addresses. + * Allwinner typically skips the first one for unknown reasons. + */ +#define RUNTIME_ADDR0 0x17 +#define RUNTIME_ADDR1 0x2d +#define RUNTIME_ADDR2 0x3a +#define RUNTIME_ADDR3 0x4e +#define RUNTIME_ADDR4 0x59 +#define RUNTIME_ADDR5 0x63 +#define RUNTIME_ADDR6 0x74 +#define RUNTIME_ADDR7 0x8b +#define RUNTIME_ADDR8 0x9c +#define RUNTIME_ADDR9 0xa6 +#define RUNTIME_ADDR10 0xb1 +#define RUNTIME_ADDR11 0xc5 +#define RUNTIME_ADDR12 0xd2 +#define RUNTIME_ADDR13 0xe8 +#define RUNTIME_ADDR14 0xff + +#define R_PRCM_BASE 0x01f01400ULL +#define R_PIO_BASE 0x01f02c00ULL +#define R_RSB_BASE 0x01f03400ULL + +#define RSB_CTRL 0x00 +#define RSB_CCR 0x04 +#define RSB_INTE 0x08 +#define RSB_INTS 0x0c +#define RSB_AR 0x10 +#define RSB_DATA 0x1c +#define RSB_LCR 0x24 +#define RSB_DMCR 0x28 +#define RSB_CMD 0x2c +#define RSB_DAR 0x30 + +#define RSB_CTRL_START_TRANS BIT(7) +#define RSB_CTRL_ABORT_TRANS BIT(6) +#define RSB_CTRL_SOFT_RESET BIT(0) + +#define RSB_INTS_TRAS_OVER BIT(0) + +#define RSB_CMD_STRA 0xE8 /* SeT Run-time Address */ +#define RSB_CMD_RD8 0x8B /* ReaD 8 bit */ +#define RSB_CMD_RD16 0x9C +#define RSB_CMD_RD32 0xA6 +#define RSB_CMD_WR8 0x4E +#define RSB_CMD_WR16 0x59 +#define RSB_CMD_WR32 0x63 + +#define RSB_DMCR_DEVICE_START BIT(31) +#define RSB_DMCR_MODE_DATA (0x7c << 16) +#define RSB_DMCR_MODE_REG (0x3e << 8) +#define RSB_DMCR_DEV_ADDR 0x00 + +struct rsb_slave { + uint16_t hw_addr; + uint8_t rt_addr; +}; + +static struct rsb_slave rsb_slaves[] = { + { AXP803_HW_ADDR, RUNTIME_ADDR1 }, /* PMIC */ +}; + +static inline void rsb_cmd(uint8_t cmd) +{ + uint32_t reg; + + /* Start the command */ + mmio_write_32(R_RSB_BASE + RSB_CTRL, cmd); + + /* + * The control register clears the set command bit, + * when the command has finished. + */ + do { + reg = mmio_read_32(R_RSB_BASE + RSB_CTRL); + } while (reg & cmd); +} + +int rsb_read(uint8_t rt_addr, uint8_t addr, uint32_t *val, size_t size) +{ + uint32_t reg, cmd, mask; + + switch (size) { + case 1: + cmd = RSB_CMD_RD8; + mask = 0xff; + break; + case 2: + cmd = RSB_CMD_RD16; + mask = 0xffff; + break; + case 3: + cmd = RSB_CMD_RD32; + mask = 0xffffffff; + break; + default: + ERROR("Invalid RSB read size %u", size); + return -1; + } + + mmio_write_32(R_RSB_BASE + RSB_CMD, cmd); + mmio_write_32(R_RSB_BASE + RSB_DAR, (rt_addr << 16)); + mmio_write_32(R_RSB_BASE + RSB_AR, addr); + + /* Start transaction and wait for completion */ + rsb_cmd(RSB_CTRL_START_TRANS); + + /* Read status */ + reg = mmio_read_32(R_RSB_BASE + RSB_INTS); + if (reg != RSB_INTS_TRAS_OVER) + return -reg; + + /* Get data */ + reg = mmio_read_32(R_RSB_BASE + RSB_DATA); + + if (val) + *val = reg & mask; + + return 0; +} + +int rsb_write(uint8_t rt_addr, uint8_t addr, uint32_t val, size_t size) +{ + uint32_t reg, cmd; + + switch (size) { + case 1: + cmd = RSB_CMD_WR8; + break; + case 2: + cmd = RSB_CMD_WR16; + break; + case 3: + cmd = RSB_CMD_WR32; + break; + default: + ERROR("Invalid RSB read size %u", size); + return -1; + } + + mmio_write_32(R_RSB_BASE + RSB_CMD, cmd); + mmio_write_32(R_RSB_BASE + RSB_DAR, (rt_addr << 16)); + mmio_write_32(R_RSB_BASE + RSB_AR, addr); + mmio_write_32(R_RSB_BASE + RSB_DATA, val); + + /* Start transaction and wait for completion */ + rsb_cmd(RSB_CTRL_START_TRANS); + + /* Read status */ + reg = mmio_read_32(R_RSB_BASE + RSB_INTS); + if (reg != RSB_INTS_TRAS_OVER) + return -1; + + return 0; +} + +/* Initialize the RSB PMIC connection. */ +static int sunxi_rsb_setup_slaves(void) +{ + size_t i; + int ret; + uint32_t reg; + + /* Initialize all devices on the bus into RSB mode */ + reg = RSB_DMCR_DEVICE_START | RSB_DMCR_MODE_DATA | + RSB_DMCR_MODE_REG | RSB_DMCR_DEV_ADDR; + mmio_write_32(R_RSB_BASE + RSB_DMCR, reg); + do { + ret = mmio_read_32(R_RSB_BASE + RSB_DMCR); + } while (ret & RSB_DMCR_DEVICE_START); + + /* Set clock speed to 3 Mhz */ + mmio_write_32(R_RSB_BASE + RSB_CCR, 0x103); + + + for (i = 0; i < ARRAY_SIZE(rsb_slaves); i++) { + uint16_t hw_addr = rsb_slaves[i].hw_addr; + uint8_t rt_addr = rsb_slaves[i].rt_addr; + + /* Set run-time address for given hardware address */ + mmio_write_32(R_RSB_BASE + RSB_DAR, hw_addr | (rt_addr << 16)); + /* Command: set the run-time address to the device */ + mmio_write_32(R_RSB_BASE + RSB_CMD, RSB_CMD_STRA); + + /* Start transaction and wait for completion */ + rsb_cmd(RSB_CTRL_START_TRANS); + + /* Read status */ + reg = mmio_read_32(R_RSB_BASE + RSB_INTS); + if (reg != RSB_INTS_TRAS_OVER) + ERROR("Could not set run-time address: 0x%x\n", reg); + } + + return 0; +} + +/* Initialize the RSB controller and its pins. */ +int sunxi_rsb_init(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("RSB: already configured for TWI\n"); + return -1; + } + + if ((reg & 0xff) == 0x22) { + NOTICE("RSB: already configured for RSB\n"); + return 0; /* 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); + + /* Soft reset of RSB block */ + rsb_cmd(RSB_CTRL_SOFT_RESET); + + /* Set clock to 400 kHz */ + mmio_write_32(R_RSB_BASE + RSB_CCR, 0x11d); + + sunxi_rsb_setup_slaves(); + + return 0; +} + |