PageRenderTime 53ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/arch/arm/mach-msm/spm-v2.c

https://gitlab.com/jhalayashraj/nkernel
C | 542 lines | 413 code | 97 blank | 32 comment | 44 complexity | 3a56713b222bfe1f94e3920f2fa79252 MD5 | raw file
  1. /* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
  2. *
  3. * This program is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License version 2 and
  5. * only version 2 as published by the Free Software Foundation.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. *
  12. */
  13. #include <linux/module.h>
  14. #include <linux/kernel.h>
  15. #include <linux/delay.h>
  16. #include <linux/init.h>
  17. #include <linux/io.h>
  18. #include <linux/slab.h>
  19. #include <mach/msm_iomap.h>
  20. #include "spm_driver.h"
  21. #define MSM_SPM_PMIC_STATE_IDLE 0
  22. enum {
  23. MSM_SPM_DEBUG_SHADOW = 1U << 0,
  24. MSM_SPM_DEBUG_VCTL = 1U << 1,
  25. };
  26. static int msm_spm_debug_mask;
  27. module_param_named(
  28. debug_mask, msm_spm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
  29. );
  30. struct saw2_data {
  31. const char *ver_name;
  32. uint32_t major;
  33. uint32_t minor;
  34. uint32_t *spm_reg_offset_ptr;
  35. };
  36. static uint32_t msm_spm_reg_offsets_saw2_v2_1[MSM_SPM_REG_NR] = {
  37. [MSM_SPM_REG_SAW2_SECURE] = 0x00,
  38. [MSM_SPM_REG_SAW2_ID] = 0x04,
  39. [MSM_SPM_REG_SAW2_CFG] = 0x08,
  40. [MSM_SPM_REG_SAW2_SPM_STS] = 0x0C,
  41. [MSM_SPM_REG_SAW2_AVS_STS] = 0x10,
  42. [MSM_SPM_REG_SAW2_PMIC_STS] = 0x14,
  43. [MSM_SPM_REG_SAW2_RST] = 0x18,
  44. [MSM_SPM_REG_SAW2_VCTL] = 0x1C,
  45. [MSM_SPM_REG_SAW2_AVS_CTL] = 0x20,
  46. [MSM_SPM_REG_SAW2_AVS_LIMIT] = 0x24,
  47. [MSM_SPM_REG_SAW2_AVS_DLY] = 0x28,
  48. [MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0x2C,
  49. [MSM_SPM_REG_SAW2_SPM_CTL] = 0x30,
  50. [MSM_SPM_REG_SAW2_SPM_DLY] = 0x34,
  51. [MSM_SPM_REG_SAW2_PMIC_DATA_0] = 0x40,
  52. [MSM_SPM_REG_SAW2_PMIC_DATA_1] = 0x44,
  53. [MSM_SPM_REG_SAW2_PMIC_DATA_2] = 0x48,
  54. [MSM_SPM_REG_SAW2_PMIC_DATA_3] = 0x4C,
  55. [MSM_SPM_REG_SAW2_PMIC_DATA_4] = 0x50,
  56. [MSM_SPM_REG_SAW2_PMIC_DATA_5] = 0x54,
  57. [MSM_SPM_REG_SAW2_PMIC_DATA_6] = 0x58,
  58. [MSM_SPM_REG_SAW2_PMIC_DATA_7] = 0x5C,
  59. [MSM_SPM_REG_SAW2_SEQ_ENTRY] = 0x80,
  60. [MSM_SPM_REG_SAW2_VERSION] = 0xFD0,
  61. };
  62. static uint32_t msm_spm_reg_offsets_saw2_v3_0[MSM_SPM_REG_NR] = {
  63. [MSM_SPM_REG_SAW2_SECURE] = 0x00,
  64. [MSM_SPM_REG_SAW2_ID] = 0x04,
  65. [MSM_SPM_REG_SAW2_CFG] = 0x08,
  66. [MSM_SPM_REG_SAW2_SPM_STS] = 0x0C,
  67. [MSM_SPM_REG_SAW2_AVS_STS] = 0x10,
  68. [MSM_SPM_REG_SAW2_PMIC_STS] = 0x14,
  69. [MSM_SPM_REG_SAW2_RST] = 0x18,
  70. [MSM_SPM_REG_SAW2_VCTL] = 0x1C,
  71. [MSM_SPM_REG_SAW2_AVS_CTL] = 0x20,
  72. [MSM_SPM_REG_SAW2_AVS_LIMIT] = 0x24,
  73. [MSM_SPM_REG_SAW2_AVS_DLY] = 0x28,
  74. [MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0x2C,
  75. [MSM_SPM_REG_SAW2_SPM_CTL] = 0x30,
  76. [MSM_SPM_REG_SAW2_SPM_DLY] = 0x34,
  77. [MSM_SPM_REG_SAW2_STS2] = 0x38,
  78. [MSM_SPM_REG_SAW2_PMIC_DATA_0] = 0x40,
  79. [MSM_SPM_REG_SAW2_PMIC_DATA_1] = 0x44,
  80. [MSM_SPM_REG_SAW2_PMIC_DATA_2] = 0x48,
  81. [MSM_SPM_REG_SAW2_PMIC_DATA_3] = 0x4C,
  82. [MSM_SPM_REG_SAW2_PMIC_DATA_4] = 0x50,
  83. [MSM_SPM_REG_SAW2_PMIC_DATA_5] = 0x54,
  84. [MSM_SPM_REG_SAW2_PMIC_DATA_6] = 0x58,
  85. [MSM_SPM_REG_SAW2_PMIC_DATA_7] = 0x5C,
  86. [MSM_SPM_REG_SAW2_SEQ_ENTRY] = 0x400,
  87. [MSM_SPM_REG_SAW2_VERSION] = 0xFD0,
  88. };
  89. static struct saw2_data saw2_info[] = {
  90. [0] = {
  91. "SAW2_v2.1",
  92. 0x2,
  93. 0x1,
  94. msm_spm_reg_offsets_saw2_v2_1,
  95. },
  96. [1] = {
  97. "SAW2_v3.0",
  98. 0x3,
  99. 0x0,
  100. msm_spm_reg_offsets_saw2_v3_0,
  101. },
  102. };
  103. static inline uint32_t msm_spm_drv_get_num_spm_entry(
  104. struct msm_spm_driver_data *dev)
  105. {
  106. return 32;
  107. }
  108. static void msm_spm_drv_flush_shadow(struct msm_spm_driver_data *dev,
  109. unsigned int reg_index)
  110. {
  111. __raw_writel(dev->reg_shadow[reg_index],
  112. dev->reg_base_addr + dev->reg_offsets[reg_index]);
  113. }
  114. static void msm_spm_drv_load_shadow(struct msm_spm_driver_data *dev,
  115. unsigned int reg_index)
  116. {
  117. dev->reg_shadow[reg_index] =
  118. __raw_readl(dev->reg_base_addr +
  119. dev->reg_offsets[reg_index]);
  120. }
  121. static inline void msm_spm_drv_set_start_addr(
  122. struct msm_spm_driver_data *dev, uint32_t addr, bool pc_mode)
  123. {
  124. addr &= 0x7F;
  125. addr <<= 4;
  126. dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] &= 0xFFFFF80F;
  127. dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] |= addr;
  128. if (dev->major != 0x3)
  129. return;
  130. dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] &= 0xFFFEFFFF;
  131. if (pc_mode)
  132. dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] |= 0x00010000;
  133. }
  134. static inline bool msm_spm_pmic_arb_present(struct msm_spm_driver_data *dev)
  135. {
  136. msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_ID);
  137. return (dev->reg_shadow[MSM_SPM_REG_SAW2_ID] >> 2) & 0x1;
  138. }
  139. static inline void msm_spm_drv_set_vctl2(struct msm_spm_driver_data *dev,
  140. uint32_t vlevel)
  141. {
  142. unsigned int pmic_data = 0;
  143. /**
  144. * VCTL_PORT has to be 0, for PMIC_STS register to be updated.
  145. * Ensure that vctl_port is always set to 0.
  146. */
  147. WARN_ON(dev->vctl_port);
  148. pmic_data |= vlevel;
  149. pmic_data |= (dev->vctl_port & 0x7) << 16;
  150. dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] &= ~0x700FF;
  151. dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] |= pmic_data;
  152. dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_3] &= ~0x700FF;
  153. dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_3] |= pmic_data;
  154. msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_VCTL);
  155. msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_PMIC_DATA_3);
  156. }
  157. static inline uint32_t msm_spm_drv_get_sts_pmic_state(
  158. struct msm_spm_driver_data *dev)
  159. {
  160. msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_PMIC_STS);
  161. return (dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_STS] >> 16) &
  162. 0x03;
  163. }
  164. uint32_t msm_spm_drv_get_sts_curr_pmic_data(
  165. struct msm_spm_driver_data *dev)
  166. {
  167. msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_PMIC_STS);
  168. return dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_STS] & 0xFF;
  169. }
  170. static inline void msm_spm_drv_get_saw2_ver(struct msm_spm_driver_data *dev,
  171. uint32_t *major, uint32_t *minor)
  172. {
  173. uint32_t val = 0;
  174. dev->reg_shadow[MSM_SPM_REG_SAW2_VERSION] =
  175. __raw_readl(dev->reg_base_addr + dev->ver_reg);
  176. val = dev->reg_shadow[MSM_SPM_REG_SAW2_VERSION];
  177. *major = (val >> 28) & 0xF;
  178. *minor = (val >> 16) & 0xFFF;
  179. }
  180. inline int msm_spm_drv_set_spm_enable(
  181. struct msm_spm_driver_data *dev, bool enable)
  182. {
  183. uint32_t value = enable ? 0x01 : 0x00;
  184. if (!dev)
  185. return -EINVAL;
  186. if ((dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] & 0x01) ^ value) {
  187. dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] &= ~0x1;
  188. dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] |= value;
  189. msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_SPM_CTL);
  190. wmb();
  191. }
  192. return 0;
  193. }
  194. void msm_spm_drv_flush_seq_entry(struct msm_spm_driver_data *dev)
  195. {
  196. int i;
  197. int num_spm_entry = msm_spm_drv_get_num_spm_entry(dev);
  198. if (!dev) {
  199. __WARN();
  200. return;
  201. }
  202. for (i = 0; i < num_spm_entry; i++) {
  203. __raw_writel(dev->reg_seq_entry_shadow[i],
  204. dev->reg_base_addr
  205. + dev->reg_offsets[MSM_SPM_REG_SAW2_SEQ_ENTRY]
  206. + 4 * i);
  207. }
  208. mb();
  209. }
  210. int msm_spm_drv_write_seq_data(struct msm_spm_driver_data *dev,
  211. uint8_t *cmd, uint32_t *offset)
  212. {
  213. uint32_t cmd_w;
  214. uint32_t offset_w = *offset / 4;
  215. uint8_t last_cmd;
  216. if (!cmd)
  217. return -EINVAL;
  218. while (1) {
  219. int i;
  220. cmd_w = 0;
  221. last_cmd = 0;
  222. cmd_w = dev->reg_seq_entry_shadow[offset_w];
  223. for (i = (*offset % 4) ; i < 4; i++) {
  224. last_cmd = *(cmd++);
  225. cmd_w |= last_cmd << (i * 8);
  226. (*offset)++;
  227. if (last_cmd == 0x0f)
  228. break;
  229. }
  230. dev->reg_seq_entry_shadow[offset_w++] = cmd_w;
  231. if (last_cmd == 0x0f)
  232. break;
  233. }
  234. return 0;
  235. }
  236. int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *dev,
  237. uint32_t addr, bool pc_mode)
  238. {
  239. /* SPM is configured to reset start address to zero after end of Program
  240. */
  241. if (!dev)
  242. return -EINVAL;
  243. msm_spm_drv_set_start_addr(dev, addr, pc_mode);
  244. msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_SPM_CTL);
  245. wmb();
  246. if (msm_spm_debug_mask & MSM_SPM_DEBUG_SHADOW) {
  247. int i;
  248. for (i = 0; i < MSM_SPM_REG_NR; i++)
  249. pr_info("%s: reg %02x = 0x%08x\n", __func__,
  250. dev->reg_offsets[i], dev->reg_shadow[i]);
  251. }
  252. return 0;
  253. }
  254. #ifdef CONFIG_MSM_AVS_HW
  255. static bool msm_spm_drv_is_avs_enabled(struct msm_spm_driver_data *dev)
  256. {
  257. msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
  258. return dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] & BIT(0);
  259. }
  260. static void msm_spm_drv_disable_avs(struct msm_spm_driver_data *dev)
  261. {
  262. msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
  263. dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] &= ~BIT(27);
  264. msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
  265. }
  266. static void msm_spm_drv_enable_avs(struct msm_spm_driver_data *dev)
  267. {
  268. dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] |= BIT(27);
  269. msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
  270. }
  271. static void msm_spm_drv_set_avs_vlevel(struct msm_spm_driver_data *dev,
  272. unsigned int vlevel)
  273. {
  274. vlevel &= 0x3f;
  275. dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] &= ~0x7efc00;
  276. dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] |= ((vlevel - 4) << 10);
  277. dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] |= (vlevel << 17);
  278. msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
  279. }
  280. #else
  281. static bool msm_spm_drv_is_avs_enabled(struct msm_spm_driver_data *dev)
  282. {
  283. return false;
  284. }
  285. static void msm_spm_drv_disable_avs(struct msm_spm_driver_data *dev) { }
  286. static void msm_spm_drv_enable_avs(struct msm_spm_driver_data *dev) { }
  287. static void msm_spm_drv_set_avs_vlevel(struct msm_spm_driver_data *dev,
  288. unsigned int vlevel) { }
  289. #endif
  290. int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev, unsigned int vlevel)
  291. {
  292. uint32_t timeout_us, new_level;
  293. bool avs_enabled;
  294. if (!dev)
  295. return -EINVAL;
  296. avs_enabled = msm_spm_drv_is_avs_enabled(dev);
  297. if (!msm_spm_pmic_arb_present(dev))
  298. return -ENOSYS;
  299. if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
  300. pr_info("%s: requesting vlevel %#x\n", __func__, vlevel);
  301. if (avs_enabled)
  302. msm_spm_drv_disable_avs(dev);
  303. /* Kick the state machine back to idle */
  304. dev->reg_shadow[MSM_SPM_REG_SAW2_RST] = 1;
  305. msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_RST);
  306. msm_spm_drv_set_vctl2(dev, vlevel);
  307. timeout_us = dev->vctl_timeout_us;
  308. /* Confirm the voltage we set was what hardware sent */
  309. do {
  310. new_level = msm_spm_drv_get_sts_curr_pmic_data(dev);
  311. if (new_level == vlevel)
  312. break;
  313. udelay(1);
  314. } while (--timeout_us);
  315. if (!timeout_us) {
  316. pr_info("Wrong level %#x\n", new_level);
  317. goto set_vdd_bail;
  318. }
  319. if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
  320. pr_info("%s: done, remaining timeout %u us\n",
  321. __func__, timeout_us);
  322. /* Set AVS min/max */
  323. if (avs_enabled) {
  324. msm_spm_drv_set_avs_vlevel(dev, vlevel);
  325. msm_spm_drv_enable_avs(dev);
  326. }
  327. return 0;
  328. set_vdd_bail:
  329. if (avs_enabled)
  330. msm_spm_drv_enable_avs(dev);
  331. pr_err("%s: failed %#x, remaining timeout %uus, vlevel %#x\n",
  332. __func__, vlevel, timeout_us, new_level);
  333. return -EIO;
  334. }
  335. static int msm_spm_drv_get_pmic_port(struct msm_spm_driver_data *dev,
  336. enum msm_spm_pmic_port port)
  337. {
  338. int index = -1;
  339. switch (port) {
  340. case MSM_SPM_PMIC_VCTL_PORT:
  341. index = dev->vctl_port;
  342. break;
  343. case MSM_SPM_PMIC_PHASE_PORT:
  344. index = dev->phase_port;
  345. break;
  346. case MSM_SPM_PMIC_PFM_PORT:
  347. index = dev->pfm_port;
  348. break;
  349. default:
  350. break;
  351. }
  352. return index;
  353. }
  354. int msm_spm_drv_set_pmic_data(struct msm_spm_driver_data *dev,
  355. enum msm_spm_pmic_port port, unsigned int data)
  356. {
  357. unsigned int pmic_data = 0;
  358. unsigned int timeout_us = 0;
  359. int index = 0;
  360. if (!msm_spm_pmic_arb_present(dev))
  361. return -ENOSYS;
  362. index = msm_spm_drv_get_pmic_port(dev, port);
  363. if (index < 0)
  364. return -ENODEV;
  365. pmic_data |= data & 0xFF;
  366. pmic_data |= (index & 0x7) << 16;
  367. dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] &= ~0x700FF;
  368. dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] |= pmic_data;
  369. msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_VCTL);
  370. mb();
  371. timeout_us = dev->vctl_timeout_us;
  372. /**
  373. * Confirm the pmic data set was what hardware sent by
  374. * checking the PMIC FSM state.
  375. * We cannot use the sts_pmic_data and check it against
  376. * the value like we do fot set_vdd, since the PMIC_STS
  377. * is only updated for SAW_VCTL sent with port index 0.
  378. */
  379. do {
  380. if (msm_spm_drv_get_sts_pmic_state(dev) ==
  381. MSM_SPM_PMIC_STATE_IDLE)
  382. break;
  383. udelay(1);
  384. } while (--timeout_us);
  385. if (!timeout_us) {
  386. pr_err("%s: failed, remaining timeout %u us, data %d\n",
  387. __func__, timeout_us, data);
  388. return -EIO;
  389. }
  390. return 0;
  391. }
  392. void msm_spm_drv_reinit(struct msm_spm_driver_data *dev)
  393. {
  394. int i;
  395. for (i = 0; i < MSM_SPM_REG_NR_INITIALIZE; i++)
  396. msm_spm_drv_flush_shadow(dev, i);
  397. msm_spm_drv_flush_seq_entry(dev);
  398. mb();
  399. }
  400. int __devinit msm_spm_drv_init(struct msm_spm_driver_data *dev,
  401. struct msm_spm_platform_data *data)
  402. {
  403. int i;
  404. int num_spm_entry;
  405. bool found = false;
  406. BUG_ON(!dev || !data);
  407. dev->vctl_port = data->vctl_port;
  408. dev->phase_port = data->phase_port;
  409. dev->pfm_port = data->pfm_port;
  410. dev->reg_base_addr = data->reg_base_addr;
  411. memcpy(dev->reg_shadow, data->reg_init_values,
  412. sizeof(data->reg_init_values));
  413. dev->vctl_timeout_us = data->vctl_timeout_us;
  414. msm_spm_drv_get_saw2_ver(dev, &dev->major, &dev->minor);
  415. for (i = 0; i < ARRAY_SIZE(saw2_info); i++)
  416. if (dev->major == saw2_info[i].major &&
  417. dev->minor == saw2_info[i].minor) {
  418. pr_debug("%s: Version found\n",
  419. saw2_info[i].ver_name);
  420. dev->reg_offsets = saw2_info[i].spm_reg_offset_ptr;
  421. found = true;
  422. break;
  423. }
  424. if (!found) {
  425. pr_err("%s: No SAW2 version found\n", __func__);
  426. BUG_ON(!found);
  427. }
  428. for (i = 0; i < MSM_SPM_REG_NR_INITIALIZE; i++)
  429. msm_spm_drv_flush_shadow(dev, i);
  430. /* barrier to ensure write completes before we update shadow
  431. * registers
  432. */
  433. mb();
  434. for (i = 0; i < MSM_SPM_REG_NR_INITIALIZE; i++)
  435. msm_spm_drv_load_shadow(dev, i);
  436. /* barrier to ensure read completes before we proceed further*/
  437. mb();
  438. num_spm_entry = msm_spm_drv_get_num_spm_entry(dev);
  439. dev->reg_seq_entry_shadow =
  440. kzalloc(sizeof(*dev->reg_seq_entry_shadow) * num_spm_entry,
  441. GFP_KERNEL);
  442. if (!dev->reg_seq_entry_shadow)
  443. return -ENOMEM;
  444. return 0;
  445. }