139 lines
3.2 KiB
C
139 lines
3.2 KiB
C
/*
|
|
* 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");
|
|
}
|