/* Copyright 2021 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #define DT_DRV_COMPAT nuvoton_npcx_cros_flash #include #include #include #include #include #include #include #include #include #include "ec_tasks.h" #include "flash.h" #include "gpio.h" #include "soc_miwu.h" #include "spi_flash_reg.h" #include "task.h" #include "../drivers/flash/spi_nor.h" LOG_MODULE_REGISTER(cros_flash, LOG_LEVEL_ERR); static int all_protected; /* Has all-flash protection been requested? */ static int addr_prot_start; static int addr_prot_length; static uint8_t flag_prot_inconsistent; static uint8_t saved_sr1; static uint8_t saved_sr2; #define CMD_READ_STATUS_REG 0x05 #define CMD_READ_STATUS_REG2 0x35 /* Device config */ struct cros_flash_npcx_config { /* flash interface unit base address */ uintptr_t base; /* clock configuration */ struct npcx_clk_cfg clk_cfg; /* Flash size (Unit:bytes) */ int size; /* pinmux configuration */ const uint8_t alts_size; const struct npcx_alt *alts_list; }; /* Device data */ struct cros_flash_npcx_data { /* flag of flash write protection */ bool write_protectied; /* mutex of flash interface controller */ struct k_sem lock_sem; }; /* TODO: Should we replace them with Kconfig variables */ #define CONFIG_FLASH_WRITE_SIZE 0x1 /* minimum write size */ #define CONFIG_FLASH_WRITE_IDEAL_SIZE 256 /* one page size for write */ /* TODO: It should be defined in the spi_nor.h in the zephyr repository */ #define SPI_NOR_CMD_FAST_READ 0x0B /* Driver convenience defines */ #define DRV_CONFIG(dev) ((const struct cros_flash_npcx_config *)(dev)->config) #define DRV_DATA(dev) ((struct cros_flash_npcx_data *)(dev)->data) #define HAL_INSTANCE(dev) (struct fiu_reg *)(DRV_CONFIG(dev)->base) /* cros ec flash local inline functions */ static inline void cros_flash_npcx_mutex_lock(const struct device *dev) { struct cros_flash_npcx_data *data = DRV_DATA(dev); k_sem_take(&data->lock_sem, K_FOREVER); } static inline void cros_flash_npcx_mutex_unlock(const struct device *dev) { struct cros_flash_npcx_data *data = DRV_DATA(dev); k_sem_give(&data->lock_sem); } static inline void cros_flash_npcx_set_address(const struct device *dev, uint32_t qspi_addr) { struct fiu_reg *const inst = HAL_INSTANCE(dev); uint8_t *addr = (uint8_t *)&qspi_addr; /* Write 3 bytes address to UMA registers */ inst->UMA_AB2 = addr[2]; inst->UMA_AB1 = addr[1]; inst->UMA_AB0 = addr[0]; } static inline void cros_flash_npcx_cs_level(const struct device *dev, int level) { struct fiu_reg *const inst = HAL_INSTANCE(dev); /* Set chip select to high/low level */ if (level == 0) inst->UMA_ECTS &= ~BIT(NPCX_UMA_ECTS_SW_CS1); else inst->UMA_ECTS |= BIT(NPCX_UMA_ECTS_SW_CS1); } static inline void cros_flash_npcx_exec_cmd(const struct device *dev, uint8_t code, uint8_t cts) { struct fiu_reg *const inst = HAL_INSTANCE(dev); #ifdef CONFIG_ASSERT struct cros_flash_npcx_data *data = DRV_DATA(dev); /* Flash mutex must be held while executing UMA commands */ __ASSERT((k_sem_count_get(&data->lock_sem) == 0), "UMA is not locked"); #endif /* set UMA_CODE */ inst->UMA_CODE = code; /* execute UMA flash transaction */ inst->UMA_CTS = cts; while (IS_BIT_SET(inst->UMA_CTS, NPCX_UMA_CTS_EXEC_DONE)) ; } static inline void cros_flash_npcx_burst_read(const struct device *dev, char *dst_data, int dst_size) { struct fiu_reg *const inst = HAL_INSTANCE(dev); /* Burst read transaction */ for (int idx = 0; idx < dst_size; idx++) { /* 1101 0101 - EXEC, RD, NO CMD, NO ADDR, 4 bytes */ inst->UMA_CTS = UMA_CODE_RD_BYTE(1); /* wait for UMA to complete */ while (IS_BIT_SET(inst->UMA_CTS, NPCX_UMA_CTS_EXEC_DONE)) ; /* Get read transaction results*/ dst_data[idx] = inst->UMA_DB0; } } static inline int cros_flash_npcx_wait_busy_bit_clear(const struct device *dev) { struct fiu_reg *const inst = HAL_INSTANCE(dev); int wait_period = 10; /* 10 us period t0 check status register */ int timeout = (10 * USEC_PER_SEC) / wait_period; /* 10 seconds */ do { /* Read status register */ inst->UMA_CTS = UMA_CODE_RD_BYTE(1); while (IS_BIT_SET(inst->UMA_CTS, NPCX_UMA_CTS_EXEC_DONE)) ; /* Status bit is clear */ if ((inst->UMA_DB0 & SPI_NOR_WIP_BIT) == 0) break; k_usleep(wait_period); } while (--timeout); /* Wait for busy bit clear */ if (timeout) { return 0; } else { return -ETIMEDOUT; } } /* cros ec flash local functions */ static int cros_flash_npcx_wait_ready(const struct device *dev) { int ret = 0; /* Drive CS to low */ cros_flash_npcx_cs_level(dev, 0); /* Command for Read status register of flash */ cros_flash_npcx_exec_cmd(dev, SPI_NOR_CMD_RDSR, UMA_CODE_CMD_ONLY); /* Wait busy bit is clear */ ret = cros_flash_npcx_wait_busy_bit_clear(dev); /* Drive CS to low */ cros_flash_npcx_cs_level(dev, 1); return ret; } static int cros_flash_npcx_set_write_enable(const struct device *dev) { struct fiu_reg *const inst = HAL_INSTANCE(dev); int ret; /* Wait for previous operation to complete */ ret = cros_flash_npcx_wait_ready(dev); if (ret != 0) return ret; /* Write enable command */ cros_flash_npcx_exec_cmd(dev, SPI_NOR_CMD_WREN, UMA_CODE_CMD_ONLY); /* Wait for flash is not busy */ ret = cros_flash_npcx_wait_ready(dev); if (ret != 0) return ret; if ((inst->UMA_DB0 & SPI_NOR_WEL_BIT) != 0) return 0; else return -EINVAL; } static void cros_flash_npcx_burst_write(const struct device *dev, unsigned int dest_addr, unsigned int bytes, const char *src_data) { /* Chip Select down */ cros_flash_npcx_cs_level(dev, 0); /* Set write address */ cros_flash_npcx_set_address(dev, dest_addr); /* Start programming */ cros_flash_npcx_exec_cmd(dev, SPI_NOR_CMD_PP, UMA_CODE_CMD_WR_ADR); for (int i = 0; i < bytes; i++) { cros_flash_npcx_exec_cmd(dev, *src_data, UMA_CODE_CMD_WR_ONLY); src_data++; } /* Chip Select up */ cros_flash_npcx_cs_level(dev, 1); } static int cros_flash_npcx_program_bytes(const struct device *dev, uint32_t offset, uint32_t bytes, const uint8_t *src_data) { int write_size; int ret = 0; while (bytes > 0) { /* Write length can not go beyond the end of the flash page */ write_size = MIN(bytes, CONFIG_FLASH_WRITE_IDEAL_SIZE - (offset & (CONFIG_FLASH_WRITE_IDEAL_SIZE - 1))); /* Enable write */ ret = cros_flash_npcx_set_write_enable(dev); if (ret != 0) return ret; /* Executr UMA burst write transaction */ cros_flash_npcx_burst_write(dev, offset, write_size, src_data); /* Wait write completed */ ret = cros_flash_npcx_wait_ready(dev); if (ret != 0) return ret; src_data += write_size; offset += write_size; bytes -= write_size; } return ret; } static int cros_flash_npcx_get_status_reg(const struct device *dev, char cmd_code, char *data) { int ret = 0; struct fiu_reg *const inst = HAL_INSTANCE(dev); if (data == 0) { return -EINVAL; } /* Lock flash interface device during reading status register */ cros_flash_npcx_mutex_lock(dev); cros_flash_npcx_exec_cmd(dev, cmd_code, UMA_CODE_CMD_RD_BYTE(1)); *data = inst->UMA_DB0; /* Unlock flash interface device */ cros_flash_npcx_mutex_unlock(dev); return ret; } static int cros_flash_npcx_set_status_reg(const struct device *dev, char *data) { int ret = 0; struct fiu_reg *const inst = HAL_INSTANCE(dev); /* Lock flash interface device */ cros_flash_npcx_mutex_lock(dev); /* Enable write */ ret = cros_flash_npcx_set_write_enable(dev); if (ret != 0) return ret; inst->UMA_DB0 = data[0]; inst->UMA_DB1 = data[1]; /* Write status register 1/2 */ cros_flash_npcx_exec_cmd(dev, SPI_NOR_CMD_WRSR, UMA_CODE_CMD_WR_BYTE(2)); /* Unlock flash interface device */ cros_flash_npcx_mutex_unlock(dev); return ret; } static int cros_flash_npcx_write_protection_set(const struct device *dev, bool enable) { int ret = 0; /* Write protection can be cleared only by core domain reset */ if (!enable) { LOG_ERR("WP can be disabled only via core domain reset "); return -ENOTSUP; } /* Lock flash interface device */ cros_flash_npcx_mutex_lock(dev); ret = npcx_pinctrl_flash_write_protect_set(); /* Unlock flash interface device */ cros_flash_npcx_mutex_unlock(dev); return ret; } static int cros_flash_npcx_write_protection_is_set(const struct device *dev) { return npcx_pinctrl_flash_write_protect_is_set(); } static int cros_flash_npcx_uma_lock(const struct device *dev, bool enable) { struct fiu_reg *const inst = HAL_INSTANCE(dev); if (enable) { inst->UMA_ECTS |= BIT(NPCX_UMA_ECTS_UMA_LOCK); } else { inst->UMA_ECTS &= ~BIT(NPCX_UMA_ECTS_UMA_LOCK); } return 0; } static int flash_get_status1(const struct device *dev) { uint8_t reg; if (all_protected) return saved_sr1; /* Lock physical flash operations */ crec_flash_lock_mapped_storage(1); cros_flash_npcx_get_status_reg(dev, CMD_READ_STATUS_REG, ®); /* Unlock physical flash operations */ crec_flash_lock_mapped_storage(0); return reg; } static int flash_get_status2(const struct device *dev) { uint8_t reg; if (all_protected) return saved_sr1; /* Lock physical flash operations */ crec_flash_lock_mapped_storage(1); cros_flash_npcx_get_status_reg(dev, CMD_READ_STATUS_REG2, ®); /* Unlock physical flash operations */ crec_flash_lock_mapped_storage(0); return reg; } static int flash_write_status_reg(const struct device *dev, uint8_t *data) { return cros_flash_npcx_set_status_reg(dev, data); } static int is_int_flash_protected(const struct device *dev) { return cros_flash_npcx_write_protection_is_set(dev); } static void flash_protect_int_flash(const struct device *dev, int enable) { /* * Please notice the type of WP_IF bit is R/W1S. Once it's set, * only rebooting EC can clear it. */ if (enable) cros_flash_npcx_write_protection_set(dev, enable); } static void flash_uma_lock(const struct device *dev, int enable) { if (enable && !all_protected) { /* * Store SR1 / SR2 for later use since we're about to lock * out all access (including read access) to these regs. */ saved_sr1 = flash_get_status1(dev); saved_sr2 = flash_get_status2(dev); } cros_flash_npcx_uma_lock(dev, enable); all_protected = enable; } static int flash_set_status_for_prot(const struct device *dev, int reg1, int reg2) { uint8_t regs[2]; /* * Writing SR regs will fail if our UMA lock is enabled. If WP * is deasserted then remove the lock and allow the write. */ if (all_protected) { if (is_int_flash_protected(dev)) return EC_ERROR_ACCESS_DENIED; if (crec_flash_get_protect() & EC_FLASH_PROTECT_GPIO_ASSERTED) return EC_ERROR_ACCESS_DENIED; flash_uma_lock(dev, 0); } /* * If WP# is active and ec doesn't protect the status registers of * internal spi-flash, protect it now before setting them. */ #ifdef CONFIG_WP_ACTIVE_HIGH flash_protect_int_flash(dev, gpio_get_level(GPIO_WP)); #else flash_protect_int_flash(dev, !gpio_get_level(GPIO_WP_L)); #endif /*_CONFIG_WP_ACTIVE_HIGH_*/ /* Lock physical flash operations */ crec_flash_lock_mapped_storage(1); regs[0] = reg1; regs[1] = reg2; flash_write_status_reg(dev, regs); /* Unlock physical flash operations */ crec_flash_lock_mapped_storage(0); spi_flash_reg_to_protect(reg1, reg2, &addr_prot_start, &addr_prot_length); return EC_SUCCESS; } static int flash_check_prot_reg(const struct device *dev, unsigned int offset, unsigned int bytes) { unsigned int start; unsigned int len; uint8_t sr1, sr2; int rv = EC_SUCCESS; /* * If WP# is active and ec doesn't protect the status registers of * internal spi-flash, protect it now. */ #ifdef CONFIG_WP_ACTIVE_HIGH flash_protect_int_flash(dev, gpio_get_level(GPIO_WP)); #else flash_protect_int_flash(dev, !gpio_get_level(GPIO_WP_L)); #endif /* CONFIG_WP_ACTIVE_HIGH */ sr1 = flash_get_status1(dev); sr2 = flash_get_status2(dev); /* Invalid value */ if (offset + bytes > CONFIG_FLASH_SIZE_BYTES) return EC_ERROR_INVAL; /* Compute current protect range */ rv = spi_flash_reg_to_protect(sr1, sr2, &start, &len); if (rv) return rv; /* Check if ranges overlap */ if (MAX(start, offset) < MIN(start + len, offset + bytes)) return EC_ERROR_ACCESS_DENIED; return EC_SUCCESS; } static int flash_write_prot_reg(const struct device *dev, unsigned int offset, unsigned int bytes, int hw_protect) { int rv; uint8_t sr1 = flash_get_status1(dev); uint8_t sr2 = flash_get_status2(dev); /* Invalid values */ if (offset + bytes > CONFIG_FLASH_SIZE_BYTES) return EC_ERROR_INVAL; /* Compute desired protect range */ rv = spi_flash_protect_to_reg(offset, bytes, &sr1, &sr2); if (rv) return rv; if (hw_protect) sr1 |= SPI_FLASH_SR1_SRP0; return flash_set_status_for_prot(dev, sr1, sr2); } static int flash_check_prot_range(unsigned int offset, unsigned int bytes) { /* Invalid value */ if (offset + bytes > CONFIG_FLASH_SIZE_BYTES) return EC_ERROR_INVAL; /* Check if ranges overlap */ if (MAX(addr_prot_start, offset) < MIN(addr_prot_start + addr_prot_length, offset + bytes)) return EC_ERROR_ACCESS_DENIED; return EC_SUCCESS; } /* cros ec flash api functions */ static int cros_flash_npcx_init(const struct device *dev) { const struct cros_flash_npcx_config *const config = DRV_CONFIG(dev); struct cros_flash_npcx_data *data = DRV_DATA(dev); /* initialize mutux for flash interface controller */ k_sem_init(&data->lock_sem, 1, 1); /* Configure pin-mux for FIU device */ npcx_pinctrl_mux_configure(config->alts_list, config->alts_size, 1); /* * Protect status registers of internal spi-flash if WP# is active * during ec initialization. */ #ifdef CONFIG_WP_ACTIVE_HIGH flash_protect_int_flash(dev, gpio_get_level(GPIO_WP)); #else flash_protect_int_flash(dev, !gpio_get_level(GPIO_WP_L)); #endif /*CONFIG_WP_ACTIVE_HIGH */ /* Initialize UMA to unlocked */ flash_uma_lock(dev, 0); return 0; } static int cros_flash_npcx_read(const struct device *dev, int offset, int size, char *dst_data) { int ret = 0; /* Unlock flash interface device during reading flash */ cros_flash_npcx_mutex_lock(dev); /* Chip Select down */ cros_flash_npcx_cs_level(dev, 0); /* Set read address */ cros_flash_npcx_set_address(dev, offset); /* Start with fast read command (skip one dummy byte) */ cros_flash_npcx_exec_cmd(dev, SPI_NOR_CMD_FAST_READ, UMA_CODE_CMD_ADR_WR_BYTE(1)); /* Execute burst read */ cros_flash_npcx_burst_read(dev, dst_data, size); /* Chip Select up */ cros_flash_npcx_cs_level(dev, 1); /* Unlock flash interface device */ cros_flash_npcx_mutex_unlock(dev); return ret; } static int cros_flash_npcx_write(const struct device *dev, int offset, int size, const char *src_data) { struct cros_flash_npcx_data *const data = DRV_DATA(dev); int ret = 0; /* check protection */ if (all_protected) return EC_ERROR_ACCESS_DENIED; /* check protection */ if (flash_check_prot_range(offset, size)) return EC_ERROR_ACCESS_DENIED; /* Is write protection enabled? */ if (data->write_protectied) { return -EACCES; } /* Invalid data pointer? */ if (src_data == 0) { return -EINVAL; } /* Unlock flash interface device during writing flash */ cros_flash_npcx_mutex_lock(dev); while (size > 0) { /* First write multiples of 256, then (size % 256) last */ int write_len = ((size % CONFIG_FLASH_WRITE_IDEAL_SIZE) == size) ? size : CONFIG_FLASH_WRITE_IDEAL_SIZE; ret = cros_flash_npcx_program_bytes(dev, offset, write_len, src_data); if (ret != 0) break; src_data += write_len; offset += write_len; size -= write_len; } /* Unlock flash interface device */ cros_flash_npcx_mutex_unlock(dev); return ret; } static int cros_flash_npcx_erase(const struct device *dev, int offset, int size) { const struct cros_flash_npcx_config *const config = DRV_CONFIG(dev); struct cros_flash_npcx_data *const data = DRV_DATA(dev); int ret = 0; /* check protection */ if (all_protected) return EC_ERROR_ACCESS_DENIED; /* check protection */ if (flash_check_prot_range(offset, size)) return EC_ERROR_ACCESS_DENIED; /* Is write protection enabled? */ if (data->write_protectied) { return -EACCES; } /* affected region should be within device */ if (offset < 0 || (offset + size) > config->size) { LOG_ERR("Flash erase address or size exceeds expected values. " "Addr: 0x%lx size %zu", (long)offset, size); return -EINVAL; } /* address must be aligned to erase size */ if ((offset % CONFIG_FLASH_ERASE_SIZE) != 0) { return -EINVAL; } /* Erase size must be a non-zero multiple of sectors */ if ((size == 0) || (size % CONFIG_FLASH_ERASE_SIZE) != 0) { return -EINVAL; } /* Unlock flash interface device during erasing flash */ cros_flash_npcx_mutex_lock(dev); /* Alignment has been checked in upper layer */ for (; size > 0; size -= CONFIG_FLASH_ERASE_SIZE, offset += CONFIG_FLASH_ERASE_SIZE) { /* Enable write */ ret = cros_flash_npcx_set_write_enable(dev); if (ret != 0) break; /* Set erase address */ cros_flash_npcx_set_address(dev, offset); /* Start erasing */ cros_flash_npcx_exec_cmd(dev, SPI_NOR_CMD_BE, UMA_CODE_CMD_ADR); /* Wait erase completed */ ret = cros_flash_npcx_wait_ready(dev); if (ret != 0) { break; } } /* Unlock flash interface device */ cros_flash_npcx_mutex_unlock(dev); return ret; } static int cros_flash_npcx_get_protect(const struct device *dev, int bank) { uint32_t addr = bank * CONFIG_FLASH_BANK_SIZE; return flash_check_prot_reg(dev, addr, CONFIG_FLASH_BANK_SIZE); } static uint32_t cros_flash_npcx_get_protect_flags(const struct device *dev) { uint32_t flags = 0; /* Check if WP region is protected in status register */ if (flash_check_prot_reg(dev, WP_BANK_OFFSET * CONFIG_FLASH_BANK_SIZE, WP_BANK_COUNT * CONFIG_FLASH_BANK_SIZE)) flags |= EC_FLASH_PROTECT_RO_AT_BOOT; /* * TODO: If status register protects a range, but SRP0 is not set, * flags should indicate EC_FLASH_PROTECT_ERROR_INCONSISTENT. */ if (flag_prot_inconsistent) flags |= EC_FLASH_PROTECT_ERROR_INCONSISTENT; /* Read all-protected state from our shadow copy */ if (all_protected) flags |= EC_FLASH_PROTECT_ALL_NOW; return flags; } static int cros_flash_npcx_protect_at_boot(const struct device *dev, uint32_t new_flags) { int ret; if ((new_flags & (EC_FLASH_PROTECT_RO_AT_BOOT | EC_FLASH_PROTECT_ALL_AT_BOOT)) == 0) { /* Clear protection bits in status register */ return flash_set_status_for_prot(dev, 0, 0); } ret = flash_write_prot_reg(dev, CONFIG_WP_STORAGE_OFF, CONFIG_WP_STORAGE_SIZE, 1); /* * Set UMA_LOCK bit for locking all UMA transaction. * But we still can read directly from flash mapping address */ if (new_flags & EC_FLASH_PROTECT_ALL_AT_BOOT) flash_uma_lock(dev, 1); return ret; } static int cros_flash_npcx_protect_now(const struct device *dev, int all) { if (all) { /* * Set UMA_LOCK bit for locking all UMA transaction. * But we still can read directly from flash mapping address */ flash_uma_lock(dev, 1); } else { /* TODO: Implement RO "now" protection */ } return EC_SUCCESS; } /* cros ec flash driver registration */ static const struct cros_flash_driver_api cros_flash_npcx_driver_api = { .init = cros_flash_npcx_init, .physical_read = cros_flash_npcx_read, .physical_write = cros_flash_npcx_write, .physical_erase = cros_flash_npcx_erase, .physical_get_protect = cros_flash_npcx_get_protect, .physical_get_protect_flags = cros_flash_npcx_get_protect_flags, .physical_protect_at_boot = cros_flash_npcx_protect_at_boot, .physical_protect_now = cros_flash_npcx_protect_now, }; static int flash_npcx_init(const struct device *dev) { const struct cros_flash_npcx_config *const config = DRV_CONFIG(dev); const struct device *clk_dev = DEVICE_DT_GET(NPCX_CLK_CTRL_NODE); int ret; /* Turn on device clock first and get source clock freq. */ ret = clock_control_on(clk_dev, (clock_control_subsys_t *)&config->clk_cfg); if (ret < 0) { LOG_ERR("Turn on FIU clock fail %d", ret); return ret; } return ret; } static const struct npcx_alt cros_flash_alts[] = NPCX_DT_ALT_ITEMS_LIST(0); static const struct cros_flash_npcx_config cros_flash_cfg = { .base = DT_INST_REG_ADDR(0), .clk_cfg = NPCX_DT_CLK_CFG_ITEM(0), .size = DT_INST_PROP(0, size), .alts_size = ARRAY_SIZE(cros_flash_alts), .alts_list = cros_flash_alts, }; static struct cros_flash_npcx_data cros_flash_data; DEVICE_DT_INST_DEFINE(0, flash_npcx_init, NULL, &cros_flash_data, &cros_flash_cfg, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &cros_flash_npcx_driver_api);