Andre Przywara | 10d69c7 | 2017-12-13 01:08:01 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2017-2018 ARM Limited and Contributors. All rights reserved. |
| 3 | * |
| 4 | * SPDX-License-Identifier: BSD-3-Clause |
| 5 | */ |
| 6 | |
| 7 | #include <debug.h> |
| 8 | #include <delay_timer.h> |
| 9 | #include <errno.h> |
| 10 | #include <mmio.h> |
| 11 | #include <sunxi_mmap.h> |
| 12 | |
| 13 | #define RSB_CTRL 0x00 |
| 14 | #define RSB_CCR 0x04 |
| 15 | #define RSB_INTE 0x08 |
| 16 | #define RSB_STAT 0x0c |
| 17 | #define RSB_DADDR0 0x10 |
| 18 | #define RSB_DLEN 0x18 |
| 19 | #define RSB_DATA0 0x1c |
| 20 | #define RSB_LCR 0x24 |
| 21 | #define RSB_PMCR 0x28 |
| 22 | #define RSB_CMD 0x2c |
| 23 | #define RSB_SADDR 0x30 |
| 24 | |
| 25 | #define RSBCMD_SRTA 0xE8 |
| 26 | #define RSBCMD_RD8 0x8B |
| 27 | #define RSBCMD_RD16 0x9C |
| 28 | #define RSBCMD_RD32 0xA6 |
| 29 | #define RSBCMD_WR8 0x4E |
| 30 | #define RSBCMD_WR16 0x59 |
| 31 | #define RSBCMD_WR32 0x63 |
| 32 | |
| 33 | #define MAX_TRIES 100000 |
| 34 | |
| 35 | static int rsb_wait_bit(const char *desc, unsigned int offset, uint32_t mask) |
| 36 | { |
| 37 | uint32_t reg, tries = MAX_TRIES; |
| 38 | |
| 39 | do |
| 40 | reg = mmio_read_32(SUNXI_R_RSB_BASE + offset); |
| 41 | while ((reg & mask) && --tries); /* transaction in progress */ |
| 42 | if (reg & mask) { |
| 43 | ERROR("%s: timed out\n", desc); |
| 44 | return -ETIMEDOUT; |
| 45 | } |
| 46 | |
| 47 | return 0; |
| 48 | } |
| 49 | |
| 50 | static int rsb_wait_stat(const char *desc) |
| 51 | { |
| 52 | uint32_t reg; |
| 53 | int ret = rsb_wait_bit(desc, RSB_CTRL, BIT(7)); |
| 54 | |
| 55 | if (ret) |
| 56 | return ret; |
| 57 | |
| 58 | reg = mmio_read_32(SUNXI_R_RSB_BASE + RSB_STAT); |
| 59 | if (reg == 0x01) |
| 60 | return 0; |
| 61 | |
| 62 | ERROR("%s: 0x%x\n", desc, reg); |
| 63 | return -reg; |
| 64 | } |
| 65 | |
| 66 | /* Initialize the RSB controller. */ |
| 67 | int rsb_init_controller(void) |
| 68 | { |
| 69 | mmio_write_32(SUNXI_R_RSB_BASE + RSB_CTRL, 0x01); /* soft reset */ |
| 70 | |
| 71 | return rsb_wait_bit("RSB: reset controller", RSB_CTRL, BIT(0)); |
| 72 | } |
| 73 | |
| 74 | int rsb_read(uint8_t rt_addr, uint8_t reg_addr) |
| 75 | { |
| 76 | int ret; |
| 77 | |
| 78 | mmio_write_32(SUNXI_R_RSB_BASE + RSB_CMD, RSBCMD_RD8); /* read a byte */ |
| 79 | mmio_write_32(SUNXI_R_RSB_BASE + RSB_SADDR, rt_addr << 16); |
| 80 | mmio_write_32(SUNXI_R_RSB_BASE + RSB_DADDR0, reg_addr); |
| 81 | mmio_write_32(SUNXI_R_RSB_BASE + RSB_CTRL, 0x80);/* start transaction */ |
| 82 | |
| 83 | ret = rsb_wait_stat("RSB: read command"); |
| 84 | if (ret) |
| 85 | return ret; |
| 86 | |
| 87 | return mmio_read_32(SUNXI_R_RSB_BASE + RSB_DATA0) & 0xff; /* result */ |
| 88 | } |
| 89 | |
| 90 | int rsb_write(uint8_t rt_addr, uint8_t reg_addr, uint8_t value) |
| 91 | { |
| 92 | mmio_write_32(SUNXI_R_RSB_BASE + RSB_CMD, RSBCMD_WR8); /* byte write */ |
| 93 | mmio_write_32(SUNXI_R_RSB_BASE + RSB_SADDR, rt_addr << 16); |
| 94 | mmio_write_32(SUNXI_R_RSB_BASE + RSB_DADDR0, reg_addr); |
| 95 | mmio_write_32(SUNXI_R_RSB_BASE + RSB_DATA0, value); |
| 96 | mmio_write_32(SUNXI_R_RSB_BASE + RSB_CTRL, 0x80);/* start transaction */ |
| 97 | |
| 98 | return rsb_wait_stat("RSB: write command"); |
| 99 | } |
| 100 | |
| 101 | int rsb_set_device_mode(uint32_t device_mode) |
| 102 | { |
| 103 | mmio_write_32(SUNXI_R_RSB_BASE + RSB_PMCR, |
| 104 | (device_mode & 0x00ffffff) | BIT(31)); |
| 105 | |
| 106 | return rsb_wait_bit("RSB: set device to RSB", RSB_PMCR, BIT(31)); |
| 107 | } |
| 108 | |
| 109 | int rsb_set_bus_speed(uint32_t source_freq, uint32_t bus_freq) |
| 110 | { |
| 111 | uint32_t reg; |
| 112 | |
| 113 | if (bus_freq == 0) |
| 114 | return -EINVAL; |
| 115 | |
| 116 | reg = source_freq / bus_freq; |
| 117 | if (reg < 2) |
| 118 | return -EINVAL; |
| 119 | |
| 120 | reg = reg / 2 - 1; |
| 121 | reg |= (1U << 8); /* one cycle of CD output delay */ |
| 122 | |
| 123 | mmio_write_32(SUNXI_R_RSB_BASE + RSB_CCR, reg); |
| 124 | |
| 125 | return 0; |
| 126 | } |
| 127 | |
| 128 | /* Initialize the RSB PMIC connection. */ |
| 129 | int rsb_assign_runtime_address(uint16_t hw_addr, uint8_t rt_addr) |
| 130 | { |
| 131 | mmio_write_32(SUNXI_R_RSB_BASE + RSB_SADDR, hw_addr | (rt_addr << 16)); |
| 132 | mmio_write_32(SUNXI_R_RSB_BASE + RSB_CMD, RSBCMD_SRTA); |
| 133 | mmio_write_32(SUNXI_R_RSB_BASE + RSB_CTRL, 0x80); |
| 134 | |
| 135 | return rsb_wait_stat("RSB: set run-time address"); |
| 136 | } |