123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- /*
- * PD Buddy - USB Power Delivery for everyone
- * Copyright (C) 2017 Clayton G. Hobbs <clay@lakeserv.net>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
- #include "config.h"
-
- #include "chprintf.h"
-
- #include "pd.h"
-
-
- /* Initialize the location of the configuration array. PDB_CONFIG_BASE is set
- * in the Makefile. */
- struct pdb_config *pdb_config_array = (struct pdb_config *) PDB_CONFIG_BASE;
-
- /* The location of the current configuration object. NULL if not known or
- * there is no current configuration. */
- struct pdb_config *config_cur = NULL;
-
-
- void pdb_config_print(BaseSequentialStream *chp, const struct pdb_config *cfg)
- {
- /* Print the status */
- chprintf(chp, "status: ");
- switch (cfg->status) {
- case PDB_CONFIG_STATUS_INVALID:
- chprintf(chp, "in");
- /* fall-through */
- case PDB_CONFIG_STATUS_VALID:
- chprintf(chp, "valid\r\n");
- break;
- case PDB_CONFIG_STATUS_EMPTY:
- chprintf(chp, "empty\r\n");
- /* Stop early because the rest of the information is meaningless in
- * this case. */
- return;
- default:
- chprintf(chp, "%04X\r\n", cfg->status);
- break;
- }
-
- /* Print the flags */
- chprintf(chp, "flags: ");
- if (cfg->flags == 0) {
- chprintf(chp, "(none)");
- }
- if (cfg->flags & PDB_CONFIG_FLAGS_GIVEBACK) {
- chprintf(chp, "GiveBack ");
- }
- if (cfg->flags & PDB_CONFIG_FLAGS_VAR_BAT) {
- chprintf(chp, "Var/Bat ");
- }
- chprintf(chp, "\r\n");
-
- /* Print voltages and current */
- chprintf(chp, "v: %d.%02d V\r\n", PD_PDV_V(cfg->v), PD_PDV_CV(cfg->v));
- chprintf(chp, "i: %d.%02d A\r\n", PD_PDI_A(cfg->i), PD_PDI_CA(cfg->i));
- if (cfg->flags & PDB_CONFIG_FLAGS_VAR_BAT) {
- chprintf(chp, "v_min: %d.%02d V\r\n", PD_PDV_V(cfg->v_min),
- PD_PDV_CV(cfg->v_min));
- chprintf(chp, "v_max: %d.%02d V\r\n", PD_PDV_V(cfg->v_max),
- PD_PDV_CV(cfg->v_max));
- }
- }
-
- /*
- * Unlock the flash interface
- */
- static void flash_unlock(void)
- {
- /* Wait till no operation is on going */
- while ((FLASH->SR & FLASH_SR_BSY) != 0) {
- /* Note: we might want a timeout here */
- }
-
- /* Check that the Flash is locked */
- if ((FLASH->CR & FLASH_CR_LOCK) != 0) {
- /* Perform unlock sequence */
- FLASH->KEYR = FLASH_KEY1;
- FLASH->KEYR = FLASH_KEY2;
- }
- }
-
- /*
- * Lock the flash interface
- */
- static void flash_lock(void)
- {
- /* Wait till no operation is on going */
- while ((FLASH->SR & FLASH_SR_BSY) != 0) {
- /* Note: we might want a timeout here */
- }
-
- /* Check that the Flash is unlocked */
- if ((FLASH->CR & FLASH_CR_LOCK) == 0) {
- /* Lock the flash */
- FLASH->CR |= FLASH_CR_LOCK;
- }
- }
-
- /*
- * Write one halfword to flash
- */
- static void flash_write_halfword(uint16_t *addr, uint16_t data)
- {
- /* Set the PG bit in the FLASH_CR register to enable programming */
- FLASH->CR |= FLASH_CR_PG;
- /* Perform the data write (half-word) at the desired address */
- *(__IO uint16_t*)(addr) = data;
- /* Wait until the BSY bit is reset in the FLASH_SR register */
- while ((FLASH->SR & FLASH_SR_BSY) != 0) {
- /* For robust implementation, add here time-out management */
- }
- /* Check the EOP flag in the FLASH_SR register */
- if ((FLASH->SR & FLASH_SR_EOP) != 0) {
- /* clear it by software by writing it at 1 */
- FLASH->SR = FLASH_SR_EOP;
- } else {
- /* Manage the error cases */
- }
- /* Reset the PG Bit to disable programming */
- FLASH->CR &= ~FLASH_CR_PG;
- }
-
- /*
- * Erase the configuration page, without any locking
- */
- static void flash_erase(void)
- {
- /* Set the PER bit in the FLASH_CR register to enable page erasing */
- FLASH->CR |= FLASH_CR_PER;
- /* Program the FLASH_AR register to select a page to erase */
- FLASH->AR = (int) pdb_config_array;
- /* Set the STRT bit in the FLASH_CR register to start the erasing */
- FLASH->CR |= FLASH_CR_STRT;
- /* Wait till no operation is on going */
- while ((FLASH->SR & FLASH_SR_BSY) != 0) {
- /* Note: we might want a timeout here */
- }
- /* Check the EOP flag in the FLASH_SR register */
- if ((FLASH->SR & FLASH_SR_EOP) != 0) {
- /* Clear EOP flag by software by writing EOP at 1 */
- FLASH->SR = FLASH_SR_EOP;
- } else {
- /* Manage the error cases */
- }
- /* Reset the PER Bit to disable the page erase */
- FLASH->CR &= ~FLASH_CR_PER;
- }
-
- void pdb_config_flash_erase(void)
- {
- /* Enter a critical zone */
- chSysLock();
-
- flash_unlock();
-
- /* Erase the flash page */
- flash_erase();
-
- flash_lock();
-
- /* There is no configuration now, so update config_cur to reflect this */
- config_cur = NULL;
-
- /* Exit the critical zone */
- chSysUnlock();
- }
-
- void pdb_config_flash_update(const struct pdb_config *cfg)
- {
- /* Enter a critical zone */
- chSysLock();
-
- flash_unlock();
-
- /* If there is an old entry, invalidate it. */
- struct pdb_config *old = pdb_config_flash_read();
- if (old != NULL) {
- flash_write_halfword(&(old->status), PDB_CONFIG_STATUS_INVALID);
- }
-
- /* Find the first empty entry */
- struct pdb_config *empty = NULL;
- for (int i = 0; i < PDB_CONFIG_ARRAY_LEN; i++) {
- /* If we've found it, return it. */
- if (pdb_config_array[i].status == PDB_CONFIG_STATUS_EMPTY) {
- empty = &pdb_config_array[i];
- break;
- }
- }
- /* If empty is still NULL, the page is full. Erase it. */
- if (empty == NULL) {
- flash_erase();
- /* Write to the first element */
- empty = &pdb_config_array[0];
- }
-
- /* Write the new configuration */
- flash_write_halfword(&(empty->status), cfg->status);
- flash_write_halfword(&(empty->flags), cfg->flags);
- flash_write_halfword(&(empty->v), cfg->v);
- flash_write_halfword(&(empty->i), cfg->i);
- flash_write_halfword(&(empty->v_min), cfg->v_min);
- flash_write_halfword(&(empty->v_max), cfg->v_max);
-
- flash_lock();
-
- /* Update config_cur for the new configuration */
- config_cur = empty;
-
- /* Exit the critical zone */
- chSysUnlock();
- }
-
- struct pdb_config *pdb_config_flash_read(void)
- {
- /* If we already know where the configuration is, return its location */
- if (config_cur != NULL) {
- return config_cur;
- }
-
- /* We don't know where the configuration is (config_cur == NULL), so we
- * need to find it and store its location if applicable. */
-
- /* If the first element is empty, there is no valid structure. */
- if (pdb_config_array[0].status == PDB_CONFIG_STATUS_EMPTY) {
- return NULL;
- }
-
- /* Find the valid structure, if there is one. */
- for (int i = 0; i < PDB_CONFIG_ARRAY_LEN; i++) {
- /* If we've found it, return it. */
- if (pdb_config_array[i].status == PDB_CONFIG_STATUS_VALID) {
- config_cur = &pdb_config_array[i];
- return config_cur;
- }
- }
-
- /* If we got to the end, none of the structures is valid. */
- return NULL;
- }
|