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
axp_read(uint8_t reg)30fb23b104SSamuel Holland int axp_read(uint8_t reg)
316d372828SIcenowy Zheng {
327060e0d8SSamuel Holland return rsb_read(AXP805_RT_ADDR, reg);
33fb23b104SSamuel Holland }
34fb23b104SSamuel Holland
axp_write(uint8_t reg,uint8_t val)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
rsb_init(void)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
sunxi_pmic_setup(uint16_t socid,const void * fdt)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
sunxi_power_down(void)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
sunxi_cpu_power_off_self(void)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