mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
HSI changes for the v4.8 series
* proper runtime pm support for omap-ssi and ssi-protocol * misc. fixes -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCgAGBQJXlnMYAAoJENju1/PIO/qaLnwP/3BTdRGsAS/8UtwWG5ZJ3Xrz AdYCKfgrOPoNj97ba95Dd3aGbrQGkTOmx6UTkYM1SO9KNN/VwoeQuV5DnWRZlF9k vgH5lUh3L2AHbpOywEuQF/0MAiZXGguqPTfI3hx66k6t7vMYxq8VuahtZ69+ICPI TSG+JXYVMpdjcIMIQrmH4CC2R1G7IsKNnQpaPDCAyIfE0WsiOkCUIlB4OuLlHemT TK5BggWQYlnKN/bbIC/dx6ME5ZWPv9pXLUNzwUqH8C8G9wdxeXj2PTEt/ZZrw8dT tskFYXno7tUuU4btR5OkgrpZLHybXzMIQS2ZSFKE8oUvu0hymQLgGGuucWg1oTVG 1kkat8Hyz49xv+YriiMbaj0XKO0WWLBcjOduexPho1RUKV95MsnMz6e55eXq2ptn ucCb71lap7Q9I4MJjntvLXaIfFlyQR+6FEyo4RVwvCy44HSsPEhHeXxHN6hIeQDB BFTOADy7RCT9B6KSuSItW5H1oT1jESY26TYp+NvWC+p6kz3mexxlO1gVDoln3NRB opktOsusml7MNr+3XBNap3CsHPKm2X8azpkWsikN9QbRrjeH/AxWhjWrVruw8Yhu 6qe2u46vh4yI0YexE8CoYqj71Em/V2I6EkCq7cuoLQsBZew9i1kT/8qO69kP9BFV djK5b44/mAZJmC0d8t3/ =WPyp -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
8448cefe49
@ -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">
|
||||
|
@ -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/
|
||||
|
@ -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/
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user