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:
Linus Torvalds 2016-07-27 15:18:53 -07:00
commit 8448cefe49
10 changed files with 239 additions and 190 deletions

View File

@ -484,7 +484,7 @@ X!Ilib/fonts/fonts.c
</para> </para>
!Iinclude/linux/hsi/hsi.h !Iinclude/linux/hsi/hsi.h
!Edrivers/hsi/hsi.c !Edrivers/hsi/hsi_core.c
</chapter> </chapter>
<chapter id="pwm"> <chapter id="pwm">

View File

@ -78,7 +78,7 @@ obj-$(CONFIG_TARGET_CORE) += target/
obj-$(CONFIG_MTD) += mtd/ obj-$(CONFIG_MTD) += mtd/
obj-$(CONFIG_SPI) += spi/ obj-$(CONFIG_SPI) += spi/
obj-$(CONFIG_SPMI) += spmi/ obj-$(CONFIG_SPMI) += spmi/
obj-y += hsi/ obj-$(CONFIG_HSI) += hsi/
obj-y += net/ obj-y += net/
obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_ATM) += atm/
obj-$(CONFIG_FUSION) += message/ obj-$(CONFIG_FUSION) += message/

View File

@ -1,7 +1,8 @@
# #
# Makefile for HSI # Makefile for HSI
# #
obj-$(CONFIG_HSI_BOARDINFO) += hsi_boardinfo.o
obj-$(CONFIG_HSI) += hsi.o obj-$(CONFIG_HSI) += hsi.o
hsi-objs := hsi_core.o
hsi-$(CONFIG_HSI_BOARDINFO) += hsi_boardinfo.o
obj-y += controllers/ obj-y += controllers/
obj-y += clients/ obj-y += clients/

View File

