Commit 8eceea41 authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 93bbcfee 45a42a3c
...@@ -198,77 +198,6 @@ static void gsi_irq_type_disable(struct gsi *gsi, enum gsi_irq_type_id type_id) ...@@ -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)); 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 /* Event ring commands are performed one at a time. Their completion
* is signaled by the event ring control GSI interrupt type, which is * is signaled by the event ring control GSI interrupt type, which is
* only enabled when we issue an event ring command. Only the event * 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) ...@@ -920,12 +849,13 @@ static void gsi_channel_program(struct gsi_channel *channel, bool doorbell)
/* All done! */ /* 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; struct gsi *gsi = channel->gsi;
int ret; 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; return 0;
mutex_lock(&gsi->mutex); mutex_lock(&gsi->mutex);
...@@ -947,7 +877,7 @@ int gsi_channel_start(struct gsi *gsi, u32 channel_id) ...@@ -947,7 +877,7 @@ int gsi_channel_start(struct gsi *gsi, u32 channel_id)
napi_enable(&channel->napi); napi_enable(&channel->napi);
gsi_irq_ieob_enable_one(gsi, channel->evt_ring_id); gsi_irq_ieob_enable_one(gsi, channel->evt_ring_id);
ret = __gsi_channel_start(channel, true); ret = __gsi_channel_start(channel, false);
if (ret) { if (ret) {
gsi_irq_ieob_disable_one(gsi, channel->evt_ring_id); gsi_irq_ieob_disable_one(gsi, channel->evt_ring_id);
napi_disable(&channel->napi); napi_disable(&channel->napi);
...@@ -971,7 +901,7 @@ static int gsi_channel_stop_retry(struct gsi_channel *channel) ...@@ -971,7 +901,7 @@ static int gsi_channel_stop_retry(struct gsi_channel *channel)
return ret; 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; struct gsi *gsi = channel->gsi;
int ret; int ret;
...@@ -979,7 +909,8 @@ static int __gsi_channel_stop(struct gsi_channel *channel, bool stop) ...@@ -979,7 +909,8 @@ static int __gsi_channel_stop(struct gsi_channel *channel, bool stop)
/* Wait for any underway transactions to complete before stopping. */ /* Wait for any underway transactions to complete before stopping. */
gsi_channel_trans_quiesce(channel); 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; return 0;
mutex_lock(&gsi->mutex); mutex_lock(&gsi->mutex);
...@@ -997,7 +928,7 @@ int gsi_channel_stop(struct gsi *gsi, u32 channel_id) ...@@ -997,7 +928,7 @@ int gsi_channel_stop(struct gsi *gsi, u32 channel_id)
struct gsi_channel *channel = &gsi->channel[channel_id]; struct gsi_channel *channel = &gsi->channel[channel_id];
int ret; int ret;
ret = __gsi_channel_stop(channel, true); ret = __gsi_channel_stop(channel, false);
if (ret) if (ret)
return ret; return ret;
...@@ -1026,13 +957,13 @@ void gsi_channel_reset(struct gsi *gsi, u32 channel_id, bool doorbell) ...@@ -1026,13 +957,13 @@ void gsi_channel_reset(struct gsi *gsi, u32 channel_id, bool doorbell)
mutex_unlock(&gsi->mutex); mutex_unlock(&gsi->mutex);
} }
/* Stop a STARTED channel for suspend (using stop if requested) */ /* Stop a started channel for suspend */
int gsi_channel_suspend(struct gsi *gsi, u32 channel_id, bool stop) int gsi_channel_suspend(struct gsi *gsi, u32 channel_id)
{ {
struct gsi_channel *channel = &gsi->channel[channel_id]; struct gsi_channel *channel = &gsi->channel[channel_id];
int ret; int ret;
ret = __gsi_channel_stop(channel, stop); ret = __gsi_channel_stop(channel, true);
if (ret) if (ret)
return ret; return ret;
...@@ -1042,12 +973,24 @@ int gsi_channel_suspend(struct gsi *gsi, u32 channel_id, bool stop) ...@@ -1042,12 +973,24 @@ int gsi_channel_suspend(struct gsi *gsi, u32 channel_id, bool stop)
return 0; return 0;
} }
/* Resume a suspended channel (starting will be requested if STOPPED) */ /* Resume a suspended channel (starting if stopped) */
int gsi_channel_resume(struct gsi *gsi, u32 channel_id, bool start) int gsi_channel_resume(struct gsi *gsi, u32 channel_id)
{ {
struct gsi_channel *channel = &gsi->channel[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) ...@@ -1372,33 +1315,20 @@ static irqreturn_t gsi_isr(int irq, void *dev_id)
return IRQ_HANDLED; 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) static int gsi_irq_init(struct gsi *gsi, struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev;
unsigned int irq;
int ret; int ret;
ret = platform_get_irq_byname(pdev, "gsi"); ret = platform_get_irq_byname(pdev, "gsi");
if (ret <= 0) if (ret <= 0)
return ret ? : -EINVAL; return ret ? : -EINVAL;
irq = ret; gsi->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;
return 0; return 0;
} }
static void gsi_irq_exit(struct gsi *gsi)
{
free_irq(gsi->irq, gsi);
}
/* Return the transaction associated with a transfer completion event */ /* Return the transaction associated with a transfer completion event */
static struct gsi_trans *gsi_event_trans(struct gsi_channel *channel, static struct gsi_trans *gsi_event_trans(struct gsi_channel *channel,
struct gsi_event *event) struct gsi_event *event)
...@@ -1876,6 +1806,93 @@ static void gsi_channel_teardown(struct gsi *gsi) ...@@ -1876,6 +1806,93 @@ static void gsi_channel_teardown(struct gsi *gsi)
gsi_irq_disable(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 */ /* Setup function for GSI. GSI firmware must be loaded and initialized */
int gsi_setup(struct gsi *gsi) int gsi_setup(struct gsi *gsi)
{ {
...@@ -1889,25 +1906,34 @@ int gsi_setup(struct gsi *gsi) ...@@ -1889,25 +1906,34 @@ int gsi_setup(struct gsi *gsi)
return -EIO; 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 */ ret = gsi_ring_setup(gsi); /* No matching teardown required */
if (ret) if (ret)
return ret; goto err_irq_teardown;
/* Initialize the error log */ /* Initialize the error log */
iowrite32(0, gsi->virt + GSI_ERROR_LOG_OFFSET); iowrite32(0, gsi->virt + GSI_ERROR_LOG_OFFSET);
/* Writing 1 indicates IRQ interrupts; 0 would be MSI */ ret = gsi_channel_setup(gsi);
iowrite32(1, gsi->virt + GSI_CNTXT_INTSET_OFFSET); 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() */ /* Inverse of gsi_setup() */
void gsi_teardown(struct gsi *gsi) void gsi_teardown(struct gsi *gsi)
{ {
gsi_channel_teardown(gsi); gsi_channel_teardown(gsi);
gsi_irq_teardown(gsi);
} }
/* Initialize a channel's event ring */ /* Initialize a channel's event ring */
...@@ -2204,20 +2230,18 @@ int gsi_init(struct gsi *gsi, struct platform_device *pdev, ...@@ -2204,20 +2230,18 @@ int gsi_init(struct gsi *gsi, struct platform_device *pdev,
init_completion(&gsi->completion); init_completion(&gsi->completion);
ret = gsi_irq_init(gsi, pdev); ret = gsi_irq_init(gsi, pdev); /* No matching exit required */
if (ret) if (ret)
goto err_iounmap; goto err_iounmap;
ret = gsi_channel_init(gsi, count, data); ret = gsi_channel_init(gsi, count, data);
if (ret) if (ret)
goto err_irq_exit; goto err_iounmap;
mutex_init(&gsi->mutex); mutex_init(&gsi->mutex);
return 0; return 0;
err_irq_exit:
gsi_irq_exit(gsi);
err_iounmap: err_iounmap:
iounmap(gsi->virt_raw); iounmap(gsi->virt_raw);
...@@ -2229,7 +2253,6 @@ void gsi_exit(struct gsi *gsi) ...@@ -2229,7 +2253,6 @@ void gsi_exit(struct gsi *gsi)
{ {
mutex_destroy(&gsi->mutex); mutex_destroy(&gsi->mutex);
gsi_channel_exit(gsi); gsi_channel_exit(gsi);
gsi_irq_exit(gsi);
iounmap(gsi->virt_raw); iounmap(gsi->virt_raw);
} }
......
...@@ -232,8 +232,35 @@ int gsi_channel_stop(struct gsi *gsi, u32 channel_id); ...@@ -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); 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 * gsi_init() - Initialize the GSI subsystem
......
...@@ -1587,7 +1587,6 @@ void ipa_endpoint_suspend_one(struct ipa_endpoint *endpoint) ...@@ -1587,7 +1587,6 @@ void ipa_endpoint_suspend_one(struct ipa_endpoint *endpoint)
{ {
struct device *dev = &endpoint->ipa->pdev->dev; struct device *dev = &endpoint->ipa->pdev->dev;
struct gsi *gsi = &endpoint->ipa->gsi; struct gsi *gsi = &endpoint->ipa->gsi;
bool stop_channel;
int ret; int ret;
if (!(endpoint->ipa->enabled & BIT(endpoint->endpoint_id))) if (!(endpoint->ipa->enabled & BIT(endpoint->endpoint_id)))
...@@ -1598,11 +1597,7 @@ void ipa_endpoint_suspend_one(struct ipa_endpoint *endpoint) ...@@ -1598,11 +1597,7 @@ void ipa_endpoint_suspend_one(struct ipa_endpoint *endpoint)
(void)ipa_endpoint_program_suspend(endpoint, true); (void)ipa_endpoint_program_suspend(endpoint, true);
} }
/* Starting with IPA v4.0, endpoints are suspended by stopping the ret = gsi_channel_suspend(gsi, endpoint->channel_id);
* 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);
if (ret) if (ret)
dev_err(dev, "error %d suspending channel %u\n", ret, dev_err(dev, "error %d suspending channel %u\n", ret,
endpoint->channel_id); endpoint->channel_id);
...@@ -1612,7 +1607,6 @@ void ipa_endpoint_resume_one(struct ipa_endpoint *endpoint) ...@@ -1612,7 +1607,6 @@ void ipa_endpoint_resume_one(struct ipa_endpoint *endpoint)
{ {
struct device *dev = &endpoint->ipa->pdev->dev; struct device *dev = &endpoint->ipa->pdev->dev;
struct gsi *gsi = &endpoint->ipa->gsi; struct gsi *gsi = &endpoint->ipa->gsi;
bool start_channel;
int ret; int ret;
if (!(endpoint->ipa->enabled & BIT(endpoint->endpoint_id))) if (!(endpoint->ipa->enabled & BIT(endpoint->endpoint_id)))
...@@ -1621,11 +1615,7 @@ void ipa_endpoint_resume_one(struct ipa_endpoint *endpoint) ...@@ -1621,11 +1615,7 @@ void ipa_endpoint_resume_one(struct ipa_endpoint *endpoint)
if (!endpoint->toward_ipa) if (!endpoint->toward_ipa)
(void)ipa_endpoint_program_suspend(endpoint, false); (void)ipa_endpoint_program_suspend(endpoint, false);
/* Starting with IPA v4.0, the underlying GSI channel must be ret = gsi_channel_resume(gsi, endpoint->channel_id);
* restarted for resume.
*/
start_channel = endpoint->ipa->version >= IPA_VERSION_4_0;
ret = gsi_channel_resume(gsi, endpoint->channel_id, start_channel);
if (ret) if (ret)
dev_err(dev, "error %d resuming channel %u\n", ret, dev_err(dev, "error %d resuming channel %u\n", ret,
endpoint->channel_id); endpoint->channel_id);
......
...@@ -892,6 +892,7 @@ static int ipa_suspend(struct device *dev) ...@@ -892,6 +892,7 @@ static int ipa_suspend(struct device *dev)
if (ipa->setup_complete) { if (ipa->setup_complete) {
__clear_bit(IPA_FLAG_RESUMED, ipa->flags); __clear_bit(IPA_FLAG_RESUMED, ipa->flags);
ipa_endpoint_suspend(ipa); ipa_endpoint_suspend(ipa);
gsi_suspend(&ipa->gsi);
} }
ipa_clock_put(ipa); ipa_clock_put(ipa);
...@@ -919,8 +920,10 @@ static int ipa_resume(struct device *dev) ...@@ -919,8 +920,10 @@ static int ipa_resume(struct device *dev)
ipa_clock_get(ipa); ipa_clock_get(ipa);
/* Endpoints aren't usable until setup is complete */ /* Endpoints aren't usable until setup is complete */
if (ipa->setup_complete) if (ipa->setup_complete) {
gsi_resume(&ipa->gsi);
ipa_endpoint_resume(ipa); ipa_endpoint_resume(ipa);
}
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment