drivers: flash: nrf_qspi_nor: Handle properly multiple XIP users
Add reference counting in nrf_qspi_nor_xip_enable() so that XIP is kept enabled as long as there is at least one user that needs it (boot time enabling done with CONFIG_NORDIC_QSPI_NOR_XIP also counts). Signed-off-by: Andrzej Głąbek <andrzej.glabek@nordicsemi.no>
This commit is contained in:
parent
dfb5a31b3e
commit
2d9ede3852
1 changed files with 42 additions and 23 deletions
|
@ -41,7 +41,7 @@ struct qspi_nor_data {
|
|||
*/
|
||||
volatile bool ready;
|
||||
#endif /* CONFIG_MULTITHREADING */
|
||||
bool xip_enabled;
|
||||
uint32_t xip_users;
|
||||
};
|
||||
|
||||
struct qspi_nor_config {
|
||||
|
@ -313,7 +313,7 @@ static void qspi_acquire(const struct device *dev)
|
|||
|
||||
qspi_lock(dev);
|
||||
|
||||
if (!dev_data->xip_enabled) {
|
||||
if (dev_data->xip_users == 0) {
|
||||
qspi_clock_div_change();
|
||||
|
||||
pm_device_busy_set(dev);
|
||||
|
@ -331,7 +331,7 @@ static void qspi_release(const struct device *dev)
|
|||
deactivate = atomic_dec(&dev_data->usage_count) == 1;
|
||||
#endif
|
||||
|
||||
if (!dev_data->xip_enabled) {
|
||||
if (dev_data->xip_users == 0) {
|
||||
qspi_clock_div_restore();
|
||||
|
||||
if (deactivate) {
|
||||
|
@ -1344,35 +1344,54 @@ static int qspi_nor_pm_action(const struct device *dev,
|
|||
}
|
||||
#endif /* CONFIG_PM_DEVICE */
|
||||
|
||||
static void on_xip_enable(const struct device *dev)
|
||||
{
|
||||
#if NRF_QSPI_HAS_XIPEN
|
||||
nrf_qspi_xip_set(NRF_QSPI, true);
|
||||
#endif
|
||||
(void)nrfx_qspi_activate(false);
|
||||
}
|
||||
|
||||
static void on_xip_disable(const struct device *dev)
|
||||
{
|
||||
/* It turns out that when the QSPI peripheral is deactivated
|
||||
* after a XIP transaction, it cannot be later successfully
|
||||
* reactivated and an attempt to perform another XIP transaction
|
||||
* results in the CPU being hung; even a debug session cannot be
|
||||
* started then and the SoC has to be recovered.
|
||||
* As a workaround, at least until the cause of such behavior
|
||||
* is fully clarified, perform a simple non-XIP transaction
|
||||
* (a read of the status register) before deactivating the QSPI.
|
||||
* This prevents the issue from occurring.
|
||||
*/
|
||||
(void)qspi_rdsr(dev, 1);
|
||||
|
||||
#if NRF_QSPI_HAS_XIPEN
|
||||
nrf_qspi_xip_set(NRF_QSPI, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void z_impl_nrf_qspi_nor_xip_enable(const struct device *dev, bool enable)
|
||||
{
|
||||
struct qspi_nor_data *dev_data = dev->data;
|
||||
|
||||
if (dev_data->xip_enabled == enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
qspi_acquire(dev);
|
||||
|
||||
#if NRF_QSPI_HAS_XIPEN
|
||||
nrf_qspi_xip_set(NRF_QSPI, enable);
|
||||
#endif
|
||||
if (enable) {
|
||||
(void)nrfx_qspi_activate(false);
|
||||
if (dev_data->xip_users == 0) {
|
||||
on_xip_enable(dev);
|
||||
}
|
||||
|
||||
++dev_data->xip_users;
|
||||
} else if (dev_data->xip_users == 0) {
|
||||
LOG_ERR("Unbalanced XIP disabling");
|
||||
} else {
|
||||
/* It turns out that when the QSPI peripheral is deactivated
|
||||
* after a XIP transaction, it cannot be later successfully
|
||||
* reactivated and an attempt to perform another XIP transaction
|
||||
* results in the CPU being hung; even a debug session cannot be
|
||||
* started then and the SoC has to be recovered.
|
||||
* As a workaround, at least until the cause of such behavior
|
||||
* is fully clarified, perform a simple non-XIP transaction
|
||||
* (a read of the status register) before deactivating the QSPI.
|
||||
* This prevents the issue from occurring.
|
||||
*/
|
||||
(void)qspi_rdsr(dev, 1);
|
||||
--dev_data->xip_users;
|
||||
|
||||
if (dev_data->xip_users == 0) {
|
||||
on_xip_disable(dev);
|
||||
}
|
||||
}
|
||||
dev_data->xip_enabled = enable;
|
||||
|
||||
qspi_release(dev);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue