summaryrefslogtreecommitdiff
path: root/plat/sun50iw1p1/sunxi_rsb.c
diff options
context:
space:
mode:
Diffstat (limited to 'plat/sun50iw1p1/sunxi_rsb.c')
-rw-r--r--plat/sun50iw1p1/sunxi_rsb.c292
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;
+}
+