@ -444,8 +444,8 @@ static void cs_hsi_read_on_control_complete(struct hsi_msg *msg)
hi->control_state &= ~SSI_CHANNEL_STATE_READING; hi->control_state &= ~SSI_CHANNEL_STATE_READING;
if (msg->status == HSI_STATUS_ERROR) { if (msg->status == HSI_STATUS_ERROR) {
dev_err(&hi->cl->device, "Control RX error detected\n"); dev_err(&hi->cl->device, "Control RX error detected\n");
cs_hsi_control_read_error(hi, msg);
spin_unlock(&hi->lock); spin_unlock(&hi->lock);
cs_hsi_control_read_error(hi, msg);
goto out; goto out;
} }
dev_dbg(&hi->cl->device, "Read on control: %08X\n", cmd); 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) if (vma->vm_end < vma->vm_start)
return -EINVAL; return -EINVAL;
if (((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) != 1) if (vma_pages(vma) != 1)
return -EINVAL; return -EINVAL;
vma->vm_flags |= VM_IO | VM_DONTDUMP | VM_DONTEXPAND; vma->vm_flags |= VM_IO | VM_DONTDUMP | VM_DONTEXPAND;

View File

@ -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_READY_CMD SSIP_CMD(SSIP_READY, 0)
#define SSIP_SWBREAK_CMD SSIP_CMD(SSIP_SW_BREAK, 0) #define SSIP_SWBREAK_CMD SSIP_CMD(SSIP_SW_BREAK, 0)
#define SSIP_WAKETEST_FLAG 0
/* Main state machine states */ /* Main state machine states */
enum { enum {
INIT, INIT,
@ -116,7 +118,7 @@ enum {
* @main_state: Main state machine * @main_state: Main state machine
* @send_state: TX state machine * @send_state: TX state machine
* @recv_state: RX 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 * @rxid: RX data id
* @txid: TX data id * @txid: TX data id
* @txqueue_len: TX queue length * @txqueue_len: TX queue length
@ -137,7 +139,7 @@ struct ssi_protocol {
unsigned int main_state; unsigned int main_state;
unsigned int send_state; unsigned int send_state;
unsigned int recv_state; unsigned int recv_state;
unsigned int waketest:1; unsigned long flags;
u8 rxid; u8 rxid;
u8 txid; u8 txid;
unsigned int txqueue_len; unsigned int txqueue_len;
@ -148,6 +150,7 @@ struct ssi_protocol {
struct net_device *netdev; struct net_device *netdev;
struct list_head txqueue; struct list_head txqueue;
struct list_head cmdqueue; struct list_head cmdqueue;
struct work_struct work;
struct hsi_client *cl; struct hsi_client *cl;
struct list_head link; struct list_head link;
atomic_t tx_usecnt; atomic_t tx_usecnt;
@ -405,15 +408,17 @@ static void ssip_reset(struct hsi_client *cl)
spin_lock_bh(&ssi->lock); spin_lock_bh(&ssi->lock);
if (ssi->send_state != SEND_IDLE) if (ssi->send_state != SEND_IDLE)
hsi_stop_tx(cl); hsi_stop_tx(cl);
if (ssi->waketest) spin_unlock_bh(&ssi->lock);
ssi_waketest(cl, 0); 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->rx_wd);
del_timer(&ssi->tx_wd); del_timer(&ssi->tx_wd);
del_timer(&ssi->keep_alive); del_timer(&ssi->keep_alive);
ssi->main_state = 0; ssi->main_state = 0;
ssi->send_state = 0; ssi->send_state = 0;
ssi->recv_state = 0; ssi->recv_state = 0;
ssi->waketest = 0; ssi->flags = 0;
ssi->rxid = 0; ssi->rxid = 0;
ssi->txid = 0; ssi->txid = 0;
list_for_each_safe(head, tmp, &ssi->txqueue) { 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, "Send state: %d\n", ssi->send_state);
dev_err(&cl->device, "CMT %s\n", (ssi->main_state == ACTIVE) ? dev_err(&cl->device, "CMT %s\n", (ssi->main_state == ACTIVE) ?
"Online" : "Offline"); "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 RX id: %d\n", ssi->rxid);
dev_err(&cl->device, "Data TX id: %d\n", ssi->txid); 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, dev_dbg(&cl->device, "RX start M(%d) R(%d)\n", ssi->main_state,
ssi->recv_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 * 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. * high transition. Therefore we need to ignore the sencond UP event.
*/ */
if ((ssi->main_state != ACTIVE) || (ssi->recv_state == RECV_READY)) { if ((ssi->main_state != ACTIVE) || (ssi->recv_state == RECV_READY)) {
spin_unlock(&ssi->lock); spin_unlock_bh(&ssi->lock);
return; return;
} }
ssip_set_rxstate(ssi, RECV_READY); ssip_set_rxstate(ssi, RECV_READY);
spin_unlock(&ssi->lock); spin_unlock_bh(&ssi->lock);
msg = ssip_claim_cmd(ssi); msg = ssip_claim_cmd(ssi);
ssip_set_cmd(msg, SSIP_READY_CMD); 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); struct ssi_protocol *ssi = hsi_client_drvdata(cl);
dev_dbg(&cl->device, "RX stop M(%d)\n", ssi->main_state); 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)) if (likely(ssi->main_state == ACTIVE))
ssip_set_rxstate(ssi, RECV_IDLE); ssip_set_rxstate(ssi, RECV_IDLE);
spin_unlock(&ssi->lock); spin_unlock_bh(&ssi->lock);
} }
static void ssip_free_strans(struct hsi_msg *msg) 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; data = msg->context;
ssip_release_cmd(msg); ssip_release_cmd(msg);
spin_lock(&ssi->lock); spin_lock_bh(&ssi->lock);
ssip_set_txstate(ssi, SENDING); ssip_set_txstate(ssi, SENDING);
spin_unlock(&ssi->lock); spin_unlock_bh(&ssi->lock);
hsi_async_write(cl, data); hsi_async_write(cl, data);
} }
@ -666,15 +672,17 @@ static void ssip_rx_bootinforeq(struct hsi_client *cl, u32 cmd)
/* Fall through */ /* Fall through */
case INIT: case INIT:
case HANDSHAKE: case HANDSHAKE:
spin_lock(&ssi->lock); spin_lock_bh(&ssi->lock);
ssi->main_state = HANDSHAKE; ssi->main_state = HANDSHAKE;
if (!ssi->waketest) { spin_unlock_bh(&ssi->lock);
ssi->waketest = 1;
if (!test_and_set_bit(SSIP_WAKETEST_FLAG, &ssi->flags))
ssi_waketest(cl, 1); /* FIXME: To be removed */ ssi_waketest(cl, 1); /* FIXME: To be removed */
}
spin_lock_bh(&ssi->lock);
/* Start boot handshake watchdog */ /* Start boot handshake watchdog */
mod_timer(&ssi->tx_wd, jiffies + msecs_to_jiffies(SSIP_WDTOUT)); 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"); dev_dbg(&cl->device, "Send BOOTINFO_RESP\n");
if (SSIP_DATA_VERSION(cmd) != SSIP_LOCAL_VERID) if (SSIP_DATA_VERSION(cmd) != SSIP_LOCAL_VERID)
dev_warn(&cl->device, "boot info req verid mismatch\n"); 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) if (SSIP_DATA_VERSION(cmd) != SSIP_LOCAL_VERID)
dev_warn(&cl->device, "boot info resp verid mismatch\n"); dev_warn(&cl->device, "boot info resp verid mismatch\n");
spin_lock(&ssi->lock); spin_lock_bh(&ssi->lock);
if (ssi->main_state != ACTIVE) if (ssi->main_state != ACTIVE)
/* Use tx_wd as a boot watchdog in non ACTIVE state */ /* Use tx_wd as a boot watchdog in non ACTIVE state */
mod_timer(&ssi->tx_wd, jiffies + msecs_to_jiffies(SSIP_WDTOUT)); mod_timer(&ssi->tx_wd, jiffies + msecs_to_jiffies(SSIP_WDTOUT));
else else
dev_dbg(&cl->device, "boot info resp ignored M(%d)\n", dev_dbg(&cl->device, "boot info resp ignored M(%d)\n",
ssi->main_state); ssi->main_state);
spin_unlock(&ssi->lock); spin_unlock_bh(&ssi->lock);
} }
static void ssip_rx_waketest(struct hsi_client *cl, u32 cmd) 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); struct ssi_protocol *ssi = hsi_client_drvdata(cl);
unsigned int wkres = SSIP_PAYLOAD(cmd); unsigned int wkres = SSIP_PAYLOAD(cmd);
spin_lock(&ssi->lock); spin_lock_bh(&ssi->lock);
if (ssi->main_state != HANDSHAKE) { if (ssi->main_state != HANDSHAKE) {
dev_dbg(&cl->device, "wake lines test ignored M(%d)\n", dev_dbg(&cl->device, "wake lines test ignored M(%d)\n",
ssi->main_state); ssi->main_state);
spin_unlock(&ssi->lock); spin_unlock_bh(&ssi->lock);
return; return;
} }
if (ssi->waketest) { spin_unlock_bh(&ssi->lock);
ssi->waketest = 0;
if (test_and_clear_bit(SSIP_WAKETEST_FLAG, &ssi->flags))
ssi_waketest(cl, 0); /* FIXME: To be removed */ ssi_waketest(cl, 0); /* FIXME: To be removed */
}
spin_lock_bh(&ssi->lock);
ssi->main_state = ACTIVE; ssi->main_state = ACTIVE;
del_timer(&ssi->tx_wd); /* Stop boot handshake timer */ 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", dev_notice(&cl->device, "WAKELINES TEST %s\n",
wkres & SSIP_WAKETEST_FAILED ? "FAILED" : "OK"); 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); struct ssi_protocol *ssi = hsi_client_drvdata(cl);
spin_lock(&ssi->lock); spin_lock_bh(&ssi->lock);
if (unlikely(ssi->main_state != ACTIVE)) { if (unlikely(ssi->main_state != ACTIVE)) {
dev_dbg(&cl->device, "READY on wrong state: S(%d) M(%d)\n", dev_dbg(&cl->device, "READY on wrong state: S(%d) M(%d)\n",
ssi->send_state, ssi->main_state); ssi->send_state, ssi->main_state);
spin_unlock(&ssi->lock); spin_unlock_bh(&ssi->lock);
return; return;
} }
if (ssi->send_state != WAIT4READY) { if (ssi->send_state != WAIT4READY) {
dev_dbg(&cl->device, "Ignore spurious READY command\n"); dev_dbg(&cl->device, "Ignore spurious READY command\n");
spin_unlock(&ssi->lock); spin_unlock_bh(&ssi->lock);
return; return;
} }
ssip_set_txstate(ssi, SEND_READY); ssip_set_txstate(ssi, SEND_READY);
spin_unlock(&ssi->lock); spin_unlock_bh(&ssi->lock);
ssip_xmit(cl); ssip_xmit(cl);
} }
@ -766,22 +776,22 @@ static void ssip_rx_strans(struct hsi_client *cl, u32 cmd)
int len = SSIP_PDU_LENGTH(cmd); int len = SSIP_PDU_LENGTH(cmd);
dev_dbg(&cl->device, "RX strans: %d frames\n", len); 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)) { if (unlikely(ssi->main_state != ACTIVE)) {
dev_err(&cl->device, "START TRANS wrong state: S(%d) M(%d)\n", dev_err(&cl->device, "START TRANS wrong state: S(%d) M(%d)\n",
ssi->send_state, ssi->main_state); ssi->send_state, ssi->main_state);
spin_unlock(&ssi->lock); spin_unlock_bh(&ssi->lock);
return; return;
} }
ssip_set_rxstate(ssi, RECEIVING); ssip_set_rxstate(ssi, RECEIVING);
if (unlikely(SSIP_MSG_ID(cmd) != ssi->rxid)) { if (unlikely(SSIP_MSG_ID(cmd) != ssi->rxid)) {
dev_err(&cl->device, "START TRANS id %d expected %d\n", dev_err(&cl->device, "START TRANS id %d expected %d\n",
SSIP_MSG_ID(cmd), ssi->rxid); SSIP_MSG_ID(cmd), ssi->rxid);
spin_unlock(&ssi->lock); spin_unlock_bh(&ssi->lock);
goto out1; goto out1;
} }
ssi->rxid++; ssi->rxid++;
spin_unlock(&ssi->lock); spin_unlock_bh(&ssi->lock);
skb = netdev_alloc_skb(ssi->netdev, len * 4); skb = netdev_alloc_skb(ssi->netdev, len * 4);
if (unlikely(!skb)) { if (unlikely(!skb)) {
dev_err(&cl->device, "No memory for rx skb\n"); 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); struct ssi_protocol *ssi = hsi_client_drvdata(cl);
ssip_release_cmd(msg); ssip_release_cmd(msg);
spin_lock(&ssi->lock); spin_lock_bh(&ssi->lock);
if (list_empty(&ssi->txqueue)) { if (list_empty(&ssi->txqueue)) {
if (atomic_read(&ssi->tx_usecnt)) { if (atomic_read(&ssi->tx_usecnt)) {
ssip_set_txstate(ssi, SEND_READY); 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); ssip_set_txstate(ssi, SEND_IDLE);
hsi_stop_tx(cl); hsi_stop_tx(cl);
} }
spin_unlock(&ssi->lock); spin_unlock_bh(&ssi->lock);
} else { } else {
spin_unlock(&ssi->lock); spin_unlock_bh(&ssi->lock);
ssip_xmit(cl); ssip_xmit(cl);
} }
netif_wake_queue(ssi->netdev); netif_wake_queue(ssi->netdev);
@ -876,17 +886,17 @@ static void ssip_tx_data_complete(struct hsi_msg *msg)
ssip_error(cl); ssip_error(cl);
goto out; goto out;
} }
spin_lock(&ssi->lock); spin_lock_bh(&ssi->lock);
if (list_empty(&ssi->txqueue)) { if (list_empty(&ssi->txqueue)) {
ssip_set_txstate(ssi, SENDING_SWBREAK); ssip_set_txstate(ssi, SENDING_SWBREAK);
spin_unlock(&ssi->lock); spin_unlock_bh(&ssi->lock);
cmsg = ssip_claim_cmd(ssi); cmsg = ssip_claim_cmd(ssi);
ssip_set_cmd(cmsg, SSIP_SWBREAK_CMD); ssip_set_cmd(cmsg, SSIP_SWBREAK_CMD);
cmsg->complete = ssip_swbreak_complete; cmsg->complete = ssip_swbreak_complete;
dev_dbg(&cl->device, "Send SWBREAK\n"); dev_dbg(&cl->device, "Send SWBREAK\n");
hsi_async_write(cl, cmsg); hsi_async_write(cl, cmsg);
} else { } else {
spin_unlock(&ssi->lock); spin_unlock_bh(&ssi->lock);
ssip_xmit(cl); ssip_xmit(cl);
} }
out: out:
@ -926,11 +936,11 @@ static int ssip_pn_open(struct net_device *dev)
} }
dev_dbg(&cl->device, "Configuring SSI port\n"); dev_dbg(&cl->device, "Configuring SSI port\n");
hsi_setup(cl); hsi_setup(cl);
spin_lock_bh(&ssi->lock);
if (!ssi->waketest) { if (!test_and_set_bit(SSIP_WAKETEST_FLAG, &ssi->flags))
ssi->waketest = 1;
ssi_waketest(cl, 1); /* FIXME: To be removed */ ssi_waketest(cl, 1); /* FIXME: To be removed */
}
spin_lock_bh(&ssi->lock);
ssi->main_state = HANDSHAKE; ssi->main_state = HANDSHAKE;
spin_unlock_bh(&ssi->lock); spin_unlock_bh(&ssi->lock);
@ -959,6 +969,15 @@ static int ssip_pn_set_mtu(struct net_device *dev, int new_mtu)
return 0; 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) static int ssip_pn_xmit(struct sk_buff *skb, struct net_device *dev)
{ {
struct hsi_client *cl = to_hsi_client(dev->dev.parent); 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", dev_dbg(&cl->device, "Start TX on SEND READY qlen %d\n",
ssi->txqueue_len); ssi->txqueue_len);
spin_unlock_bh(&ssi->lock); spin_unlock_bh(&ssi->lock);
ssip_xmit(cl); schedule_work(&ssi->work);
} else { } else {
spin_unlock_bh(&ssi->lock); spin_unlock_bh(&ssi->lock);
} }
@ -1088,6 +1107,7 @@ static int ssi_protocol_probe(struct device *dev)
atomic_set(&ssi->tx_usecnt, 0); atomic_set(&ssi->tx_usecnt, 0);
hsi_client_set_drvdata(cl, ssi); hsi_client_set_drvdata(cl, ssi);
ssi->cl = cl; ssi->cl = cl;
INIT_WORK(&ssi->work, ssip_xmit_work);
ssi->channel_id_cmd = hsi_get_channel_id_by_name(cl, "mcsaab-control"); ssi->channel_id_cmd = hsi_get_channel_id_by_name(cl, "mcsaab-control");
if (ssi->channel_id_cmd < 0) { if (ssi->channel_id_cmd < 0) {

View File

@ -35,6 +35,8 @@
#define SSI_MAX_GDD_LCH 8 #define SSI_MAX_GDD_LCH 8
#define SSI_BYTES_TO_FRAMES(x) ((((x) - 1) >> 2) + 1) #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 * struct omap_ssm_ctx - OMAP synchronous serial module (TX/RX) context
* @mode: Bit transmission mode * @mode: Bit transmission mode
@ -71,13 +73,14 @@ struct omap_ssm_ctx {
* @txqueue: TX message queues * @txqueue: TX message queues
* @rxqueue: RX message queues * @rxqueue: RX message queues
* @brkqueue: Queue of incoming HWBREAK requests (FRAME mode) * @brkqueue: Queue of incoming HWBREAK requests (FRAME mode)
* @errqueue: Queue for failed messages
* @errqueue_work: Delayed Work for failed messages
* @irq: IRQ number * @irq: IRQ number
* @wake_irq: IRQ number for incoming wake line (-1 if none) * @wake_irq: IRQ number for incoming wake line (-1 if none)
* @wake_gpio: GPIO 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 * @flags: flags to keep track of states
* @wake_tasklet: Bottom half for incoming wake events
* @wkin_cken: Keep track of clock references due to the incoming wake line
* @wk_refcount: Reference count for output wake line * @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 * @sys_mpu_enable: Context for the interrupt enable register for irq 0
* @sst: Context for the synchronous serial transmitter * @sst: Context for the synchronous serial transmitter
* @ssr: Context for the synchronous serial receiver * @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 txqueue[SSI_MAX_CHANNELS];
struct list_head rxqueue[SSI_MAX_CHANNELS]; struct list_head rxqueue[SSI_MAX_CHANNELS];
struct list_head brkqueue; struct list_head brkqueue;
struct list_head errqueue;
struct delayed_work errqueue_work;
unsigned int irq; unsigned int irq;
int wake_irq; int wake_irq;
struct gpio_desc *wake_gpio; struct gpio_desc *wake_gpio;
struct tasklet_struct pio_tasklet;
struct tasklet_struct wake_tasklet;
bool wktest:1; /* FIXME: HACK to be removed */ bool wktest:1; /* FIXME: HACK to be removed */
bool wkin_cken:1; /* Workaround */ unsigned long flags;
unsigned int wk_refcount; unsigned int wk_refcount;
struct work_struct work;
/* OMAP SSI port context */ /* OMAP SSI port context */
u32 sys_mpu_enable; /* We use only one irq */ u32 sys_mpu_enable; /* We use only one irq */
struct omap_ssm_ctx sst; struct omap_ssm_ctx sst;
@ -138,7 +142,6 @@ struct gdd_trn {
* @fck_rate: clock rate * @fck_rate: clock rate
* @loss_count: To follow if we need to restore context or not * @loss_count: To follow if we need to restore context or not
* @max_speed: Maximum TX speed (Kb/s) set by the clients. * @max_speed: Maximum TX speed (Kb/s) set by the clients.
* @sysconfig: SSI controller saved context
* @gdd_gcr: SSI GDD saved context * @gdd_gcr: SSI GDD saved context
* @get_loss: Pointer to omap_pm_get_dev_context_loss_count, if any * @get_loss: Pointer to omap_pm_get_dev_context_loss_count, if any
* @port: Array of pointers of the ports of the controller * @port: Array of pointers of the ports of the controller
@ -158,7 +161,6 @@ struct omap_ssi_controller {
u32 loss_count; u32 loss_count;
u32 max_speed; u32 max_speed;
/* OMAP SSI Controller context */ /* OMAP SSI Controller context */
u32 sysconfig;
u32 gdd_gcr; u32 gdd_gcr;
int (*get_loss)(struct device *dev); int (*get_loss)(struct device *dev);
struct omap_ssi_port **port; struct omap_ssi_port **port;

View File

@ -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, "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, "SYSCONFIG\t: 0x%08x\n", readl(sys + SSI_SYSCONFIG_REG));
seq_printf(m, "SYSSTATUS\t: 0x%08x\n", readl(sys + SSI_SYSSTATUS_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; 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))); readw(gdd + SSI_GDD_CLNK_CTRL_REG(lch)));
} }
pm_runtime_put_sync(ssi->device.parent); pm_runtime_put(ssi->device.parent);
return 0; return 0;
} }
@ -193,7 +193,7 @@ void ssi_waketest(struct hsi_client *cl, unsigned int enable)
} else { } else {
writel_relaxed(SSI_WAKE(0), writel_relaxed(SSI_WAKE(0),
omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num)); 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); 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) { if (msg->ttype == HSI_MSG_READ) {
dir = DMA_FROM_DEVICE; dir = DMA_FROM_DEVICE;
val = SSI_DATAAVAILABLE(msg->channel); val = SSI_DATAAVAILABLE(msg->channel);
pm_runtime_put_sync(ssi->device.parent); pm_runtime_put(omap_port->pdev);
} else { } else {
dir = DMA_TO_DEVICE; dir = DMA_TO_DEVICE;
val = SSI_DATAACCEPT(msg->channel); 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); spin_lock(&omap_port->lock);
list_del(&msg->link); /* Dequeue msg */ list_del(&msg->link); /* Dequeue msg */
spin_unlock(&omap_port->lock); 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; return;
} }
spin_lock(&omap_port->lock); spin_lock(&omap_port->lock);
@ -255,7 +257,13 @@ static void ssi_gdd_tasklet(unsigned long dev)
unsigned int lch; unsigned int lch;
u32 status_reg; 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); status_reg = readl(sys + SSI_GDD_MPU_IRQ_STATUS_REG);
for (lch = 0; lch < SSI_MAX_GDD_LCH; lch++) { 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); writel_relaxed(status_reg, sys + SSI_GDD_MPU_IRQ_STATUS_REG);
status_reg = readl(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) if (status_reg)
tasklet_hi_schedule(&omap_ssi->gdd_tasklet); tasklet_hi_schedule(&omap_ssi->gdd_tasklet);
@ -312,7 +320,7 @@ static int ssi_clk_event(struct notifier_block *nb, unsigned long event,
continue; continue;
/* Workaround for SWBREAK + CAwake down race in CMT */ /* Workaround for SWBREAK + CAwake down race in CMT */
tasklet_disable(&omap_port->wake_tasklet); disable_irq(omap_port->wake_irq);
/* stop all ssi communication */ /* stop all ssi communication */
pinctrl_pm_select_idle_state(omap_port->pdev); 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 */ /* resume ssi communication */
pinctrl_pm_select_default_state(omap_port->pdev); pinctrl_pm_select_default_state(omap_port->pdev);
tasklet_enable(&omap_port->wake_tasklet); enable_irq(omap_port->wake_irq);
} }
break; break;
@ -452,8 +460,6 @@ out_err:
static int ssi_hw_init(struct hsi_controller *ssi) static int ssi_hw_init(struct hsi_controller *ssi)
{ {
struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
unsigned int i;
u32 val;
int err; int err;
err = pm_runtime_get_sync(ssi->device.parent); 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); dev_err(&ssi->device, "runtime PM failed %d\n", err);
return 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 */ /* Reseting GDD */
writel_relaxed(SSI_SWRESET, omap_ssi->gdd + SSI_GDD_GRST_REG); writel_relaxed(SSI_SWRESET, omap_ssi->gdd + SSI_GDD_GRST_REG);
/* Get FCK rate in KHz */ /* Get FCK rate in KHz */
omap_ssi->fck_rate = DIV_ROUND_CLOSEST(ssi_get_clk_rate(ssi), 1000); 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); 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); writel_relaxed(SSI_CLK_AUTOGATING_ON, omap_ssi->sys + SSI_GDD_GCR_REG);
omap_ssi->gdd_gcr = SSI_CLK_AUTOGATING_ON; omap_ssi->gdd_gcr = SSI_CLK_AUTOGATING_ON;
pm_runtime_put_sync(ssi->device.parent); pm_runtime_put_sync(ssi->device.parent);
@ -552,7 +543,6 @@ static int ssi_probe(struct platform_device *pd)
if (err < 0) if (err < 0)
goto out1; goto out1;
pm_runtime_irq_safe(&pd->dev);
pm_runtime_enable(&pd->dev); pm_runtime_enable(&pd->dev);
err = ssi_hw_init(ssi); err = ssi_hw_init(ssi);

