17c26b6ecSIcenowy Zheng /* 27c26b6ecSIcenowy Zheng * Copyright (c) 2017-2018, 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 85069c1cfSIcenowy Zheng #include <arch_helpers.h> 97c26b6ecSIcenowy Zheng #include <debug.h> 106d372828SIcenowy Zheng #include <delay_timer.h> 116d372828SIcenowy Zheng #include <errno.h> 126d372828SIcenowy Zheng #include <mmio.h> 136d372828SIcenowy Zheng #include <mentor/mi2cv.h> 146d372828SIcenowy Zheng #include <string.h> 15*d5ddf67aSAndre Przywara #include <sunxi_def.h> 166d372828SIcenowy Zheng #include <sunxi_mmap.h> 174ec1a239SAndre Przywara #include <sunxi_private.h> 186d372828SIcenowy Zheng 196d372828SIcenowy Zheng #define AXP805_ADDR 0x36 206d372828SIcenowy Zheng #define AXP805_ID 0x03 216d372828SIcenowy Zheng 226d372828SIcenowy Zheng enum pmic_type { 236d372828SIcenowy Zheng NO_PMIC, 246d372828SIcenowy Zheng AXP805, 256d372828SIcenowy Zheng }; 266d372828SIcenowy Zheng 276d372828SIcenowy Zheng enum pmic_type pmic; 286d372828SIcenowy Zheng 296d372828SIcenowy Zheng int axp_i2c_read(uint8_t chip, uint8_t reg, uint8_t *val) 306d372828SIcenowy Zheng { 316d372828SIcenowy Zheng int ret; 326d372828SIcenowy Zheng 336d372828SIcenowy Zheng ret = i2c_write(chip, 0, 0, ®, 1); 346d372828SIcenowy Zheng if (ret) 356d372828SIcenowy Zheng return ret; 366d372828SIcenowy Zheng 376d372828SIcenowy Zheng return i2c_read(chip, 0, 0, val, 1); 386d372828SIcenowy Zheng } 396d372828SIcenowy Zheng 406d372828SIcenowy Zheng int axp_i2c_write(uint8_t chip, uint8_t reg, uint8_t val) 416d372828SIcenowy Zheng { 426d372828SIcenowy Zheng return i2c_write(chip, reg, 1, &val, 1); 436d372828SIcenowy Zheng } 446d372828SIcenowy Zheng 456d372828SIcenowy Zheng static int axp805_probe(void) 466d372828SIcenowy Zheng { 476d372828SIcenowy Zheng int ret; 486d372828SIcenowy Zheng uint8_t val; 496d372828SIcenowy Zheng 506d372828SIcenowy Zheng ret = axp_i2c_write(AXP805_ADDR, 0xff, 0x0); 516d372828SIcenowy Zheng if (ret) { 526d372828SIcenowy Zheng ERROR("PMIC: Cannot put AXP805 to master mode.\n"); 536d372828SIcenowy Zheng return -EPERM; 546d372828SIcenowy Zheng } 556d372828SIcenowy Zheng 566d372828SIcenowy Zheng ret = axp_i2c_read(AXP805_ADDR, AXP805_ID, &val); 576d372828SIcenowy Zheng 586d372828SIcenowy Zheng if (!ret && ((val & 0xcf) == 0x40)) 596d372828SIcenowy Zheng NOTICE("PMIC: AXP805 detected\n"); 606d372828SIcenowy Zheng else if (ret) { 616d372828SIcenowy Zheng ERROR("PMIC: Cannot communicate with AXP805.\n"); 626d372828SIcenowy Zheng return -EPERM; 636d372828SIcenowy Zheng } else { 646d372828SIcenowy Zheng ERROR("PMIC: Non-AXP805 chip attached at AXP805's address.\n"); 656d372828SIcenowy Zheng return -EINVAL; 666d372828SIcenowy Zheng } 676d372828SIcenowy Zheng 686d372828SIcenowy Zheng return 0; 696d372828SIcenowy Zheng } 707c26b6ecSIcenowy Zheng 71fe57c7d4SAndre Przywara int sunxi_pmic_setup(uint16_t socid) 727c26b6ecSIcenowy Zheng { 736d372828SIcenowy Zheng int ret; 746d372828SIcenowy Zheng 75*d5ddf67aSAndre Przywara sunxi_init_platform_r_twi(SUNXI_SOC_H6, false); 76*d5ddf67aSAndre Przywara /* initialise mi2cv driver */ 77*d5ddf67aSAndre Przywara i2c_init((void *)SUNXI_R_I2C_BASE); 786d372828SIcenowy Zheng 796d372828SIcenowy Zheng NOTICE("PMIC: Probing AXP805\n"); 806d372828SIcenowy Zheng pmic = AXP805; 816d372828SIcenowy Zheng 826d372828SIcenowy Zheng ret = axp805_probe(); 836d372828SIcenowy Zheng if (ret) 846d372828SIcenowy Zheng pmic = NO_PMIC; 856d372828SIcenowy Zheng else 866d372828SIcenowy Zheng pmic = AXP805; 877c26b6ecSIcenowy Zheng 887c26b6ecSIcenowy Zheng return 0; 897c26b6ecSIcenowy Zheng } 905069c1cfSIcenowy Zheng 915069c1cfSIcenowy Zheng void __dead2 sunxi_power_down(void) 925069c1cfSIcenowy Zheng { 935069c1cfSIcenowy Zheng uint8_t val; 945069c1cfSIcenowy Zheng 955069c1cfSIcenowy Zheng switch (pmic) { 965069c1cfSIcenowy Zheng case AXP805: 97*d5ddf67aSAndre Przywara /* Re-initialise after rich OS might have used it. */ 98*d5ddf67aSAndre Przywara sunxi_init_platform_r_twi(SUNXI_SOC_H6, false); 99*d5ddf67aSAndre Przywara /* initialise mi2cv driver */ 100*d5ddf67aSAndre Przywara i2c_init((void *)SUNXI_R_I2C_BASE); 1015069c1cfSIcenowy Zheng axp_i2c_read(AXP805_ADDR, 0x32, &val); 102159c5249SAndre Przywara axp_i2c_write(AXP805_ADDR, 0x32, val | 0x80); 1035069c1cfSIcenowy Zheng break; 1045069c1cfSIcenowy Zheng default: 1055069c1cfSIcenowy Zheng break; 1065069c1cfSIcenowy Zheng } 1075069c1cfSIcenowy Zheng 1085069c1cfSIcenowy Zheng udelay(1000); 1095069c1cfSIcenowy Zheng ERROR("PSCI: Cannot communicate with PMIC, halting\n"); 1105069c1cfSIcenowy Zheng wfi(); 1115069c1cfSIcenowy Zheng panic(); 1125069c1cfSIcenowy Zheng } 113