allwinner: Add RSB driver
authorAndre Przywara <andre.przywara@arm.com>
Wed, 13 Dec 2017 01:08:01 +0000 (01:08 +0000)
committerAndre Przywara <andre.przywara@arm.com>
Sat, 20 Oct 2018 15:23:59 +0000 (16:23 +0100)
The "Reduced Serial Bus" is an Allwinner specific bus, bearing many
similarities with I2C. It sports a much higher bus frequency, though,
(typically 3 MHz) and requires much less handholding for the typical
task of manipulating slave registers (fire-and-forget).
On most A64 boards this bus is used to connect the PMIC to the SoC.

This driver provides basic primitives to read and write slave registers,
it will be later used by the PMIC code.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
drivers/allwinner/sunxi_rsb.c [new file with mode: 0644]
include/drivers/allwinner/sunxi_rsb.h [new file with mode: 0644]

diff --git a/drivers/allwinner/sunxi_rsb.c b/drivers/allwinner/sunxi_rsb.c
new file mode 100644 (file)
index 0000000..7075c67
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2017-2018 ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <debug.h>
+#include <delay_timer.h>
+#include <errno.h>
+#include <mmio.h>
+#include <sunxi_mmap.h>
+
+#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 MAX_TRIES      100000
+
+static int rsb_wait_bit(const char *desc, unsigned int offset, uint32_t mask)
+{
+       uint32_t reg, tries = MAX_TRIES;
+
+       do
+               reg = mmio_read_32(SUNXI_R_RSB_BASE + offset);
+       while ((reg & mask) && --tries);        /* transaction in progress */
+       if (reg & mask) {
+               ERROR("%s: timed out\n", desc);
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static int rsb_wait_stat(const char *desc)
+{
+       uint32_t reg;
+       int ret = rsb_wait_bit(desc, RSB_CTRL, BIT(7));
+
+       if (ret)
+               return ret;
+
+       reg = mmio_read_32(SUNXI_R_RSB_BASE + RSB_STAT);
+       if (reg == 0x01)
+               return 0;
+
+       ERROR("%s: 0x%x\n", desc, reg);
+       return -reg;
+}
+
+/* Initialize the RSB controller. */
+int rsb_init_controller(void)
+{
+       mmio_write_32(SUNXI_R_RSB_BASE + RSB_CTRL, 0x01); /* soft reset */
+
+       return rsb_wait_bit("RSB: reset controller", RSB_CTRL, BIT(0));
+}
+
+int rsb_read(uint8_t rt_addr, uint8_t reg_addr)
+{
+       int ret;
+
+       mmio_write_32(SUNXI_R_RSB_BASE + RSB_CMD, RSBCMD_RD8); /* read a byte */
+       mmio_write_32(SUNXI_R_RSB_BASE + RSB_SADDR, rt_addr << 16);
+       mmio_write_32(SUNXI_R_RSB_BASE + RSB_DADDR0, reg_addr);
+       mmio_write_32(SUNXI_R_RSB_BASE + RSB_CTRL, 0x80);/* start transaction */
+
+       ret = rsb_wait_stat("RSB: read command");
+       if (ret)
+               return ret;
+
+       return mmio_read_32(SUNXI_R_RSB_BASE + RSB_DATA0) & 0xff; /* result */
+}
+
+int rsb_write(uint8_t rt_addr, uint8_t reg_addr, uint8_t value)
+{
+       mmio_write_32(SUNXI_R_RSB_BASE + RSB_CMD, RSBCMD_WR8);  /* byte write */
+       mmio_write_32(SUNXI_R_RSB_BASE + RSB_SADDR, rt_addr << 16);
+       mmio_write_32(SUNXI_R_RSB_BASE + RSB_DADDR0, reg_addr);
+       mmio_write_32(SUNXI_R_RSB_BASE + RSB_DATA0, value);
+       mmio_write_32(SUNXI_R_RSB_BASE + RSB_CTRL, 0x80);/* start transaction */
+
+       return rsb_wait_stat("RSB: write command");
+}
+
+int rsb_set_device_mode(uint32_t device_mode)
+{
+       mmio_write_32(SUNXI_R_RSB_BASE + RSB_PMCR,
+                     (device_mode & 0x00ffffff) | BIT(31));
+
+       return rsb_wait_bit("RSB: set device to RSB", RSB_PMCR, BIT(31));
+}
+
+int rsb_set_bus_speed(uint32_t source_freq, uint32_t bus_freq)
+{
+       uint32_t reg;
+
+       if (bus_freq == 0)
+               return -EINVAL;
+
+       reg = source_freq / bus_freq;
+       if (reg < 2)
+               return -EINVAL;
+
+       reg = reg / 2 - 1;
+       reg |= (1U << 8);               /* one cycle of CD output delay */
+
+       mmio_write_32(SUNXI_R_RSB_BASE + RSB_CCR, reg);
+
+       return 0;
+}
+
+/* Initialize the RSB PMIC connection. */
+int rsb_assign_runtime_address(uint16_t hw_addr, uint8_t rt_addr)
+{
+       mmio_write_32(SUNXI_R_RSB_BASE + RSB_SADDR, hw_addr | (rt_addr << 16));
+       mmio_write_32(SUNXI_R_RSB_BASE + RSB_CMD, RSBCMD_SRTA);
+       mmio_write_32(SUNXI_R_RSB_BASE + RSB_CTRL, 0x80);
+
+       return rsb_wait_stat("RSB: set run-time address");
+}
diff --git a/include/drivers/allwinner/sunxi_rsb.h b/include/drivers/allwinner/sunxi_rsb.h
new file mode 100644 (file)
index 0000000..5a69d35
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2017-2018 ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SUNXI_RSB_H
+#define SUNXI_RSB_H
+
+#include <stdint.h>
+
+int rsb_init_controller(void);
+int rsb_set_bus_speed(uint32_t source_freq, uint32_t bus_freq);
+int rsb_set_device_mode(uint32_t device_mode);
+int rsb_assign_runtime_address(uint16_t hw_addr, uint8_t rt_addr);
+
+int rsb_read(uint8_t rt_addr, uint8_t reg_addr);
+int rsb_write(uint8_t rt_addr, uint8_t reg_addr, uint8_t value);
+
+#endif