PD Buddy Sink Firmware
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

config.c 8.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. /*
  2. * PD Buddy Sink Firmware - Smart power jack for USB Power Delivery
  3. * Copyright (C) 2017-2018 Clayton G. Hobbs <clay@lakeserv.net>
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "config.h"
  19. #include "chprintf.h"
  20. #include <pd.h>
  21. /* Initialize the location of the configuration array. PDBS_CONFIG_BASE is set
  22. * in the Makefile. */
  23. struct pdbs_config *pdbs_config_array = (struct pdbs_config *) PDBS_CONFIG_BASE;
  24. /* The location of the current configuration object. NULL if not known or
  25. * there is no current configuration. */
  26. struct pdbs_config *config_cur = NULL;
  27. void pdbs_config_print(BaseSequentialStream *chp, const struct pdbs_config *cfg)
  28. {
  29. /* Print the status */
  30. chprintf(chp, "status: ");
  31. switch (cfg->status) {
  32. case PDBS_CONFIG_STATUS_INVALID:
  33. chprintf(chp, "in");
  34. /* fall-through */
  35. case PDBS_CONFIG_STATUS_VALID:
  36. chprintf(chp, "valid\r\n");
  37. break;
  38. case PDBS_CONFIG_STATUS_EMPTY:
  39. chprintf(chp, "empty\r\n");
  40. /* Stop early because the rest of the information is meaningless in
  41. * this case. */
  42. return;
  43. default:
  44. chprintf(chp, "%04X\r\n", cfg->status);
  45. break;
  46. }
  47. /* Print the flags */
  48. chprintf(chp, "flags:");
  49. if ((cfg->flags & ~PDBS_CONFIG_FLAGS_CURRENT_DEFN) == 0) {
  50. chprintf(chp, " (none)");
  51. }
  52. if (cfg->flags & PDBS_CONFIG_FLAGS_GIVEBACK) {
  53. chprintf(chp, " GiveBack");
  54. }
  55. if (cfg->flags & PDBS_CONFIG_FLAGS_VAR_BAT) {
  56. chprintf(chp, " Var/Bat");
  57. }
  58. if (cfg->flags & PDBS_CONFIG_FLAGS_HV_PREFERRED) {
  59. chprintf(chp, " HV_Preferred");
  60. }
  61. chprintf(chp, "\r\n");
  62. /* Print voltage */
  63. chprintf(chp, "v: %d.%03d V\r\n", PD_MV_V(cfg->v), PD_MV_MV(cfg->v));
  64. /* If either end of the range is non-zero, print the range */
  65. if (cfg->vmin != 0 || cfg->vmax != 0) {
  66. chprintf(chp, "vmin: %d.%03d V\r\n", PD_MV_V(cfg->vmin),
  67. PD_MV_MV(cfg->vmin));
  68. chprintf(chp, "vmax: %d.%03d V\r\n", PD_MV_V(cfg->vmax),
  69. PD_MV_MV(cfg->vmax));
  70. }
  71. /* Print current-deriving setting */
  72. switch (cfg->flags & PDBS_CONFIG_FLAGS_CURRENT_DEFN) {
  73. case PDBS_CONFIG_FLAGS_CURRENT_DEFN_I:
  74. chprintf(chp, "i: %d.%02d A\r\n", PD_PDI_A(cfg->i), PD_PDI_CA(cfg->i));
  75. break;
  76. case PDBS_CONFIG_FLAGS_CURRENT_DEFN_P:
  77. chprintf(chp, "p: %d.%02d W\r\n", PD_CW_W(cfg->p), PD_CW_CW(cfg->p));
  78. break;
  79. case PDBS_CONFIG_FLAGS_CURRENT_DEFN_R:
  80. chprintf(chp, "r: %d.%02d \316\251\r\n", PD_CO_O(cfg->r), PD_CO_CO(cfg->r));
  81. break;
  82. }
  83. }
  84. /*
  85. * Unlock the flash interface
  86. */
  87. static void flash_unlock(void)
  88. {
  89. /* Wait till no operation is on going */
  90. while ((FLASH->SR & FLASH_SR_BSY) != 0) {
  91. /* Note: we might want a timeout here */
  92. }
  93. /* Check that the Flash is locked */
  94. if ((FLASH->CR & FLASH_CR_LOCK) != 0) {
  95. /* Perform unlock sequence */
  96. FLASH->KEYR = FLASH_KEY1;
  97. FLASH->KEYR = FLASH_KEY2;
  98. }
  99. }
  100. /*
  101. * Lock the flash interface
  102. */
  103. static void flash_lock(void)
  104. {
  105. /* Wait till no operation is on going */
  106. while ((FLASH->SR & FLASH_SR_BSY) != 0) {
  107. /* Note: we might want a timeout here */
  108. }
  109. /* Check that the Flash is unlocked */
  110. if ((FLASH->CR & FLASH_CR_LOCK) == 0) {
  111. /* Lock the flash */
  112. FLASH->CR |= FLASH_CR_LOCK;
  113. }
  114. }
  115. /*
  116. * Write one halfword to flash
  117. */
  118. static void flash_write_halfword(uint16_t *addr, uint16_t data)
  119. {
  120. /* Set the PG bit in the FLASH_CR register to enable programming */
  121. FLASH->CR |= FLASH_CR_PG;
  122. /* Perform the data write (half-word) at the desired address */
  123. *(__IO uint16_t*)(addr) = data;
  124. /* Wait until the BSY bit is reset in the FLASH_SR register */
  125. while ((FLASH->SR & FLASH_SR_BSY) != 0) {
  126. /* For robust implementation, add here time-out management */
  127. }
  128. /* Check the EOP flag in the FLASH_SR register */
  129. if ((FLASH->SR & FLASH_SR_EOP) != 0) {
  130. /* clear it by software by writing it at 1 */
  131. FLASH->SR = FLASH_SR_EOP;
  132. } else {
  133. /* Manage the error cases */
  134. }
  135. /* Reset the PG Bit to disable programming */
  136. FLASH->CR &= ~FLASH_CR_PG;
  137. }
  138. /*
  139. * Erase the configuration page, without any locking
  140. */
  141. static void flash_erase(void)
  142. {
  143. /* Set the PER bit in the FLASH_CR register to enable page erasing */
  144. FLASH->CR |= FLASH_CR_PER;
  145. /* Program the FLASH_AR register to select a page to erase */
  146. FLASH->AR = (int) pdbs_config_array;
  147. /* Set the STRT bit in the FLASH_CR register to start the erasing */
  148. FLASH->CR |= FLASH_CR_STRT;
  149. /* Wait till no operation is on going */
  150. while ((FLASH->SR & FLASH_SR_BSY) != 0) {
  151. /* Note: we might want a timeout here */
  152. }
  153. /* Check the EOP flag in the FLASH_SR register */
  154. if ((FLASH->SR & FLASH_SR_EOP) != 0) {
  155. /* Clear EOP flag by software by writing EOP at 1 */
  156. FLASH->SR = FLASH_SR_EOP;
  157. } else {
  158. /* Manage the error cases */
  159. }
  160. /* Reset the PER Bit to disable the page erase */
  161. FLASH->CR &= ~FLASH_CR_PER;
  162. }
  163. void pdbs_config_flash_erase(void)
  164. {
  165. /* Enter a critical zone */
  166. chSysLock();
  167. flash_unlock();
  168. /* Erase the flash page */
  169. flash_erase();
  170. flash_lock();
  171. /* There is no configuration now, so update config_cur to reflect this */
  172. config_cur = NULL;
  173. /* Exit the critical zone */
  174. chSysUnlock();
  175. }
  176. void pdbs_config_flash_update(const struct pdbs_config *cfg)
  177. {
  178. /* Enter a critical zone */
  179. chSysLock();
  180. flash_unlock();
  181. /* If there is an old entry, invalidate it. */
  182. struct pdbs_config *old = pdbs_config_flash_read();
  183. if (old != NULL) {
  184. flash_write_halfword(&(old->status), PDBS_CONFIG_STATUS_INVALID);
  185. }
  186. /* Find the first empty entry */
  187. struct pdbs_config *empty = NULL;
  188. for (int i = 0; i < PDBS_CONFIG_ARRAY_LEN; i++) {
  189. /* If we've found it, return it. */
  190. if (pdbs_config_array[i].status == PDBS_CONFIG_STATUS_EMPTY) {
  191. empty = &pdbs_config_array[i];
  192. break;
  193. }
  194. }
  195. /* If empty is still NULL, the page is full. Erase it. */
  196. if (empty == NULL) {
  197. flash_erase();
  198. /* Write to the first element */
  199. empty = &pdbs_config_array[0];
  200. }
  201. /* Write the new configuration */
  202. flash_write_halfword(&(empty->status), cfg->status);
  203. flash_write_halfword(&(empty->flags), cfg->flags);
  204. flash_write_halfword(&(empty->v), cfg->v);
  205. flash_write_halfword(&(empty->i), cfg->i);
  206. flash_write_halfword(&(empty->vmin), cfg->vmin);
  207. flash_write_halfword(&(empty->vmax), cfg->vmax);
  208. flash_lock();
  209. /* Update config_cur for the new configuration */
  210. config_cur = empty;
  211. /* Exit the critical zone */
  212. chSysUnlock();
  213. }
  214. struct pdbs_config *pdbs_config_flash_read(void)
  215. {
  216. /* If we already know where the configuration is, return its location */
  217. if (config_cur != NULL) {
  218. return config_cur;
  219. }
  220. /* We don't know where the configuration is (config_cur == NULL), so we
  221. * need to find it and store its location if applicable. */
  222. /* If the first element is empty, there is no valid structure. */
  223. if (pdbs_config_array[0].status == PDBS_CONFIG_STATUS_EMPTY) {
  224. return NULL;
  225. }
  226. /* Find the valid structure, if there is one. */
  227. for (int i = 0; i < PDBS_CONFIG_ARRAY_LEN; i++) {
  228. /* If we've found it, return it. */
  229. if (pdbs_config_array[i].status == PDBS_CONFIG_STATUS_VALID) {
  230. config_cur = &pdbs_config_array[i];
  231. return config_cur;
  232. }
  233. }
  234. /* If we got to the end, none of the structures is valid. */
  235. return NULL;
  236. }