/* * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. * Copyright (C) 2017, Theobroma Systems. * * 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 #include #include #include #include "sunxi_def.h" #include "sunxi_private.h" #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #define BIT(n) (1U << (n)) /* RSB-RT address of the AXP803 */ #define PMIC_RSB_RT_ADDR 0x2d #define DVM_FINISHED_BIT BIT(7) struct regulator_details { enum axp803_regulator regulator; /* * For two-step regulators: * mvolt_low-mvolt_mid: mvolt_step1 / step * mvolt_mid+mvolt_step2-mvolt_high: mvolt_step2 / step * * For one-step regulators (set mvolt_mid and mvolt_step2 to 0): * mvolt_low-mvolt_high: mvolt_step1 / step */ uint32_t mvolt_low; uint32_t mvolt_mid; uint32_t mvolt_high; uint32_t mvolt_step1; uint32_t mvolt_step2; uint32_t voltage_control_reg; int dvm; /* DVM used or not */ uint32_t onoff_control_reg; uint8_t onoff_control_bit; }; static struct regulator_details regulator_details[] = { { .regulator = AXP803_DLDO1, .mvolt_low = 700, .mvolt_high = 3300, .mvolt_step1 = 100, .voltage_control_reg = 0x15, .onoff_control_reg = 0x12, .onoff_control_bit = 3, }, { .regulator = AXP803_DLDO2, .mvolt_low = 700, .mvolt_mid = 3400, .mvolt_high = 4200, .mvolt_step1 = 100, .mvolt_step2 = 200, .voltage_control_reg = 0x16, .onoff_control_reg = 0x12, .onoff_control_bit = 4, }, { .regulator = AXP803_DLDO3, .mvolt_low = 700, .mvolt_high = 3300, .mvolt_step1 = 100, .voltage_control_reg = 0x15, .onoff_control_reg = 0x12, .onoff_control_bit = 5, }, { .regulator = AXP803_DLDO4, .mvolt_low = 700, .mvolt_high = 3300, .mvolt_step1 = 100, .voltage_control_reg = 0x18, .onoff_control_reg = 0x12, .onoff_control_bit = 6, }, { .regulator = AXP803_ELDO1, .mvolt_low = 700, .mvolt_high = 1900, .mvolt_step1 = 50, .voltage_control_reg = 0x19, .onoff_control_reg = 0x12, .onoff_control_bit = 0, }, { .regulator = AXP803_ELDO2, .mvolt_low = 700, .mvolt_high = 1900, .mvolt_step1 = 50, .voltage_control_reg = 0x1a, .onoff_control_reg = 0x12, .onoff_control_bit = 1, }, { .regulator = AXP803_ELDO3, .mvolt_low = 700, .mvolt_high = 1900, .mvolt_step1 = 50, .voltage_control_reg = 0x1b, .onoff_control_reg = 0x12, .onoff_control_bit = 2, }, { .regulator = AXP803_FLDO1, .mvolt_low = 700, .mvolt_high = 1450, .mvolt_step1 = 50, .voltage_control_reg = 0x1c, .onoff_control_reg = 0x13, .onoff_control_bit = 2, }, { .regulator = AXP803_FLDO2, .mvolt_low = 700, .mvolt_high = 1450, .mvolt_step1 = 50, .voltage_control_reg = 0x1d, .onoff_control_reg = 0x13, .onoff_control_bit = 3, }, { .regulator = AXP803_DCDC1, .mvolt_low = 1600, .mvolt_high = 3400, .mvolt_step1 = 100, .voltage_control_reg = 0x20, .onoff_control_reg = 0x10, .onoff_control_bit = 0, }, { .regulator = AXP803_DCDC2, .mvolt_low = 500, .mvolt_mid = 1200, .mvolt_high = 1300, .mvolt_step1 = 10, .mvolt_step2 = 20, .voltage_control_reg = 0x21, .dvm = 1, .onoff_control_reg = 0x10, .onoff_control_bit = 1, }, { .regulator = AXP803_DCDC3, .mvolt_low = 500, .mvolt_mid = 1200, .mvolt_high = 1300, .mvolt_step1 = 10, .mvolt_step2 = 20, .voltage_control_reg = 0x22, .dvm = 1, .onoff_control_reg = 0x10, .onoff_control_bit = 2, }, { .regulator = AXP803_DCDC4, .mvolt_low = 500, .mvolt_mid = 1200, .mvolt_high = 1300, .mvolt_step1 = 10, .mvolt_step2 = 20, .voltage_control_reg = 0x23, .dvm = 1, .onoff_control_reg = 0x10, .onoff_control_bit = 3, }, { .regulator = AXP803_DCDC5, .mvolt_low = 800, .mvolt_mid = 1120, .mvolt_high = 1840, .mvolt_step1 = 10, .mvolt_step2 = 20, .voltage_control_reg = 0x24, .dvm = 1, .onoff_control_reg = 0x10, .onoff_control_bit = 4, }, { .regulator = AXP803_DCDC6, .mvolt_low = 600, .mvolt_mid = 1100, .mvolt_high = 1520, .mvolt_step1 = 10, .mvolt_step2 = 20, .voltage_control_reg = 0x25, .dvm = 1, .onoff_control_reg = 0x10, .onoff_control_bit = 5, }, { .regulator = AXP803_ALDO1, .mvolt_low = 700, .mvolt_high = 3300, .mvolt_step1 = 100, .voltage_control_reg = 0x28, .onoff_control_reg = 0x13, .onoff_control_bit = 5, }, { .regulator = AXP803_ALDO2, .mvolt_low = 700, .mvolt_high = 3300, .mvolt_step1 = 100, .voltage_control_reg = 0x29, .onoff_control_reg = 0x13, .onoff_control_bit = 6, }, { .regulator = AXP803_ALDO3, .mvolt_low = 700, .mvolt_high = 3300, .mvolt_step1 = 100, .voltage_control_reg = 0x2a, .onoff_control_reg = 0x13, .onoff_control_bit = 7, } }; static struct regulator_details* get_regulator_details(enum axp803_regulator r) { size_t i; for (i = 0; i < ARRAY_SIZE(regulator_details); i++) { struct regulator_details *rd = ®ulator_details[i]; if (rd->regulator == r) return rd; } return NULL; } int sunxi_pmic_read(uint8_t reg, uint8_t *val) { uint32_t v; int ret; ret = rsb_read(PMIC_RSB_RT_ADDR, reg, &v, 1); if (ret) return ret; *val = v; VERBOSE("PMIC read @0x%x: 0x%x\n", reg, *val); return 0; } int sunxi_pmic_write(uint8_t reg, uint8_t val) { VERBOSE("PMIC write @0x%x: 0x%x\n", reg, val); return rsb_write(PMIC_RSB_RT_ADDR, reg, val, 1); } static int sunxi_pmic_set_bit_val(uint8_t reg, uint8_t bit, uint32_t v) { uint8_t old_val, new_val; int ret; ret = sunxi_pmic_read(reg, &old_val); if (ret < 0) return ret; if (v) new_val = old_val | BIT(bit); else new_val = old_val & (~BIT(bit)); if (old_val != new_val) ret = sunxi_pmic_write(reg, new_val); else ret = 0; return ret; } int sunxi_pmic_set_bit(uint8_t reg, uint8_t bit) { return sunxi_pmic_set_bit_val(reg, bit, 1); } int sunxi_pmic_clear_bit(uint8_t reg, uint8_t bit) { return sunxi_pmic_set_bit_val(reg, bit, 0); } static uint8_t mvolt_to_val(struct regulator_details *rd, uint32_t mvolt) { uint8_t val; if (mvolt < rd->mvolt_low) mvolt = rd->mvolt_low; if (mvolt > rd->mvolt_high) mvolt = rd->mvolt_high; if (rd->mvolt_mid != 0 && mvolt > rd->mvolt_mid) { val = (rd->mvolt_mid - rd->mvolt_low) / rd->mvolt_step1; val += (mvolt - rd->mvolt_mid) / rd->mvolt_step2; } else val = (mvolt - rd->mvolt_low) / rd->mvolt_step1; return val; } static uint32_t val_to_mvolt(struct regulator_details *rd, uint8_t val) { uint32_t step1_max = rd->mvolt_mid / rd->mvolt_step1; uint32_t mvolt; if (rd->mvolt_mid != 0 && val > step1_max) { mvolt = rd->mvolt_mid; mvolt += (val - step1_max) * rd->mvolt_step2; } else mvolt = rd->mvolt_low + val * rd->mvolt_step1; return mvolt; } int sunxi_pmic_set_voltage(enum axp803_regulator r, uint32_t mvolt) { int ret; struct regulator_details *rd; rd = get_regulator_details(r); if (!rd) { ERROR("Unknown regulator!\n"); return -1; } /* Get the register value */ uint8_t val = mvolt_to_val(rd, mvolt); /* Set voltage control to desired voltage */ ret = sunxi_pmic_write(rd->voltage_control_reg, val); if (ret < 0) { ERROR("Failed to set voltage of regulator %d to %u mV!\n", r, mvolt); return ret; } if (rd->dvm) { /* Wait until the finished flag is set */ do { ret = sunxi_pmic_read(rd->voltage_control_reg, &val); if (ret < 0) { ERROR("Failed to read DVM finished flag (%d)!\n", ret); return ret; } } while (!(val & DVM_FINISHED_BIT)); } return 0; } int sunxi_pmic_get_voltage(enum axp803_regulator r, uint32_t *mvolt) { int ret; uint8_t val; struct regulator_details *rd; rd = get_regulator_details(r); if (!rd) { ERROR("Unknown regulator!\n"); return -1; } /* Get the register value */ ret = sunxi_pmic_read(rd->voltage_control_reg, &val); if (ret < 0) { ERROR("Failed to read voltage control register (%d)!\n", ret); return ret; } /* Remove DVM bit */ val &= ~DVM_FINISHED_BIT; /* Get the mvolt value */ *mvolt = val_to_mvolt(rd, val); return 0; } int sunxi_pmic_set_enable(enum axp803_regulator r, uint32_t enable) { int ret; struct regulator_details *rd; rd = get_regulator_details(r); if (!rd) { ERROR("Unknown regulator!\n"); return -1; } ret = sunxi_pmic_set_bit_val(rd->onoff_control_reg, rd->onoff_control_bit, enable); if (ret < 0) { ERROR("Failed to set enable bit of regulator (%d)!\n", ret); return ret; } return 0; } int sunxi_pmic_get_enable(enum axp803_regulator r, uint32_t *enable) { int ret; uint8_t val; struct regulator_details *rd; rd = get_regulator_details(r); if (!rd) { ERROR("Unknown regulator!\n"); return -1; } ret = sunxi_pmic_read(rd->onoff_control_reg, &val); if (ret < 0) { ERROR("Failed to read onoff control register (%d)!\n", ret); return ret; } *enable = (val & BIT(rd->onoff_control_bit)) ? 1 : 0; return 0; } int sunxi_pmic_run_tasks(struct pmic_task *tasks, size_t tasks_num) { size_t i; for (i = 0; i < tasks_num; i++) { VERBOSE("Running PMIC task %zu\n", i); struct pmic_task *t = &tasks[i]; if (t->t == PMIC_SET_VOLTAGE) { int ret = sunxi_pmic_set_voltage(t->r, t->val); if (ret) { ERROR("Could run set-voltage task #%zu!", i); return -1; } } else if (t->t == PMIC_SET_ENABLE) { int ret = sunxi_pmic_set_enable(t->r, t->val); if (ret) { ERROR("Could run set-enable task #%zu!", i); return -1; } } else { ERROR("Could run unknown task #%zu!", i); return -1; } } return 0; } static int pmic_setup(void) { int ret; /* * (Verified) default values of AXP803 (see table 9-29): * DCDC1: 3V0 on -> 3V3 * DCDC2: 1V1 on -> 1V04 * DCDC3: 1V1 on -> (dual-phase) * DCDC4: 1V1 off -> 1V2 on * DCDC5: 1V5 on -> 1V36 * DCDC6: 1V1 on * ALDO1: 3V3 off -> on * ALDO2: 1V8 on -> 3V3 on * ALDO3: 3V0 on -> 3V3 * ELDO1: 1V8 on * ELDO2: 0V7 off * ELDO3: 0V7 off * DLDO1: 3V3 off * DLDO2: 2V9 off * DLDO3: 2V9 off * DLDO4: 3V3 off * FLDO1: 1V2 off -> on * FLDO2: 1V1 on * * DC1SW: off -> on * DCDC2/3 dual-phase: off -> on * DCDC5/6 dual-phase: off */ /* Turn on DC1SW (part of DCDC1) */ ret = sunxi_pmic_set_bit(0x12, 7); if (ret < 0) { ERROR("Could not turn on DC1SW\n"); return -1; } /* Set DCDC2 and DCDC3 to dual-phase mode */ ret = sunxi_pmic_set_bit(0x14, 6); if (ret < 0) { ERROR("Could not enable dual-phase for DCDC2/3\n"); return -1; } struct pmic_task tasks[] = { /* Set DCDC1 to 3V3 */ { .t = PMIC_SET_VOLTAGE, .r = AXP803_DCDC1, .val = 3300 }, /* Set DCDC2/3 to 1V04 */ { .t = PMIC_SET_VOLTAGE, .r = AXP803_DCDC2, .val = 1040 }, /* Set DCDC4 (ETH PHY) to 1V2 */ { .t = PMIC_SET_VOLTAGE, .r = AXP803_DCDC4, .val = 1200 }, /* Enable DCDC4 */ { .t = PMIC_SET_ENABLE, .r = AXP803_DCDC4, .val = 1 }, /* Set DCDC5 (DDR3L) to 1V36 */ { .t = PMIC_SET_VOLTAGE, .r = AXP803_DCDC5, .val = 1360 }, /* Enable ALDO1 */ { .t = PMIC_SET_ENABLE, .r = AXP803_ALDO1, .val = 1 }, /* Set ALDO2 to 3V3 */ { .t = PMIC_SET_VOLTAGE, .r = AXP803_ALDO2, .val = 3300 }, /* Enable ALDO2 */ { .t = PMIC_SET_ENABLE, .r = AXP803_ALDO2, .val = 1 }, /* Set ALDO3 to 3V3 */ { .t = PMIC_SET_VOLTAGE, .r = AXP803_ALDO3, .val = 3300 }, /* Enable FLDO1 */ { .t = PMIC_SET_ENABLE, .r = AXP803_FLDO1, .val = 1 }, }; ret = sunxi_pmic_run_tasks(tasks, ARRAY_SIZE(tasks)); if (ret < 0) { ERROR("Could not run PMIC tasks\n"); return -1; } return 0; } /* * Program the AXP803 via the RSB bus. */ int sunxi_pmic_setup(void) { int ret; uint8_t val; NOTICE("Configuring AXP PMIC\n"); /* Test PMIC communication */ ret = sunxi_pmic_read(0x03, &val); if (ret < 0) { ERROR("PMIC: error %d reading PMIC IC type register\n", ret); return -2; } /* Check IC type number equals 0b01xx00001 */ if ((val & 0xcf) != 0x41) { ERROR("PMIC: unknown PMIC IC type 0x%x\n", val); return -3; } /* Setup the PMIC */ ret = pmic_setup(); if (!ret) NOTICE("PMIC: setup successful\n"); else ERROR("PMIC: setup failed: %d\n", ret); return ret; }