msm: bam_dmux: add subsystem restart support
Change-Id: I7eeb4900ac71f845cd5b94f428bdd2a6c040530e
Signed-off-by: Jeffrey Hugo <jhugo@codeaurora.org>
diff --git a/arch/arm/mach-msm/bam_dmux.c b/arch/arm/mach-msm/bam_dmux.c
index 603b031..6f1ebe8 100644
--- a/arch/arm/mach-msm/bam_dmux.c
+++ b/arch/arm/mach-msm/bam_dmux.c
@@ -29,9 +29,11 @@
#include <mach/sps.h>
#include <mach/bam_dmux.h>
#include <mach/msm_smsm.h>
+#include <mach/subsystem_notif.h>
#define BAM_CH_LOCAL_OPEN 0x1
#define BAM_CH_REMOTE_OPEN 0x2
+#define BAM_CH_IN_RESET 0x4
#define BAM_MUX_HDR_MAGIC_NO 0x33fc
@@ -103,6 +105,7 @@
char is_cmd;
uint32_t len;
struct work_struct work;
+ struct list_head list_node;
};
struct rx_pkt_info {
@@ -137,6 +140,8 @@
static LIST_HEAD(bam_rx_pool);
static DEFINE_MUTEX(bam_rx_pool_lock);
+static LIST_HEAD(bam_tx_pool);
+static DEFINE_MUTEX(bam_tx_pool_lock);
struct bam_mux_hdr {
uint16_t magic_num;
@@ -178,8 +183,20 @@
static struct clk *dfab_clk;
static DEFINE_RWLOCK(ul_wakeup_lock);
static DECLARE_WORK(kickoff_ul_wakeup, kickoff_ul_wakeup_func);
+static int bam_connection_is_active;
/* End A2 power collaspe */
+/* subsystem restart */
+static int restart_notifier_cb(struct notifier_block *this,
+ unsigned long code,
+ void *data);
+
+static struct notifier_block restart_notifier = {
+ .notifier_call = restart_notifier_cb,
+};
+static int in_global_reset;
+/* end subsystem restart */
+
#define bam_ch_is_open(x) \
(bam_ch[(x)].status == (BAM_CH_LOCAL_OPEN | BAM_CH_REMOTE_OPEN))
@@ -189,11 +206,17 @@
#define bam_ch_is_remote_open(x) \
(bam_ch[(x)].status & BAM_CH_REMOTE_OPEN)
+#define bam_ch_is_in_reset(x) \
+ (bam_ch[(x)].status & BAM_CH_IN_RESET)
+
static void queue_rx(void)
{
void *ptr;
struct rx_pkt_info *info;
+ if (in_global_reset)
+ return;
+
info = kmalloc(sizeof(struct rx_pkt_info), GFP_KERNEL);
if (!info)
return; /*need better way to handle this */
@@ -337,6 +360,10 @@
pkt->len = len;
pkt->dma_address = dma_address;
pkt->is_cmd = 1;
+ INIT_WORK(&pkt->work, bam_mux_write_done);
+ mutex_lock(&bam_tx_pool_lock);
+ list_add_tail(&pkt->list_node, &bam_tx_pool);
+ mutex_unlock(&bam_tx_pool_lock);
rc = sps_transfer_one(bam_tx_pipe, dma_address, len,
pkt, SPS_IOVEC_FLAG_INT | SPS_IOVEC_FLAG_EOT);
@@ -351,8 +378,20 @@
struct bam_mux_hdr *hdr;
struct tx_pkt_info *info;
unsigned long event_data;
+ struct list_head *node;
+ if (in_global_reset)
+ return;
+ mutex_lock(&bam_tx_pool_lock);
+ node = bam_tx_pool.next;
+ list_del(node);
+ mutex_unlock(&bam_tx_pool_lock);
info = container_of(work, struct tx_pkt_info, work);
+ if (info->is_cmd) {
+ kfree(info->skb);
+ kfree(info);
+ return;
+ }
skb = info->skb;
kfree(info);
hdr = (struct bam_mux_hdr *)skb->data;
@@ -453,6 +492,9 @@
pkt->dma_address = dma_address;
pkt->is_cmd = 0;
INIT_WORK(&pkt->work, bam_mux_write_done);
+ mutex_lock(&bam_tx_pool_lock);
+ list_add_tail(&pkt->list_node, &bam_tx_pool);
+ mutex_unlock(&bam_tx_pool_lock);
rc = sps_transfer_one(bam_tx_pipe, dma_address, skb->len,
pkt, SPS_IOVEC_FLAG_INT | SPS_IOVEC_FLAG_EOT);
ul_packet_written = 1;
@@ -536,7 +578,7 @@
return -ENODEV;
read_lock(&ul_wakeup_lock);
- if (!bam_is_connected) {
+ if (!bam_is_connected && !bam_ch_is_in_reset(id)) {
read_unlock(&ul_wakeup_lock);
ul_wakeup();
read_lock(&ul_wakeup_lock);
@@ -549,6 +591,12 @@
bam_ch[id].status &= ~BAM_CH_LOCAL_OPEN;
spin_unlock_irqrestore(&bam_ch[id].lock, flags);
+ if (bam_ch_is_in_reset(id)) {
+ read_unlock(&ul_wakeup_lock);
+ bam_ch[id].status &= ~BAM_CH_IN_RESET;
+ return 0;
+ }
+
hdr = kmalloc(sizeof(struct bam_mux_hdr), GFP_KERNEL);
if (hdr == NULL) {
pr_err("%s: hdr kmalloc failed. ch: %d\n", __func__, id);
@@ -580,6 +628,8 @@
while (1) { /* timer loop */
++inactive_cycles;
while (1) { /* deplete queue loop */
+ if (in_global_reset)
+ return;
sps_get_iovec(bam_rx_pipe, &iov);
if (iov.addr == 0)
break;
@@ -623,6 +673,8 @@
}
polling_mode = 0;
}
+ if (in_global_reset)
+ return;
/* handle race condition - missed packet? */
sps_get_iovec(bam_rx_pipe, &iov);
if (iov.addr == 0)
@@ -648,21 +700,21 @@
DBG("%s: event %d notified\n", __func__, notify->event_id);
+ if (in_global_reset)
+ return;
+
switch (notify->event_id) {
case SPS_EVENT_EOT:
pkt = notify->data.transfer.user;
- if (!pkt->is_cmd) {
+ if (!pkt->is_cmd)
dma_unmap_single(NULL, pkt->dma_address,
pkt->skb->len,
DMA_TO_DEVICE);
- queue_work(bam_mux_tx_workqueue, &pkt->work);
- } else {
+ else
dma_unmap_single(NULL, pkt->dma_address,
pkt->len,
DMA_TO_DEVICE);
- kfree(pkt->skb);
- kfree(pkt);
- }
+ queue_work(bam_mux_tx_workqueue, &pkt->work);
break;
default:
pr_err("%s: recieved unexpected event id %d\n", __func__,
@@ -677,6 +729,9 @@
DBG("%s: event %d notified\n", __func__, notify->event_id);
+ if (in_global_reset)
+ return;
+
switch (notify->event_id) {
case SPS_EVENT_EOT:
/* attempt to disable interrupts in this pipe */
@@ -792,6 +847,8 @@
static void ul_timeout(struct work_struct *work)
{
+ if (in_global_reset)
+ return;
write_lock(&ul_wakeup_lock);
if (ul_packet_written) {
ul_packet_written = 0;
@@ -828,6 +885,7 @@
{
int i;
+ in_global_reset = 0;
vote_dfab();
i = sps_device_reset(a2_device_handle);
if (i)
@@ -847,6 +905,7 @@
for (i = 0; i < NUM_BUFFERS; ++i)
queue_rx();
toggle_apps_ack();
+ bam_connection_is_active = 1;
complete_all(&bam_connection_completion);
}
@@ -855,6 +914,7 @@
struct list_head *node;
struct rx_pkt_info *info;
+ bam_connection_is_active = 0;
INIT_COMPLETION(bam_connection_completion);
sps_disconnect(bam_tx_pipe);
sps_disconnect(bam_rx_pipe);
@@ -886,6 +946,56 @@
clk_disable(dfab_clk);
}
+static int restart_notifier_cb(struct notifier_block *this,
+ unsigned long code,
+ void *data)
+{
+ int i;
+ struct list_head *node;
+ struct tx_pkt_info *info;
+ int temp_remote_status;
+
+ if (code != SUBSYS_AFTER_SHUTDOWN)
+ return NOTIFY_DONE;
+
+ in_global_reset = 1;
+ for (i = 0; i < BAM_DMUX_NUM_CHANNELS; ++i) {
+ temp_remote_status = bam_ch_is_remote_open(i);
+ bam_ch[i].status &= ~BAM_CH_REMOTE_OPEN;
+ if (bam_ch_is_local_open(i))
+ bam_ch[i].status |= BAM_CH_IN_RESET;
+ if (temp_remote_status) {
+ platform_device_unregister(bam_ch[i].pdev);
+ bam_ch[i].pdev = platform_device_alloc(
+ bam_ch[i].name, 2);
+ }
+ }
+ /*cleanup UL*/
+ mutex_lock(&bam_tx_pool_lock);
+ while (!list_empty(&bam_tx_pool)) {
+ node = bam_tx_pool.next;
+ list_del(node);
+ info = container_of(node, struct tx_pkt_info,
+ list_node);
+ if (!info->is_cmd) {
+ dma_unmap_single(NULL, info->dma_address,
+ info->skb->len,
+ DMA_TO_DEVICE);
+ dev_kfree_skb_any(info->skb);
+ } else {
+ dma_unmap_single(NULL, info->dma_address,
+ info->len,
+ DMA_TO_DEVICE);
+ kfree(info->skb);
+ }
+ kfree(info);
+ }
+ mutex_unlock(&bam_tx_pool_lock);
+ smsm_change_state(SMSM_APPS_STATE, SMSM_A2_POWER_CONTROL, 0);
+
+ return NOTIFY_DONE;
+}
+
static void bam_init(void)
{
u32 h;
@@ -1017,6 +1127,7 @@
for (i = 0; i < NUM_BUFFERS; ++i)
queue_rx();
toggle_apps_ack();
+ bam_connection_is_active = 1;
complete_all(&bam_connection_completion);
return;
@@ -1163,6 +1274,7 @@
if (!IS_ERR(dent))
debug_create("tbl", 0444, dent, debug_tbl);
#endif
+ subsys_notif_register_notifier("modem", &restart_notifier);
return platform_driver_register(&bam_dmux_driver);
}