17c26b6ecSIcenowy Zheng /* 2c0e109f2SSamuel Holland * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. 37c26b6ecSIcenowy Zheng * Copyright (c) 2018, Icenowy Zheng <icenowy@aosc.io> 47c26b6ecSIcenowy Zheng * 57c26b6ecSIcenowy Zheng * SPDX-License-Identifier: BSD-3-Clause 67c26b6ecSIcenowy Zheng */ 77c26b6ecSIcenowy Zheng 86d372828SIcenowy Zheng #include <errno.h> 909d40e0eSAntonio Nino Diaz 1009d40e0eSAntonio Nino Diaz #include <common/debug.h> 11*658b3154SAndre Przywara #include <common/fdt_wrappers.h> 12fb23b104SSamuel Holland #include <drivers/allwinner/axp.h> 137060e0d8SSamuel Holland #include <drivers/allwinner/sunxi_rsb.h> 14*658b3154SAndre Przywara #include <libfdt.h> 159227719dSAndre Przywara #include <lib/mmio.h> 1609d40e0eSAntonio Nino Diaz 179227719dSAndre Przywara #include <sunxi_cpucfg.h> 18d5ddf67aSAndre Przywara #include <sunxi_def.h> 196d372828SIcenowy Zheng #include <sunxi_mmap.h> 204ec1a239SAndre Przywara #include <sunxi_private.h> 216d372828SIcenowy Zheng 227060e0d8SSamuel Holland #define AXP805_HW_ADDR 0x745 237060e0d8SSamuel Holland #define AXP805_RT_ADDR 0x3a 246d372828SIcenowy Zheng 25c0e109f2SSamuel Holland static enum pmic_type { 26c0e109f2SSamuel Holland UNKNOWN, 276d372828SIcenowy Zheng AXP805, 28c0e109f2SSamuel Holland } pmic; 296d372828SIcenowy Zheng 30fb23b104SSamuel Holland int axp_read(uint8_t reg) 316d372828SIcenowy Zheng { 327060e0d8SSamuel Holland return rsb_read(AXP805_RT_ADDR, reg); 33fb23b104SSamuel Holland } 34fb23b104SSamuel Holland 35fb23b104SSamuel Holland int axp_write(uint8_t reg, uint8_t val) 366d372828SIcenowy Zheng { 377060e0d8SSamuel Holland return rsb_write(AXP805_RT_ADDR, reg, val); 386d372828SIcenowy Zheng } 396d372828SIcenowy Zheng 407060e0d8SSamuel Holland static int rsb_init(void) 416d372828SIcenowy Zheng { 426d372828SIcenowy Zheng int ret; 436d372828SIcenowy Zheng 447060e0d8SSamuel Holland ret = rsb_init_controller(); 454538c498SSamuel Holland if (ret) 464538c498SSamuel Holland return ret; 476d372828SIcenowy Zheng 487060e0d8SSamuel Holland /* Switch to the recommended 3 MHz bus clock. */ 497060e0d8SSamuel Holland ret = rsb_set_bus_speed(SUNXI_OSC24M_CLK_IN_HZ, 3000000); 504538c498SSamuel Holland if (ret) 514538c498SSamuel Holland return ret; 526d372828SIcenowy Zheng 537060e0d8SSamuel Holland /* Initiate an I2C transaction to switch the PMIC to RSB mode. */ 547060e0d8SSamuel Holland ret = rsb_set_device_mode(AXP20X_MODE_RSB << 16 | AXP20X_MODE_REG << 8); 557060e0d8SSamuel Holland if (ret) 567060e0d8SSamuel Holland return ret; 577060e0d8SSamuel Holland 587060e0d8SSamuel Holland /* Associate the 8-bit runtime address with the 12-bit bus address. */ 597060e0d8SSamuel Holland ret = rsb_assign_runtime_address(AXP805_HW_ADDR, AXP805_RT_ADDR); 607060e0d8SSamuel Holland if (ret) 617060e0d8SSamuel Holland return ret; 627060e0d8SSamuel Holland 637060e0d8SSamuel Holland return axp_check_id(); 646d372828SIcenowy Zheng } 657c26b6ecSIcenowy Zheng 66df301601SAndre Przywara int sunxi_pmic_setup(uint16_t socid, const void *fdt) 677c26b6ecSIcenowy Zheng { 68*658b3154SAndre Przywara int node, ret; 69*658b3154SAndre Przywara 70*658b3154SAndre Przywara node = fdt_node_offset_by_compatible(fdt, 0, "allwinner,sun8i-a23-rsb"); 71*658b3154SAndre Przywara if ((node < 0) || !fdt_node_is_enabled(fdt, node)) { 72*658b3154SAndre Przywara return -ENODEV; 73*658b3154SAndre Przywara } 746d372828SIcenowy Zheng 757060e0d8SSamuel Holland INFO("PMIC: Probing AXP805 on RSB\n"); 764538c498SSamuel Holland 777060e0d8SSamuel Holland ret = sunxi_init_platform_r_twi(socid, true); 784538c498SSamuel Holland if (ret) 794538c498SSamuel Holland return ret; 804538c498SSamuel Holland 817060e0d8SSamuel Holland ret = rsb_init(); 827060e0d8SSamuel Holland if (ret) 837060e0d8SSamuel Holland return ret; 846d372828SIcenowy Zheng 857060e0d8SSamuel Holland /* Switch the AXP805 to master/single-PMIC mode. */ 867060e0d8SSamuel Holland ret = axp_write(0xff, 0x0); 876d372828SIcenowy Zheng if (ret) 88c0e109f2SSamuel Holland return ret; 89c0e109f2SSamuel Holland 906d372828SIcenowy Zheng pmic = AXP805; 91fb23b104SSamuel Holland axp_setup_regulators(fdt); 927c26b6ecSIcenowy Zheng 937060e0d8SSamuel Holland /* Switch the PMIC back to I2C mode. */ 947060e0d8SSamuel Holland ret = axp_write(AXP20X_MODE_REG, AXP20X_MODE_I2C); 957060e0d8SSamuel Holland if (ret) 967060e0d8SSamuel Holland return ret; 977060e0d8SSamuel Holland 987c26b6ecSIcenowy Zheng return 0; 997c26b6ecSIcenowy Zheng } 1005069c1cfSIcenowy Zheng 101818e6732SSamuel Holland void sunxi_power_down(void) 1025069c1cfSIcenowy Zheng { 1035069c1cfSIcenowy Zheng switch (pmic) { 1045069c1cfSIcenowy Zheng case AXP805: 1057060e0d8SSamuel Holland /* (Re-)init RSB in case the rich OS has disabled it. */ 1067060e0d8SSamuel Holland sunxi_init_platform_r_twi(SUNXI_SOC_H6, true); 1077060e0d8SSamuel Holland rsb_init(); 108fb23b104SSamuel Holland axp_power_off(); 1095069c1cfSIcenowy Zheng break; 1105069c1cfSIcenowy Zheng default: 1115069c1cfSIcenowy Zheng break; 1125069c1cfSIcenowy Zheng } 1135069c1cfSIcenowy Zheng } 1149227719dSAndre Przywara 1159227719dSAndre Przywara void sunxi_cpu_power_off_self(void) 1169227719dSAndre Przywara { 1179227719dSAndre Przywara u_register_t mpidr = read_mpidr(); 1189227719dSAndre Przywara unsigned int core = MPIDR_AFFLVL0_VAL(mpidr); 1199227719dSAndre Przywara 1209227719dSAndre Przywara /* Enable the CPUIDLE hardware (only really needs to be done once). */ 1219227719dSAndre Przywara mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0x16aa0000); 1229227719dSAndre Przywara mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0xaa160001); 1239227719dSAndre Przywara 1249227719dSAndre Przywara /* Trigger power off for this core. */ 1259227719dSAndre Przywara mmio_write_32(SUNXI_CORE_CLOSE_REG, BIT_32(core)); 1269227719dSAndre Przywara } 127