sunxi_power.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. /*
  2. * Copyright (c) 2017-2020, ARM Limited. All rights reserved.
  3. * Copyright (c) 2018, Icenowy Zheng <icenowy@aosc.io>
  4. *
  5. * SPDX-License-Identifier: BSD-3-Clause
  6. */
  7. #include <errno.h>
  8. #include <string.h>
  9. #include <arch_helpers.h>
  10. #include <common/debug.h>
  11. #include <common/fdt_wrappers.h>
  12. #include <drivers/allwinner/axp.h>
  13. #include <drivers/allwinner/sunxi_rsb.h>
  14. #include <drivers/mentor/mi2cv.h>
  15. #include <lib/mmio.h>
  16. #include <libfdt.h>
  17. #include <sunxi_cpucfg.h>
  18. #include <sunxi_def.h>
  19. #include <sunxi_mmap.h>
  20. #include <sunxi_private.h>
  21. static uint16_t pmic_bus_addr;
  22. static uint8_t rsb_rt_addr;
  23. static bool is_using_rsb(void)
  24. {
  25. return rsb_rt_addr != 0;
  26. }
  27. static enum pmic_type {
  28. UNKNOWN,
  29. AXP305,
  30. AXP313,
  31. AXP717,
  32. } pmic;
  33. static uint8_t get_rsb_rt_address(uint16_t hw_addr)
  34. {
  35. switch (hw_addr) {
  36. case 0x3a3: return 0x2d;
  37. case 0x745: return 0x3a;
  38. }
  39. return 0;
  40. }
  41. int axp_read(uint8_t reg)
  42. {
  43. uint8_t val;
  44. int ret;
  45. if (is_using_rsb()) {
  46. return rsb_read(rsb_rt_addr, reg);
  47. }
  48. ret = i2c_write(pmic_bus_addr, 0, 0, &reg, 1);
  49. if (ret == 0) {
  50. ret = i2c_read(pmic_bus_addr, 0, 0, &val, 1);
  51. }
  52. if (ret) {
  53. ERROR("PMIC: Cannot read PMIC register %02x\n", reg);
  54. return ret;
  55. }
  56. return val;
  57. }
  58. int axp_write(uint8_t reg, uint8_t val)
  59. {
  60. int ret;
  61. if (is_using_rsb()) {
  62. return rsb_write(rsb_rt_addr, reg, val);
  63. }
  64. ret = i2c_write(pmic_bus_addr, reg, 1, &val, 1);
  65. if (ret) {
  66. ERROR("PMIC: Cannot write PMIC register %02x\n", reg);
  67. }
  68. return ret;
  69. }
  70. static int rsb_init(int rsb_hw_addr)
  71. {
  72. int ret;
  73. ret = rsb_init_controller();
  74. if (ret) {
  75. return ret;
  76. }
  77. /* Switch to the recommended 3 MHz bus clock. */
  78. ret = rsb_set_bus_speed(SUNXI_OSC24M_CLK_IN_HZ, 3000000);
  79. if (ret) {
  80. return ret;
  81. }
  82. /* Initiate an I2C transaction to switch the PMIC to RSB mode. */
  83. ret = rsb_set_device_mode(AXP20X_MODE_RSB << 16 | AXP20X_MODE_REG << 8);
  84. if (ret) {
  85. return ret;
  86. }
  87. /* Associate the 8-bit runtime address with the 12-bit bus address. */
  88. ret = rsb_assign_runtime_address(rsb_hw_addr, rsb_rt_addr);
  89. if (ret) {
  90. return ret;
  91. }
  92. return 0;
  93. }
  94. static int pmic_bus_init(uint16_t socid, uint16_t rsb_hw_addr)
  95. {
  96. int ret;
  97. ret = sunxi_init_platform_r_twi(socid, is_using_rsb());
  98. if (ret) {
  99. INFO("Could not init platform bus: %d\n", ret);
  100. pmic = UNKNOWN;
  101. return ret;
  102. }
  103. if (is_using_rsb()) {
  104. ret = rsb_init(rsb_hw_addr);
  105. if (ret) {
  106. pmic = UNKNOWN;
  107. return ret;
  108. }
  109. } else {
  110. /* initialise mi2cv driver */
  111. i2c_init((void *)SUNXI_R_I2C_BASE);
  112. }
  113. return 0;
  114. }
  115. int sunxi_pmic_setup(uint16_t socid, const void *fdt)
  116. {
  117. int node, parent, ret;
  118. uint32_t reg;
  119. node = fdt_node_offset_by_compatible(fdt, 0, "x-powers,axp806");
  120. if (node >= 0) {
  121. pmic = AXP305;
  122. }
  123. if (pmic == UNKNOWN) {
  124. node = fdt_node_offset_by_compatible(fdt, 0, "x-powers,axp313a");
  125. if (node >= 0) {
  126. pmic = AXP313;
  127. }
  128. }
  129. if (pmic == UNKNOWN) {
  130. node = fdt_node_offset_by_compatible(fdt, 0, "x-powers,axp717");
  131. if (node >= 0) {
  132. pmic = AXP717;
  133. }
  134. }
  135. if (pmic == UNKNOWN) {
  136. INFO("PMIC: No known PMIC in DT, skipping setup.\n");
  137. return -ENODEV;
  138. }
  139. if (fdt_read_uint32(fdt, node, "reg", &reg)) {
  140. ERROR("PMIC: PMIC DT node does not contain reg property.\n");
  141. return -EINVAL;
  142. }
  143. pmic_bus_addr = reg;
  144. parent = fdt_parent_offset(fdt, node);
  145. ret = fdt_node_check_compatible(fdt, parent, "allwinner,sun8i-a23-rsb");
  146. if (ret == 0) {
  147. rsb_rt_addr = get_rsb_rt_address(pmic_bus_addr);
  148. if (rsb_rt_addr == 0) {
  149. ERROR("PMIC: no mapping for RSB address 0x%x\n",
  150. pmic_bus_addr);
  151. return -EINVAL;
  152. }
  153. }
  154. INFO("Probing for PMIC on %s:\n", is_using_rsb() ? "RSB" : "I2C");
  155. ret = pmic_bus_init(socid, pmic_bus_addr);
  156. if (ret) {
  157. return ret;
  158. }
  159. ret = axp_read(0x03);
  160. switch (ret & 0xcf) {
  161. case 0x40: /* AXP305 */
  162. if (pmic == AXP305) {
  163. INFO("PMIC: found AXP305, setting up regulators\n");
  164. axp_setup_regulators(fdt);
  165. } else {
  166. pmic = UNKNOWN;
  167. }
  168. break;
  169. case 0x48: /* AXP1530 */
  170. case 0x4b: /* AXP313A */
  171. case 0x4c: /* AXP313B */
  172. if (pmic == AXP313) {
  173. INFO("PMIC: found AXP313\n");
  174. /* no regulators to set up */
  175. } else {
  176. pmic = UNKNOWN;
  177. }
  178. break;
  179. case 0xcf: /* version reg not implemented on AXP717 */
  180. if (pmic == AXP717) {
  181. INFO("PMIC: found AXP717\n");
  182. /* no regulators to set up, U-Boot takes care of this */
  183. } else {
  184. pmic = UNKNOWN;
  185. }
  186. break;
  187. }
  188. if (is_using_rsb()) {
  189. /* Switch the PMIC back to I2C mode. */
  190. return rsb_write(rsb_rt_addr, AXP20X_MODE_REG, AXP20X_MODE_I2C);
  191. }
  192. if (pmic == UNKNOWN) {
  193. INFO("Incompatible or unknown PMIC found.\n");
  194. return -ENODEV;
  195. }
  196. return 0;
  197. }
  198. void sunxi_power_down(void)
  199. {
  200. int ret;
  201. if (pmic == UNKNOWN) {
  202. return;
  203. }
  204. /* Re-initialise after rich OS might have used it. */
  205. ret = pmic_bus_init(SUNXI_SOC_H616, pmic_bus_addr);
  206. if (ret) {
  207. return;
  208. }
  209. switch (pmic) {
  210. case AXP305:
  211. axp_setbits(0x32, BIT(7));
  212. break;
  213. case AXP313:
  214. axp_setbits(0x1a, BIT(7));
  215. break;
  216. case AXP717:
  217. axp_setbits(0x27, BIT(0));
  218. break;
  219. default:
  220. break;
  221. }
  222. }
  223. void sunxi_cpu_power_off_self(void)
  224. {
  225. u_register_t mpidr = read_mpidr();
  226. unsigned int core = MPIDR_AFFLVL0_VAL(mpidr);
  227. /* Enable the CPUIDLE hardware (only really needs to be done once). */
  228. mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0x16aa0000);
  229. mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0xaa160001);
  230. /* Trigger power off for this core. */
  231. mmio_write_32(SUNXI_CORE_CLOSE_REG, BIT_32(core));
  232. }