diff options
Diffstat (limited to 'drivers/allwinner/sunxi_rsb.c')
-rw-r--r-- | drivers/allwinner/sunxi_rsb.c | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/drivers/allwinner/sunxi_rsb.c b/drivers/allwinner/sunxi_rsb.c new file mode 100644 index 0000000..67f5b7e --- /dev/null +++ b/drivers/allwinner/sunxi_rsb.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2017-2018 ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <errno.h> + +#include <common/debug.h> +#include <drivers/delay_timer.h> +#include <lib/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"); +} |