blob: 8e29310739a9ab828568e2781d640d6fc3f1e0bd [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>
Andre Przywara456208a2018-10-14 12:02:02 +010016#include <sunxi_private.h>
Icenowy Zheng8d769822018-07-22 21:30:14 +080017
18#define AXP805_ADDR 0x36
19#define AXP805_ID 0x03
20
21enum pmic_type {
22 NO_PMIC,
23 AXP805,
24};
25
26enum pmic_type pmic;
27
28static int sunxi_init_r_i2c(void)
29{
30 uint32_t reg;
31
Icenowy Zheng8d769822018-07-22 21:30:14 +080032 /* switch pins PL0 and PL1 to I2C */
Andre Przywaraf916d062018-09-09 00:38:19 +010033 reg = mmio_read_32(SUNXI_R_PIO_BASE + 0x00);
Icenowy Zheng8d769822018-07-22 21:30:14 +080034 mmio_write_32(SUNXI_R_PIO_BASE + 0x00, (reg & ~0xff) | 0x33);
35
36 /* level 2 drive strength */
37 reg = mmio_read_32(SUNXI_R_PIO_BASE + 0x14);
38 mmio_write_32(SUNXI_R_PIO_BASE + 0x14, (reg & ~0x0f) | 0xa);
39
40 /* set both ports to pull-up */
41 reg = mmio_read_32(SUNXI_R_PIO_BASE + 0x1c);
42 mmio_write_32(SUNXI_R_PIO_BASE + 0x1c, (reg & ~0x0f) | 0x5);
43
44 /* assert & de-assert reset of R_I2C */
45 reg = mmio_read_32(SUNXI_R_PRCM_BASE + 0x19c);
Andre Przywaraf916d062018-09-09 00:38:19 +010046 mmio_write_32(SUNXI_R_PRCM_BASE + 0x19c, reg & ~BIT(16));
47 mmio_write_32(SUNXI_R_PRCM_BASE + 0x19c, reg | BIT(16));
Icenowy Zheng8d769822018-07-22 21:30:14 +080048
49 /* un-gate R_I2C clock */
Andre Przywaraf916d062018-09-09 00:38:19 +010050 mmio_write_32(SUNXI_R_PRCM_BASE + 0x19c, reg | BIT(16) | BIT(0));
Icenowy Zheng8d769822018-07-22 21:30:14 +080051
52 /* call mi2cv driver */
53 i2c_init((void *)SUNXI_R_I2C_BASE);
54
55 return 0;
56}
57
58int axp_i2c_read(uint8_t chip, uint8_t reg, uint8_t *val)
59{
60 int ret;
61
62 ret = i2c_write(chip, 0, 0, &reg, 1);
63 if (ret)
64 return ret;
65
66 return i2c_read(chip, 0, 0, val, 1);
67}
68
69int axp_i2c_write(uint8_t chip, uint8_t reg, uint8_t val)
70{
71 return i2c_write(chip, reg, 1, &val, 1);
72}
73
74static int axp805_probe(void)
75{
76 int ret;
77 uint8_t val;
78
79 ret = axp_i2c_write(AXP805_ADDR, 0xff, 0x0);
80 if (ret) {
81 ERROR("PMIC: Cannot put AXP805 to master mode.\n");
82 return -EPERM;
83 }
84
85 ret = axp_i2c_read(AXP805_ADDR, AXP805_ID, &val);
86
87 if (!ret && ((val & 0xcf) == 0x40))
88 NOTICE("PMIC: AXP805 detected\n");
89 else if (ret) {
90 ERROR("PMIC: Cannot communicate with AXP805.\n");
91 return -EPERM;
92 } else {
93 ERROR("PMIC: Non-AXP805 chip attached at AXP805's address.\n");
94 return -EINVAL;
95 }
96
97 return 0;
98}
Icenowy Zheng7508bef2018-07-21 20:41:12 +080099
Andre Przywara3464e3f2018-09-08 19:18:37 +0100100int sunxi_pmic_setup(uint16_t socid)
Icenowy Zheng7508bef2018-07-21 20:41:12 +0800101{
Icenowy Zheng8d769822018-07-22 21:30:14 +0800102 int ret;
103
104 sunxi_init_r_i2c();
105
106 NOTICE("PMIC: Probing AXP805\n");
107 pmic = AXP805;
108
109 ret = axp805_probe();
110 if (ret)
111 pmic = NO_PMIC;
112 else
113 pmic = AXP805;
Icenowy Zheng7508bef2018-07-21 20:41:12 +0800114
115 return 0;
116}
Icenowy Zhengbd57eb52018-07-22 21:52:50 +0800117
118void __dead2 sunxi_power_down(void)
119{
120 uint8_t val;
121
122 switch (pmic) {
123 case AXP805:
Andre Przywaraaffb9322018-09-09 00:38:58 +0100124 sunxi_init_r_i2c();
Icenowy Zhengbd57eb52018-07-22 21:52:50 +0800125 axp_i2c_read(AXP805_ADDR, 0x32, &val);
Andre Przywaraaffb9322018-09-09 00:38:58 +0100126 axp_i2c_write(AXP805_ADDR, 0x32, val | 0x80);
Icenowy Zhengbd57eb52018-07-22 21:52:50 +0800127 break;
128 default:
129 break;
130 }
131
132 udelay(1000);
133 ERROR("PSCI: Cannot communicate with PMIC, halting\n");
134 wfi();
135 panic();
136}