mirror of
https://github.com/Fishwaldo/build.git
synced 2025-03-28 09:41:41 +00:00
648 lines
18 KiB
Diff
648 lines
18 KiB
Diff
From 7f15fa4721edf35fa3eb6494c9518379eea4604d Mon Sep 17 00:00:00 2001
|
|
From: Andre Przywara <andre.przywara@arm.com>
|
|
Date: Sun, 19 Mar 2017 01:02:19 +0000
|
|
Subject: [PATCH] sunxi: power: refactor power specific code into extra file
|
|
|
|
At the moment the power code mixes the code to access the RSB bus with
|
|
the actual PMIC register control and setup via this bus.
|
|
Separate those two to make the code more readable, also to provide
|
|
better abstraction.
|
|
This for instance later allows using I2C to control different PMICs,
|
|
for instance.
|
|
|
|
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
|
|
---
|
|
plat/sun50iw1p1/bl31_sunxi_setup.c | 8 +-
|
|
plat/sun50iw1p1/platform.mk | 1 +
|
|
plat/sun50iw1p1/sunxi_power.c | 278 +++++++++++--------------------------
|
|
plat/sun50iw1p1/sunxi_private.h | 9 +-
|
|
plat/sun50iw1p1/sunxi_rsb.c | 198 ++++++++++++++++++++++++++
|
|
5 files changed, 286 insertions(+), 208 deletions(-)
|
|
create mode 100644 plat/sun50iw1p1/sunxi_rsb.c
|
|
|
|
diff --git a/plat/sun50iw1p1/bl31_sunxi_setup.c b/plat/sun50iw1p1/bl31_sunxi_setup.c
|
|
index dc9b3435a..7c1109f4e 100644
|
|
--- a/plat/sun50iw1p1/bl31_sunxi_setup.c
|
|
+++ b/plat/sun50iw1p1/bl31_sunxi_setup.c
|
|
@@ -246,13 +246,7 @@ void bl31_platform_setup(void)
|
|
/* Detect if this SoC is a multi-cluster one. */
|
|
plat_setup_topology();
|
|
|
|
- switch (socid) {
|
|
- case 0x1689:
|
|
- sunxi_pmic_setup();
|
|
- break;
|
|
- case 0x1718:
|
|
- break;
|
|
- }
|
|
+ sunxi_power_setup(socid);
|
|
|
|
sunxi_setup_clocks(socid);
|
|
|
|
diff --git a/plat/sun50iw1p1/platform.mk b/plat/sun50iw1p1/platform.mk
|
|
index 188e4e84d..95cff562f 100644
|
|
--- a/plat/sun50iw1p1/platform.mk
|
|
+++ b/plat/sun50iw1p1/platform.mk
|
|
@@ -52,5 +52,6 @@ BL31_SOURCES += drivers/arm/gic/arm_gic.c \
|
|
plat/sun50iw1p1/sunxi_clocks.c \
|
|
plat/sun50iw1p1/sunxi_sip_svc.c \
|
|
plat/sun50iw1p1/sunxi_scpi.c \
|
|
+ plat/sun50iw1p1/sunxi_rsb.c \
|
|
plat/sun50iw1p1/aarch64/sunxi_common.c
|
|
|
|
diff --git a/plat/sun50iw1p1/sunxi_power.c b/plat/sun50iw1p1/sunxi_power.c
|
|
index 095a0e757..5aa63fe4f 100644
|
|
--- a/plat/sun50iw1p1/sunxi_power.c
|
|
+++ b/plat/sun50iw1p1/sunxi_power.c
|
|
@@ -35,153 +35,17 @@
|
|
#include "sunxi_def.h"
|
|
#include "sunxi_private.h"
|
|
|
|
-#define R_PRCM_BASE 0x1f01400ULL
|
|
-#define R_TWI_BASE 0x1f02400ULL
|
|
-#define R_PIO_BASE 0x1f02c00ULL
|
|
-
|
|
-#define RSB_BASE 0x1f03400ULL
|
|
-#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 BIT(n) (1U << (n))
|
|
|
|
#define RUNTIME_ADDR 0x2d
|
|
#define AXP803_HW_ADDR 0x3a3
|
|
|
|
-/* Initialize the RSB controller and its pins. */
|
|
-static int init_rsb(void)
|
|
-{
|
|
- uint32_t reg;
|
|
-
|
|
- /* un-gate PIO clock */
|
|
- reg = mmio_read_32(R_PRCM_BASE + 0x28);
|
|
- mmio_write_32(R_PRCM_BASE + 0x28, reg | 0x01);
|
|
-
|
|
- /* get currently configured function for pins PL0 and PL1 */
|
|
- reg = mmio_read_32(R_PIO_BASE + 0x00);
|
|
- if ((reg & 0xff) == 0x33) {
|
|
- NOTICE("already configured for TWI\n");
|
|
- return -EBUSY;
|
|
- }
|
|
-
|
|
- if ((reg & 0xff) == 0x22) {
|
|
- NOTICE("PMIC: already configured for RSB\n");
|
|
- return -EEXIST; /* configured for RSB mode already */
|
|
- }
|
|
-
|
|
- /* switch pins PL0 and PL1 to RSB */
|
|
- mmio_write_32(R_PIO_BASE + 0, (reg & ~0xff) | 0x22);
|
|
-
|
|
- /* level 2 drive strength */
|
|
- reg = mmio_read_32(R_PIO_BASE + 0x14);
|
|
- mmio_write_32(R_PIO_BASE + 0x14, (reg & ~0x0f) | 0xa);
|
|
-
|
|
- /* set both ports to pull-up */
|
|
- reg = mmio_read_32(R_PIO_BASE + 0x1c);
|
|
- mmio_write_32(R_PIO_BASE + 0x1c, (reg & ~0x0f) | 0x5);
|
|
-
|
|
- /* assert & de-assert reset of RSB */
|
|
- reg = mmio_read_32(R_PRCM_BASE + 0xb0);
|
|
- mmio_write_32(R_PRCM_BASE + 0xb0, reg & ~0x08);
|
|
- reg = mmio_read_32(R_PRCM_BASE + 0xb0);
|
|
- mmio_write_32(R_PRCM_BASE + 0xb0, reg | 0x08);
|
|
-
|
|
- /* un-gate RSB clock */
|
|
- reg = mmio_read_32(R_PRCM_BASE + 0x28);
|
|
- mmio_write_32(R_PRCM_BASE + 0x28, reg | 0x08);
|
|
-
|
|
- mmio_write_32(RSB_BASE + RSB_CTRL, 0x01); /* soft reset */
|
|
-
|
|
- mmio_write_32(RSB_BASE + RSB_CCR, 0x11d); /* clock to 400 KHz */
|
|
-
|
|
- do {
|
|
- reg = mmio_read_32(RSB_BASE + RSB_CTRL);
|
|
- } while (reg & 1); /* transaction in progress */
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-int sunxi_pmic_read(uint8_t address)
|
|
-{
|
|
- uint32_t reg;
|
|
-
|
|
- mmio_write_32(RSB_BASE + RSB_DLEN, 0x10); /* read a byte, snake oil? */
|
|
- mmio_write_32(RSB_BASE + RSB_CMD, RSBCMD_RD8); /* read a byte */
|
|
- mmio_write_32(RSB_BASE + RSB_DADDR0, address);
|
|
- mmio_write_32(RSB_BASE + RSB_CTRL, 0x80); /* start transaction */
|
|
- do {
|
|
- reg = mmio_read_32(RSB_BASE + RSB_CTRL);
|
|
- } while (reg & 0x80); /* transaction in progress */
|
|
-
|
|
- reg = mmio_read_32(RSB_BASE + RSB_STAT);
|
|
- if (reg == 0x01) { /* transaction complete */
|
|
- reg = mmio_read_32(RSB_BASE + RSB_DATA0); /* result register */
|
|
- return reg & 0xff;
|
|
- }
|
|
-
|
|
- return -reg;
|
|
-}
|
|
-
|
|
-int sunxi_pmic_write(uint8_t address, uint8_t value)
|
|
-{
|
|
- uint32_t reg;
|
|
-
|
|
- mmio_write_32(RSB_BASE + RSB_DLEN, 0x00); /* write a byte, snake oil? */
|
|
- mmio_write_32(RSB_BASE + RSB_CMD, RSBCMD_WR8); /* write a byte */
|
|
- mmio_write_32(RSB_BASE + RSB_DADDR0, address);
|
|
- mmio_write_32(RSB_BASE + RSB_DATA0, value);
|
|
- mmio_write_32(RSB_BASE + RSB_CTRL, 0x80); /* start transaction */
|
|
- do {
|
|
- reg = mmio_read_32(RSB_BASE + RSB_CTRL);
|
|
- } while (reg & 0x80); /* transaction in progress */
|
|
-
|
|
- reg = mmio_read_32(RSB_BASE + RSB_STAT);
|
|
- if (reg == 0x01) /* transaction complete */
|
|
- return 0;
|
|
-
|
|
- return -reg;
|
|
-}
|
|
-
|
|
-static void rsb_wait(const char *desc)
|
|
-{
|
|
- uint32_t reg;
|
|
- int cnt = 0;
|
|
-
|
|
- do {
|
|
- reg = mmio_read_32(RSB_BASE + RSB_CTRL);
|
|
- cnt++;
|
|
- } while (reg & 0x80); /* transaction in progress */
|
|
-
|
|
- reg = mmio_read_32(RSB_BASE + RSB_STAT);
|
|
- if (reg == 0x01)
|
|
- return;
|
|
-
|
|
- ERROR("%s: 0x%x\n", desc, reg);
|
|
-}
|
|
-
|
|
static int axp803_set_cpu_voltage(int millivolt)
|
|
{
|
|
uint8_t reg;
|
|
|
|
if (millivolt <= 0) { /* power off system */
|
|
- sunxi_pmic_write(0x32, sunxi_pmic_read(0x32) | 0x80);
|
|
+ sunxi_rsb_write(0x32, sunxi_rsb_read(0x32) | 0x80);
|
|
return 0; /* hopefully not ... */
|
|
}
|
|
|
|
@@ -193,56 +57,30 @@ static int axp803_set_cpu_voltage(int millivolt)
|
|
else
|
|
reg = (millivolt - 500) / 10 + 0;
|
|
|
|
- sunxi_pmic_write(0x21, reg); /* DCDC2 */
|
|
+ sunxi_rsb_write(0x21, reg); /* DCDC2 */
|
|
|
|
- while (!(sunxi_pmic_read(0x21) & 0x80))
|
|
+ while (!(sunxi_rsb_read(0x21) & 0x80))
|
|
;
|
|
|
|
return 0;
|
|
}
|
|
|
|
-/* Initialize the RSB PMIC connection. */
|
|
-static int pmic_init(uint16_t hw_addr, uint8_t rt_addr)
|
|
-{
|
|
- int ret;
|
|
-
|
|
- /* Switch PMIC to RSB mode */
|
|
- mmio_write_32(RSB_BASE + RSB_PMCR,
|
|
- 0x00 | (0x3e << 8) | (0x7c << 16) | BIT(31));
|
|
- do {
|
|
- ret = mmio_read_32(RSB_BASE + RSB_PMCR);
|
|
- } while (ret & (1U << 31)); /* transaction in progress */
|
|
-
|
|
- mmio_write_32(RSB_BASE + RSB_CCR, 0x103); /* 3 MHz */
|
|
-
|
|
- mmio_write_32(RSB_BASE + RSB_SADDR, hw_addr | (rt_addr << 16));
|
|
- mmio_write_32(RSB_BASE + RSB_CMD, RSBCMD_SRTA);
|
|
- mmio_write_32(RSB_BASE + RSB_CTRL, 0x80);
|
|
- rsb_wait("set run-time address");
|
|
-
|
|
- /* Set slave runtime address */
|
|
- mmio_write_32(RSB_BASE + RSB_SADDR, rt_addr << 16);
|
|
-
|
|
- ret = sunxi_pmic_read(0x03);
|
|
- if (ret < 0) {
|
|
- ERROR("PMIC: error %d reading PMIC type\n", ret);
|
|
- return -2;
|
|
- }
|
|
-
|
|
- if ((ret & 0xcf) != 0x41) {
|
|
- ERROR("PMIC: unknown PMIC type number 0x%x\n", ret);
|
|
- return -3;
|
|
- }
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-/* Setup the PMIC: DCDC1 to 3.3V, enable DC1SW and DLDO4 */
|
|
-static int pmic_setup(void)
|
|
+/*
|
|
+ * Initial PMIC setup for boards using the AXP803 PMIC.
|
|
+ * DCDC1 must be corrected to 3.3 volts. Also we enable:
|
|
+ * - DC1SW: Ethernet PHY on most boards
|
|
+ * - DLDO1: HDMI power
|
|
+ * - DLDO4: WiFi power
|
|
+ * Technically those should be enabled by the users (via SCPI), but until
|
|
+ * U-Boot learns how to do this we do it here.
|
|
+ * Also this contains a quirk to fix the DRAM voltage on Pine64 boards,
|
|
+ * which have a wrong default (1.24V instead of 1.36V).
|
|
+ */
|
|
+static int axp803_initial_setup(void)
|
|
{
|
|
int ret;
|
|
|
|
- ret = sunxi_pmic_read(0x20);
|
|
+ ret = sunxi_rsb_read(0x20);
|
|
if (ret != 0x0e && ret != 0x11) {
|
|
int voltage = (ret & 0x1f) * 10 + 16;
|
|
|
|
@@ -253,14 +91,14 @@ static int pmic_setup(void)
|
|
|
|
if (ret != 0x11) {
|
|
/* Set DCDC1 voltage to 3.3 Volts */
|
|
- ret = sunxi_pmic_write(0x20, 0x11);
|
|
+ ret = sunxi_rsb_write(0x20, 0x11);
|
|
if (ret < 0) {
|
|
NOTICE("PMIC: error %d writing DCDC1 voltage\n", ret);
|
|
return -2;
|
|
}
|
|
}
|
|
|
|
- ret = sunxi_pmic_read(0x12);
|
|
+ ret = sunxi_rsb_read(0x12);
|
|
if ((ret & 0x37) != 0x01) {
|
|
NOTICE("PMIC: Output power control 2 is an unexpected 0x%x\n",
|
|
ret);
|
|
@@ -268,10 +106,12 @@ static int pmic_setup(void)
|
|
}
|
|
|
|
if ((ret & 0xc9) != 0xc9) {
|
|
- /* Enable DC1SW to power PHY, DLDO4 for WiFi and DLDO1 for HDMI */
|
|
- ret = sunxi_pmic_write(0x12, ret | 0xc8);
|
|
+ /* Enable DC1SW to power PHY, DLDO4 for WiFi, DLDO1 for HDMI */
|
|
+ /* TODO: keep WiFi disabled, as not needed in U-Boot? */
|
|
+ ret = sunxi_rsb_write(0x12, ret | 0xc8);
|
|
if (ret < 0) {
|
|
- NOTICE("PMIC: error %d enabling DC1SW/DLDO4/DLDO1\n", ret);
|
|
+ NOTICE("PMIC: error %d enabling DC1SW/DLDO4/DLDO1\n",
|
|
+ ret);
|
|
return -4;
|
|
}
|
|
}
|
|
@@ -283,13 +123,13 @@ static int pmic_setup(void)
|
|
* changes. This should be further confined once we are able to
|
|
* reliably detect a Pine64 board.
|
|
*/
|
|
- ret = sunxi_pmic_read(0x24); /* read DCDC5 register */
|
|
+ ret = sunxi_rsb_read(0x24); /* read DCDC5 register */
|
|
if ((ret & 0x7f) == 0x26) { /* check for 1.24V value */
|
|
NOTICE("PMIC: fixing DRAM voltage from 1.24V to 1.36V\n");
|
|
- sunxi_pmic_write(0x24, 0x2c);
|
|
+ sunxi_rsb_write(0x24, 0x2c);
|
|
}
|
|
|
|
- sunxi_pmic_write(0x15, 0x1a); /* DLDO1 = VCC3V3_HDMI voltage = 3.3V */
|
|
+ sunxi_rsb_write(0x15, 0x1a); /* DLDO1 = VCC3V3_HDMI voltage = 3.3V */
|
|
|
|
ret = sunxi_rsb_read(0x14);
|
|
sunxi_rsb_write(0x14, ret | 0x40); /* DCDC2/3 dual phase */
|
|
@@ -302,31 +142,69 @@ static int pmic_setup(void)
|
|
/*
|
|
* Program the AXP803 via the RSB bus.
|
|
*/
|
|
-int sunxi_pmic_setup(void)
|
|
+static int axp803_probe(void)
|
|
{
|
|
int ret;
|
|
|
|
- NOTICE("Configuring AXP PMIC\n");
|
|
-
|
|
- ret = init_rsb();
|
|
+ ret = sunxi_rsb_init();
|
|
if (ret && ret != -EEXIST) {
|
|
ERROR("Could not init RSB controller.\n");
|
|
return -1;
|
|
}
|
|
|
|
- if (ret != -EEXIST) {
|
|
- ret = pmic_init(AXP803_HW_ADDR, RUNTIME_ADDR);
|
|
+ if (ret == -EEXIST)
|
|
+ return ret;
|
|
+
|
|
+ ret = sunxi_rsb_configure(AXP803_HW_ADDR, RUNTIME_ADDR);
|
|
+ if (ret) {
|
|
+ ERROR("Could not configure RSB.\n");
|
|
+ return -2;
|
|
+ }
|
|
+ ret = sunxi_rsb_read(0x03);
|
|
+ if (ret < 0) {
|
|
+ ERROR("PMIC: error %d reading PMIC type\n", ret);
|
|
+ return -2;
|
|
+ }
|
|
+ if ((ret & 0xcf) != 0x41) {
|
|
+ ERROR("PMIC: unknown PMIC type number 0x%x\n", ret);
|
|
+ return -3;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+enum pmic_type {
|
|
+ PMIC_AXP803,
|
|
+} pmic_type;
|
|
+
|
|
+int sunxi_power_setup(uint16_t socid)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ switch (socid) {
|
|
+ case 0x1689:
|
|
+ pmic_type = PMIC_AXP803;
|
|
+
|
|
+ NOTICE("PMIC: Probing for AXP803 on A64\n");
|
|
+ ret = axp803_probe();
|
|
if (ret) {
|
|
- ERROR("Could not connect to AXP PMIC.\n");
|
|
- return -2;
|
|
+ ERROR("PMIC: AXP803 initialization failed: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ ret = axp803_initial_setup();
|
|
+ if (ret) {
|
|
+ ERROR("PMIC: AXP803 power setup failed: %d\n", ret);
|
|
+ return ret;
|
|
}
|
|
+ NOTICE("PMIC: AXP803 successfully setup\n");
|
|
+ break;
|
|
+ case 0x1718:
|
|
+ ret = -ENXIO;
|
|
+ break;
|
|
+ default:
|
|
+ NOTICE("power setup not defined for SoC 0x%04x\n", socid);
|
|
+ ret = -ENODEV;
|
|
}
|
|
|
|
- ret = pmic_setup();
|
|
- if (!ret)
|
|
- NOTICE("PMIC: setup successful\n");
|
|
- else
|
|
- ERROR("PMIC: setup failed: %d\n", ret);
|
|
-
|
|
return ret;
|
|
}
|
|
diff --git a/plat/sun50iw1p1/sunxi_private.h b/plat/sun50iw1p1/sunxi_private.h
|
|
index 0c8b640bd..07a329598 100644
|
|
--- a/plat/sun50iw1p1/sunxi_private.h
|
|
+++ b/plat/sun50iw1p1/sunxi_private.h
|
|
@@ -71,7 +71,7 @@ void sunxi_security_setup(void);
|
|
int sunxi_pmic_setup(void);
|
|
int sunxi_pmic_read(uint8_t address);
|
|
int sunxi_pmic_write(uint8_t address, uint8_t value);
|
|
-
|
|
+int sunxi_power_setup(uint16_t socid);
|
|
int sunxi_power_set_cpu_voltage(int millivolt);
|
|
|
|
void udelay(unsigned int delay);
|
|
@@ -90,6 +90,13 @@ int sunxi_clock_set_rate(int clocknr, uint32_t freq);
|
|
|
|
int sunxi_clock_set_cpu_clock(uint32_t freq_mhz, int enable);
|
|
|
|
+/* Declarations for sunxi_rsb.c */
|
|
+int sunxi_rsb_init(void);
|
|
+int sunxi_rsb_read(uint8_t address);
|
|
+int sunxi_rsb_write(uint8_t address, uint8_t value);
|
|
+void sunxi_rsb_wait(const char *desc);
|
|
+int sunxi_rsb_configure(uint16_t hw_addr, uint8_t rt_addr);
|
|
+
|
|
/* Gets the SPSR for BL33 entry */
|
|
uint32_t sunxi_get_spsr_for_bl33_entry(int aarch);
|
|
|
|
diff --git a/plat/sun50iw1p1/sunxi_rsb.c b/plat/sun50iw1p1/sunxi_rsb.c
|
|
new file mode 100644
|
|
index 000000000..098c8ad0f
|
|
--- /dev/null
|
|
+++ b/plat/sun50iw1p1/sunxi_rsb.c
|
|
@@ -0,0 +1,198 @@
|
|
+/*
|
|
+ * Copyright (c) 2017 ARM Limited and Contributors. All rights reserved.
|
|
+ *
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
+ * modification, are permitted provided that the following conditions are met:
|
|
+ *
|
|
+ * Redistributions of source code must retain the above copyright notice, this
|
|
+ * list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * Redistributions in binary form must reproduce the above copyright notice,
|
|
+ * this list of conditions and the following disclaimer in the documentation
|
|
+ * and/or other materials provided with the distribution.
|
|
+ *
|
|
+ * Neither the name of ARM nor the names of its contributors may be used
|
|
+ * to endorse or promote products derived from this software without specific
|
|
+ * prior written permission.
|
|
+ *
|
|
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
+ * POSSIBILITY OF SUCH DAMAGE.
|
|
+ */
|
|
+
|
|
+#include <debug.h>
|
|
+#include <plat_config.h>
|
|
+#include <mmio.h>
|
|
+#include <sys/errno.h>
|
|
+#include "sunxi_def.h"
|
|
+#include "sunxi_private.h"
|
|
+
|
|
+#define R_PRCM_BASE 0x1f01400ULL
|
|
+#define R_TWI_BASE 0x1f02400ULL
|
|
+#define R_PIO_BASE 0x1f02c00ULL
|
|
+
|
|
+#define RSB_BASE 0x1f03400ULL
|
|
+#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 BIT(n) (1U << (n))
|
|
+
|
|
+/* Initialize the RSB controller and its pins. */
|
|
+int sunxi_rsb_init(void)
|
|
+{
|
|
+ uint32_t reg;
|
|
+
|
|
+ /* un-gate PIO clock */
|
|
+ reg = mmio_read_32(R_PRCM_BASE + 0x28);
|
|
+ mmio_write_32(R_PRCM_BASE + 0x28, reg | 0x01);
|
|
+
|
|
+ /* get currently configured function for pins PL0 and PL1 */
|
|
+ reg = mmio_read_32(R_PIO_BASE + 0x00);
|
|
+ if ((reg & 0xff) == 0x33) {
|
|
+ NOTICE("already configured for TWI\n");
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ if ((reg & 0xff) == 0x22) {
|
|
+ NOTICE("PMIC: already configured for RSB\n");
|
|
+ return -EEXIST; /* configured for RSB mode already */
|
|
+ }
|
|
+
|
|
+ /* switch pins PL0 and PL1 to RSB */
|
|
+ mmio_write_32(R_PIO_BASE + 0, (reg & ~0xff) | 0x22);
|
|
+
|
|
+ /* level 2 drive strength */
|
|
+ reg = mmio_read_32(R_PIO_BASE + 0x14);
|
|
+ mmio_write_32(R_PIO_BASE + 0x14, (reg & ~0x0f) | 0xa);
|
|
+
|
|
+ /* set both ports to pull-up */
|
|
+ reg = mmio_read_32(R_PIO_BASE + 0x1c);
|
|
+ mmio_write_32(R_PIO_BASE + 0x1c, (reg & ~0x0f) | 0x5);
|
|
+
|
|
+ /* assert & de-assert reset of RSB */
|
|
+ reg = mmio_read_32(R_PRCM_BASE + 0xb0);
|
|
+ mmio_write_32(R_PRCM_BASE + 0xb0, reg & ~0x08);
|
|
+ reg = mmio_read_32(R_PRCM_BASE + 0xb0);
|
|
+ mmio_write_32(R_PRCM_BASE + 0xb0, reg | 0x08);
|
|
+
|
|
+ /* un-gate RSB clock */
|
|
+ reg = mmio_read_32(R_PRCM_BASE + 0x28);
|
|
+ mmio_write_32(R_PRCM_BASE + 0x28, reg | 0x08);
|
|
+
|
|
+ mmio_write_32(RSB_BASE + RSB_CTRL, 0x01); /* soft reset */
|
|
+
|
|
+ mmio_write_32(RSB_BASE + RSB_CCR, 0x11d); /* clock to 400 KHz */
|
|
+
|
|
+ do {
|
|
+ reg = mmio_read_32(RSB_BASE + RSB_CTRL);
|
|
+ } while (reg & 1); /* transaction in progress */
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int sunxi_rsb_read(uint8_t address)
|
|
+{
|
|
+ uint32_t reg;
|
|
+
|
|
+ mmio_write_32(RSB_BASE + RSB_DLEN, 0x10); /* read a byte, snake oil? */
|
|
+ mmio_write_32(RSB_BASE + RSB_CMD, RSBCMD_RD8); /* read a byte */
|
|
+ mmio_write_32(RSB_BASE + RSB_DADDR0, address);
|
|
+ mmio_write_32(RSB_BASE + RSB_CTRL, 0x80); /* start transaction */
|
|
+ do {
|
|
+ reg = mmio_read_32(RSB_BASE + RSB_CTRL);
|
|
+ } while (reg & 0x80); /* transaction in progress */
|
|
+
|
|
+ reg = mmio_read_32(RSB_BASE + RSB_STAT);
|
|
+ if (reg == 0x01) { /* transaction complete */
|
|
+ reg = mmio_read_32(RSB_BASE + RSB_DATA0); /* result register */
|
|
+ return reg & 0xff;
|
|
+ }
|
|
+
|
|
+ return -reg;
|
|
+}
|
|
+
|
|
+int sunxi_rsb_write(uint8_t address, uint8_t value)
|
|
+{
|
|
+ uint32_t reg;
|
|
+
|
|
+ mmio_write_32(RSB_BASE + RSB_DLEN, 0x00); /* write a byte, snake oil? */
|
|
+ mmio_write_32(RSB_BASE + RSB_CMD, RSBCMD_WR8); /* write a byte */
|
|
+ mmio_write_32(RSB_BASE + RSB_DADDR0, address);
|
|
+ mmio_write_32(RSB_BASE + RSB_DATA0, value);
|
|
+ mmio_write_32(RSB_BASE + RSB_CTRL, 0x80); /* start transaction */
|
|
+ do {
|
|
+ reg = mmio_read_32(RSB_BASE + RSB_CTRL);
|
|
+ } while (reg & 0x80); /* transaction in progress */
|
|
+
|
|
+ reg = mmio_read_32(RSB_BASE + RSB_STAT);
|
|
+ if (reg == 0x01) /* transaction complete */
|
|
+ return 0;
|
|
+
|
|
+ return -reg;
|
|
+}
|
|
+
|
|
+void sunxi_rsb_wait(const char *desc)
|
|
+{
|
|
+ uint32_t reg;
|
|
+ int cnt = 0;
|
|
+
|
|
+ do {
|
|
+ reg = mmio_read_32(RSB_BASE + RSB_CTRL);
|
|
+ cnt++;
|
|
+ } while (reg & 0x80); /* transaction in progress */
|
|
+
|
|
+ reg = mmio_read_32(RSB_BASE + RSB_STAT);
|
|
+ if (reg == 0x01)
|
|
+ return;
|
|
+
|
|
+ ERROR("%s: 0x%x\n", desc, reg);
|
|
+}
|
|
+
|
|
+/* Initialize the RSB PMIC connection. */
|
|
+int sunxi_rsb_configure(uint16_t hw_addr, uint8_t rt_addr)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ mmio_write_32(RSB_BASE + RSB_PMCR,
|
|
+ 0x00 | (0x3e << 8) | (0x7c << 16) | BIT(31));
|
|
+
|
|
+ do {
|
|
+ ret = mmio_read_32(RSB_BASE + RSB_PMCR);
|
|
+ } while (ret & (1U << 31)); /* transaction in progress */
|
|
+
|
|
+ mmio_write_32(RSB_BASE + RSB_CCR, 0x103); /* 3 MHz */
|
|
+ mmio_write_32(RSB_BASE + RSB_SADDR, hw_addr | (rt_addr << 16));
|
|
+ mmio_write_32(RSB_BASE + RSB_CMD, RSBCMD_SRTA);
|
|
+ mmio_write_32(RSB_BASE + RSB_CTRL, 0x80);
|
|
+ sunxi_rsb_wait("set run-time address");
|
|
+
|
|
+ /* Set slave runtime address */
|
|
+ mmio_write_32(RSB_BASE + RSB_SADDR, rt_addr << 16);
|
|
+
|
|
+ return 0;
|
|
+}
|