summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-07-28 01:18:53 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2016-07-28 01:18:53 +0300
commit8448cefe499a45222430ebbcabcd9e26369895c3 (patch)
treecaaec5478658b15aee643ab0732fdabef6101fb7
parent818e607b57c94ade9824dad63a96c2ea6b21baf3 (diff)
parent9c99e5e51988798af2a687ef9b1716dd79388550 (diff)
downloadlinux-8448cefe499a45222430ebbcabcd9e26369895c3.tar.xz
Merge tag 'hsi-for-4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-hsi
Pull HSI updates from Sebastian Reichel: - proper runtime pm support for omap-ssi and ssi-protocol - misc fixes * tag 'hsi-for-4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-hsi: (24 commits) HSI: omap_ssi: drop pm_runtime_irq_safe HSI: omap_ssi_port: use rpm autosuspend API HSI: omap_ssi: call msg->complete() from process context HSI: omap_ssi_port: ensure clocks are kept enabled during transfer HSI: omap_ssi_port: replace pm_runtime_put_sync with non-sync variant HSI: omap_ssi_port: avoid calling runtime_pm_*_sync inside spinlock HSI: omap_ssi_port: avoid pm_runtime_get_sync in ssi_start_dma and ssi_start_pio HSI: omap_ssi_port: switch to threaded pio irq HSI: omap_ssi_core: remove pm_runtime_get_sync call from tasklet HSI: omap_ssi_core: use pm_runtime_put instead of pm_runtime_put_sync HSI: omap_ssi_port: prepare start_tx/stop_tx for blocking pm_runtime calls HSI: core: switch port event notifier from atomic to blocking HSI: omap_ssi_port: replace wkin_cken with atomic bitmap operations HSI: omap_ssi: convert cawake irq handler to thread HSI: ssi_protocol: fix ssip_xmit invocation HSI: ssi_protocol: replace spin_lock with spin_lock_bh HSI: ssi_protocol: avoid ssi_waketest call with held spinlock HSI: omap_ssi: do not reset module HSI: omap_ssi_port: remove useless newline hsi: Only descend into hsi directory when CONFIG_HSI is set ...
-rw-r--r--Documentation/DocBook/device-drivers.tmpl2
-rw-r--r--drivers/Makefile2
-rw-r--r--drivers/hsi/Makefile3
-rw-r--r--drivers/hsi/clients/cmt_speech.c4
-rw-r--r--drivers/hsi/clients/ssi_protocol.c110
-rw-r--r--drivers/hsi/controllers/omap_ssi.h18
-rw-r--r--drivers/hsi/controllers/omap_ssi_core.c46
-rw-r--r--drivers/hsi/controllers/omap_ssi_port.c226
-rw-r--r--drivers/hsi/hsi_core.c (renamed from drivers/hsi/hsi.c)16
-rw-r--r--include/linux/hsi/hsi.h2
10 files changed, 239 insertions, 190 deletions
diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl
index 7febdeb08159..99cdc05bbb7a 100644
--- a/Documentation/DocBook/device-drivers.tmpl
+++ b/Documentation/DocBook/device-drivers.tmpl
@@ -484,7 +484,7 @@ X!Ilib/fonts/fonts.c
</para>
!Iinclude/linux/hsi/hsi.h
-!Edrivers/hsi/hsi.c
+!Edrivers/hsi/hsi_core.c
</chapter>
<chapter id="pwm">
diff --git a/drivers/Makefile b/drivers/Makefile
index 6b0ad573e0d7..954824438fd1 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -78,7 +78,7 @@ obj-$(CONFIG_TARGET_CORE) += target/
obj-$(CONFIG_MTD) += mtd/
obj-$(CONFIG_SPI) += spi/
obj-$(CONFIG_SPMI) += spmi/
-obj-y += hsi/
+obj-$(CONFIG_HSI) += hsi/
obj-y += net/
obj-$(CONFIG_ATM) += atm/
obj-$(CONFIG_FUSION) += message/
diff --git a/drivers/hsi/Makefile b/drivers/hsi/Makefile
index 360371e134f1..96944783d584 100644
--- a/drivers/hsi/Makefile
+++ b/drivers/hsi/Makefile
@@ -1,7 +1,8 @@
#
# Makefile for HSI
#
-obj-$(CONFIG_HSI_BOARDINFO) += hsi_boardinfo.o
obj-$(CONFIG_HSI) += hsi.o
+hsi-objs := hsi_core.o
+hsi-$(CONFIG_HSI_BOARDINFO) += hsi_boardinfo.o
obj-y += controllers/
obj-y += clients/
diff --git a/drivers/hsi/clients/cmt_speech.c b/drivers/hsi/clients/cmt_speech.c
index 95638df73d1c..3deef6cc7d7c 100644
--- a/drivers/hsi/clients/cmt_speech.c
+++ b/drivers/hsi/clients/cmt_speech.c
@@ -444,8 +444,8 @@ static void cs_hsi_read_on_control_complete(struct hsi_msg *msg)
hi->control_state &= ~SSI_CHANNEL_STATE_READING;
if (msg->status == HSI_STATUS_ERROR) {
dev_err(&hi->cl->device, "Control RX error detected\n");
- cs_hsi_control_read_error(hi, msg);
spin_unlock(&hi->lock);
+ cs_hsi_control_read_error(hi, msg);
goto out;
}
dev_dbg(&hi->cl->device, "Read on control: %08X\n", cmd);
@@ -1275,7 +1275,7 @@ static int cs_char_mmap(struct file *file, struct vm_area_struct *vma)
if (vma->vm_end < vma->vm_start)
return -EINVAL;
- if (((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) != 1)
+ if (vma_pages(vma) != 1)
return -EINVAL;
vma->vm_flags |= VM_IO | VM_DONTDUMP | VM_DONTEXPAND;
diff --git a/drivers/hsi/clients/ssi_protocol.c b/drivers/hsi/clients/ssi_protocol.c
index 6595d2091268..6031cd146556 100644
--- a/drivers/hsi/clients/ssi_protocol.c
+++ b/drivers/hsi/clients/ssi_protocol.c
@@ -88,6 +88,8 @@ void ssi_waketest(struct hsi_client *cl, unsigned int enable);
#define SSIP_READY_CMD SSIP_CMD(SSIP_READY, 0)
#define SSIP_SWBREAK_CMD SSIP_CMD(SSIP_SW_BREAK, 0)
+#define SSIP_WAKETEST_FLAG 0
+
/* Main state machine states */
enum {
INIT,
@@ -116,7 +118,7 @@ enum {
* @main_state: Main state machine
* @send_state: TX state machine
* @recv_state: RX state machine
- * @waketest: Flag to follow wake line test
+ * @flags: Flags, currently only used to follow wake line test
* @rxid: RX data id
* @txid: TX data id
* @txqueue_len: TX queue length
@@ -137,7 +139,7 @@ struct ssi_protocol {
unsigned int main_state;
unsigned int send_state;
unsigned int recv_state;
- unsigned int waketest:1;
+ unsigned long flags;
u8 rxid;
u8 txid;
unsigned int txqueue_len;
@@ -148,6 +150,7 @@ struct ssi_protocol {
struct net_device *netdev;
struct list_head txqueue;
struct list_head cmdqueue;
+ struct work_struct work;
struct hsi_client *cl;
struct list_head link;
atomic_t tx_usecnt;
@@ -405,15 +408,17 @@ static void ssip_reset(struct hsi_client *cl)
spin_lock_bh(&ssi->lock);
if (ssi->send_state != SEND_IDLE)
hsi_stop_tx(cl);
- if (ssi->waketest)
- ssi_waketest(cl, 0);
+ spin_unlock_bh(&ssi->lock);
+ if (test_and_clear_bit(SSIP_WAKETEST_FLAG, &ssi->flags))
+ ssi_waketest(cl, 0); /* FIXME: To be removed */
+ spin_lock_bh(&ssi->lock);
del_timer(&ssi->rx_wd);
del_timer(&ssi->tx_wd);
del_timer(&ssi->keep_alive);
ssi->main_state = 0;
ssi->send_state = 0;
ssi->recv_state = 0;
- ssi->waketest = 0;
+ ssi->flags = 0;
ssi->rxid = 0;
ssi->txid = 0;
list_for_each_safe(head, tmp, &ssi->txqueue) {
@@ -437,7 +442,8 @@ static void ssip_dump_state(struct hsi_client *cl)
dev_err(&cl->device, "Send state: %d\n", ssi->send_state);
dev_err(&cl->device, "CMT %s\n", (ssi->main_state == ACTIVE) ?
"Online" : "Offline");
- dev_err(&cl->device, "Wake test %d\n", ssi->waketest);
+ dev_err(&cl->device, "Wake test %d\n",
+ test_bit(SSIP_WAKETEST_FLAG, &ssi->flags));
dev_err(&cl->device, "Data RX id: %d\n", ssi->rxid);
dev_err(&cl->device, "Data TX id: %d\n", ssi->txid);
@@ -515,17 +521,17 @@ static void ssip_start_rx(struct hsi_client *cl)
dev_dbg(&cl->device, "RX start M(%d) R(%d)\n", ssi->main_state,
ssi->recv_state);
- spin_lock(&ssi->lock);
+ spin_lock_bh(&ssi->lock);
/*
* We can have two UP events in a row due to a short low
* high transition. Therefore we need to ignore the sencond UP event.
*/
if ((ssi->main_state != ACTIVE) || (ssi->recv_state == RECV_READY)) {
- spin_unlock(&ssi->lock);
+ spin_unlock_bh(&ssi->lock);
return;
}
ssip_set_rxstate(ssi, RECV_READY);
- spin_unlock(&ssi->lock);
+ spin_unlock_bh(&ssi->lock);
msg = ssip_claim_cmd(ssi);
ssip_set_cmd(msg, SSIP_READY_CMD);
@@ -539,10 +545,10 @@ static void ssip_stop_rx(struct hsi_client *cl)
struct ssi_protocol *ssi = hsi_client_drvdata(cl);
dev_dbg(&cl->device, "RX stop M(%d)\n", ssi->main_state);
- spin_lock(&ssi->lock);
+ spin_lock_bh(&ssi->lock);
if (likely(ssi->main_state == ACTIVE))
ssip_set_rxstate(ssi, RECV_IDLE);
- spin_unlock(&ssi->lock);
+ spin_unlock_bh(&ssi->lock);
}
static void ssip_free_strans(struct hsi_msg *msg)
@@ -559,9 +565,9 @@ static void ssip_strans_complete(struct hsi_msg *msg)
data = msg->context;
ssip_release_cmd(msg);
- spin_lock(&ssi->lock);
+ spin_lock_bh(&ssi->lock);
ssip_set_txstate(ssi, SENDING);
- spin_unlock(&ssi->lock);
+ spin_unlock_bh(&ssi->lock);
hsi_async_write(cl, data);
}
@@ -666,15 +672,17 @@ static void ssip_rx_bootinforeq(struct hsi_client *cl, u32 cmd)
/* Fall through */
case INIT:
case HANDSHAKE:
- spin_lock(&ssi->lock);
+ spin_lock_bh(&ssi->lock);
ssi->main_state = HANDSHAKE;
- if (!ssi->waketest) {
- ssi->waketest = 1;
+ spin_unlock_bh(&ssi->lock);
+
+ if (!test_and_set_bit(SSIP_WAKETEST_FLAG, &ssi->flags))
ssi_waketest(cl, 1); /* FIXME: To be removed */
- }
+
+ spin_lock_bh(&ssi->lock);
/* Start boot handshake watchdog */
mod_timer(&ssi->tx_wd, jiffies + msecs_to_jiffies(SSIP_WDTOUT));
- spin_unlock(&ssi->lock);
+ spin_unlock_bh(&ssi->lock);
dev_dbg(&cl->device, "Send BOOTINFO_RESP\n");
if (SSIP_DATA_VERSION(cmd) != SSIP_LOCAL_VERID)
dev_warn(&cl->device, "boot info req verid mismatch\n");
@@ -696,14 +704,14 @@ static void ssip_rx_bootinforesp(struct hsi_client *cl, u32 cmd)
if (SSIP_DATA_VERSION(cmd) != SSIP_LOCAL_VERID)
dev_warn(&cl->device, "boot info resp verid mismatch\n");
- spin_lock(&ssi->lock);
+ spin_lock_bh(&ssi->lock);
if (ssi->main_state != ACTIVE)
/* Use tx_wd as a boot watchdog in non ACTIVE state */
mod_timer(&ssi->tx_wd, jiffies + msecs_to_jiffies(SSIP_WDTOUT));
else
dev_dbg(&cl->device, "boot info resp ignored M(%d)\n",
ssi->main_state);
- spin_unlock(&ssi->lock);
+ spin_unlock_bh(&ssi->lock);
}
static void ssip_rx_waketest(struct hsi_client *cl, u32 cmd)
@@ -711,20 +719,22 @@ static void ssip_rx_waketest(struct hsi_client *cl, u32 cmd)
struct ssi_protocol *ssi = hsi_client_drvdata(cl);
unsigned int wkres = SSIP_PAYLOAD(cmd);
- spin_lock(&ssi->lock);
+ spin_lock_bh(&ssi->lock);
if (ssi->main_state != HANDSHAKE) {
dev_dbg(&cl->device, "wake lines test ignored M(%d)\n",
ssi->main_state);
- spin_unlock(&ssi->lock);
+ spin_unlock_bh(&ssi->lock);
return;
}
- if (ssi->waketest) {
- ssi->waketest = 0;
+ spin_unlock_bh(&ssi->lock);
+
+ if (test_and_clear_bit(SSIP_WAKETEST_FLAG, &ssi->flags))
ssi_waketest(cl, 0); /* FIXME: To be removed */
- }
+
+ spin_lock_bh(&ssi->lock);
ssi->main_state = ACTIVE;
del_timer(&ssi->tx_wd); /* Stop boot handshake timer */
- spin_unlock(&ssi->lock);
+ spin_unlock_bh(&ssi->lock);
dev_notice(&cl->device, "WAKELINES TEST %s\n",
wkres & SSIP_WAKETEST_FAILED ? "FAILED" : "OK");
@@ -741,20 +751,20 @@ static void ssip_rx_ready(struct hsi_client *cl)
{
struct ssi_protocol *ssi = hsi_client_drvdata(cl);
- spin_lock(&ssi->lock);
+ spin_lock_bh(&ssi->lock);
if (unlikely(ssi->main_state != ACTIVE)) {
dev_dbg(&cl->device, "READY on wrong state: S(%d) M(%d)\n",
ssi->send_state, ssi->main_state);
- spin_unlock(&ssi->lock);
+ spin_unlock_bh(&ssi->lock);
return;
}
if (ssi->send_state != WAIT4READY) {
dev_dbg(&cl->device, "Ignore spurious READY command\n");
- spin_unlock(&ssi->lock);
+ spin_unlock_bh(&ssi->lock);
return;
}
ssip_set_txstate(ssi, SEND_READY);
- spin_unlock(&ssi->lock);
+ spin_unlock_bh(&ssi->lock);
ssip_xmit(cl);
}
@@ -766,22 +776,22 @@ static void ssip_rx_strans(struct hsi_client *cl, u32 cmd)
int len = SSIP_PDU_LENGTH(cmd);
dev_dbg(&cl->device, "RX strans: %d frames\n", len);
- spin_lock(&ssi->lock);
+ spin_lock_bh(&ssi->lock);
if (unlikely(ssi->main_state != ACTIVE)) {
dev_err(&cl->device, "START TRANS wrong state: S(%d) M(%d)\n",
ssi->send_state, ssi->main_state);
- spin_unlock(&ssi->lock);
+ spin_unlock_bh(&ssi->lock);
return;
}
ssip_set_rxstate(ssi, RECEIVING);
if (unlikely(SSIP_MSG_ID(cmd) != ssi->rxid)) {
dev_err(&cl->device, "START TRANS id %d expected %d\n",
SSIP_MSG_ID(cmd), ssi->rxid);
- spin_unlock(&ssi->lock);
+ spin_unlock_bh(&ssi->lock);
goto out1;
}
ssi->rxid++;
- spin_unlock(&ssi->lock);
+ spin_unlock_bh(&ssi->lock);
skb = netdev_alloc_skb(ssi->netdev, len * 4);
if (unlikely(!skb)) {
dev_err(&cl->device, "No memory for rx skb\n");
@@ -849,7 +859,7 @@ static void ssip_swbreak_complete(struct hsi_msg *msg)
struct ssi_protocol *ssi = hsi_client_drvdata(cl);
ssip_release_cmd(msg);
- spin_lock(&ssi->lock);
+ spin_lock_bh(&ssi->lock);
if (list_empty(&ssi->txqueue)) {
if (atomic_read(&ssi->tx_usecnt)) {
ssip_set_txstate(ssi, SEND_READY);
@@ -857,9 +867,9 @@ static void ssip_swbreak_complete(struct hsi_msg *msg)
ssip_set_txstate(ssi, SEND_IDLE);
hsi_stop_tx(cl);
}
- spin_unlock(&ssi->lock);
+ spin_unlock_bh(&ssi->lock);
} else {
- spin_unlock(&ssi->lock);
+ spin_unlock_bh(&ssi->lock);
ssip_xmit(cl);
}
netif_wake_queue(ssi->netdev);
@@ -876,17 +886,17 @@ static void ssip_tx_data_complete(struct hsi_msg *msg)
ssip_error(cl);
goto out;
}
- spin_lock(&ssi->lock);
+ spin_lock_bh(&ssi->lock);
if (list_empty(&ssi->txqueue)) {
ssip_set_txstate(ssi, SENDING_SWBREAK);
- spin_unlock(&ssi->lock);
+ spin_unlock_bh(&ssi->lock);
cmsg = ssip_claim_cmd(ssi);
ssip_set_cmd(cmsg, SSIP_SWBREAK_CMD);
cmsg->complete = ssip_swbreak_complete;
dev_dbg(&cl->device, "Send SWBREAK\n");
hsi_async_write(cl, cmsg);
} else {
- spin_unlock(&ssi->lock);
+ spin_unlock_bh(&ssi->lock);
ssip_xmit(cl);
}
out:
@@ -926,11 +936,11 @@ static int ssip_pn_open(struct net_device *dev)
}
dev_dbg(&cl->device, "Configuring SSI port\n");
hsi_setup(cl);
- spin_lock_bh(&ssi->lock);
- if (!ssi->waketest) {
- ssi->waketest = 1;
+
+ if (!test_and_set_bit(SSIP_WAKETEST_FLAG, &ssi->flags))
ssi_waketest(cl, 1); /* FIXME: To be removed */
- }
+
+ spin_lock_bh(&ssi->lock);
ssi->main_state = HANDSHAKE;
spin_unlock_bh(&ssi->lock);
@@ -959,6 +969,15 @@ static int ssip_pn_set_mtu(struct net_device *dev, int new_mtu)
return 0;
}
+static void ssip_xmit_work(struct work_struct *work)
+{
+ struct ssi_protocol *ssi =
+ container_of(work, struct ssi_protocol, work);
+ struct hsi_client *cl = ssi->cl;
+
+ ssip_xmit(cl);
+}
+
static int ssip_pn_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct hsi_client *cl = to_hsi_client(dev->dev.parent);
@@ -1011,7 +1030,7 @@ static int ssip_pn_xmit(struct sk_buff *skb, struct net_device *dev)
dev_dbg(&cl->device, "Start TX on SEND READY qlen %d\n",
ssi->txqueue_len);
spin_unlock_bh(&ssi->lock);
- ssip_xmit(cl);
+ schedule_work(&ssi->work);
} else {
spin_unlock_bh(&ssi->lock);
}
@@ -1088,6 +1107,7 @@ static int ssi_protocol_probe(struct device *dev)
atomic_set(&ssi->tx_usecnt, 0);
hsi_client_set_drvdata(cl, ssi);
ssi->cl = cl;
+ INIT_WORK(&ssi->work, ssip_xmit_work);
ssi->channel_id_cmd = hsi_get_channel_id_by_name(cl, "mcsaab-control");
if (ssi->channel_id_cmd < 0) {
diff --git a/drivers/hsi/controllers/omap_ssi.h b/drivers/hsi/controllers/omap_ssi.h
index 7b4dec2c69ff..32ced0c8f789 100644
--- a/drivers/hsi/controllers/omap_ssi.h
+++ b/drivers/hsi/controllers/omap_ssi.h
@@ -35,6 +35,8 @@
#define SSI_MAX_GDD_LCH 8
#define SSI_BYTES_TO_FRAMES(x) ((((x) - 1) >> 2) + 1)
+#define SSI_WAKE_EN 0
+
/**
* struct omap_ssm_ctx - OMAP synchronous serial module (TX/RX) context
* @mode: Bit transmission mode
@@ -71,13 +73,14 @@ struct omap_ssm_ctx {
* @txqueue: TX message queues
* @rxqueue: RX message queues
* @brkqueue: Queue of incoming HWBREAK requests (FRAME mode)
+ * @errqueue: Queue for failed messages
+ * @errqueue_work: Delayed Work for failed messages
* @irq: IRQ number
* @wake_irq: IRQ number for incoming wake line (-1 if none)
* @wake_gpio: GPIO number for incoming wake line (-1 if none)
- * @pio_tasklet: Bottom half for PIO transfers and events
- * @wake_tasklet: Bottom half for incoming wake events
- * @wkin_cken: Keep track of clock references due to the incoming wake line
+ * @flags: flags to keep track of states
* @wk_refcount: Reference count for output wake line
+ * @work: worker for starting TX
* @sys_mpu_enable: Context for the interrupt enable register for irq 0
* @sst: Context for the synchronous serial transmitter
* @ssr: Context for the synchronous serial receiver
@@ -95,14 +98,15 @@ struct omap_ssi_port {
struct list_head txqueue[SSI_MAX_CHANNELS];
struct list_head rxqueue[SSI_MAX_CHANNELS];
struct list_head brkqueue;
+ struct list_head errqueue;
+ struct delayed_work errqueue_work;
unsigned int irq;
int wake_irq;
struct gpio_desc *wake_gpio;
- struct tasklet_struct pio_tasklet;
- struct tasklet_struct wake_tasklet;
bool wktest:1; /* FIXME: HACK to be removed */
- bool wkin_cken:1; /* Workaround */
+ unsigned long flags;
unsigned int wk_refcount;
+ struct work_struct work;
/* OMAP SSI port context */
u32 sys_mpu_enable; /* We use only one irq */
struct omap_ssm_ctx sst;
@@ -138,7 +142,6 @@ struct gdd_trn {
* @fck_rate: clock rate
* @loss_count: To follow if we need to restore context or not
* @max_speed: Maximum TX speed (Kb/s) set by the clients.
- * @sysconfig: SSI controller saved context
* @gdd_gcr: SSI GDD saved context
* @get_loss: Pointer to omap_pm_get_dev_context_loss_count, if any
* @port: Array of pointers of the ports of the controller
@@ -158,7 +161,6 @@ struct omap_ssi_controller {
u32 loss_count;
u32 max_speed;
/* OMAP SSI Controller context */
- u32 sysconfig;
u32 gdd_gcr;
int (*get_loss)(struct device *dev);
struct omap_ssi_port **port;
diff --git a/drivers/hsi/controllers/omap_ssi_core.c b/drivers/hsi/controllers/omap_ssi_core.c
index a3e0febfb64a..9a29b34ed2c8 100644
--- a/drivers/hsi/controllers/omap_ssi_core.c
+++ b/drivers/hsi/controllers/omap_ssi_core.c
@@ -58,7 +58,7 @@ static int ssi_debug_show(struct seq_file *m, void *p __maybe_unused)
seq_printf(m, "REVISION\t: 0x%08x\n", readl(sys + SSI_REVISION_REG));
seq_printf(m, "SYSCONFIG\t: 0x%08x\n", readl(sys + SSI_SYSCONFIG_REG));
seq_printf(m, "SYSSTATUS\t: 0x%08x\n", readl(sys + SSI_SYSSTATUS_REG));
- pm_runtime_put_sync(ssi->device.parent);
+ pm_runtime_put(ssi->device.parent);
return 0;
}
@@ -112,7 +112,7 @@ static int ssi_debug_gdd_show(struct seq_file *m, void *p __maybe_unused)
readw(gdd + SSI_GDD_CLNK_CTRL_REG(lch)));
}
- pm_runtime_put_sync(ssi->device.parent);
+ pm_runtime_put(ssi->device.parent);
return 0;
}
@@ -193,7 +193,7 @@ void ssi_waketest(struct hsi_client *cl, unsigned int enable)
} else {
writel_relaxed(SSI_WAKE(0),
omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num));
- pm_runtime_put_sync(ssi->device.parent);
+ pm_runtime_put(ssi->device.parent);
}
}
EXPORT_SYMBOL_GPL(ssi_waketest);
@@ -217,7 +217,7 @@ static void ssi_gdd_complete(struct hsi_controller *ssi, unsigned int lch)
if (msg->ttype == HSI_MSG_READ) {
dir = DMA_FROM_DEVICE;
val = SSI_DATAAVAILABLE(msg->channel);
- pm_runtime_put_sync(ssi->device.parent);
+ pm_runtime_put(omap_port->pdev);
} else {
dir = DMA_TO_DEVICE;
val = SSI_DATAACCEPT(msg->channel);
@@ -235,7 +235,9 @@ static void ssi_gdd_complete(struct hsi_controller *ssi, unsigned int lch)
spin_lock(&omap_port->lock);
list_del(&msg->link); /* Dequeue msg */
spin_unlock(&omap_port->lock);
- msg->complete(msg);
+
+ list_add_tail(&msg->link, &omap_port->errqueue);
+ schedule_delayed_work(&omap_port->errqueue_work, 0);
return;
}
spin_lock(&omap_port->lock);
@@ -255,7 +257,13 @@ static void ssi_gdd_tasklet(unsigned long dev)
unsigned int lch;
u32 status_reg;
- pm_runtime_get_sync(ssi->device.parent);
+ pm_runtime_get(ssi->device.parent);
+
+ if (!pm_runtime_active(ssi->device.parent)) {
+ dev_warn(ssi->device.parent, "ssi_gdd_tasklet called without runtime PM!\n");
+ pm_runtime_put(ssi->device.parent);
+ return;
+ }
status_reg = readl(sys + SSI_GDD_MPU_IRQ_STATUS_REG);
for (lch = 0; lch < SSI_MAX_GDD_LCH; lch++) {
@@ -265,7 +273,7 @@ static void ssi_gdd_tasklet(unsigned long dev)
writel_relaxed(status_reg, sys + SSI_GDD_MPU_IRQ_STATUS_REG);
status_reg = readl(sys + SSI_GDD_MPU_IRQ_STATUS_REG);
- pm_runtime_put_sync(ssi->device.parent);
+ pm_runtime_put(ssi->device.parent);
if (status_reg)
tasklet_hi_schedule(&omap_ssi->gdd_tasklet);
@@ -312,7 +320,7 @@ static int ssi_clk_event(struct notifier_block *nb, unsigned long event,
continue;
/* Workaround for SWBREAK + CAwake down race in CMT */
- tasklet_disable(&omap_port->wake_tasklet);
+ disable_irq(omap_port->wake_irq);
/* stop all ssi communication */
pinctrl_pm_select_idle_state(omap_port->pdev);
@@ -338,7 +346,7 @@ static int ssi_clk_event(struct notifier_block *nb, unsigned long event,
/* resume ssi communication */
pinctrl_pm_select_default_state(omap_port->pdev);
- tasklet_enable(&omap_port->wake_tasklet);
+ enable_irq(omap_port->wake_irq);
}
break;
@@ -452,8 +460,6 @@ out_err:
static int ssi_hw_init(struct hsi_controller *ssi)
{
struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
- unsigned int i;
- u32 val;
int err;
err = pm_runtime_get_sync(ssi->device.parent);
@@ -461,27 +467,12 @@ static int ssi_hw_init(struct hsi_controller *ssi)
dev_err(&ssi->device, "runtime PM failed %d\n", err);
return err;
}
- /* Reseting SSI controller */
- writel_relaxed(SSI_SOFTRESET, omap_ssi->sys + SSI_SYSCONFIG_REG);
- val = readl(omap_ssi->sys + SSI_SYSSTATUS_REG);
- for (i = 0; ((i < 20) && !(val & SSI_RESETDONE)); i++) {
- msleep(20);
- val = readl(omap_ssi->sys + SSI_SYSSTATUS_REG);
- }
- if (!(val & SSI_RESETDONE)) {
- dev_err(&ssi->device, "SSI HW reset failed\n");
- pm_runtime_put_sync(ssi->device.parent);
- return -EIO;
- }
/* Reseting GDD */
writel_relaxed(SSI_SWRESET, omap_ssi->gdd + SSI_GDD_GRST_REG);
/* Get FCK rate in KHz */
omap_ssi->fck_rate = DIV_ROUND_CLOSEST(ssi_get_clk_rate(ssi), 1000);
dev_dbg(&ssi->device, "SSI fck rate %lu KHz\n", omap_ssi->fck_rate);
- /* Set default PM settings */
- val = SSI_AUTOIDLE | SSI_SIDLEMODE_SMART | SSI_MIDLEMODE_SMART;
- writel_relaxed(val, omap_ssi->sys + SSI_SYSCONFIG_REG);
- omap_ssi->sysconfig = val;
+
writel_relaxed(SSI_CLK_AUTOGATING_ON, omap_ssi->sys + SSI_GDD_GCR_REG);
omap_ssi->gdd_gcr = SSI_CLK_AUTOGATING_ON;
pm_runtime_put_sync(ssi->device.parent);
@@ -552,7 +543,6 @@ static int ssi_probe(struct platform_device *pd)
if (err < 0)
goto out1;
- pm_runtime_irq_safe(&pd->dev);
pm_runtime_enable(&pd->dev);
err = ssi_hw_init(ssi);
diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c
index 6b8f7739768a..7765de2f1ef1 100644
--- a/drivers/hsi/controllers/omap_ssi_port.c
+++ b/drivers/hsi/controllers/omap_ssi_port.c
@@ -126,7 +126,7 @@ static int ssi_debug_port_show(struct seq_file *m, void *p __maybe_unused)
seq_printf(m, "BUFFER_CH%d\t: 0x%08x\n", ch,
readl(base + SSI_SSR_BUFFER_CH_REG(ch)));
}
- pm_runtime_put_sync(omap_port->pdev);
+ pm_runtime_put_autosuspend(omap_port->pdev);
return 0;
}
@@ -150,7 +150,7 @@ static int ssi_div_get(void *data, u64 *val)
pm_runtime_get_sync(omap_port->pdev);
*val = readl(omap_port->sst_base + SSI_SST_DIVISOR_REG);
- pm_runtime_put_sync(omap_port->pdev);
+ pm_runtime_put_autosuspend(omap_port->pdev);
return 0;
}
@@ -166,7 +166,7 @@ static int ssi_div_set(void *data, u64 val)
pm_runtime_get_sync(omap_port->pdev);
writel(val, omap_port->sst_base + SSI_SST_DIVISOR_REG);
omap_port->sst.divisor = val;
- pm_runtime_put_sync(omap_port->pdev);
+ pm_runtime_put_autosuspend(omap_port->pdev);
return 0;
}
@@ -193,6 +193,21 @@ static int ssi_debug_add_port(struct omap_ssi_port *omap_port,
}
#endif
+static void ssi_process_errqueue(struct work_struct *work)
+{
+ struct omap_ssi_port *omap_port;
+ struct list_head *head, *tmp;
+ struct hsi_msg *msg;
+
+ omap_port = container_of(work, struct omap_ssi_port, errqueue_work.work);
+
+ list_for_each_safe(head, tmp, &omap_port->errqueue) {
+ msg = list_entry(head, struct hsi_msg, link);
+ msg->complete(msg);
+ list_del(head);
+ }
+}
+
static int ssi_claim_lch(struct hsi_msg *msg)
{
@@ -225,11 +240,21 @@ static int ssi_start_dma(struct hsi_msg *msg, int lch)
u32 d_addr;
u32 tmp;
+ /* Hold clocks during the transfer */
+ pm_runtime_get(omap_port->pdev);
+
+ if (!pm_runtime_active(omap_port->pdev)) {
+ dev_warn(&port->device, "ssi_start_dma called without runtime PM!\n");
+ pm_runtime_put_autosuspend(omap_port->pdev);
+ return -EREMOTEIO;
+ }
+
if (msg->ttype == HSI_MSG_READ) {
err = dma_map_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents,
DMA_FROM_DEVICE);
if (err < 0) {
dev_dbg(&ssi->device, "DMA map SG failed !\n");
+ pm_runtime_put_autosuspend(omap_port->pdev);
return err;
}
csdp = SSI_DST_BURST_4x32_BIT | SSI_DST_MEMORY_PORT |
@@ -246,6 +271,7 @@ static int ssi_start_dma(struct hsi_msg *msg, int lch)
DMA_TO_DEVICE);
if (err < 0) {
dev_dbg(&ssi->device, "DMA map SG failed !\n");
+ pm_runtime_put_autosuspend(omap_port->pdev);
return err;
}
csdp = SSI_SRC_BURST_4x32_BIT | SSI_SRC_MEMORY_PORT |
@@ -261,9 +287,6 @@ static int ssi_start_dma(struct hsi_msg *msg, int lch)
dev_dbg(&ssi->device, "lch %d cdsp %08x ccr %04x s_addr %08x d_addr %08x\n",
lch, csdp, ccr, s_addr, d_addr);
- /* Hold clocks during the transfer */
- pm_runtime_get_sync(omap_port->pdev);
-
writew_relaxed(csdp, gdd + SSI_GDD_CSDP_REG(lch));
writew_relaxed(SSI_BLOCK_IE | SSI_TOUT_IE, gdd + SSI_GDD_CICR_REG(lch));
writel_relaxed(d_addr, gdd + SSI_GDD_CDSA_REG(lch));
@@ -290,11 +313,18 @@ static int ssi_start_pio(struct hsi_msg *msg)
struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
u32 val;
- pm_runtime_get_sync(omap_port->pdev);
+ pm_runtime_get(omap_port->pdev);
+
+ if (!pm_runtime_active(omap_port->pdev)) {
+ dev_warn(&port->device, "ssi_start_pio called without runtime PM!\n");
+ pm_runtime_put_autosuspend(omap_port->pdev);
+ return -EREMOTEIO;
+ }
+
if (msg->ttype == HSI_MSG_WRITE) {
val = SSI_DATAACCEPT(msg->channel);
/* Hold clocks for pio writes */
- pm_runtime_get_sync(omap_port->pdev);
+ pm_runtime_get(omap_port->pdev);
} else {
val = SSI_DATAAVAILABLE(msg->channel) | SSI_ERROROCCURED;
}
@@ -302,7 +332,7 @@ static int ssi_start_pio(struct hsi_msg *msg)
msg->ttype ? "write" : "read");
val |= readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
writel(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
- pm_runtime_put_sync(omap_port->pdev);
+ pm_runtime_put_autosuspend(omap_port->pdev);
msg->actual_len = 0;
msg->status = HSI_STATUS_PROCEEDING;
@@ -360,7 +390,8 @@ static int ssi_async_break(struct hsi_msg *msg)
spin_unlock_bh(&omap_port->lock);
}
out:
- pm_runtime_put_sync(omap_port->pdev);
+ pm_runtime_mark_last_busy(omap_port->pdev);
+ pm_runtime_put_autosuspend(omap_port->pdev);
return err;
}
@@ -388,6 +419,8 @@ static int ssi_async(struct hsi_msg *msg)
queue = &omap_port->rxqueue[msg->channel];
}
msg->status = HSI_STATUS_QUEUED;
+
+ pm_runtime_get_sync(omap_port->pdev);
spin_lock_bh(&omap_port->lock);
list_add_tail(&msg->link, queue);
err = ssi_start_transfer(queue);
@@ -396,6 +429,8 @@ static int ssi_async(struct hsi_msg *msg)
msg->status = HSI_STATUS_ERROR;
}
spin_unlock_bh(&omap_port->lock);
+ pm_runtime_mark_last_busy(omap_port->pdev);
+ pm_runtime_put_autosuspend(omap_port->pdev);
dev_dbg(&port->device, "msg status %d ttype %d ch %d\n",
msg->status, msg->ttype, msg->channel);
@@ -497,7 +532,8 @@ static int ssi_setup(struct hsi_client *cl)
omap_port->ssr.mode = cl->rx_cfg.mode;
out:
spin_unlock_bh(&omap_port->lock);
- pm_runtime_put_sync(omap_port->pdev);
+ pm_runtime_mark_last_busy(omap_port->pdev);
+ pm_runtime_put_autosuspend(omap_port->pdev);
return err;
}
@@ -528,7 +564,7 @@ static int ssi_flush(struct hsi_client *cl)
continue;
writew_relaxed(0, omap_ssi->gdd + SSI_GDD_CCR_REG(i));
if (msg->ttype == HSI_MSG_READ)
- pm_runtime_put_sync(omap_port->pdev);
+ pm_runtime_put_autosuspend(omap_port->pdev);
omap_ssi->gdd_trn[i].msg = NULL;
}
/* Flush all SST buffers */
@@ -552,7 +588,7 @@ static int ssi_flush(struct hsi_client *cl)
for (i = 0; i < omap_port->channels; i++) {
/* Release write clocks */
if (!list_empty(&omap_port->txqueue[i]))
- pm_runtime_put_sync(omap_port->pdev);
+ pm_runtime_put_autosuspend(omap_port->pdev);
ssi_flush_queue(&omap_port->txqueue[i], NULL);
ssi_flush_queue(&omap_port->rxqueue[i], NULL);
}
@@ -562,17 +598,28 @@ static int ssi_flush(struct hsi_client *cl)
pinctrl_pm_select_default_state(omap_port->pdev);
spin_unlock_bh(&omap_port->lock);
- pm_runtime_put_sync(omap_port->pdev);
+ pm_runtime_mark_last_busy(omap_port->pdev);
+ pm_runtime_put_autosuspend(omap_port->pdev);
return 0;
}
+static void start_tx_work(struct work_struct *work)
+{
+ struct omap_ssi_port *omap_port =
+ container_of(work, struct omap_ssi_port, work);
+ struct hsi_port *port = to_hsi_port(omap_port->dev);
+ struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
+ struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
+
+ pm_runtime_get_sync(omap_port->pdev); /* Grab clocks */
+ writel(SSI_WAKE(0), omap_ssi->sys + SSI_SET_WAKE_REG(port->num));
+}
+
static int ssi_start_tx(struct hsi_client *cl)
{
struct hsi_port *port = hsi_get_port(cl);
struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
- struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
- struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
dev_dbg(&port->device, "Wake out high %d\n", omap_port->wk_refcount);
@@ -581,10 +628,10 @@ static int ssi_start_tx(struct hsi_client *cl)
spin_unlock_bh(&omap_port->wk_lock);
return 0;
}
- pm_runtime_get_sync(omap_port->pdev); /* Grab clocks */
- writel(SSI_WAKE(0), omap_ssi->sys + SSI_SET_WAKE_REG(port->num));
spin_unlock_bh(&omap_port->wk_lock);
+ schedule_work(&omap_port->work);
+
return 0;
}
@@ -604,9 +651,12 @@ static int ssi_stop_tx(struct hsi_client *cl)
return 0;
}
writel(SSI_WAKE(0), omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num));
- pm_runtime_put_sync(omap_port->pdev); /* Release clocks */
spin_unlock_bh(&omap_port->wk_lock);
+ pm_runtime_mark_last_busy(omap_port->pdev);
+ pm_runtime_put_autosuspend(omap_port->pdev); /* Release clocks */
+
+
return 0;
}
@@ -616,6 +666,7 @@ static void ssi_transfer(struct omap_ssi_port *omap_port,
struct hsi_msg *msg;
int err = -1;
+ pm_runtime_get(omap_port->pdev);
spin_lock_bh(&omap_port->lock);
while (err < 0) {
err = ssi_start_transfer(queue);
@@ -630,6 +681,8 @@ static void ssi_transfer(struct omap_ssi_port *omap_port,
}
}
spin_unlock_bh(&omap_port->lock);
+ pm_runtime_mark_last_busy(omap_port->pdev);
+ pm_runtime_put_autosuspend(omap_port->pdev);
}
static void ssi_cleanup_queues(struct hsi_client *cl)
@@ -658,7 +711,8 @@ static void ssi_cleanup_queues(struct hsi_client *cl)
txbufstate |= (1 << i);
status |= SSI_DATAACCEPT(i);
/* Release the clocks writes, also GDD ones */
- pm_runtime_put_sync(omap_port->pdev);
+ pm_runtime_mark_last_busy(omap_port->pdev);
+ pm_runtime_put_autosuspend(omap_port->pdev);
}
ssi_flush_queue(&omap_port->txqueue[i], cl);
}
@@ -712,8 +766,10 @@ static void ssi_cleanup_gdd(struct hsi_controller *ssi, struct hsi_client *cl)
* Clock references for write will be handled in
* ssi_cleanup_queues
*/
- if (msg->ttype == HSI_MSG_READ)
- pm_runtime_put_sync(omap_port->pdev);
+ if (msg->ttype == HSI_MSG_READ) {
+ pm_runtime_mark_last_busy(omap_port->pdev);
+ pm_runtime_put_autosuspend(omap_port->pdev);
+ }
omap_ssi->gdd_trn[i].msg = NULL;
}
tmp = readl_relaxed(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG);
@@ -738,32 +794,30 @@ static int ssi_release(struct hsi_client *cl)
struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
- spin_lock_bh(&omap_port->lock);
pm_runtime_get_sync(omap_port->pdev);
+ spin_lock_bh(&omap_port->lock);
/* Stop all the pending DMA requests for that client */
ssi_cleanup_gdd(ssi, cl);
/* Now cleanup all the queues */
ssi_cleanup_queues(cl);
- pm_runtime_put_sync(omap_port->pdev);
/* If it is the last client of the port, do extra checks and cleanup */
if (port->claimed <= 1) {
/*
* Drop the clock reference for the incoming wake line
* if it is still kept high by the other side.
*/
- if (omap_port->wkin_cken) {
+ if (test_and_clear_bit(SSI_WAKE_EN, &omap_port->flags))
pm_runtime_put_sync(omap_port->pdev);
- omap_port->wkin_cken = 0;
- }
- pm_runtime_get_sync(omap_port->pdev);
+ pm_runtime_get(omap_port->pdev);
/* Stop any SSI TX/RX without a client */
ssi_set_port_mode(omap_port, SSI_MODE_SLEEP);
omap_port->sst.mode = SSI_MODE_SLEEP;
omap_port->ssr.mode = SSI_MODE_SLEEP;
- pm_runtime_put_sync(omap_port->pdev);
+ pm_runtime_put(omap_port->pdev);
WARN_ON(omap_port->wk_refcount != 0);
}
spin_unlock_bh(&omap_port->lock);
+ pm_runtime_put_sync(omap_port->pdev);
return 0;
}
@@ -868,7 +922,7 @@ static void ssi_pio_complete(struct hsi_port *port, struct list_head *queue)
u32 reg;
u32 val;
- spin_lock(&omap_port->lock);
+ spin_lock_bh(&omap_port->lock);
msg = list_first_entry(queue, struct hsi_msg, link);
if ((!msg->sgt.nents) || (!msg->sgt.sgl->length)) {
msg->actual_len = 0;
@@ -900,7 +954,7 @@ static void ssi_pio_complete(struct hsi_port *port, struct list_head *queue)
(msg->ttype == HSI_MSG_WRITE))) {
writel(val, omap_ssi->sys +
SSI_MPU_STATUS_REG(port->num, 0));
- spin_unlock(&omap_port->lock);
+ spin_unlock_bh(&omap_port->lock);
return;
}
@@ -910,18 +964,19 @@ static void ssi_pio_complete(struct hsi_port *port, struct list_head *queue)
reg = readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
if (msg->ttype == HSI_MSG_WRITE) {
/* Release clocks for write transfer */
- pm_runtime_put_sync(omap_port->pdev);
+ pm_runtime_mark_last_busy(omap_port->pdev);
+ pm_runtime_put_autosuspend(omap_port->pdev);
}
reg &= ~val;
writel_relaxed(reg, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
writel_relaxed(val, omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0));
list_del(&msg->link);
- spin_unlock(&omap_port->lock);
+ spin_unlock_bh(&omap_port->lock);
msg->complete(msg);
ssi_transfer(omap_port, queue);
}
-static void ssi_pio_tasklet(unsigned long ssi_port)
+static irqreturn_t ssi_pio_thread(int irq, void *ssi_port)
{
struct hsi_port *port = (struct hsi_port *)ssi_port;
struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
@@ -932,41 +987,35 @@ static void ssi_pio_tasklet(unsigned long ssi_port)
u32 status_reg;
pm_runtime_get_sync(omap_port->pdev);
- status_reg = readl(sys + SSI_MPU_STATUS_REG(port->num, 0));
- status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0));
- for (ch = 0; ch < omap_port->channels; ch++) {
- if (status_reg & SSI_DATAACCEPT(ch))
- ssi_pio_complete(port, &omap_port->txqueue[ch]);
- if (status_reg & SSI_DATAAVAILABLE(ch))
- ssi_pio_complete(port, &omap_port->rxqueue[ch]);
- }
- if (status_reg & SSI_BREAKDETECTED)
- ssi_break_complete(port);
- if (status_reg & SSI_ERROROCCURED)
- ssi_error(port);
+ do {
+ status_reg = readl(sys + SSI_MPU_STATUS_REG(port->num, 0));
+ status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0));
- status_reg = readl(sys + SSI_MPU_STATUS_REG(port->num, 0));
- status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0));
- pm_runtime_put_sync(omap_port->pdev);
+ for (ch = 0; ch < omap_port->channels; ch++) {
+ if (status_reg & SSI_DATAACCEPT(ch))
+ ssi_pio_complete(port, &omap_port->txqueue[ch]);
+ if (status_reg & SSI_DATAAVAILABLE(ch))
+ ssi_pio_complete(port, &omap_port->rxqueue[ch]);
+ }
+ if (status_reg & SSI_BREAKDETECTED)
+ ssi_break_complete(port);
+ if (status_reg & SSI_ERROROCCURED)
+ ssi_error(port);
- if (status_reg)
- tasklet_hi_schedule(&omap_port->pio_tasklet);
- else
- enable_irq(omap_port->irq);
-}
+ status_reg = readl(sys + SSI_MPU_STATUS_REG(port->num, 0));
+ status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0));
-static irqreturn_t ssi_pio_isr(int irq, void *port)
-{
- struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
+ /* TODO: sleep if we retry? */
+ } while (status_reg);
- tasklet_hi_schedule(&omap_port->pio_tasklet);
- disable_irq_nosync(irq);
+ pm_runtime_mark_last_busy(omap_port->pdev);
+ pm_runtime_put_autosuspend(omap_port->pdev);
return IRQ_HANDLED;
}
-static void ssi_wake_tasklet(unsigned long ssi_port)
+static irqreturn_t ssi_wake_thread(int irq __maybe_unused, void *ssi_port)
{
struct hsi_port *port = (struct hsi_port *)ssi_port;
struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
@@ -981,12 +1030,8 @@ static void ssi_wake_tasklet(unsigned long ssi_port)
* This workaround will avoid breaking the clock reference
* count when such a situation ocurrs.
*/
- spin_lock(&omap_port->lock);
- if (!omap_port->wkin_cken) {
- omap_port->wkin_cken = 1;
+ if (!test_and_set_bit(SSI_WAKE_EN, &omap_port->flags))
pm_runtime_get_sync(omap_port->pdev);
- }
- spin_unlock(&omap_port->lock);
dev_dbg(&ssi->device, "Wake in high\n");
if (omap_port->wktest) { /* FIXME: HACK ! To be removed */
writel(SSI_WAKE(0),
@@ -1000,26 +1045,16 @@ static void ssi_wake_tasklet(unsigned long ssi_port)
omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num));
}
hsi_event(port, HSI_EVENT_STOP_RX);
- spin_lock(&omap_port->lock);
- if (omap_port->wkin_cken) {
- pm_runtime_put_sync(omap_port->pdev);
- omap_port->wkin_cken = 0;
+ if (test_and_clear_bit(SSI_WAKE_EN, &omap_port->flags)) {
+ pm_runtime_mark_last_busy(omap_port->pdev);
+ pm_runtime_put_autosuspend(omap_port->pdev);
}
- spin_unlock(&omap_port->lock);
}
-}
-
-static irqreturn_t ssi_wake_isr(int irq __maybe_unused, void *ssi_port)
-{
- struct omap_ssi_port *omap_port = hsi_port_drvdata(ssi_port);
-
- tasklet_hi_schedule(&omap_port->wake_tasklet);
return IRQ_HANDLED;
}
-static int ssi_port_irq(struct hsi_port *port,
- struct platform_device *pd)
+static int ssi_port_irq(struct hsi_port *port, struct platform_device *pd)
{
struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
int err;
@@ -1030,18 +1065,15 @@ static int ssi_port_irq(struct hsi_port *port,
return err;
}
omap_port->irq = err;
- tasklet_init(&omap_port->pio_tasklet, ssi_pio_tasklet,
- (unsigned long)port);
- err = devm_request_irq(&port->device, omap_port->irq, ssi_pio_isr,
- 0, "mpu_irq0", port);
+ err = devm_request_threaded_irq(&port->device, omap_port->irq, NULL,
+ ssi_pio_thread, IRQF_ONESHOT, "SSI PORT", port);
if (err < 0)
dev_err(&port->device, "Request IRQ %d failed (%d)\n",
omap_port->irq, err);
return err;
}
-static int ssi_wake_irq(struct hsi_port *port,
- struct platform_device *pd)
+static int ssi_wake_irq(struct hsi_port *port, struct platform_device *pd)
{
struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
int cawake_irq;
@@ -1053,13 +1085,12 @@ static int ssi_wake_irq(struct hsi_port *port,
}
cawake_irq = gpiod_to_irq(omap_port->wake_gpio);
-
omap_port->wake_irq = cawake_irq;
- tasklet_init(&omap_port->wake_tasklet, ssi_wake_tasklet,
- (unsigned long)port);
- err = devm_request_irq(&port->device, cawake_irq, ssi_wake_isr,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- "cawake", port);
+
+ err = devm_request_threaded_irq(&port->device, cawake_irq, NULL,
+ ssi_wake_thread,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "SSI cawake", port);
if (err < 0)
dev_err(&port->device, "Request Wake in IRQ %d failed %d\n",
cawake_irq, err);
@@ -1169,6 +1200,9 @@ static int ssi_port_probe(struct platform_device *pd)
omap_port->pdev = &pd->dev;
omap_port->port_id = port_id;
+ INIT_DEFERRABLE_WORK(&omap_port->errqueue_work, ssi_process_errqueue);
+ INIT_WORK(&omap_port->work, start_tx_work);
+
/* initialize HSI port */
port->async = ssi_async;
port->setup = ssi_setup;
@@ -1202,7 +1236,8 @@ static int ssi_port_probe(struct platform_device *pd)
spin_lock_init(&omap_port->wk_lock);
omap_port->dev = &port->device;
- pm_runtime_irq_safe(omap_port->pdev);
+ pm_runtime_use_autosuspend(omap_port->pdev);
+ pm_runtime_set_autosuspend_delay(omap_port->pdev, 250);
pm_runtime_enable(omap_port->pdev);
#ifdef CONFIG_DEBUG_FS
@@ -1234,10 +1269,9 @@ static int ssi_port_remove(struct platform_device *pd)
ssi_debug_remove_port(port);
#endif
- hsi_port_unregister_clients(port);
+ cancel_delayed_work_sync(&omap_port->errqueue_work);
- tasklet_kill(&omap_port->wake_tasklet);
- tasklet_kill(&omap_port->pio_tasklet);
+ hsi_port_unregister_clients(port);
port->async = hsi_dummy_msg;
port->setup = hsi_dummy_cl;
@@ -1248,6 +1282,8 @@ static int ssi_port_remove(struct platform_device *pd)
omap_ssi->port[omap_port->port_id] = NULL;
platform_set_drvdata(pd, NULL);
+
+ pm_runtime_dont_use_autosuspend(&pd->dev);
pm_runtime_disable(&pd->dev);
return 0;
diff --git a/drivers/hsi/hsi.c b/drivers/hsi/hsi_core.c
index df380d55c58f..c2a2a9795b0b 100644
--- a/drivers/hsi/hsi.c
+++ b/drivers/hsi/hsi_core.c
@@ -90,19 +90,19 @@ struct hsi_client *hsi_new_client(struct hsi_port *port,
cl->tx_cfg = info->tx_cfg;
if (cl->tx_cfg.channels) {
size = cl->tx_cfg.num_channels * sizeof(*cl->tx_cfg.channels);
- cl->tx_cfg.channels = kzalloc(size , GFP_KERNEL);
+ cl->tx_cfg.channels = kmemdup(info->tx_cfg.channels, size,
+ GFP_KERNEL);
if (!cl->tx_cfg.channels)
goto err_tx;
- memcpy(cl->tx_cfg.channels, info->tx_cfg.channels, size);
}
cl->rx_cfg = info->rx_cfg;
if (cl->rx_cfg.channels) {
size = cl->rx_cfg.num_channels * sizeof(*cl->rx_cfg.channels);
- cl->rx_cfg.channels = kzalloc(size , GFP_KERNEL);
+ cl->rx_cfg.channels = kmemdup(info->rx_cfg.channels, size,
+ GFP_KERNEL);
if (!cl->rx_cfg.channels)
goto err_rx;
- memcpy(cl->rx_cfg.channels, info->rx_cfg.channels, size);
}
cl->device.bus = &hsi_bus_type;
@@ -507,7 +507,7 @@ struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags)
port[i]->stop_tx = hsi_dummy_cl;
port[i]->release = hsi_dummy_cl;
mutex_init(&port[i]->lock);
- ATOMIC_INIT_NOTIFIER_HEAD(&port[i]->n_head);
+ BLOCKING_INIT_NOTIFIER_HEAD(&port[i]->n_head);
dev_set_name(&port[i]->device, "port%d", i);
hsi->port[i]->device.release = hsi_port_release;
device_initialize(&hsi->port[i]->device);
@@ -689,7 +689,7 @@ int hsi_register_port_event(struct hsi_client *cl,
cl->ehandler = handler;
cl->nb.notifier_call = hsi_event_notifier_call;
- return atomic_notifier_chain_register(&port->n_head, &cl->nb);
+ return blocking_notifier_chain_register(&port->n_head, &cl->nb);
}
EXPORT_SYMBOL_GPL(hsi_register_port_event);
@@ -709,7 +709,7 @@ int hsi_unregister_port_event(struct hsi_client *cl)
WARN_ON(!hsi_port_claimed(cl));
- err = atomic_notifier_chain_unregister(&port->n_head, &cl->nb);
+ err = blocking_notifier_chain_unregister(&port->n_head, &cl->nb);
if (!err)
cl->ehandler = NULL;
@@ -734,7 +734,7 @@ EXPORT_SYMBOL_GPL(hsi_unregister_port_event);
*/
int hsi_event(struct hsi_port *port, unsigned long event)
{
- return atomic_notifier_call_chain(&port->n_head, event, NULL);
+ return blocking_notifier_call_chain(&port->n_head, event, NULL);
}
EXPORT_SYMBOL_GPL(hsi_event);
diff --git a/include/linux/hsi/hsi.h b/include/linux/hsi/hsi.h
index 2790591c77cf..57402544b53f 100644
--- a/include/linux/hsi/hsi.h
+++ b/include/linux/hsi/hsi.h
@@ -246,7 +246,7 @@ struct hsi_port {
int (*stop_tx)(struct hsi_client *cl);
int (*release)(struct hsi_client *cl);
/* private */
- struct atomic_notifier_head n_head;
+ struct blocking_notifier_head n_head;
};
#define to_hsi_port(dev) container_of(dev, struct hsi_port, device)