View File

@ -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, seq_printf(m, "BUFFER_CH%d\t: 0x%08x\n", ch,
readl(base + SSI_SSR_BUFFER_CH_REG(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; return 0;
} }
@ -150,7 +150,7 @@ static int ssi_div_get(void *data, u64 *val)
pm_runtime_get_sync(omap_port->pdev); pm_runtime_get_sync(omap_port->pdev);
*val = readl(omap_port->sst_base + SSI_SST_DIVISOR_REG); *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; return 0;
} }
@ -166,7 +166,7 @@ static int ssi_div_set(void *data, u64 val)
pm_runtime_get_sync(omap_port->pdev); pm_runtime_get_sync(omap_port->pdev);
writel(val, omap_port->sst_base + SSI_SST_DIVISOR_REG); writel(val, omap_port->sst_base + SSI_SST_DIVISOR_REG);
omap_port->sst.divisor = val; omap_port->sst.divisor = val;
pm_runtime_put_sync(omap_port->pdev); pm_runtime_put_autosuspend(omap_port->pdev);
return 0; return 0;
} }
@ -193,6 +193,21 @@ static int ssi_debug_add_port(struct omap_ssi_port *omap_port,
} }
#endif #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) 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 d_addr;
u32 tmp; 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) { if (msg->ttype == HSI_MSG_READ) {
err = dma_map_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents, err = dma_map_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents,
DMA_FROM_DEVICE); DMA_FROM_DEVICE);
if (err < 0) { if (err < 0) {
dev_dbg(&ssi->device, "DMA map SG failed !\n"); dev_dbg(&ssi->device, "DMA map SG failed !\n");
pm_runtime_put_autosuspend(omap_port->pdev);
return err; return err;
} }
csdp = SSI_DST_BURST_4x32_BIT | SSI_DST_MEMORY_PORT | 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); DMA_TO_DEVICE);
if (err < 0) { if (err < 0) {
dev_dbg(&ssi->device, "DMA map SG failed !\n"); dev_dbg(&ssi->device, "DMA map SG failed !\n");
pm_runtime_put_autosuspend(omap_port->pdev);
return err; return err;
} }
csdp = SSI_SRC_BURST_4x32_BIT | SSI_SRC_MEMORY_PORT | 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", dev_dbg(&ssi->device, "lch %d cdsp %08x ccr %04x s_addr %08x d_addr %08x\n",
lch, csdp, ccr, s_addr, d_addr); 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(csdp, gdd + SSI_GDD_CSDP_REG(lch));
writew_relaxed(SSI_BLOCK_IE | SSI_TOUT_IE, gdd + SSI_GDD_CICR_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)); 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); struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi);
u32 val; 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) { if (msg->ttype == HSI_MSG_WRITE) {
val = SSI_DATAACCEPT(msg->channel); val = SSI_DATAACCEPT(msg->channel);
/* Hold clocks for pio writes */ /* Hold clocks for pio writes */
pm_runtime_get_sync(omap_port->pdev); pm_runtime_get(omap_port->pdev);
} else { } else {
val = SSI_DATAAVAILABLE(msg->channel) | SSI_ERROROCCURED; val = SSI_DATAAVAILABLE(msg->channel) | SSI_ERROROCCURED;
} }
@ -302,7 +332,7 @@ static int ssi_start_pio(struct hsi_msg *msg)
msg->ttype ? "write" : "read"); msg->ttype ? "write" : "read");
val |= readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); val |= readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
writel(val, 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->actual_len = 0;
msg->status = HSI_STATUS_PROCEEDING; msg->status = HSI_STATUS_PROCEEDING;
@ -360,7 +390,8 @@ static int ssi_async_break(struct hsi_msg *msg)
spin_unlock_bh(&omap_port->lock); spin_unlock_bh(&omap_port->lock);
} }
out: 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; return err;
} }
@ -388,6 +419,8 @@ static int ssi_async(struct hsi_msg *msg)
queue = &omap_port->rxqueue[msg->channel]; queue = &omap_port->rxqueue[msg->channel];
} }
msg->status = HSI_STATUS_QUEUED; msg->status = HSI_STATUS_QUEUED;
pm_runtime_get_sync(omap_port->pdev);
spin_lock_bh(&omap_port->lock); spin_lock_bh(&omap_port->lock);
list_add_tail(&msg->link, queue); list_add_tail(&msg->link, queue);
err = ssi_start_transfer(queue); err = ssi_start_transfer(queue);
@ -396,6 +429,8 @@ static int ssi_async(struct hsi_msg *msg)
msg->status = HSI_STATUS_ERROR; msg->status = HSI_STATUS_ERROR;
} }
spin_unlock_bh(&omap_port->lock); 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", dev_dbg(&port->device, "msg status %d ttype %d ch %d\n",
msg->status, msg->ttype, msg->channel); 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; omap_port->ssr.mode = cl->rx_cfg.mode;
out: out:
spin_unlock_bh(&omap_port->lock); 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; return err;
} }
@ -528,7 +564,7 @@ static int ssi_flush(struct hsi_client *cl)
continue; continue;
writew_relaxed(0, omap_ssi->gdd + SSI_GDD_CCR_REG(i)); writew_relaxed(0, omap_ssi->gdd + SSI_GDD_CCR_REG(i));
if (msg->ttype == HSI_MSG_READ) 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; omap_ssi->gdd_trn[i].msg = NULL;
} }
/* Flush all SST buffers */ /* Flush all SST buffers */
@ -552,7 +588,7 @@ static int ssi_flush(struct hsi_client *cl)
for (i = 0; i < omap_port->channels; i++) { for (i = 0; i < omap_port->channels; i++) {
/* Release write clocks */ /* Release write clocks */
if (!list_empty(&omap_port->txqueue[i])) 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->txqueue[i], NULL);
ssi_flush_queue(&omap_port->rxqueue[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); pinctrl_pm_select_default_state(omap_port->pdev);
spin_unlock_bh(&omap_port->lock); 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; 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) static int ssi_start_tx(struct hsi_client *cl)
{ {
struct hsi_port *port = hsi_get_port(cl); struct hsi_port *port = hsi_get_port(cl);
struct omap_ssi_port *omap_port = hsi_port_drvdata(port); 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); 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); spin_unlock_bh(&omap_port->wk_lock);
return 0; 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); spin_unlock_bh(&omap_port->wk_lock);
schedule_work(&omap_port->work);
return 0; return 0;
} }
@ -604,9 +651,12 @@ static int ssi_stop_tx(struct hsi_client *cl)
return 0; return 0;
} }
writel(SSI_WAKE(0), omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num)); 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); 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; return 0;
} }
@ -616,6 +666,7 @@ static void ssi_transfer(struct omap_ssi_port *omap_port,
struct hsi_msg *msg; struct hsi_msg *msg;
int err = -1; int err = -1;
pm_runtime_get(omap_port->pdev);
spin_lock_bh(&omap_port->lock); spin_lock_bh(&omap_port->lock);
while (err < 0) { while (err < 0) {
err = ssi_start_transfer(queue); 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); 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) 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); txbufstate |= (1 << i);
status |= SSI_DATAACCEPT(i); status |= SSI_DATAACCEPT(i);
/* Release the clocks writes, also GDD ones */ /* 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); 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 * Clock references for write will be handled in
* ssi_cleanup_queues * ssi_cleanup_queues
*/ */
if (msg->ttype == HSI_MSG_READ) if (msg->ttype == HSI_MSG_READ) {
pm_runtime_put_sync(omap_port->pdev); pm_runtime_mark_last_busy(omap_port->pdev);
pm_runtime_put_autosuspend(omap_port->pdev);
}
omap_ssi->gdd_trn[i].msg = NULL; omap_ssi->gdd_trn[i].msg = NULL;
} }
tmp = readl_relaxed(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG); 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 omap_ssi_port *omap_port = hsi_port_drvdata(port);
struct hsi_controller *ssi = to_hsi_controller(port->device.parent); struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
spin_lock_bh(&omap_port->lock);
pm_runtime_get_sync(omap_port->pdev); pm_runtime_get_sync(omap_port->pdev);
spin_lock_bh(&omap_port->lock);
/* Stop all the pending DMA requests for that client */ /* Stop all the pending DMA requests for that client */
ssi_cleanup_gdd(ssi, cl); ssi_cleanup_gdd(ssi, cl);
/* Now cleanup all the queues */ /* Now cleanup all the queues */
ssi_cleanup_queues(cl); 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 it is the last client of the port, do extra checks and cleanup */
if (port->claimed <= 1) { if (port->claimed <= 1) {
/* /*
* Drop the clock reference for the incoming wake line * Drop the clock reference for the incoming wake line
* if it is still kept high by the other side. * 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); pm_runtime_put_sync(omap_port->pdev);
omap_port->wkin_cken = 0; pm_runtime_get(omap_port->pdev);
}
pm_runtime_get_sync(omap_port->pdev);
/* Stop any SSI TX/RX without a client */ /* Stop any SSI TX/RX without a client */
ssi_set_port_mode(omap_port, SSI_MODE_SLEEP); ssi_set_port_mode(omap_port, SSI_MODE_SLEEP);
omap_port->sst.mode = SSI_MODE_SLEEP; omap_port->sst.mode = SSI_MODE_SLEEP;
omap_port->ssr.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); WARN_ON(omap_port->wk_refcount != 0);
} }
spin_unlock_bh(&omap_port->lock); spin_unlock_bh(&omap_port->lock);
pm_runtime_put_sync(omap_port->pdev);
return 0; return 0;
} }
@ -868,7 +922,7 @@ static void ssi_pio_complete(struct hsi_port *port, struct list_head *queue)
u32 reg; u32 reg;
u32 val; u32 val;
spin_lock(&omap_port->lock); spin_lock_bh(&omap_port->lock);
msg = list_first_entry(queue, struct hsi_msg, link); msg = list_first_entry(queue, struct hsi_msg, link);
if ((!msg->sgt.nents) || (!msg->sgt.sgl->length)) { if ((!msg->sgt.nents) || (!msg->sgt.sgl->length)) {
msg->actual_len = 0; 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))) { (msg->ttype == HSI_MSG_WRITE))) {
writel(val, omap_ssi->sys + writel(val, omap_ssi->sys +
SSI_MPU_STATUS_REG(port->num, 0)); SSI_MPU_STATUS_REG(port->num, 0));
spin_unlock(&omap_port->lock); spin_unlock_bh(&omap_port->lock);
return; 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)); reg = readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0));
if (msg->ttype == HSI_MSG_WRITE) { if (msg->ttype == HSI_MSG_WRITE) {
/* Release clocks for write transfer */ /* 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; reg &= ~val;
writel_relaxed(reg, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); 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)); writel_relaxed(val, omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0));
list_del(&msg->link); list_del(&msg->link);
spin_unlock(&omap_port->lock); spin_unlock_bh(&omap_port->lock);
msg->complete(msg); msg->complete(msg);
ssi_transfer(omap_port, queue); 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_port *port = (struct hsi_port *)ssi_port;
struct hsi_controller *ssi = to_hsi_controller(port->device.parent); struct hsi_controller *ssi = to_hsi_controller(port->device.parent);
@ -932,6 +987,8 @@ static void ssi_pio_tasklet(unsigned long ssi_port)
u32 status_reg; u32 status_reg;
pm_runtime_get_sync(omap_port->pdev); pm_runtime_get_sync(omap_port->pdev);
do {
status_reg = readl(sys + SSI_MPU_STATUS_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)); status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0));
@ -948,25 +1005,17 @@ static void ssi_pio_tasklet(unsigned long ssi_port)
status_reg = readl(sys + SSI_MPU_STATUS_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)); status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0));
pm_runtime_put_sync(omap_port->pdev);
if (status_reg) /* TODO: sleep if we retry? */
tasklet_hi_schedule(&omap_port->pio_tasklet); } while (status_reg);
else
enable_irq(omap_port->irq);
}
static irqreturn_t ssi_pio_isr(int irq, void *port) pm_runtime_mark_last_busy(omap_port->pdev);
{ pm_runtime_put_autosuspend(omap_port->pdev);
struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
tasklet_hi_schedule(&omap_port->pio_tasklet);
disable_irq_nosync(irq);
return IRQ_HANDLED; 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_port *port = (struct hsi_port *)ssi_port;
struct hsi_controller *ssi = to_hsi_controller(port->device.parent); 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 * This workaround will avoid breaking the clock reference
* count when such a situation ocurrs. * count when such a situation ocurrs.
*/ */
spin_lock(&omap_port->lock); if (!test_and_set_bit(SSI_WAKE_EN, &omap_port->flags))
if (!omap_port->wkin_cken) {
omap_port->wkin_cken = 1;
pm_runtime_get_sync(omap_port->pdev); pm_runtime_get_sync(omap_port->pdev);
}
spin_unlock(&omap_port->lock);
dev_dbg(&ssi->device, "Wake in high\n"); dev_dbg(&ssi->device, "Wake in high\n");
if (omap_port->wktest) { /* FIXME: HACK ! To be removed */ if (omap_port->wktest) { /* FIXME: HACK ! To be removed */
writel(SSI_WAKE(0), 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)); omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num));
} }
hsi_event(port, HSI_EVENT_STOP_RX); hsi_event(port, HSI_EVENT_STOP_RX);
spin_lock(&omap_port->lock); if (test_and_clear_bit(SSI_WAKE_EN, &omap_port->flags)) {
if (omap_port->wkin_cken) { pm_runtime_mark_last_busy(omap_port->pdev);
pm_runtime_put_sync(omap_port->pdev); pm_runtime_put_autosuspend(omap_port->pdev);
omap_port->wkin_cken = 0;
} }
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; return IRQ_HANDLED;
} }
static int ssi_port_irq(struct hsi_port *port, static int ssi_port_irq(struct hsi_port *port, struct platform_device *pd)
struct platform_device *pd)
{ {
struct omap_ssi_port *omap_port = hsi_port_drvdata(port); struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
int err; int err;
@ -1030,18 +1065,15 @@ static int ssi_port_irq(struct hsi_port *port,
return err; return err;
} }
omap_port->irq = err; omap_port->irq = err;
tasklet_init(&omap_port->pio_tasklet, ssi_pio_tasklet, err = devm_request_threaded_irq(&port->device, omap_port->irq, NULL,
(unsigned long)port); ssi_pio_thread, IRQF_ONESHOT, "SSI PORT", port);
err = devm_request_irq(&port->device, omap_port->irq, ssi_pio_isr,
0, "mpu_irq0", port);
if (err < 0) if (err < 0)
dev_err(&port->device, "Request IRQ %d failed (%d)\n", dev_err(&port->device, "Request IRQ %d failed (%d)\n",
omap_port->irq, err); omap_port->irq, err);
return err; return err;
} }
static int ssi_wake_irq(struct hsi_port *port, static int ssi_wake_irq(struct hsi_port *port, struct platform_device *pd)
struct platform_device *pd)
{ {
struct omap_ssi_port *omap_port = hsi_port_drvdata(port); struct omap_ssi_port *omap_port = hsi_port_drvdata(port);
int cawake_irq; 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); cawake_irq = gpiod_to_irq(omap_port->wake_gpio);
omap_port->wake_irq = cawake_irq; omap_port->wake_irq = cawake_irq;
tasklet_init(&omap_port->wake_tasklet, ssi_wake_tasklet,
(unsigned long)port); err = devm_request_threaded_irq(&port->device, cawake_irq, NULL,
err = devm_request_irq(&port->device, cawake_irq, ssi_wake_isr, ssi_wake_thread,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"cawake", port); "SSI cawake", port);
if (err < 0) if (err < 0)
dev_err(&port->device, "Request Wake in IRQ %d failed %d\n", dev_err(&port->device, "Request Wake in IRQ %d failed %d\n",
cawake_irq, err); cawake_irq, err);
@ -1169,6 +1200,9 @@ static int ssi_port_probe(struct platform_device *pd)
omap_port->pdev = &pd->dev; omap_port->pdev = &pd->dev;
omap_port->port_id = port_id; 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 */ /* initialize HSI port */
port->async = ssi_async; port->async = ssi_async;
port->setup = ssi_setup; port->setup = ssi_setup;
@ -1202,7 +1236,8 @@ static int ssi_port_probe(struct platform_device *pd)
spin_lock_init(&omap_port->wk_lock); spin_lock_init(&omap_port->wk_lock);
omap_port->dev = &port->device; 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); pm_runtime_enable(omap_port->pdev);
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
@ -1234,10 +1269,9 @@ static int ssi_port_remove(struct platform_device *pd)
ssi_debug_remove_port(port); ssi_debug_remove_port(port);
#endif #endif
hsi_port_unregister_clients(port); cancel_delayed_work_sync(&omap_port->errqueue_work);
tasklet_kill(&omap_port->wake_tasklet); hsi_port_unregister_clients(port);
tasklet_kill(&omap_port->pio_tasklet);
port->async = hsi_dummy_msg; port->async = hsi_dummy_msg;
port->setup = hsi_dummy_cl; 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; omap_ssi->port[omap_port->port_id] = NULL;
platform_set_drvdata(pd, NULL); platform_set_drvdata(pd, NULL);
pm_runtime_dont_use_autosuspend(&pd->dev);
pm_runtime_disable(&pd->dev); pm_runtime_disable(&pd->dev);
return 0; return 0;

