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;
|
volatile bool ready;
|
||||||
#endif /* CONFIG_MULTITHREADING */
|
#endif /* CONFIG_MULTITHREADING */
|
||||||
bool xip_enabled;
|
uint32_t xip_users;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct qspi_nor_config {
|
struct qspi_nor_config {
|
||||||
|
@ -313,7 +313,7 @@ static void qspi_acquire(const struct device *dev)
|
||||||
|
|
||||||
qspi_lock(dev);
|
qspi_lock(dev);
|
||||||
|
|
||||||
if (!dev_data->xip_enabled) {
|
if (dev_data->xip_users == 0) {
|
||||||
qspi_clock_div_change();
|
qspi_clock_div_change();
|
||||||
|
|
||||||
pm_device_busy_set(dev);
|
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;
|
deactivate = atomic_dec(&dev_data->usage_count) == 1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!dev_data->xip_enabled) {
|
if (dev_data->xip_users == 0) {
|
||||||
qspi_clock_div_restore();
|
qspi_clock_div_restore();
|
||||||
|
|
||||||
if (deactivate) {
|
if (deactivate) {
|
||||||
|
@ -1344,35 +1344,54 @@ static int qspi_nor_pm_action(const struct device *dev,
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_PM_DEVICE */
|
#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)
|
void z_impl_nrf_qspi_nor_xip_enable(const struct device *dev, bool enable)
|
||||||
{
|
{
|
||||||
struct qspi_nor_data *dev_data = dev->data;
|
struct qspi_nor_data *dev_data = dev->data;
|
||||||
|
|
||||||
if (dev_data->xip_enabled == enable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
qspi_acquire(dev);
|
qspi_acquire(dev);
|
||||||
|
|
||||||
#if NRF_QSPI_HAS_XIPEN
|
|
||||||
nrf_qspi_xip_set(NRF_QSPI, enable);
|
|
||||||
#endif
|
|
||||||
if (enable) {
|
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 {
|
} else {
|
||||||
/* It turns out that when the QSPI peripheral is deactivated
|
--dev_data->xip_users;
|
||||||
* after a XIP transaction, it cannot be later successfully
|
|
||||||
* reactivated and an attempt to perform another XIP transaction
|
if (dev_data->xip_users == 0) {
|
||||||
* results in the CPU being hung; even a debug session cannot be
|
on_xip_disable(dev);
|
||||||
* 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_enabled = enable;
|
|
||||||
|
|
||||||
qspi_release(dev);
|
qspi_release(dev);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue