Merge branch 'ipa-pm-irqs'

Alex Elder says:

====================
net: ipa: prepare GSI interrupts for runtime PM

The last patch in this series arranges for GSI interrupts to be
disabled when the IPA hardware is suspended.  This ensures the clock
is always operational when a GSI interrupt fires.  Leading up to
that are patches that rearrange the code a bit to allow this to
be done.

The first two patches aren't *directly* related.  They remove some
flag arguments to some GSI suspend/resume related functions, using
the version field now present in the GSI structure.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2021-08-04 10:12:05 +01:00
commit 8eceea4134
4 changed files with 166 additions and 123 deletions

View file

@ -198,77 +198,6 @@ static void gsi_irq_type_disable(struct gsi *gsi, enum gsi_irq_type_id type_id)
gsi_irq_type_update(gsi, gsi->type_enabled_bitmap & ~BIT(type_id));
}
/* Turn off all GSI interrupts initially; there is no gsi_irq_teardown() */
static void gsi_irq_setup(struct gsi *gsi)
{
/* Disable all interrupt types */
gsi_irq_type_update(gsi, 0);
/* Clear all type-specific interrupt masks */
iowrite32(0, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_MSK_OFFSET);
iowrite32(0, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_MSK_OFFSET);
iowrite32(0, gsi->virt + GSI_CNTXT_GLOB_IRQ_EN_OFFSET);
iowrite32(0, gsi->virt + GSI_CNTXT_SRC_IEOB_IRQ_MSK_OFFSET);
/* The inter-EE interrupts are not supported for IPA v3.0-v3.1 */
if (gsi->version > IPA_VERSION_3_1) {
u32 offset;
/* These registers are in the non-adjusted address range */
offset = GSI_INTER_EE_SRC_CH_IRQ_MSK_OFFSET;
iowrite32(0, gsi->virt_raw + offset);
offset = GSI_INTER_EE_SRC_EV_CH_IRQ_MSK_OFFSET;
iowrite32(0, gsi->virt_raw + offset);
}
iowrite32(0, gsi->virt + GSI_CNTXT_GSI_IRQ_EN_OFFSET);
}
/* Get # supported channel and event rings; there is no gsi_ring_teardown() */
static int gsi_ring_setup(struct gsi *gsi)
{
struct device *dev = gsi->dev;
u32 count;
u32 val;
if (gsi->version < IPA_VERSION_3_5_1) {
/* No HW_PARAM_2 register prior to IPA v3.5.1, assume the max */
gsi->channel_count = GSI_CHANNEL_COUNT_MAX;
gsi->evt_ring_count = GSI_EVT_RING_COUNT_MAX;
return 0;
}
val = ioread32(gsi->virt + GSI_GSI_HW_PARAM_2_OFFSET);
count = u32_get_bits(val, NUM_CH_PER_EE_FMASK);
if (!count) {
dev_err(dev, "GSI reports zero channels supported\n");
return -EINVAL;
}
if (count > GSI_CHANNEL_COUNT_MAX) {
dev_warn(dev, "limiting to %u channels; hardware supports %u\n",
GSI_CHANNEL_COUNT_MAX, count);
count = GSI_CHANNEL_COUNT_MAX;
}
gsi->channel_count = count;
count = u32_get_bits(val, NUM_EV_PER_EE_FMASK);
if (!count) {
dev_err(dev, "GSI reports zero event rings supported\n");
return -EINVAL;
}
if (count > GSI_EVT_RING_COUNT_MAX) {
dev_warn(dev,
"limiting to %u event rings; hardware supports %u\n",
GSI_EVT_RING_COUNT_MAX, count);
count = GSI_EVT_RING_COUNT_MAX;
}
gsi->evt_ring_count = count;
return 0;
}
/* Event ring commands are performed one at a time. Their completion
* is signaled by the event ring control GSI interrupt type, which is
* only enabled when we issue an event ring command. Only the event
@ -920,12 +849,13 @@ static void gsi_channel_program(struct gsi_channel *channel, bool doorbell)
/* All done! */
}
static int __gsi_channel_start(struct gsi_channel *channel, bool start)
static int __gsi_channel_start(struct gsi_channel *channel, bool resume)
{
struct gsi *gsi = channel->gsi;
int ret;
if (!start)
/* Prior to IPA v4.0 suspend/resume is not implemented by GSI */
if (resume && gsi->version < IPA_VERSION_4_0)
return 0;
mutex_lock(&gsi->mutex);
@ -947,7 +877,7 @@ int gsi_channel_start(struct gsi *gsi, u32 channel_id)
napi_enable(&channel->napi);
gsi_irq_ieob_enable_one(gsi, channel->evt_ring_id);
ret = __gsi_channel_start(channel, true);
ret = __gsi_channel_start(channel, false);
if (ret) {
gsi_irq_ieob_disable_one(gsi, channel->evt_ring_id);
napi_disable(&channel->napi);
@ -971,7 +901,7 @@ static int gsi_channel_stop_retry(struct gsi_channel *channel)
return ret;
}
static int __gsi_channel_stop(struct gsi_channel *channel, bool stop)
static int __gsi_channel_stop(struct gsi_channel *channel, bool suspend)
{
struct gsi *gsi = channel->gsi;
int ret;
@ -979,7 +909,8 @@ static int __gsi_channel_stop(struct gsi_channel *channel, bool stop)
/* Wait for any underway transactions to complete before stopping. */
gsi_channel_trans_quiesce(channel);
if (!stop)
/* Prior to IPA v4.0 suspend/resume is not implemented by GSI */
if (suspend && gsi->version < IPA_VERSION_4_0)
return 0;
mutex_lock(&gsi->mutex);
@ -997,7 +928,7 @@ int gsi_channel_stop(struct gsi *gsi, u32 channel_id)
struct gsi_channel *channel = &gsi->channel[channel_id];
int ret;
ret = __gsi_channel_stop(channel, true);
ret = __gsi_channel_stop(channel, false);
if (ret)
return ret;
@ -1026,13 +957,13 @@ void gsi_channel_reset(struct gsi *gsi, u32 channel_id, bool doorbell)
mutex_unlock(&gsi->mutex);
}
/* Stop a STARTED channel for suspend (using stop if requested) */
int gsi_channel_suspend(struct gsi *gsi, u32 channel_id, bool stop)
/* Stop a started channel for suspend */
int gsi_channel_suspend(struct gsi *gsi, u32 channel_id)
{
struct gsi_channel *channel = &gsi->channel[channel_id];
int ret;
ret = __gsi_channel_stop(channel, stop);
ret = __gsi_channel_stop(channel, true);
if (ret)
return ret;
@ -1042,12 +973,24 @@ int gsi_channel_suspend(struct gsi *gsi, u32 channel_id, bool stop)
return 0;
}
/* Resume a suspended channel (starting will be requested if STOPPED) */
int gsi_channel_resume(struct gsi *gsi, u32 channel_id, bool start)
/* Resume a suspended channel (starting if stopped) */
int gsi_channel_resume(struct gsi *gsi, u32 channel_id)
{
struct gsi_channel *channel = &gsi->channel[channel_id];
return __gsi_channel_start(channel, start);
return __gsi_channel_start(channel, true);
}
/* Prevent all GSI interrupts while suspended */
void gsi_suspend(struct gsi *gsi)
{
disable_irq(gsi->irq);
}
/* Allow all GSI interrupts again when resuming */
void gsi_resume(struct gsi *gsi)
{
enable_irq(gsi->irq);
}
/**
@ -1372,33 +1315,20 @@ static irqreturn_t gsi_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
/* Init function for GSI IRQ lookup; there is no gsi_irq_exit() */
static int gsi_irq_init(struct gsi *gsi, struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
unsigned int irq;
int ret;
ret = platform_get_irq_byname(pdev, "gsi");
if (ret <= 0)
return ret ? : -EINVAL;
irq = ret;
ret = request_irq(irq, gsi_isr, 0, "gsi", gsi);
if (ret) {
dev_err(dev, "error %d requesting \"gsi\" IRQ\n", ret);
return ret;
}
gsi->irq = irq;
gsi->irq = ret;
return 0;
}
static void gsi_irq_exit(struct gsi *gsi)
{
free_irq(gsi->irq, gsi);
}
/* Return the transaction associated with a transfer completion event */
static struct gsi_trans *gsi_event_trans(struct gsi_channel *channel,
struct gsi_event *event)
@ -1876,6 +1806,93 @@ static void gsi_channel_teardown(struct gsi *gsi)
gsi_irq_disable(gsi);
}
/* Turn off all GSI interrupts initially */
static int gsi_irq_setup(struct gsi *gsi)
{
int ret;
/* Writing 1 indicates IRQ interrupts; 0 would be MSI */
iowrite32(1, gsi->virt + GSI_CNTXT_INTSET_OFFSET);
/* Disable all interrupt types */
gsi_irq_type_update(gsi, 0);
/* Clear all type-specific interrupt masks */
iowrite32(0, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_MSK_OFFSET);
iowrite32(0, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_MSK_OFFSET);
iowrite32(0, gsi->virt + GSI_CNTXT_GLOB_IRQ_EN_OFFSET);
iowrite32(0, gsi->virt + GSI_CNTXT_SRC_IEOB_IRQ_MSK_OFFSET);
/* The inter-EE interrupts are not supported for IPA v3.0-v3.1 */
if (gsi->version > IPA_VERSION_3_1) {
u32 offset;
/* These registers are in the non-adjusted address range */
offset = GSI_INTER_EE_SRC_CH_IRQ_MSK_OFFSET;
iowrite32(0, gsi->virt_raw + offset);
offset = GSI_INTER_EE_SRC_EV_CH_IRQ_MSK_OFFSET;
iowrite32(0, gsi->virt_raw + offset);
}
iowrite32(0, gsi->virt + GSI_CNTXT_GSI_IRQ_EN_OFFSET);
ret = request_irq(gsi->irq, gsi_isr, 0, "gsi", gsi);
if (ret)
dev_err(gsi->dev, "error %d requesting \"gsi\" IRQ\n", ret);
return ret;
}
static void gsi_irq_teardown(struct gsi *gsi)
{
free_irq(gsi->irq, gsi);
}
/* Get # supported channel and event rings; there is no gsi_ring_teardown() */
static int gsi_ring_setup(struct gsi *gsi)
{
struct device *dev = gsi->dev;
u32 count;
u32 val;
if (gsi->version < IPA_VERSION_3_5_1) {
/* No HW_PARAM_2 register prior to IPA v3.5.1, assume the max */
gsi->channel_count = GSI_CHANNEL_COUNT_MAX;
gsi->evt_ring_count = GSI_EVT_RING_COUNT_MAX;
return 0;
}
val = ioread32(gsi->virt + GSI_GSI_HW_PARAM_2_OFFSET);
count = u32_get_bits(val, NUM_CH_PER_EE_FMASK);
if (!count) {
dev_err(dev, "GSI reports zero channels supported\n");
return -EINVAL;
}
if (count > GSI_CHANNEL_COUNT_MAX) {
dev_warn(dev, "limiting to %u channels; hardware supports %u\n",
GSI_CHANNEL_COUNT_MAX, count);
count = GSI_CHANNEL_COUNT_MAX;
}
gsi->channel_count = count;
count = u32_get_bits(val, NUM_EV_PER_EE_FMASK);
if (!count) {
dev_err(dev, "GSI reports zero event rings supported\n");
return -EINVAL;
}
if (count > GSI_EVT_RING_COUNT_MAX) {
dev_warn(dev,
"limiting to %u event rings; hardware supports %u\n",
GSI_EVT_RING_COUNT_MAX, count);
count = GSI_EVT_RING_COUNT_MAX;
}
gsi->evt_ring_count = count;
return 0;
}
/* Setup function for GSI. GSI firmware must be loaded and initialized */
int gsi_setup(struct gsi *gsi)
{
@ -1889,25 +1906,34 @@ int gsi_setup(struct gsi *gsi)
return -EIO;
}
gsi_irq_setup(gsi); /* No matching teardown required */
ret = gsi_irq_setup(gsi);
if (ret)
return ret;
ret = gsi_ring_setup(gsi); /* No matching teardown required */
if (ret)
return ret;
goto err_irq_teardown;
/* Initialize the error log */
iowrite32(0, gsi->virt + GSI_ERROR_LOG_OFFSET);
/* Writing 1 indicates IRQ interrupts; 0 would be MSI */
iowrite32(1, gsi->virt + GSI_CNTXT_INTSET_OFFSET);
ret = gsi_channel_setup(gsi);
if (ret)
goto err_irq_teardown;
return gsi_channel_setup(gsi);
return 0;
err_irq_teardown:
gsi_irq_teardown(gsi);
return ret;
}
/* Inverse of gsi_setup() */
void gsi_teardown(struct gsi *gsi)
{
gsi_channel_teardown(gsi);
gsi_irq_teardown(gsi);
}
/* Initialize a channel's event ring */
@ -2204,20 +2230,18 @@ int gsi_init(struct gsi *gsi, struct platform_device *pdev,
init_completion(&gsi->completion);
ret = gsi_irq_init(gsi, pdev);
ret = gsi_irq_init(gsi, pdev); /* No matching exit required */
if (ret)
goto err_iounmap;
ret = gsi_channel_init(gsi, count, data);
if (ret)
goto err_irq_exit;
goto err_iounmap;
mutex_init(&gsi->mutex);
return 0;
err_irq_exit:
gsi_irq_exit(gsi);
err_iounmap:
iounmap(gsi->virt_raw);
@ -2229,7 +2253,6 @@ void gsi_exit(struct gsi *gsi)
{
mutex_destroy(&gsi->mutex);
gsi_channel_exit(gsi);
gsi_irq_exit(gsi);
iounmap(gsi->virt_raw);
}

View file

@ -232,8 +232,35 @@ int gsi_channel_stop(struct gsi *gsi, u32 channel_id);
*/
void gsi_channel_reset(struct gsi *gsi, u32 channel_id, bool doorbell);
int gsi_channel_suspend(struct gsi *gsi, u32 channel_id, bool stop);
int gsi_channel_resume(struct gsi *gsi, u32 channel_id, bool start);
/**
* gsi_suspend() - Prepare the GSI subsystem for suspend
* @gsi: GSI pointer
*/
void gsi_suspend(struct gsi *gsi);
/**
* gsi_resume() - Resume the GSI subsystem following suspend
* @gsi: GSI pointer
*/
void gsi_resume(struct gsi *gsi);
/**
* gsi_channel_suspend() - Suspend a GSI channel
* @gsi: GSI pointer
* @channel_id: Channel to suspend
*
* For IPA v4.0+, suspend is implemented by stopping the channel.
*/
int gsi_channel_suspend(struct gsi *gsi, u32 channel_id);
/**
* gsi_channel_resume() - Resume a suspended GSI channel
* @gsi: GSI pointer
* @channel_id: Channel to resume
*
* For IPA v4.0+, the stopped channel is started again.
*/
int gsi_channel_resume(struct gsi *gsi, u32 channel_id);
/**
* gsi_init() - Initialize the GSI subsystem

View file

@ -1587,7 +1587,6 @@ void ipa_endpoint_suspend_one(struct ipa_endpoint *endpoint)
{
struct device *dev = &endpoint->ipa->pdev->dev;
struct gsi *gsi = &endpoint->ipa->gsi;
bool stop_channel;
int ret;
if (!(endpoint->ipa->enabled & BIT(endpoint->endpoint_id)))
@ -1598,11 +1597,7 @@ void ipa_endpoint_suspend_one(struct ipa_endpoint *endpoint)
(void)ipa_endpoint_program_suspend(endpoint, true);
}
/* Starting with IPA v4.0, endpoints are suspended by stopping the
* underlying GSI channel rather than using endpoint suspend mode.
*/
stop_channel = endpoint->ipa->version >= IPA_VERSION_4_0;
ret = gsi_channel_suspend(gsi, endpoint->channel_id, stop_channel);
ret = gsi_channel_suspend(gsi, endpoint->channel_id);
if (ret)
dev_err(dev, "error %d suspending channel %u\n", ret,
endpoint->channel_id);
@ -1612,7 +1607,6 @@ void ipa_endpoint_resume_one(struct ipa_endpoint *endpoint)
{
struct device *dev = &endpoint->ipa->pdev->dev;
struct gsi *gsi = &endpoint->ipa->gsi;
bool start_channel;
int ret;
if (!(endpoint->ipa->enabled & BIT(endpoint->endpoint_id)))
@ -1621,11 +1615,7 @@ void ipa_endpoint_resume_one(struct ipa_endpoint *endpoint)
if (!endpoint->toward_ipa)
(void)ipa_endpoint_program_suspend(endpoint, false);
/* Starting with IPA v4.0, the underlying GSI channel must be
* restarted for resume.
*/
start_channel = endpoint->ipa->version >= IPA_VERSION_4_0;
ret = gsi_channel_resume(gsi, endpoint->channel_id, start_channel);
ret = gsi_channel_resume(gsi, endpoint->channel_id);
if (ret)
dev_err(dev, "error %d resuming channel %u\n", ret,
endpoint->channel_id);

View file

@ -892,6 +892,7 @@ static int ipa_suspend(struct device *dev)
if (ipa->setup_complete) {
__clear_bit(IPA_FLAG_RESUMED, ipa->flags);
ipa_endpoint_suspend(ipa);
gsi_suspend(&ipa->gsi);
}
ipa_clock_put(ipa);
@ -919,8 +920,10 @@ static int ipa_resume(struct device *dev)
ipa_clock_get(ipa);
/* Endpoints aren't usable until setup is complete */
if (ipa->setup_complete)
if (ipa->setup_complete) {
gsi_resume(&ipa->gsi);
ipa_endpoint_resume(ipa);
}
return 0;
}