View File

@ -90,19 +90,19 @@ struct hsi_client *hsi_new_client(struct hsi_port *port,
cl->tx_cfg = info->tx_cfg; cl->tx_cfg = info->tx_cfg;
if (cl->tx_cfg.channels) { if (cl->tx_cfg.channels) {
size = cl->tx_cfg.num_channels * sizeof(*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) if (!cl->tx_cfg.channels)
goto err_tx; goto err_tx;
memcpy(cl->tx_cfg.channels, info->tx_cfg.channels, size);
} }
cl->rx_cfg = info->rx_cfg; cl->rx_cfg = info->rx_cfg;
if (cl->rx_cfg.channels) { if (cl->rx_cfg.channels) {
size = cl->rx_cfg.num_channels * sizeof(*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) if (!cl->rx_cfg.channels)
goto err_rx; goto err_rx;
memcpy(cl->rx_cfg.channels, info->rx_cfg.channels, size);
} }
cl->device.bus = &hsi_bus_type; 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]->stop_tx = hsi_dummy_cl;
port[i]->release = hsi_dummy_cl; port[i]->release = hsi_dummy_cl;
mutex_init(&port[i]->lock); 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); dev_set_name(&port[i]->device, "port%d", i);
hsi->port[i]->device.release = hsi_port_release; hsi->port[i]->device.release = hsi_port_release;
device_initialize(&hsi->port[i]->device); device_initialize(&hsi->port[i]->device);
@ -689,7 +689,7 @@ int hsi_register_port_event(struct hsi_client *cl,
cl->ehandler = handler; cl->ehandler = handler;
cl->nb.notifier_call = hsi_event_notifier_call; 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); 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)); 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) if (!err)
cl->ehandler = NULL; cl->ehandler = NULL;
@ -734,7 +734,7 @@ EXPORT_SYMBOL_GPL(hsi_unregister_port_event);
*/ */
int hsi_event(struct hsi_port *port, unsigned long 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); EXPORT_SYMBOL_GPL(hsi_event);

View File

@ -246,7 +246,7 @@ struct hsi_port {
int (*stop_tx)(struct hsi_client *cl); int (*stop_tx)(struct hsi_client *cl);
int (*release)(struct hsi_client *cl); int (*release)(struct hsi_client *cl);
/* private */ /* 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) #define to_hsi_port(dev) container_of(dev, struct hsi_port, device)