Commit e2f154e6 authored by Alex Elder's avatar Alex Elder Committed by David S. Miller

net: ipa: introduce ipa_uc_clock()

The first time it's booted, the modem loads and starts the
IPA-resident microcontroller.  Once the microcontroller has
completed its initialization, it notifies the AP it's "ready"
by sending an INIT_COMPLETED response message.

Until it receives that microcontroller message, the AP must ensure
the IPA core clock remains operational.  Currently, a "proxy" clock
reference is taken in ipa_uc_config(), dropping it again once the
message is received.

However there could be a long delay between when ipa_config()
completes and when modem actually starts.  And because the
microcontroller gets loaded by the modem, there's no need to
get the modem "proxy clock" until the first time it starts.

Create a new function ipa_uc_clock() which takes the "proxy" clock
reference for the microcontroller.  Call it when we get remoteproc
SSR notification that the modem is about to start.  Keep an
additional flag to record whether this proxy clock reference needs
to be dropped at shutdown time, and issue a warning if we get the
microcontroller message either before the clock reference is taken,
or after it has already been dropped.

Drop the nearby use of "hh" length modifiers, which are no longer
encouraged in the kernel.
Signed-off-by: default avatarAlex Elder <elder@linaro.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent dc8f7e39
...@@ -51,6 +51,7 @@ enum ipa_flag { ...@@ -51,6 +51,7 @@ enum ipa_flag {
* @table_addr: DMA address of filter/route table content * @table_addr: DMA address of filter/route table content
* @table_virt: Virtual address of filter/route table content * @table_virt: Virtual address of filter/route table content
* @interrupt: IPA Interrupt information * @interrupt: IPA Interrupt information
* @uc_clocked: true if clock is active by proxy for microcontroller
* @uc_loaded: true after microcontroller has reported it's ready * @uc_loaded: true after microcontroller has reported it's ready
* @reg_addr: DMA address used for IPA register access * @reg_addr: DMA address used for IPA register access
* @reg_virt: Virtual address used for IPA register access * @reg_virt: Virtual address used for IPA register access
...@@ -95,6 +96,7 @@ struct ipa { ...@@ -95,6 +96,7 @@ struct ipa {
__le64 *table_virt; __le64 *table_virt;
struct ipa_interrupt *interrupt; struct ipa_interrupt *interrupt;
bool uc_clocked;
bool uc_loaded; bool uc_loaded;
dma_addr_t reg_addr; dma_addr_t reg_addr;
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "ipa_modem.h" #include "ipa_modem.h"
#include "ipa_smp2p.h" #include "ipa_smp2p.h"
#include "ipa_qmi.h" #include "ipa_qmi.h"
#include "ipa_uc.h"
#define IPA_NETDEV_NAME "rmnet_ipa%d" #define IPA_NETDEV_NAME "rmnet_ipa%d"
#define IPA_NETDEV_TAILROOM 0 /* for padding by mux layer */ #define IPA_NETDEV_TAILROOM 0 /* for padding by mux layer */
...@@ -314,6 +315,7 @@ static int ipa_modem_notify(struct notifier_block *nb, unsigned long action, ...@@ -314,6 +315,7 @@ static int ipa_modem_notify(struct notifier_block *nb, unsigned long action,
switch (action) { switch (action) {
case QCOM_SSR_BEFORE_POWERUP: case QCOM_SSR_BEFORE_POWERUP:
dev_info(dev, "received modem starting event\n"); dev_info(dev, "received modem starting event\n");
ipa_uc_clock(ipa);
ipa_smp2p_notify_reset(ipa); ipa_smp2p_notify_reset(ipa);
break; break;
......
...@@ -131,7 +131,7 @@ static void ipa_uc_event_handler(struct ipa *ipa, enum ipa_irq_id irq_id) ...@@ -131,7 +131,7 @@ static void ipa_uc_event_handler(struct ipa *ipa, enum ipa_irq_id irq_id)
if (shared->event == IPA_UC_EVENT_ERROR) if (shared->event == IPA_UC_EVENT_ERROR)
dev_err(dev, "microcontroller error event\n"); dev_err(dev, "microcontroller error event\n");
else if (shared->event != IPA_UC_EVENT_LOG_INFO) else if (shared->event != IPA_UC_EVENT_LOG_INFO)
dev_err(dev, "unsupported microcontroller event %hhu\n", dev_err(dev, "unsupported microcontroller event %u\n",
shared->event); shared->event);
/* The LOG_INFO event can be safely ignored */ /* The LOG_INFO event can be safely ignored */
} }
...@@ -140,23 +140,28 @@ static void ipa_uc_event_handler(struct ipa *ipa, enum ipa_irq_id irq_id) ...@@ -140,23 +140,28 @@ static void ipa_uc_event_handler(struct ipa *ipa, enum ipa_irq_id irq_id)
static void ipa_uc_response_hdlr(struct ipa *ipa, enum ipa_irq_id irq_id) static void ipa_uc_response_hdlr(struct ipa *ipa, enum ipa_irq_id irq_id)
{ {
struct ipa_uc_mem_area *shared = ipa_uc_shared(ipa); struct ipa_uc_mem_area *shared = ipa_uc_shared(ipa);
struct device *dev = &ipa->pdev->dev;
/* An INIT_COMPLETED response message is sent to the AP by the /* An INIT_COMPLETED response message is sent to the AP by the
* microcontroller when it is operational. Other than this, the AP * microcontroller when it is operational. Other than this, the AP
* should only receive responses from the microcontroller when it has * should only receive responses from the microcontroller when it has
* sent it a request message. * sent it a request message.
* *
* We can drop the clock reference taken in ipa_uc_setup() once we * We can drop the clock reference taken in ipa_uc_clock() once we
* know the microcontroller has finished its initialization. * know the microcontroller has finished its initialization.
*/ */
switch (shared->response) { switch (shared->response) {
case IPA_UC_RESPONSE_INIT_COMPLETED: case IPA_UC_RESPONSE_INIT_COMPLETED:
if (ipa->uc_clocked) {
ipa->uc_loaded = true; ipa->uc_loaded = true;
ipa_clock_put(ipa); ipa_clock_put(ipa);
ipa->uc_clocked = false;
} else {
dev_warn(dev, "unexpected init_completed response\n");
}
break; break;
default: default:
dev_warn(&ipa->pdev->dev, dev_warn(dev, "unsupported microcontroller response %u\n",
"unsupported microcontroller response %hhu\n",
shared->response); shared->response);
break; break;
} }
...@@ -165,16 +170,7 @@ static void ipa_uc_response_hdlr(struct ipa *ipa, enum ipa_irq_id irq_id) ...@@ -165,16 +170,7 @@ static void ipa_uc_response_hdlr(struct ipa *ipa, enum ipa_irq_id irq_id)
/* Configure the IPA microcontroller subsystem */ /* Configure the IPA microcontroller subsystem */
void ipa_uc_config(struct ipa *ipa) void ipa_uc_config(struct ipa *ipa)
{ {
/* The microcontroller needs the IPA clock running until it has ipa->uc_clocked = false;
* completed its initialization. It signals this by sending an
* INIT_COMPLETED response message to the AP. This could occur after
* we have finished doing the rest of the IPA initialization, so we
* need to take an extra "proxy" reference, and hold it until we've
* received that signal. (This reference is dropped in
* ipa_uc_response_hdlr(), above.)
*/
ipa_clock_get(ipa);
ipa->uc_loaded = false; ipa->uc_loaded = false;
ipa_interrupt_add(ipa->interrupt, IPA_IRQ_UC_0, ipa_uc_event_handler); ipa_interrupt_add(ipa->interrupt, IPA_IRQ_UC_0, ipa_uc_event_handler);
ipa_interrupt_add(ipa->interrupt, IPA_IRQ_UC_1, ipa_uc_response_hdlr); ipa_interrupt_add(ipa->interrupt, IPA_IRQ_UC_1, ipa_uc_response_hdlr);
...@@ -185,10 +181,24 @@ void ipa_uc_deconfig(struct ipa *ipa) ...@@ -185,10 +181,24 @@ void ipa_uc_deconfig(struct ipa *ipa)
{ {
ipa_interrupt_remove(ipa->interrupt, IPA_IRQ_UC_1); ipa_interrupt_remove(ipa->interrupt, IPA_IRQ_UC_1);
ipa_interrupt_remove(ipa->interrupt, IPA_IRQ_UC_0); ipa_interrupt_remove(ipa->interrupt, IPA_IRQ_UC_0);
if (!ipa->uc_loaded) if (ipa->uc_clocked)
ipa_clock_put(ipa); ipa_clock_put(ipa);
} }
/* Take a proxy clock reference for the microcontroller */
void ipa_uc_clock(struct ipa *ipa)
{
static bool already;
if (already)
return;
already = true; /* Only do this on first boot */
/* This clock reference dropped in ipa_uc_response_hdlr() above */
ipa_clock_get(ipa);
ipa->uc_clocked = true;
}
/* Send a command to the microcontroller */ /* Send a command to the microcontroller */
static void send_uc_command(struct ipa *ipa, u32 command, u32 command_param) static void send_uc_command(struct ipa *ipa, u32 command, u32 command_param)
{ {
......
...@@ -20,6 +20,20 @@ void ipa_uc_config(struct ipa *ipa); ...@@ -20,6 +20,20 @@ void ipa_uc_config(struct ipa *ipa);
*/ */
void ipa_uc_deconfig(struct ipa *ipa); void ipa_uc_deconfig(struct ipa *ipa);
/**
* ipa_uc_clock() - Take a proxy clock reference for the microcontroller
* @ipa: IPA pointer
*
* The first time the modem boots, it loads firmware for and starts the
* IPA-resident microcontroller. The microcontroller signals that it
* has completed its initialization by sending an INIT_COMPLETED response
* message to the AP. The AP must ensure the IPA core clock is operating
* until it receives this message, and to do so we take a "proxy" clock
* reference on its behalf here. Once we receive the INIT_COMPLETED
* message (in ipa_uc_response_hdlr()) we drop this clock reference.
*/
void ipa_uc_clock(struct ipa *ipa);
/** /**
* ipa_uc_panic_notifier() * ipa_uc_panic_notifier()
* @ipa: IPA pointer * @ipa: IPA pointer
......
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