/* * Copyright (c) 2017-2018 ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #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"); }