msm: mhi_rmnet: add support for shutdown and system error notification
Add support to handle system error and shutdown notification from MHI host. CRs-Fixed: 2022936 Change-Id: Id36097dffd7571490d7d53d2e496bfe024702a42 Signed-off-by: Sujeev Dias <sdias@codeaurora.org>
This commit is contained in:
parent
afe468938a
commit
371e2918df
1 changed files with 246 additions and 213 deletions
|
@ -35,6 +35,7 @@
|
|||
#define MHI_NAPI_WEIGHT_VALUE 12
|
||||
#define WATCHDOG_TIMEOUT (30 * HZ)
|
||||
#define RMNET_IPC_LOG_PAGES (100)
|
||||
#define IRQ_MASKED_BIT (0)
|
||||
|
||||
enum DBG_LVL {
|
||||
MSG_VERBOSE = 0x1,
|
||||
|
@ -100,14 +101,15 @@ struct rmnet_mhi_private {
|
|||
u32 mhi_enabled;
|
||||
struct platform_device *pdev;
|
||||
struct net_device *dev;
|
||||
atomic_t irq_masked_cntr;
|
||||
unsigned long flags;
|
||||
int wake_count;
|
||||
spinlock_t out_chan_full_lock; /* tx queue lock */
|
||||
atomic_t pending_data;
|
||||
struct sk_buff *frag_skb;
|
||||
struct work_struct alloc_work;
|
||||
/* lock to queue hardware and internal queue */
|
||||
spinlock_t alloc_lock;
|
||||
void *rmnet_ipc_log;
|
||||
rwlock_t pm_lock; /* state change lock */
|
||||
struct debug_params debug;
|
||||
struct dentry *dentry;
|
||||
};
|
||||
|
@ -130,12 +132,12 @@ static int rmnet_mhi_process_fragment(struct rmnet_mhi_private *rmnet_mhi_ptr,
|
|||
rmnet_mhi_ptr->frag_skb = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
kfree_skb(rmnet_mhi_ptr->frag_skb);
|
||||
dev_kfree_skb_any(rmnet_mhi_ptr->frag_skb);
|
||||
rmnet_mhi_ptr->frag_skb = temp_skb;
|
||||
memcpy(skb_put(rmnet_mhi_ptr->frag_skb, skb->len),
|
||||
skb->data,
|
||||
skb->len);
|
||||
kfree_skb(skb);
|
||||
dev_kfree_skb_any(skb);
|
||||
if (!frag) {
|
||||
/* Last fragmented piece was received, ship it */
|
||||
netif_receive_skb(rmnet_mhi_ptr->frag_skb);
|
||||
|
@ -196,7 +198,6 @@ static int rmnet_alloc_rx(struct rmnet_mhi_private *rmnet_mhi_ptr,
|
|||
{
|
||||
u32 cur_mru = rmnet_mhi_ptr->mru;
|
||||
struct mhi_skb_priv *skb_priv;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
struct sk_buff *skb;
|
||||
|
||||
|
@ -215,7 +216,7 @@ static int rmnet_alloc_rx(struct rmnet_mhi_private *rmnet_mhi_ptr,
|
|||
skb_priv->dma_addr = 0;
|
||||
|
||||
/* These steps must be in atomic context */
|
||||
spin_lock_irqsave(&rmnet_mhi_ptr->alloc_lock, flags);
|
||||
spin_lock_bh(&rmnet_mhi_ptr->alloc_lock);
|
||||
|
||||
/* It's possible by the time alloc_skb (GFP_KERNEL)
|
||||
* returns we already called rmnet_alloc_rx
|
||||
|
@ -224,14 +225,22 @@ static int rmnet_alloc_rx(struct rmnet_mhi_private *rmnet_mhi_ptr,
|
|||
*/
|
||||
if (unlikely(atomic_read(&rmnet_mhi_ptr->rx_pool_len) >=
|
||||
rmnet_mhi_ptr->rx_buffers_max)) {
|
||||
spin_unlock_irqrestore(&rmnet_mhi_ptr->alloc_lock,
|
||||
flags);
|
||||
spin_unlock_bh(&rmnet_mhi_ptr->alloc_lock);
|
||||
dev_kfree_skb_any(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = mhi_queue_xfer(
|
||||
rmnet_mhi_ptr->rx_client_handle,
|
||||
read_lock_bh(&rmnet_mhi_ptr->pm_lock);
|
||||
if (unlikely(!rmnet_mhi_ptr->mhi_enabled)) {
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_INFO,
|
||||
"!interface is disabled\n");
|
||||
dev_kfree_skb_any(skb);
|
||||
read_unlock_bh(&rmnet_mhi_ptr->pm_lock);
|
||||
spin_unlock_bh(&rmnet_mhi_ptr->alloc_lock);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = mhi_queue_xfer(rmnet_mhi_ptr->rx_client_handle,
|
||||
skb->data,
|
||||
skb_priv->dma_size,
|
||||
MHI_EOT);
|
||||
|
@ -239,14 +248,15 @@ static int rmnet_alloc_rx(struct rmnet_mhi_private *rmnet_mhi_ptr,
|
|||
rmnet_log(rmnet_mhi_ptr,
|
||||
MSG_CRITICAL,
|
||||
"mhi_queue_xfer failed, error %d", ret);
|
||||
spin_unlock_irqrestore(&rmnet_mhi_ptr->alloc_lock,
|
||||
flags);
|
||||
read_unlock_bh(&rmnet_mhi_ptr->pm_lock);
|
||||
spin_unlock_bh(&rmnet_mhi_ptr->alloc_lock);
|
||||
dev_kfree_skb_any(skb);
|
||||
return ret;
|
||||
}
|
||||
skb_queue_tail(&rmnet_mhi_ptr->rx_buffers, skb);
|
||||
atomic_inc(&rmnet_mhi_ptr->rx_pool_len);
|
||||
spin_unlock_irqrestore(&rmnet_mhi_ptr->alloc_lock, flags);
|
||||
read_unlock_bh(&rmnet_mhi_ptr->pm_lock);
|
||||
spin_unlock_bh(&rmnet_mhi_ptr->alloc_lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -258,13 +268,25 @@ static void rmnet_mhi_alloc_work(struct work_struct *work)
|
|||
struct rmnet_mhi_private,
|
||||
alloc_work);
|
||||
int ret;
|
||||
/* sleep about 1 sec and retry, that should be enough time
|
||||
* for system to reclaim freed memory back.
|
||||
*/
|
||||
const int sleep_ms = 1000;
|
||||
int retry = 60;
|
||||
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered\n");
|
||||
ret = rmnet_alloc_rx(rmnet_mhi_ptr,
|
||||
rmnet_mhi_ptr->allocation_flags);
|
||||
do {
|
||||
ret = rmnet_alloc_rx(rmnet_mhi_ptr,
|
||||
rmnet_mhi_ptr->allocation_flags);
|
||||
/* sleep and try again */
|
||||
if (ret == -ENOMEM) {
|
||||
msleep(sleep_ms);
|
||||
retry--;
|
||||
}
|
||||
} while (ret == -ENOMEM && retry);
|
||||
|
||||
WARN_ON(ret == -ENOMEM);
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exit\n");
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exit with status:%d retry:%d\n",
|
||||
ret, retry);
|
||||
}
|
||||
|
||||
static int rmnet_mhi_poll(struct napi_struct *napi, int budget)
|
||||
|
@ -281,6 +303,12 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget)
|
|||
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Entered\n");
|
||||
|
||||
read_lock_bh(&rmnet_mhi_ptr->pm_lock);
|
||||
if (unlikely(!rmnet_mhi_ptr->mhi_enabled)) {
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_INFO, "interface is disabled!\n");
|
||||
read_unlock_bh(&rmnet_mhi_ptr->pm_lock);
|
||||
return 0;
|
||||
}
|
||||
while (received_packets < budget) {
|
||||
struct mhi_result *result =
|
||||
mhi_poll(rmnet_mhi_ptr->rx_client_handle);
|
||||
|
@ -338,77 +366,50 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget)
|
|||
dev->stats.rx_bytes += result->bytes_xferd;
|
||||
|
||||
} /* while (received_packets < budget) or any other error */
|
||||
read_unlock_bh(&rmnet_mhi_ptr->pm_lock);
|
||||
|
||||
/* Queue new buffers */
|
||||
res = rmnet_alloc_rx(rmnet_mhi_ptr, GFP_ATOMIC);
|
||||
if (res == -ENOMEM) {
|
||||
rmnet_log(rmnet_mhi_ptr,
|
||||
MSG_INFO,
|
||||
"out of mem, queuing bg worker\n");
|
||||
rmnet_mhi_ptr->alloc_fail++;
|
||||
schedule_work(&rmnet_mhi_ptr->alloc_work);
|
||||
}
|
||||
|
||||
napi_complete(napi);
|
||||
|
||||
/* We got a NULL descriptor back */
|
||||
if (should_reschedule == false) {
|
||||
if (atomic_read(&rmnet_mhi_ptr->irq_masked_cntr)) {
|
||||
atomic_dec(&rmnet_mhi_ptr->irq_masked_cntr);
|
||||
mhi_unmask_irq(rmnet_mhi_ptr->rx_client_handle);
|
||||
mhi_set_lpm(rmnet_mhi_ptr->rx_client_handle, true);
|
||||
read_lock_bh(&rmnet_mhi_ptr->pm_lock);
|
||||
if (likely(rmnet_mhi_ptr->mhi_enabled)) {
|
||||
if (res == -ENOMEM) {
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_INFO,
|
||||
"out of mem, queuing bg worker\n");
|
||||
rmnet_mhi_ptr->alloc_fail++;
|
||||
schedule_work(&rmnet_mhi_ptr->alloc_work);
|
||||
}
|
||||
} else {
|
||||
if (received_packets == budget)
|
||||
rmnet_mhi_ptr->debug.rx_napi_budget_overflow++;
|
||||
napi_reschedule(napi);
|
||||
|
||||
napi_complete(napi);
|
||||
|
||||
/* We got a NULL descriptor back */
|
||||
if (!should_reschedule) {
|
||||
if (test_and_clear_bit(IRQ_MASKED_BIT,
|
||||
&rmnet_mhi_ptr->flags))
|
||||
mhi_unmask_irq(rmnet_mhi_ptr->rx_client_handle);
|
||||
mhi_set_lpm(rmnet_mhi_ptr->rx_client_handle, true);
|
||||
rmnet_mhi_ptr->wake_count--;
|
||||
} else {
|
||||
if (received_packets == budget)
|
||||
rmnet_mhi_ptr->debug.rx_napi_budget_overflow++;
|
||||
napi_reschedule(napi);
|
||||
}
|
||||
|
||||
rmnet_mhi_ptr->debug.rx_napi_skb_burst_min =
|
||||
min((u64)received_packets,
|
||||
rmnet_mhi_ptr->debug.rx_napi_skb_burst_min);
|
||||
|
||||
rmnet_mhi_ptr->debug.rx_napi_skb_burst_max =
|
||||
max((u64)received_packets,
|
||||
rmnet_mhi_ptr->debug.rx_napi_skb_burst_max);
|
||||
}
|
||||
|
||||
rmnet_mhi_ptr->debug.rx_napi_skb_burst_min =
|
||||
min((u64)received_packets,
|
||||
rmnet_mhi_ptr->debug.rx_napi_skb_burst_min);
|
||||
|
||||
rmnet_mhi_ptr->debug.rx_napi_skb_burst_max =
|
||||
max((u64)received_packets,
|
||||
rmnet_mhi_ptr->debug.rx_napi_skb_burst_max);
|
||||
read_unlock_bh(&rmnet_mhi_ptr->pm_lock);
|
||||
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE,
|
||||
"Exited, polled %d pkts\n", received_packets);
|
||||
return received_packets;
|
||||
}
|
||||
|
||||
void rmnet_mhi_clean_buffers(struct net_device *dev)
|
||||
{
|
||||
struct rmnet_mhi_private *rmnet_mhi_ptr =
|
||||
*(struct rmnet_mhi_private **)netdev_priv(dev);
|
||||
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered\n");
|
||||
/* Clean TX buffers */
|
||||
rmnet_mhi_internal_clean_unmap_buffers(dev,
|
||||
&rmnet_mhi_ptr->tx_buffers,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
/* Clean RX buffers */
|
||||
rmnet_mhi_internal_clean_unmap_buffers(dev,
|
||||
&rmnet_mhi_ptr->rx_buffers,
|
||||
DMA_FROM_DEVICE);
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exited\n");
|
||||
}
|
||||
|
||||
static int rmnet_mhi_disable_channels(struct rmnet_mhi_private *rmnet_mhi_ptr)
|
||||
{
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Closing MHI TX channel\n");
|
||||
mhi_close_channel(rmnet_mhi_ptr->tx_client_handle);
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Closing MHI RX channel\n");
|
||||
mhi_close_channel(rmnet_mhi_ptr->rx_client_handle);
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Clearing Pending TX buffers.\n");
|
||||
rmnet_mhi_clean_buffers(rmnet_mhi_ptr->dev);
|
||||
rmnet_mhi_ptr->tx_client_handle = NULL;
|
||||
rmnet_mhi_ptr->rx_client_handle = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmnet_mhi_init_inbound(struct rmnet_mhi_private *rmnet_mhi_ptr)
|
||||
{
|
||||
int res;
|
||||
|
@ -431,7 +432,7 @@ static void rmnet_mhi_tx_cb(struct mhi_result *result)
|
|||
struct net_device *dev;
|
||||
struct rmnet_mhi_private *rmnet_mhi_ptr;
|
||||
unsigned long burst_counter = 0;
|
||||
unsigned long flags;
|
||||
unsigned long flags, pm_flags;
|
||||
|
||||
rmnet_mhi_ptr = result->user_data;
|
||||
dev = rmnet_mhi_ptr->dev;
|
||||
|
@ -451,10 +452,10 @@ static void rmnet_mhi_tx_cb(struct mhi_result *result)
|
|||
break;
|
||||
} else {
|
||||
if (skb->data == result->buf_addr) {
|
||||
kfree_skb(skb);
|
||||
dev_kfree_skb_any(skb);
|
||||
break;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
dev_kfree_skb_any(skb);
|
||||
burst_counter++;
|
||||
|
||||
/* Update statistics */
|
||||
|
@ -477,10 +478,15 @@ static void rmnet_mhi_tx_cb(struct mhi_result *result)
|
|||
rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_max);
|
||||
|
||||
/* In case we couldn't write again, now we can! */
|
||||
spin_lock_irqsave(&rmnet_mhi_ptr->out_chan_full_lock, flags);
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Waking up queue\n");
|
||||
netif_wake_queue(dev);
|
||||
spin_unlock_irqrestore(&rmnet_mhi_ptr->out_chan_full_lock, flags);
|
||||
read_lock_irqsave(&rmnet_mhi_ptr->pm_lock, pm_flags);
|
||||
if (likely(rmnet_mhi_ptr->mhi_enabled)) {
|
||||
spin_lock_irqsave(&rmnet_mhi_ptr->out_chan_full_lock, flags);
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Waking up queue\n");
|
||||
netif_wake_queue(dev);
|
||||
spin_unlock_irqrestore(&rmnet_mhi_ptr->out_chan_full_lock,
|
||||
flags);
|
||||
}
|
||||
read_unlock_irqrestore(&rmnet_mhi_ptr->pm_lock, pm_flags);
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Exited\n");
|
||||
}
|
||||
|
||||
|
@ -488,20 +494,27 @@ static void rmnet_mhi_rx_cb(struct mhi_result *result)
|
|||
{
|
||||
struct net_device *dev;
|
||||
struct rmnet_mhi_private *rmnet_mhi_ptr;
|
||||
unsigned long flags;
|
||||
|
||||
rmnet_mhi_ptr = result->user_data;
|
||||
dev = rmnet_mhi_ptr->dev;
|
||||
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Entered\n");
|
||||
rmnet_mhi_ptr->debug.rx_interrupts_count++;
|
||||
|
||||
if (napi_schedule_prep(&(rmnet_mhi_ptr->napi))) {
|
||||
mhi_mask_irq(rmnet_mhi_ptr->rx_client_handle);
|
||||
atomic_inc(&rmnet_mhi_ptr->irq_masked_cntr);
|
||||
mhi_set_lpm(rmnet_mhi_ptr->rx_client_handle, false);
|
||||
__napi_schedule(&(rmnet_mhi_ptr->napi));
|
||||
} else {
|
||||
rmnet_mhi_ptr->debug.rx_interrupts_in_masked_irq++;
|
||||
read_lock_irqsave(&rmnet_mhi_ptr->pm_lock, flags);
|
||||
if (likely(rmnet_mhi_ptr->mhi_enabled)) {
|
||||
if (napi_schedule_prep(&rmnet_mhi_ptr->napi)) {
|
||||
if (!test_and_set_bit(IRQ_MASKED_BIT,
|
||||
&rmnet_mhi_ptr->flags))
|
||||
mhi_mask_irq(rmnet_mhi_ptr->rx_client_handle);
|
||||
mhi_set_lpm(rmnet_mhi_ptr->rx_client_handle, false);
|
||||
rmnet_mhi_ptr->wake_count++;
|
||||
__napi_schedule(&rmnet_mhi_ptr->napi);
|
||||
} else {
|
||||
rmnet_mhi_ptr->debug.rx_interrupts_in_masked_irq++;
|
||||
}
|
||||
}
|
||||
read_unlock_irqrestore(&rmnet_mhi_ptr->pm_lock, flags);
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Exited\n");
|
||||
}
|
||||
|
||||
|
@ -510,8 +523,7 @@ static int rmnet_mhi_open(struct net_device *dev)
|
|||
struct rmnet_mhi_private *rmnet_mhi_ptr =
|
||||
*(struct rmnet_mhi_private **)netdev_priv(dev);
|
||||
|
||||
rmnet_log(rmnet_mhi_ptr,
|
||||
MSG_INFO,
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_INFO,
|
||||
"Opened net dev interface for MHI chans %d and %d\n",
|
||||
rmnet_mhi_ptr->tx_channel,
|
||||
rmnet_mhi_ptr->rx_channel);
|
||||
|
@ -527,43 +539,35 @@ static int rmnet_mhi_open(struct net_device *dev)
|
|||
/* Poll to check if any buffers are accumulated in the
|
||||
* transport buffers
|
||||
*/
|
||||
if (napi_schedule_prep(&(rmnet_mhi_ptr->napi))) {
|
||||
mhi_mask_irq(rmnet_mhi_ptr->rx_client_handle);
|
||||
atomic_inc(&rmnet_mhi_ptr->irq_masked_cntr);
|
||||
mhi_set_lpm(rmnet_mhi_ptr->rx_client_handle, false);
|
||||
__napi_schedule(&(rmnet_mhi_ptr->napi));
|
||||
} else {
|
||||
rmnet_mhi_ptr->debug.rx_interrupts_in_masked_irq++;
|
||||
read_lock_bh(&rmnet_mhi_ptr->pm_lock);
|
||||
if (likely(rmnet_mhi_ptr->mhi_enabled)) {
|
||||
if (napi_schedule_prep(&rmnet_mhi_ptr->napi)) {
|
||||
if (!test_and_set_bit(IRQ_MASKED_BIT,
|
||||
&rmnet_mhi_ptr->flags)) {
|
||||
mhi_mask_irq(rmnet_mhi_ptr->rx_client_handle);
|
||||
}
|
||||
mhi_set_lpm(rmnet_mhi_ptr->rx_client_handle, false);
|
||||
rmnet_mhi_ptr->wake_count++;
|
||||
__napi_schedule(&rmnet_mhi_ptr->napi);
|
||||
} else {
|
||||
rmnet_mhi_ptr->debug.rx_interrupts_in_masked_irq++;
|
||||
}
|
||||
}
|
||||
read_unlock_bh(&rmnet_mhi_ptr->pm_lock);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int rmnet_mhi_disable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr)
|
||||
{
|
||||
rmnet_mhi_ptr->rx_enabled = 0;
|
||||
rmnet_mhi_ptr->tx_enabled = 0;
|
||||
rmnet_mhi_ptr->mhi_enabled = 0;
|
||||
if (rmnet_mhi_ptr->dev != 0) {
|
||||
netif_stop_queue(rmnet_mhi_ptr->dev);
|
||||
netif_napi_del(&(rmnet_mhi_ptr->napi));
|
||||
rmnet_mhi_disable_channels(rmnet_mhi_ptr);
|
||||
unregister_netdev(rmnet_mhi_ptr->dev);
|
||||
free_netdev(rmnet_mhi_ptr->dev);
|
||||
rmnet_mhi_ptr->dev = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmnet_mhi_disable(struct rmnet_mhi_private *rmnet_mhi_ptr)
|
||||
{
|
||||
rmnet_mhi_ptr->mhi_enabled = 0;
|
||||
rmnet_mhi_disable_iface(rmnet_mhi_ptr);
|
||||
napi_disable(&(rmnet_mhi_ptr->napi));
|
||||
if (atomic_read(&rmnet_mhi_ptr->irq_masked_cntr)) {
|
||||
rmnet_mhi_ptr->rx_enabled = 0;
|
||||
rmnet_mhi_internal_clean_unmap_buffers(rmnet_mhi_ptr->dev,
|
||||
&rmnet_mhi_ptr->rx_buffers,
|
||||
DMA_FROM_DEVICE);
|
||||
if (test_and_clear_bit(IRQ_MASKED_BIT, &rmnet_mhi_ptr->flags))
|
||||
mhi_unmask_irq(rmnet_mhi_ptr->rx_client_handle);
|
||||
atomic_dec(&rmnet_mhi_ptr->irq_masked_cntr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -574,11 +578,9 @@ static int rmnet_mhi_stop(struct net_device *dev)
|
|||
|
||||
netif_stop_queue(dev);
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Entered\n");
|
||||
if (atomic_read(&rmnet_mhi_ptr->irq_masked_cntr)) {
|
||||
if (test_and_clear_bit(IRQ_MASKED_BIT, &rmnet_mhi_ptr->flags)) {
|
||||
mhi_unmask_irq(rmnet_mhi_ptr->rx_client_handle);
|
||||
atomic_dec(&rmnet_mhi_ptr->irq_masked_cntr);
|
||||
rmnet_log(rmnet_mhi_ptr,
|
||||
MSG_ERROR,
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_ERROR,
|
||||
"IRQ was masked, unmasking...\n");
|
||||
}
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Exited\n");
|
||||
|
@ -605,14 +607,23 @@ static int rmnet_mhi_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
unsigned long flags;
|
||||
struct mhi_skb_priv *tx_priv;
|
||||
|
||||
rmnet_log(rmnet_mhi_ptr,
|
||||
MSG_VERBOSE,
|
||||
"Entered chan %d\n",
|
||||
rmnet_mhi_ptr->tx_channel);
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE,
|
||||
"Entered chan %d\n", rmnet_mhi_ptr->tx_channel);
|
||||
|
||||
tx_priv = (struct mhi_skb_priv *)(skb->cb);
|
||||
tx_priv->dma_size = skb->len;
|
||||
tx_priv->dma_addr = 0;
|
||||
read_lock_bh(&rmnet_mhi_ptr->pm_lock);
|
||||
if (unlikely(!rmnet_mhi_ptr->mhi_enabled)) {
|
||||
/* Only reason interface could be disabled and we get data
|
||||
* is due to an SSR. We do not want to stop the queue and
|
||||
* return error. instead we will flush all the uplink packets
|
||||
* and return successful
|
||||
*/
|
||||
res = NETDEV_TX_OK;
|
||||
dev_kfree_skb_any(skb);
|
||||
goto mhi_xmit_exit;
|
||||
}
|
||||
|
||||
if (mhi_get_free_desc(rmnet_mhi_ptr->tx_client_handle) <= 0) {
|
||||
rmnet_log(rmnet_mhi_ptr,
|
||||
|
@ -624,7 +635,8 @@ static int rmnet_mhi_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
netif_stop_queue(dev);
|
||||
spin_unlock_irqrestore(&rmnet_mhi_ptr->out_chan_full_lock,
|
||||
flags);
|
||||
return NETDEV_TX_BUSY;
|
||||
res = NETDEV_TX_BUSY;
|
||||
goto mhi_xmit_exit;
|
||||
}
|
||||
res = mhi_queue_xfer(rmnet_mhi_ptr->tx_client_handle,
|
||||
skb->data,
|
||||
|
@ -641,15 +653,17 @@ static int rmnet_mhi_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
netif_stop_queue(dev);
|
||||
spin_unlock_irqrestore(&rmnet_mhi_ptr->out_chan_full_lock,
|
||||
flags);
|
||||
return NETDEV_TX_BUSY;
|
||||
res = NETDEV_TX_BUSY;
|
||||
goto mhi_xmit_exit;
|
||||
}
|
||||
|
||||
res = NETDEV_TX_OK;
|
||||
skb_queue_tail(&(rmnet_mhi_ptr->tx_buffers), skb);
|
||||
dev->trans_start = jiffies;
|
||||
rmnet_mhi_ptr->debug.tx_queued_packets_count++;
|
||||
|
||||
mhi_xmit_exit:
|
||||
read_unlock_bh(&rmnet_mhi_ptr->pm_lock);
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Exited\n");
|
||||
return NETDEV_TX_OK;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr)
|
||||
|
@ -698,16 +712,19 @@ static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr)
|
|||
sizeof(ext_cmd.u.if_name));
|
||||
break;
|
||||
case RMNET_IOCTL_SET_SLEEP_STATE:
|
||||
read_lock_bh(&rmnet_mhi_ptr->pm_lock);
|
||||
if (rmnet_mhi_ptr->mhi_enabled &&
|
||||
rmnet_mhi_ptr->tx_client_handle != NULL) {
|
||||
rmnet_mhi_ptr->wake_count += (ext_cmd.u.data) ? -1 : 1;
|
||||
mhi_set_lpm(rmnet_mhi_ptr->tx_client_handle,
|
||||
ext_cmd.u.data);
|
||||
} else {
|
||||
rmnet_log(rmnet_mhi_ptr,
|
||||
MSG_ERROR,
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_ERROR,
|
||||
"Cannot set LPM value, MHI is not up.\n");
|
||||
read_unlock_bh(&rmnet_mhi_ptr->pm_lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
read_unlock_bh(&rmnet_mhi_ptr->pm_lock);
|
||||
break;
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
|
@ -832,9 +849,8 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr)
|
|||
"Failed to start TX chan ret %d\n",
|
||||
r);
|
||||
goto mhi_tx_chan_start_fail;
|
||||
} else {
|
||||
rmnet_mhi_ptr->tx_enabled = 1;
|
||||
}
|
||||
|
||||
client_handle = rmnet_mhi_ptr->tx_client_handle;
|
||||
}
|
||||
if (rmnet_mhi_ptr->rx_client_handle != NULL) {
|
||||
|
@ -848,8 +864,6 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr)
|
|||
"Failed to start RX chan ret %d\n",
|
||||
r);
|
||||
goto mhi_rx_chan_start_fail;
|
||||
} else {
|
||||
rmnet_mhi_ptr->rx_enabled = 1;
|
||||
}
|
||||
/* Both tx & rx client handle contain same device info */
|
||||
client_handle = rmnet_mhi_ptr->rx_client_handle;
|
||||
|
@ -860,62 +874,64 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr)
|
|||
goto net_dev_alloc_fail;
|
||||
}
|
||||
|
||||
snprintf(ifalias,
|
||||
sizeof(ifalias),
|
||||
"%s_%04x_%02u.%02u.%02u_%u",
|
||||
rmnet_mhi_ptr->interface_name,
|
||||
client_handle->dev_id,
|
||||
client_handle->domain,
|
||||
client_handle->bus,
|
||||
client_handle->slot,
|
||||
rmnet_mhi_ptr->dev_id);
|
||||
|
||||
snprintf(ifname, sizeof(ifname), "%s%%d",
|
||||
rmnet_mhi_ptr->interface_name);
|
||||
|
||||
rtnl_lock();
|
||||
rmnet_mhi_ptr->dev =
|
||||
alloc_netdev(sizeof(struct rmnet_mhi_private *),
|
||||
ifname, NET_NAME_PREDICTABLE, rmnet_mhi_setup);
|
||||
if (!rmnet_mhi_ptr->dev) {
|
||||
rmnet_log(rmnet_mhi_ptr,
|
||||
MSG_CRITICAL,
|
||||
"Network device allocation failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto net_dev_alloc_fail;
|
||||
}
|
||||
SET_NETDEV_DEV(rmnet_mhi_ptr->dev, &rmnet_mhi_ptr->pdev->dev);
|
||||
dev_set_alias(rmnet_mhi_ptr->dev, ifalias, strlen(ifalias));
|
||||
rmnet_mhi_ctxt = netdev_priv(rmnet_mhi_ptr->dev);
|
||||
rtnl_unlock();
|
||||
*rmnet_mhi_ctxt = rmnet_mhi_ptr;
|
||||
snprintf(ifalias, sizeof(ifalias),
|
||||
"%s_%04x_%02u.%02u.%02u_%u",
|
||||
rmnet_mhi_ptr->interface_name,
|
||||
client_handle->dev_id,
|
||||
client_handle->domain,
|
||||
client_handle->bus,
|
||||
client_handle->slot,
|
||||
rmnet_mhi_ptr->dev_id);
|
||||
|
||||
ret = dma_set_mask(&(rmnet_mhi_ptr->dev->dev),
|
||||
MHI_DMA_MASK);
|
||||
if (ret)
|
||||
rmnet_mhi_ptr->allocation_flags = GFP_KERNEL;
|
||||
else
|
||||
rmnet_mhi_ptr->allocation_flags = GFP_DMA;
|
||||
snprintf(ifname, sizeof(ifname), "%s%%d",
|
||||
rmnet_mhi_ptr->interface_name);
|
||||
|
||||
rtnl_lock();
|
||||
rmnet_mhi_ptr->dev = alloc_netdev(
|
||||
sizeof(struct rmnet_mhi_private *),
|
||||
ifname, NET_NAME_PREDICTABLE, rmnet_mhi_setup);
|
||||
|
||||
if (!rmnet_mhi_ptr->dev) {
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_CRITICAL,
|
||||
"Network device allocation failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto net_dev_alloc_fail;
|
||||
}
|
||||
SET_NETDEV_DEV(rmnet_mhi_ptr->dev, &rmnet_mhi_ptr->pdev->dev);
|
||||
dev_set_alias(rmnet_mhi_ptr->dev, ifalias, strlen(ifalias));
|
||||
rmnet_mhi_ctxt = netdev_priv(rmnet_mhi_ptr->dev);
|
||||
rtnl_unlock();
|
||||
*rmnet_mhi_ctxt = rmnet_mhi_ptr;
|
||||
|
||||
ret = dma_set_mask(&rmnet_mhi_ptr->dev->dev, MHI_DMA_MASK);
|
||||
if (ret)
|
||||
rmnet_mhi_ptr->allocation_flags = GFP_KERNEL;
|
||||
else
|
||||
rmnet_mhi_ptr->allocation_flags = GFP_DMA;
|
||||
|
||||
netif_napi_add(rmnet_mhi_ptr->dev, &rmnet_mhi_ptr->napi,
|
||||
rmnet_mhi_poll, MHI_NAPI_WEIGHT_VALUE);
|
||||
|
||||
ret = register_netdev(rmnet_mhi_ptr->dev);
|
||||
if (ret) {
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_CRITICAL,
|
||||
"Network device registration failed\n");
|
||||
goto net_dev_reg_fail;
|
||||
}
|
||||
}
|
||||
|
||||
write_lock_irq(&rmnet_mhi_ptr->pm_lock);
|
||||
rmnet_mhi_ptr->mhi_enabled = 1;
|
||||
write_unlock_irq(&rmnet_mhi_ptr->pm_lock);
|
||||
|
||||
r = rmnet_mhi_init_inbound(rmnet_mhi_ptr);
|
||||
if (r) {
|
||||
rmnet_log(rmnet_mhi_ptr,
|
||||
MSG_CRITICAL,
|
||||
"Failed to init inbound ret %d\n",
|
||||
r);
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_INFO,
|
||||
"Failed to init inbound ret %d\n", r);
|
||||
}
|
||||
|
||||
netif_napi_add(rmnet_mhi_ptr->dev, &(rmnet_mhi_ptr->napi),
|
||||
rmnet_mhi_poll, MHI_NAPI_WEIGHT_VALUE);
|
||||
|
||||
rmnet_mhi_ptr->mhi_enabled = 1;
|
||||
ret = register_netdev(rmnet_mhi_ptr->dev);
|
||||
if (ret) {
|
||||
rmnet_log(rmnet_mhi_ptr,
|
||||
MSG_CRITICAL,
|
||||
"Network device registration failed\n");
|
||||
goto net_dev_reg_fail;
|
||||
}
|
||||
napi_enable(&(rmnet_mhi_ptr->napi));
|
||||
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exited.\n");
|
||||
|
@ -951,25 +967,47 @@ static void rmnet_mhi_cb(struct mhi_cb_info *cb_info)
|
|||
|
||||
switch (cb_info->cb_reason) {
|
||||
case MHI_CB_MHI_DISABLED:
|
||||
rmnet_log(rmnet_mhi_ptr,
|
||||
MSG_CRITICAL,
|
||||
"Got MHI_DISABLED notification. Stopping stack\n");
|
||||
if (rmnet_mhi_ptr->mhi_enabled) {
|
||||
rmnet_mhi_ptr->mhi_enabled = 0;
|
||||
/* Ensure MHI is disabled before other mem ops */
|
||||
wmb();
|
||||
while (atomic_read(&rmnet_mhi_ptr->pending_data)) {
|
||||
rmnet_log(rmnet_mhi_ptr,
|
||||
MSG_CRITICAL,
|
||||
"Waiting for channels to stop.\n");
|
||||
msleep(25);
|
||||
}
|
||||
case MHI_CB_MHI_SHUTDOWN:
|
||||
case MHI_CB_SYS_ERROR:
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_INFO,
|
||||
"Got MHI_SYS_ERROR notification. Stopping stack\n");
|
||||
|
||||
/* Disable interface on first notification. Long
|
||||
* as we set mhi_enabled = 0, we gurantee rest of
|
||||
* driver will not touch any critical data.
|
||||
*/
|
||||
write_lock_irq(&rmnet_mhi_ptr->pm_lock);
|
||||
rmnet_mhi_ptr->mhi_enabled = 0;
|
||||
write_unlock_irq(&rmnet_mhi_ptr->pm_lock);
|
||||
|
||||
if (cb_info->chan == rmnet_mhi_ptr->rx_channel) {
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_INFO,
|
||||
"Receive MHI_DISABLE notification for rx path\n");
|
||||
rmnet_mhi_disable(rmnet_mhi_ptr);
|
||||
} else {
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_INFO,
|
||||
"Receive MHI_DISABLE notification for tx path\n");
|
||||
rmnet_mhi_ptr->tx_enabled = 0;
|
||||
rmnet_mhi_internal_clean_unmap_buffers
|
||||
(rmnet_mhi_ptr->dev, &rmnet_mhi_ptr->tx_buffers,
|
||||
DMA_TO_DEVICE);
|
||||
}
|
||||
|
||||
/* Remove all votes disabling low power mode */
|
||||
if (!rmnet_mhi_ptr->tx_enabled && !rmnet_mhi_ptr->rx_enabled) {
|
||||
struct mhi_client_handle *handle =
|
||||
rmnet_mhi_ptr->rx_client_handle;
|
||||
|
||||
if (!handle)
|
||||
handle = rmnet_mhi_ptr->tx_client_handle;
|
||||
while (rmnet_mhi_ptr->wake_count) {
|
||||
mhi_set_lpm(handle, true);
|
||||
rmnet_mhi_ptr->wake_count--;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MHI_CB_MHI_ENABLED:
|
||||
rmnet_log(rmnet_mhi_ptr,
|
||||
MSG_CRITICAL,
|
||||
rmnet_log(rmnet_mhi_ptr, MSG_INFO,
|
||||
"Got MHI_ENABLED notification. Starting stack\n");
|
||||
if (cb_info->chan == rmnet_mhi_ptr->rx_channel)
|
||||
rmnet_mhi_ptr->rx_enabled = 1;
|
||||
|
@ -998,16 +1036,10 @@ static void rmnet_mhi_cb(struct mhi_cb_info *cb_info)
|
|||
}
|
||||
break;
|
||||
case MHI_CB_XFER:
|
||||
atomic_inc(&rmnet_mhi_ptr->pending_data);
|
||||
/* Flush pending data is set before any other mem operations */
|
||||
wmb();
|
||||
if (rmnet_mhi_ptr->mhi_enabled) {
|
||||
if (cb_info->chan == rmnet_mhi_ptr->rx_channel)
|
||||
rmnet_mhi_rx_cb(cb_info->result);
|
||||
else
|
||||
rmnet_mhi_tx_cb(cb_info->result);
|
||||
}
|
||||
atomic_dec(&rmnet_mhi_ptr->pending_data);
|
||||
if (cb_info->chan == rmnet_mhi_ptr->rx_channel)
|
||||
rmnet_mhi_rx_cb(cb_info->result);
|
||||
else
|
||||
rmnet_mhi_tx_cb(cb_info->result);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -1172,6 +1204,7 @@ static int rmnet_mhi_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
rmnet_mhi_ptr->pdev = pdev;
|
||||
spin_lock_init(&rmnet_mhi_ptr->out_chan_full_lock);
|
||||
rwlock_init(&rmnet_mhi_ptr->pm_lock);
|
||||
|
||||
rc = of_property_read_u32(pdev->dev.of_node,
|
||||
"qcom,mhi-mru",
|
||||
|
|
Loading…
Add table
Reference in a new issue