-
Notifications
You must be signed in to change notification settings - Fork 6.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
dma: dma_nxp_edma: fix for dynamic IRQ handling and support for per-channel PDs #82431
Changes from all commits
7a48789
80f4d20
a231d87
1172a3e
5599511
4006c15
6f7204a
67a0c66
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,8 @@ | |
|
||
#include "dma_nxp_edma.h" | ||
|
||
#define EDMA_ACTIVE_TIMEOUT 50 | ||
|
||
/* TODO list: | ||
* 1) Support for requesting a specific channel. | ||
* 2) Support for checking if DMA transfer is pending when attempting config. (?) | ||
|
@@ -28,6 +30,11 @@ static void edma_isr(const void *parameter) | |
cfg = chan->dev->config; | ||
data = chan->dev->data; | ||
|
||
if (chan->state == CHAN_STATE_RELEASING || chan->state == CHAN_STATE_INIT) { | ||
/* skip, not safe to access channel register space */ | ||
return; | ||
} | ||
|
||
if (!EDMA_ChannelRegRead(data->hal_cfg, chan->id, EDMA_TCD_CH_INT)) { | ||
/* skip, interrupt was probably triggered by another channel */ | ||
return; | ||
|
@@ -270,11 +277,11 @@ static int edma_config(const struct device *dev, uint32_t chan_id, | |
chan->cyclic_buffer = false; | ||
} | ||
|
||
/* change channel's state to CONFIGURED */ | ||
ret = channel_change_state(chan, CHAN_STATE_CONFIGURED); | ||
if (ret < 0) { | ||
LOG_ERR("failed to change channel %d state to CONFIGURED", chan_id); | ||
return ret; | ||
/* check if transition to CONFIGURED is allowed */ | ||
if (!channel_allows_transition(chan, CHAN_STATE_CONFIGURED)) { | ||
LOG_ERR("chan %d transition from %d to CONFIGURED not allowed", | ||
chan_id, chan->state); | ||
return -EPERM; | ||
} | ||
|
||
ret = get_transfer_type(dma_cfg->channel_direction, &transfer_type); | ||
|
@@ -335,6 +342,8 @@ static int edma_config(const struct device *dev, uint32_t chan_id, | |
/* dump register status - for debugging purposes */ | ||
edma_dump_channel_registers(data, chan_id); | ||
|
||
chan->state = CHAN_STATE_CONFIGURED; | ||
|
||
return 0; | ||
} | ||
|
||
|
@@ -389,7 +398,6 @@ static int edma_suspend(const struct device *dev, uint32_t chan_id) | |
struct edma_data *data; | ||
const struct edma_config *cfg; | ||
struct edma_channel *chan; | ||
int ret; | ||
|
||
data = dev->data; | ||
cfg = dev->config; | ||
|
@@ -403,11 +411,11 @@ static int edma_suspend(const struct device *dev, uint32_t chan_id) | |
|
||
edma_dump_channel_registers(data, chan_id); | ||
|
||
/* change channel's state to SUSPENDED */ | ||
ret = channel_change_state(chan, CHAN_STATE_SUSPENDED); | ||
if (ret < 0) { | ||
LOG_ERR("failed to change channel %d state to SUSPENDED", chan_id); | ||
return ret; | ||
/* check if transition to SUSPENDED is allowed */ | ||
if (!channel_allows_transition(chan, CHAN_STATE_SUSPENDED)) { | ||
LOG_ERR("chan %d transition from %d to SUSPENDED not allowed", | ||
chan_id, chan->state); | ||
return -EPERM; | ||
} | ||
|
||
LOG_DBG("suspending channel %u", chan_id); | ||
|
@@ -416,6 +424,8 @@ static int edma_suspend(const struct device *dev, uint32_t chan_id) | |
EDMA_ChannelRegUpdate(data->hal_cfg, chan_id, | ||
EDMA_TCD_CH_CSR, 0, EDMA_TCD_CH_CSR_ERQ_MASK); | ||
|
||
chan->state = CHAN_STATE_SUSPENDED; | ||
|
||
return 0; | ||
} | ||
|
||
|
@@ -439,11 +449,11 @@ static int edma_stop(const struct device *dev, uint32_t chan_id) | |
|
||
prev_state = chan->state; | ||
|
||
/* change channel's state to STOPPED */ | ||
ret = channel_change_state(chan, CHAN_STATE_STOPPED); | ||
if (ret < 0) { | ||
LOG_ERR("failed to change channel %d state to STOPPED", chan_id); | ||
return ret; | ||
/* check if transition to STOPPED is allowed */ | ||
if (!channel_allows_transition(chan, CHAN_STATE_STOPPED)) { | ||
LOG_ERR("chan %d transition from %d to STOPPED not allowed", | ||
chan_id, chan->state); | ||
return -EPERM; | ||
} | ||
|
||
LOG_DBG("stopping channel %u", chan_id); | ||
|
@@ -459,11 +469,8 @@ static int edma_stop(const struct device *dev, uint32_t chan_id) | |
/* disable HW requests */ | ||
EDMA_ChannelRegUpdate(data->hal_cfg, chan_id, EDMA_TCD_CH_CSR, 0, | ||
EDMA_TCD_CH_CSR_ERQ_MASK); | ||
|
||
out_release_channel: | ||
|
||
irq_disable(chan->irq); | ||
|
||
/* clear the channel MUX so that it can used by a different peripheral. | ||
* | ||
* note: because the channel is released during dma_stop() that means | ||
|
@@ -482,6 +489,8 @@ static int edma_stop(const struct device *dev, uint32_t chan_id) | |
|
||
edma_dump_channel_registers(data, chan_id); | ||
|
||
chan->state = CHAN_STATE_STOPPED; | ||
|
||
return 0; | ||
} | ||
|
||
|
@@ -490,7 +499,6 @@ static int edma_start(const struct device *dev, uint32_t chan_id) | |
struct edma_data *data; | ||
const struct edma_config *cfg; | ||
struct edma_channel *chan; | ||
int ret; | ||
|
||
data = dev->data; | ||
cfg = dev->config; | ||
|
@@ -502,21 +510,21 @@ static int edma_start(const struct device *dev, uint32_t chan_id) | |
return -EINVAL; | ||
} | ||
|
||
/* change channel's state to STARTED */ | ||
ret = channel_change_state(chan, CHAN_STATE_STARTED); | ||
if (ret < 0) { | ||
LOG_ERR("failed to change channel %d state to STARTED", chan_id); | ||
return ret; | ||
/* check if transition to STARTED is allowed */ | ||
if (!channel_allows_transition(chan, CHAN_STATE_STARTED)) { | ||
LOG_ERR("chan %d transition from %d to STARTED not allowed", | ||
chan_id, chan->state); | ||
return -EPERM; | ||
} | ||
|
||
LOG_DBG("starting channel %u", chan_id); | ||
|
||
irq_enable(chan->irq); | ||
|
||
/* enable HW requests */ | ||
EDMA_ChannelRegUpdate(data->hal_cfg, chan_id, | ||
EDMA_TCD_CH_CSR, EDMA_TCD_CH_CSR_ERQ_MASK, 0); | ||
|
||
chan->state = CHAN_STATE_STARTED; | ||
|
||
return 0; | ||
} | ||
|
||
|
@@ -577,19 +585,80 @@ static int edma_get_attribute(const struct device *dev, uint32_t type, uint32_t | |
|
||
static bool edma_channel_filter(const struct device *dev, int chan_id, void *param) | ||
{ | ||
int *requested_channel; | ||
struct edma_channel *chan; | ||
int ret; | ||
|
||
if (!param) { | ||
return false; | ||
} | ||
|
||
requested_channel = param; | ||
if (*(int *)param != chan_id) { | ||
return false; | ||
} | ||
|
||
chan = lookup_channel(dev, chan_id); | ||
if (!chan) { | ||
return false; | ||
} | ||
|
||
if (*requested_channel == chan_id && lookup_channel(dev, chan_id)) { | ||
return true; | ||
if (chan->pd_dev) { | ||
ret = pm_device_runtime_get(chan->pd_dev); | ||
if (ret < 0) { | ||
LOG_ERR("failed to PM get channel %d PD dev: %d", | ||
chan_id, ret); | ||
return false; | ||
} | ||
} | ||
|
||
irq_enable(chan->irq); | ||
|
||
return true; | ||
} | ||
|
||
static void edma_channel_release(const struct device *dev, uint32_t chan_id) | ||
{ | ||
struct edma_channel *chan; | ||
struct edma_data *data; | ||
int ret; | ||
|
||
chan = lookup_channel(dev, chan_id); | ||
if (!chan) { | ||
return; | ||
} | ||
|
||
data = dev->data; | ||
|
||
if (!channel_allows_transition(chan, CHAN_STATE_RELEASING)) { | ||
LOG_ERR("chan %d transition from %d to RELEASING not allowed", | ||
chan_id, chan->state); | ||
return; | ||
} | ||
|
||
/* channel needs to be INACTIVE before transitioning */ | ||
if (!WAIT_FOR(!EDMA_CHAN_IS_ACTIVE(data, chan), | ||
EDMA_ACTIVE_TIMEOUT, k_busy_wait(1))) { | ||
LOG_ERR("timed out while waiting for chan %d to become inactive", | ||
chan->id); | ||
return; | ||
} | ||
|
||
/* start the process of disabling IRQ and PD */ | ||
chan->state = CHAN_STATE_RELEASING; | ||
|
||
#ifdef CONFIG_NXP_IRQSTEER | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you disable the irq only for CONFIG_NXP_IRQSTEER, in other cases what happens? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is somewhat the way we used to do things. We used to call |
||
irq_disable(chan->irq); | ||
#endif /* CONFIG_NXP_IRQSTEER */ | ||
|
||
if (chan->pd_dev) { | ||
ret = pm_device_runtime_put(chan->pd_dev); | ||
if (ret < 0) { | ||
LOG_ERR("failed to PM put channel %d PD dev: %d", | ||
chan_id, ret); | ||
} | ||
} | ||
|
||
return false; | ||
/* done, proceed with next state */ | ||
chan->state = CHAN_STATE_INIT; | ||
} | ||
|
||
static const struct dma_driver_api edma_api = { | ||
|
@@ -602,6 +671,7 @@ static const struct dma_driver_api edma_api = { | |
.get_status = edma_get_status, | ||
.get_attribute = edma_get_attribute, | ||
.chan_filter = edma_channel_filter, | ||
.chan_release = edma_channel_release, | ||
}; | ||
|
||
static edma_config_t *edma_hal_cfg_get(const struct edma_config *cfg) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You add the
irq_enable()
ondma_chan_filter()
- which is a different function thandma_request_channel(
) as mentioned in the commit message.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, the commit message is supposed to refer to
dma_request_channel
anddma_release_channel
. The filter function is called as part ofdma_request_channel
.