diff options
Diffstat (limited to 'plat/sun50iw1p1/sunxi_rsb.c')
-rw-r--r-- | plat/sun50iw1p1/sunxi_rsb.c | 292 |
1 files changed, 292 insertions, 0 deletions
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; +} + |