blob: 12438b33975c74f0ea92914f2db515ffa4a6b920 [file] [log] [blame]
Icenowy Zheng7508bef2018-07-21 20:41:12 +08001/*
2 * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
3 * Copyright (c) 2018, Icenowy Zheng <icenowy@aosc.io>
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
Icenowy Zhengbd57eb52018-07-22 21:52:50 +08008#include <arch_helpers.h>
Icenowy Zheng7508bef2018-07-21 20:41:12 +08009#include <debug.h>
Icenowy Zheng8d769822018-07-22 21:30:14 +080010#include <delay_timer.h>
11#include <errno.h>
12#include <mmio.h>
13#include <mentor/mi2cv.h>
14#include <string.h>
15#include <sunxi_mmap.h>
16
17#define AXP805_ADDR 0x36
18#define AXP805_ID 0x03
19
20enum pmic_type {
21 NO_PMIC,
22 AXP805,
23};
24
25enum pmic_type pmic;
26
27static int sunxi_init_r_i2c(void)
28{
29 uint32_t reg;
30
Icenowy Zheng8d769822018-07-22 21:30:14 +080031 /* switch pins PL0 and PL1 to I2C */
Andre Przywaraf916d062018-09-09 00:38:19 +010032 reg = mmio_read_32(SUNXI_R_PIO_BASE + 0x00);
Icenowy Zheng8d769822018-07-22 21:30:14 +080033 mmio_write_32(SUNXI_R_PIO_BASE + 0x00, (reg & ~0xff) | 0x33);
34
35 /* level 2 drive strength */
36 reg = mmio_read_32(SUNXI_R_PIO_BASE + 0x14);
37 mmio_write_32(SUNXI_R_PIO_BASE + 0x14, (reg & ~0x0f) | 0xa);
38
39 /* set both ports to pull-up */
40 reg = mmio_read_32(SUNXI_R_PIO_BASE + 0x1c);
41 mmio_write_32(SUNXI_R_PIO_BASE + 0x1c, (reg & ~0x0f) | 0x5);
42
43 /* assert & de-assert reset of R_I2C */
44 reg = mmio_read_32(SUNXI_R_PRCM_BASE + 0x19c);
Andre Przywaraf916d062018-09-09 00:38:19 +010045 mmio_write_32(SUNXI_R_PRCM_BASE + 0x19c, reg & ~BIT(16));
46 mmio_write_32(SUNXI_R_PRCM_BASE + 0x19c, reg | BIT(16));
Icenowy Zheng8d769822018-07-22 21:30:14 +080047
48 /* un-gate R_I2C clock */
Andre Przywaraf916d062018-09-09 00:38:19 +010049 mmio_write_32(SUNXI_R_PRCM_BASE + 0x19c, reg | BIT(16) | BIT(0));
Icenowy Zheng8d769822018-07-22 21:30:14 +080050
51 /* call mi2cv driver */
52 i2c_init((void *)SUNXI_R_I2C_BASE);
53
54 return 0;
55}
56
57int axp_i2c_read(uint8_t chip, uint8_t reg, uint8_t *val)
58{
59 int ret;
60
61 ret = i2c_write(chip, 0, 0, &reg, 1);
62 if (ret)
63 return ret;
64
65 return i2c_read(chip, 0, 0, val, 1);
66}
67
68int axp_i2c_write(uint8_t chip, uint8_t reg, uint8_t val)
69{
70 return i2c_write(chip, reg, 1, &val, 1);
71}
72
73static int axp805_probe(void)
74{
75 int ret;
76 uint8_t val;
77
78 ret = axp_i2c_write(AXP805_ADDR, 0xff, 0x0);
79 if (ret) {
80 ERROR("PMIC: Cannot put AXP805 to master mode.\n");
81 return -EPERM;
82 }
83
84 ret = axp_i2c_read(AXP805_ADDR, AXP805_ID, &val);
85
86 if (!ret && ((val & 0xcf) == 0x40))
87 NOTICE("PMIC: AXP805 detected\n");
88 else if (ret) {
89 ERROR("PMIC: Cannot communicate with AXP805.\n");
90 return -EPERM;
91 } else {
92 ERROR("PMIC: Non-AXP805 chip attached at AXP805's address.\n");
93 return -EINVAL;
94 }
95
96 return 0;
97}
Icenowy Zheng7508bef2018-07-21 20:41:12 +080098
99int sunxi_pmic_setup(void)
100{
Icenowy Zheng8d769822018-07-22 21:30:14 +0800101 int ret;
102
103 sunxi_init_r_i2c();
104
105 NOTICE("PMIC: Probing AXP805\n");
106 pmic = AXP805;
107
108 ret = axp805_probe();
109 if (ret)
110 pmic = NO_PMIC;
111 else
112 pmic = AXP805;
Icenowy Zheng7508bef2018-07-21 20:41:12 +0800113
114 return 0;
115}
Icenowy Zhengbd57eb52018-07-22 21:52:50 +0800116
117void __dead2 sunxi_power_down(void)
118{
119 uint8_t val;
120
121 switch (pmic) {
122 case AXP805:
Andre Przywaraaffb9322018-09-09 00:38:58 +0100123 sunxi_init_r_i2c();
Icenowy Zhengbd57eb52018-07-22 21:52:50 +0800124 axp_i2c_read(AXP805_ADDR, 0x32, &val);
Andre Przywaraaffb9322018-09-09 00:38:58 +0100125 axp_i2c_write(AXP805_ADDR, 0x32, val | 0x80);
Icenowy Zhengbd57eb52018-07-22 21:52:50 +0800126 break;
127 default:
128 break;
129 }
130
131 udelay(1000);
132 ERROR("PSCI: Cannot communicate with PMIC, halting\n");
133 wfi();
134 panic();
135}