From 7042836522a938409f9a4265c8b7dcabccc03473 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 20 Oct 2011 10:07:30 +0200 Subject: drivers: shrm: Add shared memory (shrm) driver Signed-off-by: Robert Marklund --- drivers/net/Makefile | 4 + drivers/net/u8500_shrm.c | 336 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 340 insertions(+) create mode 100644 drivers/net/u8500_shrm.c (limited to 'drivers/net') diff --git a/drivers/net/Makefile b/drivers/net/Makefile index a6b8ce11a22..c1d8099a34e 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -70,3 +70,7 @@ obj-$(CONFIG_USB_IPHETH) += usb/ obj-$(CONFIG_USB_CDC_PHONET) += usb/ obj-$(CONFIG_HYPERV_NET) += hyperv/ + +ifdef CONFIG_PHONET +obj-$(CONFIG_U8500_SHRM) += u8500_shrm.o +endif diff --git a/drivers/net/u8500_shrm.c b/drivers/net/u8500_shrm.c new file mode 100644 index 00000000000..55983a59761 --- /dev/null +++ b/drivers/net/u8500_shrm.c @@ -0,0 +1,336 @@ +/* + * Copyright (C) ST-Ericsson SA 2009 + * + * Author: Biju Das for ST-Ericsson + * Author: Kumar Sanghvi for ST-Ericsson + * Author: Arun Murthy for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static u8 wr_isi_msg[10*1024]; + +/** + * shrm_net_receive() - receive data and copy to user space buffer + * @dev: pointer to the network device structure + * @data: pointer to the receive buffer + * + * Copy data from ISI queue to the user space buffer. + */ +int shrm_net_receive(struct net_device *dev, u8 *data) +{ + struct sk_buff *skb; + struct isadev_context *isadev; + struct message_queue *q; + u32 msgsize; + u32 size = 0; + struct shrm_net_iface_priv *net_iface_priv = + (struct shrm_net_iface_priv *)netdev_priv(dev); + struct shrm_dev *shrm = net_iface_priv->shrm_device; + + if (data == NULL) + goto out; + + isadev = &shrm->isa_context->isadev[ISI_MESSAGING]; + q = &isadev->dl_queue; + + spin_lock_bh(&q->update_lock); + if (list_empty(&q->msg_list)) { + spin_unlock_bh(&q->update_lock); + dev_dbg(shrm->dev, "Empty Shrm queue\n"); + return 0; + } + spin_unlock_bh(&q->update_lock); + + msgsize = get_size_of_new_msg(q); + if (msgsize <= 0) + return msgsize; + + if ((q->readptr+msgsize) >= q->size) { + size = (q->size-q->readptr); + /*Copy First Part of msg*/ + memcpy(data, + (u8 *)(q->fifo_base + q->readptr), size); + /*Copy Second Part of msg at the top of fifo*/ + memcpy(data+size, + (u8 *)(q->fifo_base), (msgsize - size)); + } else { + memcpy(data, + (u8 *)(q->fifo_base+q->readptr), msgsize); + } + + spin_lock_bh(&q->update_lock); + remove_msg_from_queue(q); + spin_unlock_bh(&q->update_lock); + + dev_dbg(shrm->dev, "Data len at shrm_net_receive: %d\n", msgsize); + + /* + * The packet has been retrieved from the transmission + * medium. Build an skb around it, so upper layers can handle it + */ + + skb = dev_alloc_skb(msgsize); + if (!skb) { + if (printk_ratelimit()) + dev_notice(shrm->dev, + "isa rx: low on mem - packet dropped\n"); + dev->stats.rx_dropped++; + goto out; + } + skb_copy_to_linear_data(skb, data, msgsize); + skb_put(skb, msgsize); + skb_reset_mac_header(skb); + __skb_pull(skb, dev->hard_header_len); + /*Write metadata, and then pass to the receive level*/ + skb->dev = dev;/*kmalloc(sizeof(struct net_device), GFP_ATOMIC);*/ + skb->protocol = htons(ETH_P_PHONET); + skb->priority = 0; + skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */ + if (likely(netif_rx_ni(skb) == NET_RX_SUCCESS)) { + dev->stats.rx_packets++; + dev->stats.rx_bytes += msgsize; + } else + dev->stats.rx_dropped++; + + return msgsize; +out: + return -ENOMEM; +} + +static int netdev_isa_open(struct net_device *dev) +{ + struct shrm_net_iface_priv *net_iface_priv = + (struct shrm_net_iface_priv *)netdev_priv(dev); + struct shrm_dev *shrm = net_iface_priv->shrm_device; + + shrm->netdev_flag_up = 1; + if (!netif_carrier_ok(dev)) + netif_carrier_on(dev); + netif_wake_queue(dev); + return 0; +} + +static int netdev_isa_close(struct net_device *dev) +{ + struct shrm_net_iface_priv *net_iface_priv = + (struct shrm_net_iface_priv *)netdev_priv(dev); + struct shrm_dev *shrm = net_iface_priv->shrm_device; + + shrm->netdev_flag_up = 0; + netif_stop_queue(dev); + netif_carrier_off(dev); + return 0; +} + +static int netdev_isa_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct if_phonet_req *req = (struct if_phonet_req *)ifr; + + switch (cmd) { + case SIOCPNGAUTOCONF: + req->ifr_phonet_autoconf.device = PN_DEV_HOST; + return 0; + } + return -ENOIOCTLCMD; +} + +static struct net_device_stats *netdev_isa_stats(struct net_device *dev) +{ + return &dev->stats; +} + +/** + * netdev_isa_write() - write through the net interface + * @skb: pointer to the socket buffer + * @dev: pointer to the network device structure + * + * Copies data(ISI message) from the user buffer to the kernel buffer and + * schedule transfer thread to transmit the message to the modem via FIFO. + */ +static netdev_tx_t netdev_isa_write(struct sk_buff *skb, struct net_device *dev) +{ + int err; + int retval = 0; + struct shrm_net_iface_priv *net_iface_priv = + (struct shrm_net_iface_priv *)netdev_priv(dev); + struct shrm_dev *shrm = net_iface_priv->shrm_device; + + /* + * FIXME: + * U8500 modem requires that Pipe created/enabled Indication should + * be sent from the port corresponding to GPRS socket. + * Also, the U8500 modem does not implement Pipe controller + * which takes care of port manipulations for GPRS traffic. + * + * Now, APE has GPRS socket and the socket for sending + * Indication msgs bound to different ports. + * Phonet stack does not allow an indication msg to be sent + * from GPRS socket, since Phonet stack assumes the presence + * of Pipe controller in modem. + * + * So, due to lack of Pipe controller implementation in the + * U8500 modem, carry out the port manipulation related to + * GPRS traffic here. + * Ideally, it should be done either by Pipe controller in + * modem OR some implementation of Pipe controller on APE side + */ + if (skb->data[RESOURCE_ID_INDEX] == PN_PIPE) { + if ((skb->data[MSG_ID_INDEX] == PNS_PIPE_CREATED_IND) || + (skb->data[MSG_ID_INDEX] == PNS_PIPE_ENABLED_IND) || + (skb->data[MSG_ID_INDEX] == PNS_PIPE_DISABLED_IND)) + skb->data[SRC_OBJ_INDEX] = skb->data[PIPE_HDL_INDEX]; + } + + if ((void *)wr_isi_msg != + memcpy((void *)wr_isi_msg, skb->data, skb->len)) { + dev_err(shrm->dev, "memcpy failed\n"); + dev_kfree_skb(skb); + return -EFAULT; + } + + spin_lock_bh(&shrm->isa_context->common_tx); + err = shm_write_msg(shrm, ISI_MESSAGING, (void *)wr_isi_msg, + skb->len); + if (!err) { + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + retval = NETDEV_TX_OK; + dev_kfree_skb(skb); + } else { + dev->stats.tx_dropped++; + retval = NETDEV_TX_BUSY; + } + spin_unlock_bh(&shrm->isa_context->common_tx); + + return retval; +} + +static const struct net_device_ops shrm_netdev_ops = { + .ndo_open = netdev_isa_open, + .ndo_stop = netdev_isa_close, + .ndo_do_ioctl = netdev_isa_ioctl, + .ndo_start_xmit = netdev_isa_write, + .ndo_get_stats = netdev_isa_stats, +}; + +static void shm_net_init(struct net_device *dev) +{ + struct shrm_net_iface_priv *net_iface_priv; + + dev->netdev_ops = &shrm_netdev_ops; + dev->header_ops = &phonet_header_ops; + dev->type = ARPHRD_PHONET; + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + dev->mtu = PHONET_MAX_MTU; + dev->hard_header_len = SHRM_HLEN; + dev->addr_len = PHONET_ALEN; + dev->tx_queue_len = PN_TX_QUEUE_LEN; + dev->destructor = free_netdev; + dev->dev_addr[0] = PN_LINK_ADDR; + net_iface_priv = netdev_priv(dev); + memset(net_iface_priv, 0 , sizeof(struct shrm_net_iface_priv)); +} + +int shrm_register_netdev(struct shrm_dev *shrm) +{ + struct net_device *nw_device; + struct shrm_net_iface_priv *net_iface_priv; + char *devname = "shrm%d"; + int err; + + /* allocate the net device */ + nw_device = shrm->ndev = alloc_netdev( + sizeof(struct shrm_net_iface_priv), + devname, shm_net_init); + if (nw_device == NULL) { + dev_err(shrm->dev, "Failed to allocate SHRM Netdev\n"); + return -ENOMEM; + } + err = register_netdev(shrm->ndev); + if (err) { + dev_err(shrm->dev, "Err %i in reg shrm-netdev\n", err); + free_netdev(shrm->ndev); + return -ENODEV; + } + dev_info(shrm->dev, "Registered shrm netdev\n"); + + net_iface_priv = (struct shrm_net_iface_priv *)netdev_priv(nw_device); + net_iface_priv->shrm_device = shrm; + net_iface_priv->iface_num = 0; + + return err; +} + +int shrm_stop_netdev(struct net_device *dev) +{ + struct shrm_net_iface_priv *net_iface_priv = + (struct shrm_net_iface_priv *)netdev_priv(dev); + + netif_stop_queue(dev); + return 0; +} + +int shrm_restart_netdev(struct net_device *dev) +{ + struct shrm_net_iface_priv *net_iface_priv = + (struct shrm_net_iface_priv *)netdev_priv(dev); + + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + return 0; +} + +int shrm_start_netdev(struct net_device *dev) +{ + struct shrm_net_iface_priv *net_iface_priv = + (struct shrm_net_iface_priv *)netdev_priv(dev); + struct shrm_dev *shrm = net_iface_priv->shrm_device; + + if (!netif_carrier_ok(dev)) + netif_carrier_on(dev); + netif_start_queue(dev); + shrm->netdev_flag_up = 1; + return 0; +} + +int shrm_suspend_netdev(struct net_device *dev) +{ + if (netif_running(dev)) { + netif_stop_queue(dev); + netif_carrier_off(dev); + } + netif_device_detach(dev); + + return 0; +} + +int shrm_resume_netdev(struct net_device *dev) +{ + netif_device_attach(dev); + if (netif_running(dev)) { + netif_carrier_on(dev); + netif_wake_queue(dev); + } + + return 0; +} + +void shrm_unregister_netdev(struct shrm_dev *shrm) +{ + unregister_netdev(shrm->ndev); +} -- cgit v1.2.3 From 99555ce4ab970b2e5cd10820d189fe46df5c1052 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 28 Apr 2011 14:56:34 +0200 Subject: Fix introduced warnings Signed-off-by: Philippe Langlais --- drivers/net/u8500_shrm.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/u8500_shrm.c b/drivers/net/u8500_shrm.c index 55983a59761..aaec346c781 100644 --- a/drivers/net/u8500_shrm.c +++ b/drivers/net/u8500_shrm.c @@ -278,18 +278,12 @@ int shrm_register_netdev(struct shrm_dev *shrm) int shrm_stop_netdev(struct net_device *dev) { - struct shrm_net_iface_priv *net_iface_priv = - (struct shrm_net_iface_priv *)netdev_priv(dev); - netif_stop_queue(dev); return 0; } int shrm_restart_netdev(struct net_device *dev) { - struct shrm_net_iface_priv *net_iface_priv = - (struct shrm_net_iface_priv *)netdev_priv(dev); - if (netif_queue_stopped(dev)) netif_wake_queue(dev); return 0; -- cgit v1.2.3 From 799343c4798be4d6234b7e95c15ad2d5d7c594d4 Mon Sep 17 00:00:00 2001 From: Kumar Sanghvi Date: Sat, 26 Mar 2011 13:53:09 +0530 Subject: u8500: shrm: Misc updates and optimisation This patch does following: 1. Protects reset of char queues to prevent a potential kernel crash if MSR is run in rapid succession continuously without allowing the user-space to settle. 2. Removes un-needed memcpy from tx/rx path in shrm net interface. 3. Eliminates use of ph_recv_buf in rx path. 3. Fixes compiler warning in net interface. 4. Updates the protection of writes to shrm fifo. ST-Ericsson ID: ER326117 Change-Id: I6aac23e20a20efc1f9b8a620db455f53e067f9bd Signed-off-by: Kumar Sanghvi Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/19226 Reviewed-by: Hemant-vilas RAMDASI Reviewed-by: Srinidhi KASAGAR --- arch/arm/mach-ux500/include/mach/shrm_net.h | 2 +- drivers/char/shrm_char.c | 3 ++ drivers/misc/shrm/modem_shrm_driver.c | 3 +- drivers/misc/shrm/shrm_fifo.c | 30 +++++++------- drivers/net/u8500_shrm.c | 62 ++++++++++++----------------- 5 files changed, 44 insertions(+), 56 deletions(-) (limited to 'drivers/net') diff --git a/arch/arm/mach-ux500/include/mach/shrm_net.h b/arch/arm/mach-ux500/include/mach/shrm_net.h index 4e675a98f3d..a97b276ee15 100644 --- a/arch/arm/mach-ux500/include/mach/shrm_net.h +++ b/arch/arm/mach-ux500/include/mach/shrm_net.h @@ -33,7 +33,7 @@ struct shrm_net_iface_priv { }; int shrm_register_netdev(struct shrm_dev *shrm_dev_data); -int shrm_net_receive(struct net_device *dev, unsigned char *data); +int shrm_net_receive(struct net_device *dev); int shrm_suspend_netdev(struct net_device *dev); int shrm_resume_netdev(struct net_device *dev); int shrm_stop_netdev(struct net_device *dev); diff --git a/drivers/char/shrm_char.c b/drivers/char/shrm_char.c index 2890f152d2d..714d38e42f1 100644 --- a/drivers/char/shrm_char.c +++ b/drivers/char/shrm_char.c @@ -112,6 +112,7 @@ void shrm_char_reset_queues(struct shrm_dev *shrm) isadev = &isa_context->isadev[no_dev]; q = &isadev->dl_queue; + spin_lock_bh(&q->update_lock); /* empty out the msg queue */ list_for_each_safe(cur_msg_ptr, msg_ptr, &q->msg_list) { cur_msg = list_entry(cur_msg_ptr, @@ -129,6 +130,8 @@ void shrm_char_reset_queues(struct shrm_dev *shrm) /* wake up the blocking read/select */ atomic_set(&q->q_rp, 1); wake_up_interruptible(&q->wq_readable); + + spin_unlock_bh(&q->update_lock); } } diff --git a/drivers/misc/shrm/modem_shrm_driver.c b/drivers/misc/shrm/modem_shrm_driver.c index d94c007098b..29368950256 100644 --- a/drivers/misc/shrm/modem_shrm_driver.c +++ b/drivers/misc/shrm/modem_shrm_driver.c @@ -42,7 +42,6 @@ static struct hrtimer timer; #define PHONET_TASKLET #define MAX_RCV_LEN 2048 -static u8 ph_recv_buf[MAX_RCV_LEN]; void do_phonet_rcv_tasklet(unsigned long unused); struct tasklet_struct phonet_rcv_tasklet; @@ -308,7 +307,7 @@ void do_phonet_rcv_tasklet(unsigned long unused) dev_dbg(shrm->dev, "%s IN\n", __func__); for (;;) { - ret = shrm_net_receive(shrm->ndev, ph_recv_buf); + ret = shrm_net_receive(shrm->ndev); if (ret == 0) { dev_dbg(shrm->dev, "len is zero, queue empty\n"); break; diff --git a/drivers/misc/shrm/shrm_fifo.c b/drivers/misc/shrm/shrm_fifo.c index ecf4b7e4466..cbe0949a56d 100644 --- a/drivers/misc/shrm/shrm_fifo.c +++ b/drivers/misc/shrm/shrm_fifo.c @@ -125,6 +125,7 @@ void write_boot_info_resp(struct shrm_dev *shrm, u32 config, u8 msg_length; version = SHRM_VER; + spin_lock_bh(&fifo->fifo_update_lock); /* Read L1 header read content of reader_local_rptr */ msg = (u32 *) (fifo->writer_local_wptr+fifo->fifo_virtual_addr); @@ -144,10 +145,9 @@ void write_boot_info_resp(struct shrm_dev *shrm, u32 config, *msg = ca_csc_inactivity_timer; msg_length = L1_NORMAL_MSG; } - spin_lock(&fifo->fifo_update_lock); fifo->writer_local_wptr += msg_length; fifo->availablesize -= msg_length; - spin_unlock(&fifo->fifo_update_lock); + spin_unlock_bh(&fifo->fifo_update_lock); } /** @@ -240,6 +240,7 @@ int shm_write_msg_to_fifo(struct shrm_dev *shrm, u8 channel, */ l2_header = ((l2header << L2_HEADER_OFFSET) | ((length) & MASK_0_39_BIT)); + spin_lock_bh(&fifo->fifo_update_lock); /* Check Local Rptr is less than or equal to Local WPtr */ if (fifo->writer_local_rptr <= fifo->writer_local_wptr) { msg = (u32 *) @@ -257,11 +258,9 @@ int shm_write_msg_to_fifo(struct shrm_dev *shrm, u8 channel, /* copy the l2 message in 1 memcpy */ memcpy((void *)msg, addr, length); /* UpdateWptr */ - spin_lock_bh(&fifo->fifo_update_lock); fifo->writer_local_wptr += requiredsize; fifo->availablesize -= requiredsize; fifo->writer_local_wptr %= fifo->end_addr_fifo; - spin_unlock_bh(&fifo->fifo_update_lock); } else { /* * message is split between and of FIFO and beg of FIFO @@ -273,7 +272,6 @@ int shm_write_msg_to_fifo(struct shrm_dev *shrm, u8 channel, /* Add L1 header */ *msg = l1_header; msg++; - spin_lock_bh(&fifo->fifo_update_lock); /* UpdateWptr */ fifo->writer_local_wptr = 0; fifo->availablesize -= size; @@ -293,7 +291,6 @@ int shm_write_msg_to_fifo(struct shrm_dev *shrm, u8 channel, requiredsize-size; fifo->availablesize -= (requiredsize-size); - spin_unlock_bh(&fifo->fifo_update_lock); } else if (size == 2) { /* Add L1 header and L2 header */ *msg = l1_header; @@ -301,7 +298,6 @@ int shm_write_msg_to_fifo(struct shrm_dev *shrm, u8 channel, *msg = l2_header; msg++; - spin_lock_bh(&fifo->fifo_update_lock); /* UpdateWptr */ fifo->writer_local_wptr = 0; fifo->availablesize -= size; @@ -320,7 +316,6 @@ int shm_write_msg_to_fifo(struct shrm_dev *shrm, u8 channel, requiredsize-size; fifo->availablesize -= (requiredsize-size); - spin_unlock_bh(&fifo->fifo_update_lock); } else { /* Add L1 header and L2 header */ *msg = l1_header; @@ -331,7 +326,6 @@ int shm_write_msg_to_fifo(struct shrm_dev *shrm, u8 channel, /* copy the l2 message in 1 memcpy */ memcpy((void *)msg, addr, (size-2)*4); - spin_lock_bh(&fifo->fifo_update_lock); /* UpdateWptr */ fifo->writer_local_wptr = 0; @@ -351,7 +345,6 @@ int shm_write_msg_to_fifo(struct shrm_dev *shrm, u8 channel, requiredsize-size; fifo->availablesize -= (requiredsize-size); - spin_unlock_bh(&fifo->fifo_update_lock); } } @@ -370,13 +363,12 @@ int shm_write_msg_to_fifo(struct shrm_dev *shrm, u8 channel, */ memcpy((void *)msg, addr, length); - spin_lock_bh(&fifo->fifo_update_lock); /* UpdateWptr */ fifo->writer_local_wptr += requiredsize; fifo->availablesize -= requiredsize; - spin_unlock_bh(&fifo->fifo_update_lock); } + spin_unlock_bh(&fifo->fifo_update_lock); return length; } @@ -667,6 +659,7 @@ void update_ac_common_local_rptr(struct shrm_dev *shrm) fifo = &ape_shm_fifo_0; + spin_lock_bh(&fifo->fifo_update_lock); fifo->shared_rptr = (*((u32 *)shrm->ac_common_shared_rptr)); @@ -680,10 +673,9 @@ void update_ac_common_local_rptr(struct shrm_dev *shrm) } /* Chance of race condition of below variables with write_msg */ - spin_lock(&fifo->fifo_update_lock); fifo->availablesize += free_space; fifo->writer_local_rptr = fifo->shared_rptr; - spin_unlock(&fifo->fifo_update_lock); + spin_unlock_bh(&fifo->fifo_update_lock); } void update_ac_audio_local_rptr(struct shrm_dev *shrm) @@ -696,6 +688,7 @@ void update_ac_audio_local_rptr(struct shrm_dev *shrm) u32 free_space = 0; fifo = &ape_shm_fifo_1; + spin_lock_bh(&fifo->fifo_update_lock); fifo->shared_rptr = (*((u32 *)shrm->ac_audio_shared_rptr)); @@ -709,10 +702,9 @@ void update_ac_audio_local_rptr(struct shrm_dev *shrm) } /* Chance of race condition of below variables with write_msg */ - spin_lock(&fifo->fifo_update_lock); fifo->availablesize += free_space; fifo->writer_local_rptr = fifo->shared_rptr; - spin_unlock(&fifo->fifo_update_lock); + spin_unlock_bh(&fifo->fifo_update_lock); } void update_ac_common_shared_wptr(struct shrm_dev *shrm) @@ -724,11 +716,13 @@ void update_ac_common_shared_wptr(struct shrm_dev *shrm) struct fifo_write_params *fifo; fifo = &ape_shm_fifo_0; + spin_lock_bh(&fifo->fifo_update_lock); /* Update shared pointer fifo offset of the IPC zone */ (*((u32 *)shrm->ac_common_shared_wptr)) = fifo->writer_local_wptr; fifo->shared_wptr = fifo->writer_local_wptr; + spin_unlock_bh(&fifo->fifo_update_lock); } void update_ac_audio_shared_wptr(struct shrm_dev *shrm) @@ -740,10 +734,12 @@ void update_ac_audio_shared_wptr(struct shrm_dev *shrm) struct fifo_write_params *fifo; fifo = &ape_shm_fifo_1; + spin_lock_bh(&fifo->fifo_update_lock); /* Update shared pointer fifo offset of the IPC zone */ (*((u32 *)shrm->ac_audio_shared_wptr)) = fifo->writer_local_wptr; fifo->shared_wptr = fifo->writer_local_wptr; + spin_unlock_bh(&fifo->fifo_update_lock); } void update_ca_common_shared_rptr(struct shrm_dev *shrm) @@ -803,9 +799,11 @@ void get_writer_pointers(u8 channel_type, u32 *writer_local_rptr, else /* channel_type = AUDIO_CHANNEL */ fifo = &ape_shm_fifo_1; + spin_lock_bh(&fifo->fifo_update_lock); *writer_local_rptr = fifo->writer_local_rptr; *writer_local_wptr = fifo->writer_local_wptr; *shared_wptr = fifo->shared_wptr; + spin_unlock_bh(&fifo->fifo_update_lock); } void set_ca_msg_0_read_notif_send(u8 val) diff --git a/drivers/net/u8500_shrm.c b/drivers/net/u8500_shrm.c index aaec346c781..488f96f2c04 100644 --- a/drivers/net/u8500_shrm.c +++ b/drivers/net/u8500_shrm.c @@ -22,16 +22,13 @@ #include #include -static u8 wr_isi_msg[10*1024]; - /** * shrm_net_receive() - receive data and copy to user space buffer * @dev: pointer to the network device structure - * @data: pointer to the receive buffer * * Copy data from ISI queue to the user space buffer. */ -int shrm_net_receive(struct net_device *dev, u8 *data) +int shrm_net_receive(struct net_device *dev) { struct sk_buff *skb; struct isadev_context *isadev; @@ -42,9 +39,6 @@ int shrm_net_receive(struct net_device *dev, u8 *data) (struct shrm_net_iface_priv *)netdev_priv(dev); struct shrm_dev *shrm = net_iface_priv->shrm_device; - if (data == NULL) - goto out; - isadev = &shrm->isa_context->isadev[ISI_MESSAGING]; q = &isadev->dl_queue; @@ -60,30 +54,10 @@ int shrm_net_receive(struct net_device *dev, u8 *data) if (msgsize <= 0) return msgsize; - if ((q->readptr+msgsize) >= q->size) { - size = (q->size-q->readptr); - /*Copy First Part of msg*/ - memcpy(data, - (u8 *)(q->fifo_base + q->readptr), size); - /*Copy Second Part of msg at the top of fifo*/ - memcpy(data+size, - (u8 *)(q->fifo_base), (msgsize - size)); - } else { - memcpy(data, - (u8 *)(q->fifo_base+q->readptr), msgsize); - } - - spin_lock_bh(&q->update_lock); - remove_msg_from_queue(q); - spin_unlock_bh(&q->update_lock); - - dev_dbg(shrm->dev, "Data len at shrm_net_receive: %d\n", msgsize); - /* * The packet has been retrieved from the transmission * medium. Build an skb around it, so upper layers can handle it */ - skb = dev_alloc_skb(msgsize); if (!skb) { if (printk_ratelimit()) @@ -92,8 +66,29 @@ int shrm_net_receive(struct net_device *dev, u8 *data) dev->stats.rx_dropped++; goto out; } - skb_copy_to_linear_data(skb, data, msgsize); - skb_put(skb, msgsize); + + if ((q->readptr+msgsize) >= q->size) { + size = (q->size-q->readptr); + /*Copy First Part of msg*/ + skb_copy_to_linear_data(skb, + (u8 *)(q->fifo_base + q->readptr), size); + skb_put(skb, size); + + /*Copy Second Part of msg at the top of fifo*/ + skb_copy_to_linear_data_offset(skb, size, + (u8 *)(q->fifo_base), (msgsize - size)); + skb_put(skb, msgsize-size); + + } else { + skb_copy_to_linear_data(skb, + (u8 *)(q->fifo_base+q->readptr), msgsize); + skb_put(skb, msgsize); + } + + spin_lock_bh(&q->update_lock); + remove_msg_from_queue(q); + spin_unlock_bh(&q->update_lock); + skb_reset_mac_header(skb); __skb_pull(skb, dev->hard_header_len); /*Write metadata, and then pass to the receive level*/ @@ -196,15 +191,8 @@ static netdev_tx_t netdev_isa_write(struct sk_buff *skb, struct net_device *dev) skb->data[SRC_OBJ_INDEX] = skb->data[PIPE_HDL_INDEX]; } - if ((void *)wr_isi_msg != - memcpy((void *)wr_isi_msg, skb->data, skb->len)) { - dev_err(shrm->dev, "memcpy failed\n"); - dev_kfree_skb(skb); - return -EFAULT; - } - spin_lock_bh(&shrm->isa_context->common_tx); - err = shm_write_msg(shrm, ISI_MESSAGING, (void *)wr_isi_msg, + err = shm_write_msg(shrm, ISI_MESSAGING, skb->data, skb->len); if (!err) { dev->stats.tx_packets++; -- cgit v1.2.3 From 3cea57241b2be162293c5ac43fa0dd6b99af0fe5 Mon Sep 17 00:00:00 2001 From: Kumar Sanghvi Date: Thu, 26 May 2011 14:38:19 +0530 Subject: u8500: shrm: Move shrm to drivers/modem Moves shrm specific files from arch/arm/mach-ux500/mach/include and drivers/misc to include/linux/modem/shrm and drivers/modem/shrm respectively ST-Ericsson ID: CR329459 Change-Id: I3a08f83e5302429d51eb865ee1c5e4e0ec73e31b Signed-off-by: Kumar Sanghvi Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23980 Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR --- Documentation/DocBook/shrm.tmpl | 10 +- arch/arm/mach-ux500/include/mach/shrm.h | 23 - arch/arm/mach-ux500/include/mach/shrm_config.h | 111 -- arch/arm/mach-ux500/include/mach/shrm_driver.h | 204 ---- arch/arm/mach-ux500/include/mach/shrm_net.h | 44 - arch/arm/mach-ux500/include/mach/shrm_private.h | 181 --- drivers/char/shrm_char.c | 8 +- drivers/misc/shrm/Kconfig | 43 - drivers/misc/shrm/Makefile | 11 - drivers/misc/shrm/modem_shrm_driver.c | 671 ----------- drivers/misc/shrm/shrm_driver.c | 1439 ----------------------- drivers/misc/shrm/shrm_fifo.c | 827 ------------- drivers/misc/shrm/shrm_protocol.c | 1191 ------------------- drivers/modem/Kconfig | 2 + drivers/modem/Makefile | 1 + drivers/modem/shrm/Kconfig | 43 + drivers/modem/shrm/Makefile | 11 + drivers/modem/shrm/modem_shrm_driver.c | 669 +++++++++++ drivers/modem/shrm/shrm_driver.c | 1439 +++++++++++++++++++++++ drivers/modem/shrm/shrm_fifo.c | 827 +++++++++++++ drivers/modem/shrm/shrm_protocol.c | 1191 +++++++++++++++++++ drivers/net/u8500_shrm.c | 10 +- include/linux/modem/shrm/shrm.h | 23 + include/linux/modem/shrm/shrm_config.h | 111 ++ include/linux/modem/shrm/shrm_driver.h | 202 ++++ include/linux/modem/shrm/shrm_net.h | 44 + include/linux/modem/shrm/shrm_private.h | 181 +++ 27 files changed, 4758 insertions(+), 4759 deletions(-) delete mode 100644 arch/arm/mach-ux500/include/mach/shrm.h delete mode 100644 arch/arm/mach-ux500/include/mach/shrm_config.h delete mode 100644 arch/arm/mach-ux500/include/mach/shrm_driver.h delete mode 100644 arch/arm/mach-ux500/include/mach/shrm_net.h delete mode 100644 arch/arm/mach-ux500/include/mach/shrm_private.h delete mode 100644 drivers/misc/shrm/Kconfig delete mode 100644 drivers/misc/shrm/Makefile delete mode 100644 drivers/misc/shrm/modem_shrm_driver.c delete mode 100644 drivers/misc/shrm/shrm_driver.c delete mode 100644 drivers/misc/shrm/shrm_fifo.c delete mode 100644 drivers/misc/shrm/shrm_protocol.c create mode 100644 drivers/modem/shrm/Kconfig create mode 100644 drivers/modem/shrm/Makefile create mode 100644 drivers/modem/shrm/modem_shrm_driver.c create mode 100644 drivers/modem/shrm/shrm_driver.c create mode 100644 drivers/modem/shrm/shrm_fifo.c create mode 100644 drivers/modem/shrm/shrm_protocol.c create mode 100644 include/linux/modem/shrm/shrm.h create mode 100644 include/linux/modem/shrm/shrm_config.h create mode 100644 include/linux/modem/shrm/shrm_driver.h create mode 100644 include/linux/modem/shrm/shrm_net.h create mode 100644 include/linux/modem/shrm/shrm_private.h (limited to 'drivers/net') diff --git a/Documentation/DocBook/shrm.tmpl b/Documentation/DocBook/shrm.tmpl index 7f21cd3f7dd..2773b821bf5 100644 --- a/Documentation/DocBook/shrm.tmpl +++ b/Documentation/DocBook/shrm.tmpl @@ -117,15 +117,15 @@ This Section lists the functions used internally by the SHRM driver to implement FIFO management. It physically reads/writes data to/from memory. -!Idrivers/misc/shrm/shrm_fifo.c +!Idrivers/modem/shrm/shrm_fifo.c This Section lists the functions used internally by the SHRM driver to implement the SHM protocol and handle all interrupt callback. -!Idrivers/misc/shrm/shrm_protocol.c +!Idrivers/modem/shrm/shrm_protocol.c This Section lists the functions used internally by the SHRM driver to implement Modem-Host communication L1 interface specifications. -!Idrivers/misc/shrm/modem_shrm_driver.c +!Idrivers/modem/shrm/modem_shrm_driver.c @@ -133,7 +133,7 @@ This Section lists some of the Data structure used by the SHRM driver. -!Iarch/arm/mach-ux500/include/mach/shrm_driver.h -!Iarch/arm/mach-ux500/include/mach/shrm_private.h +!Iinclude/linux/modem/shrm/shrm_driver.h +!Iinclude/linux/modem/shrm/shrm_private.h diff --git a/arch/arm/mach-ux500/include/mach/shrm.h b/arch/arm/mach-ux500/include/mach/shrm.h deleted file mode 100644 index 6deeeb16ba8..00000000000 --- a/arch/arm/mach-ux500/include/mach/shrm.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Biju Das for ST-Ericsson - * Author: Kumar Sanghavi for ST-Ericsson - * Author: Arun Murthy for ST-Ericsson - * License terms: GNU General Public License (GPL) version 2 - */ - -#ifndef __SHM_DRIVER_IF_H__ -#define __SHM_DRIVER_IF_H__ - -#include - -/* forward declaration */ -struct shrm_dev; - -typedef void (*rx_cb)(void *data, unsigned int length); -typedef void (*received_msg_handler)(unsigned char l2_header, - void *msg_ptr, unsigned int length, - struct shrm_dev *shrm); - -#endif diff --git a/arch/arm/mach-ux500/include/mach/shrm_config.h b/arch/arm/mach-ux500/include/mach/shrm_config.h deleted file mode 100644 index a82b35ef77b..00000000000 --- a/arch/arm/mach-ux500/include/mach/shrm_config.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Biju Das for ST-Ericsson - * Author: Kumar Sanghavi for ST-Ericsson - * Author: Arun Murthy for ST-Ericsson - * License terms: GNU General Public License (GPL) version 2 - */ - -#ifndef __SHRM_CONFIG_H -#define __SHRM_CONFIG_H - - -/* -Note: modem need to define IPC as a non-cacheable area. -In Cortex R4 MPU requires that base address of NC area is aligned on a -region-sized boundary.On modem side, only 1 NC area can be defined, hence -the whole IPC area must be defined as NC (at least). - -*/ - -/* cache line size = 32bytes*/ -#define SHM_CACHE_LINE 32 -#define SHM_PTR_SIZE 4 - -/* FIFO 0 address configuration */ -/* ---------------------------- */ -/* 128KB */ -#define SHM_FIFO_0_SIZE (128*1024) - - -/* == APE addresses == */ -#ifdef CONFIG_SHRM_V1_UPDATES_VERSION -#define SHM_IPC_BASE_AMCU 0x06F80000 -#else -#define SHM_IPC_BASE_AMCU 0x06000000 -#endif - -/* offset pointers */ -#define SHM_ACFIFO_0_WRITE_AMCU SHM_IPC_BASE_AMCU -#define SHM_ACFIFO_0_READ_AMCU (SHM_ACFIFO_0_WRITE_AMCU + SHM_PTR_SIZE) -#define SHM_CAFIFO_0_WRITE_AMCU (SHM_ACFIFO_0_WRITE_AMCU + SHM_CACHE_LINE) -#define SHM_CAFIFO_0_READ_AMCU (SHM_CAFIFO_0_WRITE_AMCU + SHM_PTR_SIZE) -/* FIFO start */ -#define SHM_ACFIFO_0_START_AMCU (SHM_CAFIFO_0_WRITE_AMCU + SHM_CACHE_LINE) -#define SHM_CAFIFO_0_START_AMCU (SHM_ACFIFO_0_START_AMCU + SHM_FIFO_0_SIZE) - - -/* == CMT addresses ==*/ -#define SHM_IPC_BASE_CMCU (SHM_IPC_BASE_AMCU+0x08000000) -/* offset pointers */ -#define SHM_ACFIFO_0_WRITE_CMCU SHM_IPC_BASE_CMCU -#define SHM_ACFIFO_0_READ_CMCU (SHM_ACFIFO_0_WRITE_CMCU + SHM_PTR_SIZE) -#define SHM_CAFIFO_0_WRITE_CMCU (SHM_ACFIFO_0_WRITE_CMCU + SHM_CACHE_LINE) -#define SHM_CAFIFO_0_READ_CMCU (SHM_CAFIFO_0_WRITE_CMCU + SHM_PTR_SIZE) -/* FIFO*/ -#define SHM_ACFIFO_0_START_CMCU (SHM_CAFIFO_0_WRITE_CMCU + SHM_CACHE_LINE) -#define SHM_CAFIFO_0_START_CMCU (SHM_ACFIFO_0_START_CMCU + SHM_FIFO_0_SIZE) - - -/* ADSP addresses*/ -#define SHM_ACFIFO_0_START_ADSP 0x0 -#define SHM_CAFIFO_0_START_ADSP 0x0 -#define SHM_ACFIFO_0_WRITE_ADSP 0x0 -#define SHM_ACFIFO_0_READ_ADSP 0x0 -#define SHM_CAFIFO_0_WRITE_ADSP 0x0 -#define SHM_CAFIFO_0_READ_ADSP 0x0 - -/* FIFO 1 address configuration */ -/* ---------------------------- */ - - -/* FIFO 1 - 4K */ -#define SHM_FIFO_1_SIZE (4*1024) - - -/* == APE addresses == */ -#define SHM_ACFIFO_1_WRITE_AMCU (SHM_CAFIFO_0_START_AMCU + SHM_FIFO_0_SIZE) -#define SHM_ACFIFO_1_READ_AMCU (SHM_ACFIFO_1_WRITE_AMCU + SHM_PTR_SIZE) -#define SHM_CAFIFO_1_WRITE_AMCU (SHM_ACFIFO_1_WRITE_AMCU + SHM_CACHE_LINE) -#define SHM_CAFIFO_1_READ_AMCU (SHM_CAFIFO_1_WRITE_AMCU + SHM_PTR_SIZE) -/* FIFO*/ -#define SHM_ACFIFO_1_START_AMCU (SHM_CAFIFO_1_WRITE_AMCU + SHM_CACHE_LINE) -#define SHM_CAFIFO_1_START_AMCU (SHM_ACFIFO_1_START_AMCU + SHM_FIFO_1_SIZE) - - -/* == CMT addresses ==*/ -#define SHM_ACFIFO_1_WRITE_CMCU (SHM_CAFIFO_0_START_CMCU + SHM_FIFO_0_SIZE) -#define SHM_ACFIFO_1_READ_CMCU (SHM_ACFIFO_1_WRITE_CMCU + SHM_PTR_SIZE) -#define SHM_CAFIFO_1_WRITE_CMCU (SHM_ACFIFO_1_WRITE_CMCU + SHM_CACHE_LINE) -#define SHM_CAFIFO_1_READ_CMCU (SHM_CAFIFO_1_WRITE_CMCU + SHM_PTR_SIZE) -/* FIFO1 start */ -#define SHM_ACFIFO_1_START_CMCU (SHM_CAFIFO_1_WRITE_CMCU + SHM_CACHE_LINE) -#define SHM_CAFIFO_1_START_CMCU (SHM_ACFIFO_1_START_CMCU + SHM_FIFO_1_SIZE) - - -/* ADSP addresses*/ -#define SHM_ACFIFO_1_START_ADSP 0x0 -#define SHM_CAFIFO_1_START_ADSP 0x0 -#define SHM_ACFIFO_1_WRITE_ADSP 0x0 -#define SHM_ACFIFO_1_READ_ADSP 0x0 -#define SHM_CAFIFO_1_WRITE_ADSP 0x0 -#define SHM_CAFIFO_1_READ_ADSP 0x0 - - -#define U8500_SHM_FIFO_APE_COMMON_BASE (SHM_ACFIFO_0_START_AMCU) -#define U8500_SHM_FIFO_CMT_COMMON_BASE (SHM_CAFIFO_0_START_AMCU) -#define U8500_SHM_FIFO_APE_AUDIO_BASE (SHM_ACFIFO_1_START_AMCU) -#define U8500_SHM_FIFO_CMT_AUDIO_BASE (SHM_CAFIFO_1_START_AMCU) - -#endif /* __SHRM_CONFIG_H */ diff --git a/arch/arm/mach-ux500/include/mach/shrm_driver.h b/arch/arm/mach-ux500/include/mach/shrm_driver.h deleted file mode 100644 index 71b3fbcf229..00000000000 --- a/arch/arm/mach-ux500/include/mach/shrm_driver.h +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Biju Das for ST-Ericsson - * Author: Kumar Sanghavi for ST-Ericsson - * Author: Arun Murthy for ST-Ericsson - * License terms: GNU General Public License (GPL) version 2 - */ - -#ifndef __SHRM_DRIVER_H__ -#define __SHRM_DRIVER_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#define ISA_DEVICES 6 - -#define BOOT_INIT (0) -#define BOOT_INFO_SYNC (1) -#define BOOT_DONE (2) -#define BOOT_UNKNOWN (3) - -/** - * struct shrm_dev - shrm device information - * @ca_wake_irq: CMT wake interrupt number - * @ac_read_notif_0_irq: ape-cmt common channel read notify interrupt - * @ac_read_notif_1_irq: ape-cmt audio channel read notify interrupt - * @ca_msg_pending_notif_0_irq: cmt-ape common channel msg pending interrupt - * @ca_msg_pending_notif_1_irq: cmt-ape audio channel msg pending interrupt - * @intr_base: interrupt base register address - * @ape_common_fifo_base: ape side common channel fifo base addr - * @ape_audio_fifo_base: ape side audio channel fifo base addr - * @cmt_common_fifo_base: cmt side common channel fifo base addr - * @cmt_audio_fifo_base: cmt side audio channel fifo base addr - * @ape_common_fifo_base_phy: physical addr of ape common fifo - * @ape_audio_fifo_base_phy: physical addr of ape audio fifo - * @cmt_common_fifo_base_phy: physical addr of cmt common fifo - * @cmt_audio_fifo_base_phy: physical addr of cmt audio fifo - * @ape_common_fifo_size: ape side common channel fifo size - * @ape_audio_fifo_size: ape side audio channel fifo size - * @cmt_common_fifo_size: cmt side common channel fifo size - * @cmt_audio_fifo_size: cmt side audio channel fifo size - * @netdev_flag_up: flag to indicate up/down of netwok device - * @msr_flag: flag to check on-going MSR sequence - * @ac_common_shared_wptr: ape-cmt common channel write pointer - * @ac_common_shared_rptr: ape-cmt common channel read pointer - * @ca_common_shared_wptr: cmt-ape common channel write pointer - * @ca_common_shared_rptr: cmt-ape common channel read pointer - * @ac_audio_shared_wptr: ape-cmt audio channel write pointer - * @ac_audio_shared_rptr: ape-cmt audio channel read pointer - * @ca_audio_shared_wptr: cmt-ape audio channel write pointer - * @ca_audio_shared_rptr: cmt-ape audio channel read pointer - * @dev: pointer to the driver device - * @ndev: pointer to the network device structure - * @isa_context: pointer to t_isa_driver_sontext dtructure - * @shm_common_ch_wr_wq: work queue for writing to common channel - * @shm_audio_ch_wr_wq: workqueue for writing to audio channel - * @shm_ac_wake_wq: workqueue for receiving ape-cmt wake requests - * @shm_ca_wake_wq: workqueue for receiving cmt-ape wake requests - * @shm_ac_sleep_wq: workqueue for recieving ape-cmt sleep requests - * @send_ac_msg_pend_notify_0: work for handling pending message on common - * channel - * @send_ac_msg_pend_notify_1: work for handling pending message on audio - * channel - * @shm_ac_wake_req: work to send ape-cmt wake request - * @shm_ca_wake_req: work to send cmt-ape wake request - * @shm_ca_sleep_req: work to send cmt-ape sleep request - * @shm_ac_sleep_req: work to send ape-cmt sleep request - */ -struct shrm_dev { - u8 ca_wake_irq; - u8 ac_read_notif_0_irq; - u8 ac_read_notif_1_irq; - u8 ca_msg_pending_notif_0_irq; - u8 ca_msg_pending_notif_1_irq; - void __iomem *intr_base; - void __iomem *ape_common_fifo_base; - void __iomem *ape_audio_fifo_base; - void __iomem *cmt_common_fifo_base; - void __iomem *cmt_audio_fifo_base; - - u32 *ape_common_fifo_base_phy; - u32 *ape_audio_fifo_base_phy; - u32 *cmt_common_fifo_base_phy; - u32 *cmt_audio_fifo_base_phy; - - int ape_common_fifo_size; - int ape_audio_fifo_size; - int cmt_common_fifo_size; - int cmt_audio_fifo_size; - int netdev_flag_up; - int msr_flag; - - void __iomem *ac_common_shared_wptr; - void __iomem *ac_common_shared_rptr; - void __iomem *ca_common_shared_wptr; - void __iomem *ca_common_shared_rptr; - - void __iomem *ac_audio_shared_wptr; - void __iomem *ac_audio_shared_rptr; - void __iomem *ca_audio_shared_wptr; - void __iomem *ca_audio_shared_rptr; - - struct device *dev; - struct net_device *ndev; - struct modem *modem; - struct isa_driver_context *isa_context; - struct workqueue_struct *shm_common_ch_wr_wq; - struct workqueue_struct *shm_audio_ch_wr_wq; - struct workqueue_struct *shm_ac_wake_wq; - struct workqueue_struct *shm_ca_wake_wq; - struct workqueue_struct *shm_ac_sleep_wq; - struct work_struct send_ac_msg_pend_notify_0; - struct work_struct send_ac_msg_pend_notify_1; - struct work_struct shm_ac_wake_req; - struct work_struct shm_ca_wake_req; - struct work_struct shm_ca_sleep_req; - struct work_struct shm_ac_sleep_req; -}; - -/** - * struct queue_element - information to add an element to queue - * @entry: list entry - * @offset: message offset - * @size: message size - * @no: total number of messages - */ -struct queue_element { - struct list_head entry; - u32 offset; - u32 size; - u32 no; -}; - -/** - * struct message_queue - ISI, RPC, AUDIO, SECURITY message queue information - * @fifo_base: pointer to the respective fifo base - * @size: size of the data to be read - * @readptr: fifo read pointer - * @writeptr: fifo write pointer - * @no: total number of messages - * @update_lock: spinlock for protecting the queue read operation - * @q_rp: queue write pointer - * @wq_readable: wait queue head - * @msg_list: message list - * @shrm: pointer to shrm device information structure - */ -struct message_queue { - u8 *fifo_base; - u32 size; - u32 readptr; - u32 writeptr; - u32 no; - spinlock_t update_lock; - atomic_t q_rp; - wait_queue_head_t wq_readable; - struct list_head msg_list; - struct shrm_dev *shrm; -}; - -/** - * struct isadev_context - shrm char interface context - * @dl_queue: structre to store the queue related info - * @device_id: message id(ISI, RPC, AUDIO, SECURITY) - * @addr: device addresses. - */ -struct isadev_context { - struct message_queue dl_queue; - u8 device_id; - void *addr; -}; - -/** - * struct isa_driver_context - shrm char interface device information - * @is_open: flag to check the usage of queue - * @isadev: pointer to struct t_isadev_context - * @common_tx: spinlock for protecting common channel - * @tx_audio_mutex: mutex for protecting audio channel - * @cdev: character device structre - * @shm_class: pointer to the class structure - */ -struct isa_driver_context { - atomic_t is_open[ISA_DEVICES]; - struct isadev_context *isadev; - spinlock_t common_tx; - struct mutex tx_audio_mutex; - struct cdev cdev; - struct class *shm_class; -}; - -#endif diff --git a/arch/arm/mach-ux500/include/mach/shrm_net.h b/arch/arm/mach-ux500/include/mach/shrm_net.h deleted file mode 100644 index a97b276ee15..00000000000 --- a/arch/arm/mach-ux500/include/mach/shrm_net.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2009 - * - * Author: Kumar Sanghvi for ST-Ericsson - * License terms: GNU General Public License (GPL) version 2 - */ - -#ifndef __SHRM_NET_H -#define __SHRM_NET_H - -#define SHRM_HLEN 1 -#define PHONET_ALEN 1 - -#define PN_PIPE 0xD9 -#define PN_DEV_HOST 0x00 -#define PN_LINK_ADDR 0x26 -#define PN_TX_QUEUE_LEN 3 - -#define RESOURCE_ID_INDEX 3 -#define SRC_OBJ_INDEX 7 -#define MSG_ID_INDEX 9 -#define PIPE_HDL_INDEX 10 -#define NETLINK_SHRM 20 - -/** - * struct shrm_net_iface_priv - shrm net interface device information - * @shrm_device: pointer to the shrm device information structure - * @iface_num: flag used to indicate the up/down of netdev - */ -struct shrm_net_iface_priv { - struct shrm_dev *shrm_device; - unsigned int iface_num; -}; - -int shrm_register_netdev(struct shrm_dev *shrm_dev_data); -int shrm_net_receive(struct net_device *dev); -int shrm_suspend_netdev(struct net_device *dev); -int shrm_resume_netdev(struct net_device *dev); -int shrm_stop_netdev(struct net_device *dev); -int shrm_restart_netdev(struct net_device *dev); -int shrm_start_netdev(struct net_device *dev); -void shrm_unregister_netdev(struct shrm_dev *shrm_dev_data); - -#endif /* __SHRM_NET_H */ diff --git a/arch/arm/mach-ux500/include/mach/shrm_private.h b/arch/arm/mach-ux500/include/mach/shrm_private.h deleted file mode 100644 index 1f09e7bef94..00000000000 --- a/arch/arm/mach-ux500/include/mach/shrm_private.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Biju Das for ST-Ericsson - * Author: Kumar Sanghavi for ST-Ericsson - * Author: Arun Murthy for ST-Ericsson - * License terms: GNU General Public License (GPL) version 2 - */ - -#ifndef __SHRM_PRIVATE_INCLUDED -#define __SHRM_PRIVATE_INCLUDED - -#include -#include -#include -#include - -#define GOP_OUTPUT_REGISTER_BASE (0x0) -#define GOP_SET_REGISTER_BASE (0x4) -#define GOP_CLEAR_REGISTER_BASE (0x8) -#define GOP_TOGGLE_REGISTER_BASE (0xc) - - -#define GOP_AUDIO_AC_READ_NOTIFICATION_BIT (0) -#define GOP_AUDIO_CA_MSG_PENDING_NOTIFICATION_BIT (1) -#define GOP_COMMON_AC_READ_NOTIFICATION_BIT (2) -#define GOP_COMMON_CA_MSG_PENDING_NOTIFICATION_BIT (3) -#define GOP_CA_WAKE_REQ_BIT (7) -#define GOP_AUDIO_CA_READ_NOTIFICATION_BIT (23) -#define GOP_AUDIO_AC_MSG_PENDING_NOTIFICATION_BIT (24) -#define GOP_COMMON_CA_READ_NOTIFICATION_BIT (25) -#define GOP_COMMON_AC_MSG_PENDING_NOTIFICATION_BIT (26) -#define GOP_CA_WAKE_ACK_BIT (27) - -#define L2_MSG_MAPID_OFFSET (24) -#define L1_MSG_MAPID_OFFSET (28) - -#define SHRM_SLEEP_STATE (0) -#define SHRM_PTR_FREE (1) -#define SHRM_PTR_BUSY (2) -#define SHRM_IDLE (3) - -#define ISI_MESSAGING (0) -#define RPC_MESSAGING (1) -#define AUDIO_MESSAGING (2) -#define SECURITY_MESSAGING (3) -#define COMMON_LOOPBACK_MESSAGING (0xC0) -#define AUDIO_LOOPBACK_MESSAGING (0x80) - -#define COMMON_CHANNEL 0 -#define AUDIO_CHANNEL 1 - -typedef void (*MSG_PENDING_NOTIF)(const u32 Wptr); - -/** - * struct fifo_write_params - parameters used for FIFO write operation. - * @writer_local_rptr: pointer to local read buffer - * @writer_local_wptr: pointer to local write buffer - * @shared_wptr: write pointer shared by cmt and ape - * @shared_rptr: read pointer shared by cmt and ape - * @availablesize: available memory in fifo - * @end_addr_fifo: fifo end addr - * @fifo_virtual_addr: fifo virtual addr - * @fifo_update_lock: spin lock to update fifo. - * - * On writting a message to FIFO the same has to be read by the modem before - * writing the next message to the FIFO. In oder to over come this a local - * write and read pointer is used for internal purpose. - */ -struct fifo_write_params { - u32 writer_local_rptr; - u32 writer_local_wptr; - u32 shared_wptr; - u32 shared_rptr; - u32 availablesize; - u32 end_addr_fifo; - u32 *fifo_virtual_addr; - spinlock_t fifo_update_lock; -} ; - -/** - * struct fifo_read_params - parameters used for FIFO read operation - * @reader_local_rptr: pointer to local read buffer - * @reader_local_wptr: pointer to local write buffer - * @shared_wptr: write pointer shared by cmt and ape - * @shared_rptr: read pointer shared by cmt and ape - * @availablesize: available memory in fifo - * @end_addr_fifo: fifo end add - * @fifo_virtual_addr: fifo virtual addr - */ -struct fifo_read_params{ - u32 reader_local_rptr; - u32 reader_local_wptr; - u32 shared_wptr; - u32 shared_rptr; - u32 availablesize; - u32 end_addr_fifo; - u32 *fifo_virtual_addr; - -} ; - -int shrm_protocol_init(struct shrm_dev *shrm, - received_msg_handler common_rx_handler, - received_msg_handler audio_rx_handler); -void shrm_protocol_deinit(struct shrm_dev *shrm); -void shm_fifo_init(struct shrm_dev *shrm); -int shm_write_msg_to_fifo(struct shrm_dev *shrm, u8 channel, - u8 l2header, void *addr, u32 length); -int shm_write_msg(struct shrm_dev *shrm, - u8 l2_header, void *addr, u32 length); - -u8 is_the_only_one_unread_message(struct shrm_dev *shrm, - u8 channel, u32 length); -u8 read_remaining_messages_common(void); -u8 read_remaining_messages_audio(void); -u8 read_one_l2msg_audio(struct shrm_dev *shrm, - u8 *p_l2_msg, u32 *p_len); -u8 read_one_l2msg_common(struct shrm_dev *shrm, - u8 *p_l2_msg, u32 *p_len); -void receive_messages_common(struct shrm_dev *shrm); -void receive_messages_audio(struct shrm_dev *shrm); - -void update_ac_common_local_rptr(struct shrm_dev *shrm); -void update_ac_audio_local_rptr(struct shrm_dev *shrm); -void update_ca_common_local_wptr(struct shrm_dev *shrm); -void update_ca_audio_local_wptr(struct shrm_dev *shrm); -void update_ac_common_shared_wptr(struct shrm_dev *shrm); -void update_ac_audio_shared_wptr(struct shrm_dev *shrm); -void update_ca_common_shared_rptr(struct shrm_dev *shrm); -void update_ca_audio_shared_rptr(struct shrm_dev *shrm); - - -void get_writer_pointers(u8 msg_type, u32 *WriterLocalRptr, \ - u32 *WriterLocalWptr, u32 *SharedWptr); -void get_reader_pointers(u8 msg_type, u32 *ReaderLocalRptr, \ - u32 *ReaderLocalWptr, u32 *SharedRptr); -u8 read_boot_info_req(struct shrm_dev *shrm, - u32 *pConfig, - u32 *pVersion); -void write_boot_info_resp(struct shrm_dev *shrm, u32 Config, - u32 Version); - -void send_ac_msg_pending_notification_0(struct shrm_dev *shrm); -void send_ac_msg_pending_notification_1(struct shrm_dev *shrm); -void ca_msg_read_notification_0(struct shrm_dev *shrm); -void ca_msg_read_notification_1(struct shrm_dev *shrm); - -void set_ca_msg_0_read_notif_send(u8 val); -u8 get_ca_msg_0_read_notif_send(void); -void set_ca_msg_1_read_notif_send(u8 val); -u8 get_ca_msg_1_read_notif_send(void); - -irqreturn_t ca_wake_irq_handler(int irq, void *ctrlr); -irqreturn_t ac_read_notif_0_irq_handler(int irq, void *ctrlr); -irqreturn_t ac_read_notif_1_irq_handler(int irq, void *ctrlr); -irqreturn_t ca_msg_pending_notif_0_irq_handler(int irq, void *ctrlr); -irqreturn_t ca_msg_pending_notif_1_irq_handler(int irq, void *ctrlr); - -void shm_ca_msgpending_0_tasklet(unsigned long); -void shm_ca_msgpending_1_tasklet(unsigned long); -void shm_ac_read_notif_0_tasklet(unsigned long); -void shm_ac_read_notif_1_tasklet(unsigned long); -void shm_ca_wake_req_tasklet(unsigned long); - -u8 get_boot_state(void); - -int get_ca_wake_req_state(void); - -/* shrm character interface */ -int isa_init(struct shrm_dev *shrm); -void isa_exit(struct shrm_dev *shrm); -int add_msg_to_queue(struct message_queue *q, u32 size); -ssize_t isa_read(struct file *filp, char __user *buf, size_t len, - loff_t *ppos); -int get_size_of_new_msg(struct message_queue *q); -int remove_msg_from_queue(struct message_queue *q); -void shrm_char_reset_queues(struct shrm_dev *shrm); -int shrm_get_cdev_index(u8 l2_header); -int shrm_get_cdev_l2header(u8 idx); - -#endif diff --git a/drivers/char/shrm_char.c b/drivers/char/shrm_char.c index 6375d7f1ea3..31f1fedd704 100644 --- a/drivers/char/shrm_char.c +++ b/drivers/char/shrm_char.c @@ -15,13 +15,13 @@ #include #include #include +#include +#include +#include +#include #include #include -#include -#include -#include -#include #define NAME "IPC_ISA" diff --git a/drivers/misc/shrm/Kconfig b/drivers/misc/shrm/Kconfig deleted file mode 100644 index 6bafdeec5b9..00000000000 --- a/drivers/misc/shrm/Kconfig +++ /dev/null @@ -1,43 +0,0 @@ -# -# SHM HW kernel configuration -# -config U8500_SHRM - tristate "U8500 SHRM hardware driver" - depends on ARCH_U8500 && PHONET - default Y - ---help--- - If you say Y here, you will enable the STN8500 SHM hardware driver. - - If unsure, say N. -choice - prompt "Modem Image Version" - depends on U8500_SHRM - default SHRM_V1_UPDATES_VERSION - - config SHRM_V1_UPDATES_VERSION - depends on U8500_SHRM - bool "SHRM V1 UPDATES" - help - Modem Images with V1 Updates - -endchoice - -config U8500_SHRM_LOOP_BACK - tristate "U8500 SHRM loopback" - depends on U8500_SHRM - default n - ---help--- - If you say Y here, you will enable the shm loopback - - If unsure, say N. - -config U8500_SHRM_MODEM_SILENT_RESET - bool "U8500 SHRM Modem Silent Reset" - depends on U8500_SHRM - default n - ---help--- - If you say Y here, you will enable the modem silent reset feature - - If unsure, say N. - - diff --git a/drivers/misc/shrm/Makefile b/drivers/misc/shrm/Makefile deleted file mode 100644 index 8115c24920b..00000000000 --- a/drivers/misc/shrm/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# -# Makefile for SHRM drivers -# - -ifdef CONFIG_PHONET -u8500_shrm-objs := modem_shrm_driver.o shrm_fifo.o shrm_protocol.o -else -u8500_shrm-objs := shrm_driver.o shrm_fifo.o shrm_protocol.o -endif - -obj-$(CONFIG_U8500_SHRM) += u8500_shrm.o diff --git a/drivers/misc/shrm/modem_shrm_driver.c b/drivers/misc/shrm/modem_shrm_driver.c deleted file mode 100644 index a3c8d4bda1b..00000000000 --- a/drivers/misc/shrm/modem_shrm_driver.c +++ /dev/null @@ -1,671 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Biju Das for ST-Ericsson - * Author: Kumar Sanghvi for ST-Ericsson - * Author: Arun Murthy for ST-Ericsson - * License terms: GNU General Public License (GPL) version 2 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#ifdef CONFIG_HIGH_RES_TIMERS -#include -static struct hrtimer timer; -#endif -#include -#include -#include - -/* debug functionality */ -#define ISA_DEBUG 0 - -#define PHONET_TASKLET -#define MAX_RCV_LEN 2048 - -void do_phonet_rcv_tasklet(unsigned long unused); -struct tasklet_struct phonet_rcv_tasklet; - -/** - * audio_receive() - Receive audio channel completion callback - * @shrm: pointer to shrm device information structure - * @data: message pointer - * @n_bytes: message size - * @l2_header: L2 header/device ID 2->audio, 5->audio_loopback - * - * This fucntion is called from the audio receive handler. Copies the audio - * message from the FIFO to the AUDIO queue. The message is later copied from - * this queue to the user buffer through the char or net interface read - * operation. - */ -static int audio_receive(struct shrm_dev *shrm, void *data, - u32 n_bytes, u8 l2_header) -{ - u32 size = 0; - int ret = 0; - int idx; - u8 *psrc; - struct message_queue *q; - struct isadev_context *audiodev; - - dev_dbg(shrm->dev, "%s IN\n", __func__); - idx = shrm_get_cdev_index(l2_header); - if (idx < 0) { - dev_err(shrm->dev, "failed to get index\n"); - return idx; - } - audiodev = &shrm->isa_context->isadev[idx]; - q = &audiodev->dl_queue; - spin_lock(&q->update_lock); - /* Memcopy RX data first */ - if ((q->writeptr+n_bytes) >= q->size) { - psrc = (u8 *)data; - size = (q->size-q->writeptr); - /* Copy First Part of msg */ - memcpy((q->fifo_base+q->writeptr), psrc, size); - psrc += size; - /* Copy Second Part of msg at the top of fifo */ - memcpy(q->fifo_base, psrc, (n_bytes-size)); - } else { - memcpy((q->fifo_base+q->writeptr), data, n_bytes); - } - ret = add_msg_to_queue(q, n_bytes); - spin_unlock(&q->update_lock); - if (ret < 0) - dev_err(shrm->dev, "Adding a msg to message queue failed"); - dev_dbg(shrm->dev, "%s OUT\n", __func__); - return ret; -} - -/** - * common_receive() - Receive common channel completion callback - * @shrm: pointer to the shrm device information structure - * @data: message pointer - * @n_bytes: message size - * @l2_header: L2 header / device ID - * - * This function is called from the receive handler to copy the respective - * ISI, RPC, SECURITY message to its respective queue. The message is then - * copied from queue to the user buffer on char net interface read operation. - */ -static int common_receive(struct shrm_dev *shrm, void *data, - u32 n_bytes, u8 l2_header) -{ - u32 size = 0; - int ret = 0; - int idx; - u8 *psrc; - struct message_queue *q; - struct isadev_context *isa_dev; - - dev_dbg(shrm->dev, "%s IN\n", __func__); - idx = shrm_get_cdev_index(l2_header); - if (idx < 0) { - dev_err(shrm->dev, "failed to get index\n"); - return idx; - } - isa_dev = &shrm->isa_context->isadev[idx]; - q = &isa_dev->dl_queue; - spin_lock(&q->update_lock); - /* Memcopy RX data first */ - if ((q->writeptr+n_bytes) >= q->size) { - dev_dbg(shrm->dev, "Inside Loop Back\n"); - psrc = (u8 *)data; - size = (q->size-q->writeptr); - /* Copy First Part of msg */ - memcpy((q->fifo_base+q->writeptr), psrc, size); - psrc += size; - /* Copy Second Part of msg at the top of fifo */ - memcpy(q->fifo_base, psrc, (n_bytes-size)); - } else { - memcpy((q->fifo_base+q->writeptr), data, n_bytes); - } - ret = add_msg_to_queue(q, n_bytes); - spin_unlock(&q->update_lock); - if (ret < 0) { - dev_err(shrm->dev, "Adding a msg to message queue failed"); - return ret; - } - - - if (l2_header == ISI_MESSAGING) { - if (shrm->netdev_flag_up) { - dev_dbg(shrm->dev, - "scheduling the phonet tasklet from %s!\n", - __func__); - tasklet_schedule(&phonet_rcv_tasklet); - } - dev_dbg(shrm->dev, - "Out of phonet tasklet %s!!!\n", __func__); - } - dev_dbg(shrm->dev, "%s OUT\n", __func__); - return ret; -} - -/** - * rx_common_l2msg_handler() - common channel receive handler - * @l2_header: L2 header - * @msg: pointer to the receive buffer - * @length: length of the msg to read - * @shrm: pointer to shrm device information structure - * - * This function is called to receive the message from CaMsgPendingNotification - * interrupt handler. - */ -static void rx_common_l2msg_handler(u8 l2_header, - void *msg, u32 length, - struct shrm_dev *shrm) -{ - int ret = 0; - dev_dbg(shrm->dev, "%s IN\n", __func__); - - ret = common_receive(shrm, msg, length, l2_header); - if (ret < 0) - dev_err(shrm->dev, - "common receive with l2 header %d failed\n", l2_header); - - dev_dbg(shrm->dev, "%s OUT\n", __func__); -} - -/** - * rx_audio_l2msg_handler() - audio channel receive handler - * @l2_header: L2 header - * @msg: pointer to the receive buffer - * @length: length of the msg to read - * @shrm: pointer to shrm device information structure - * - * This function is called to receive the message from CaMsgPendingNotification - * interrupt handler. - */ -static void rx_audio_l2msg_handler(u8 l2_header, - void *msg, u32 length, - struct shrm_dev *shrm) -{ - int ret = 0; - - dev_dbg(shrm->dev, "%s IN\n", __func__); - ret = audio_receive(shrm, msg, length, l2_header); - if (ret < 0) - dev_err(shrm->dev, "audio receive failed\n"); - dev_dbg(shrm->dev, "%s OUT\n", __func__); -} - -static int __init shm_initialise_irq(struct shrm_dev *shrm) -{ - int err = 0; - - err = shrm_protocol_init(shrm, - rx_common_l2msg_handler, rx_audio_l2msg_handler); - if (err < 0) { - dev_err(shrm->dev, "SHM Protocol Init Failure\n"); - return err; - } - - err = request_irq(shrm->ca_wake_irq, - ca_wake_irq_handler, IRQF_TRIGGER_RISING, - "ca_wake-up", shrm); - if (err < 0) { - dev_err(shrm->dev, - "Unable to allocate shm tx interrupt line\n"); - free_irq(shrm->ca_wake_irq, shrm); - return err; - } - - err = request_irq(shrm->ac_read_notif_0_irq, - ac_read_notif_0_irq_handler, 0, - "ac_read_notif_0", shrm); - - if (err < 0) { - dev_err(shrm->dev, - "error ac_read_notif_0_irq interrupt line\n"); - goto irq_err1; - } - - err = request_irq(shrm->ac_read_notif_1_irq, - ac_read_notif_1_irq_handler, 0, - "ac_read_notif_1", shrm); - - if (err < 0) { - dev_err(shrm->dev, - "error ac_read_notif_1_irq interrupt line\n"); - goto irq_err2; - } - - err = request_irq(shrm->ca_msg_pending_notif_0_irq, - ca_msg_pending_notif_0_irq_handler, 0, - "ca_msg_pending_notif_0", shrm); - - if (err < 0) { - dev_err(shrm->dev, - "error ca_msg_pending_notif_0_irq line\n"); - goto irq_err3; - } - - err = request_irq(shrm->ca_msg_pending_notif_1_irq, - ca_msg_pending_notif_1_irq_handler, 0, - "ca_msg_pending_notif_1", shrm); - - if (err < 0) { - dev_err(shrm->dev, - "error ca_msg_pending_notif_1_irq interrupt line\n"); - goto irq_err4; - } - return err; -irq_err4: - free_irq(shrm->ca_msg_pending_notif_0_irq, shrm); -irq_err3: - free_irq(shrm->ac_read_notif_1_irq, shrm); -irq_err2: - free_irq(shrm->ac_read_notif_0_irq, shrm); -irq_err1: - free_irq(shrm->ca_wake_irq, shrm); - return err; -} - -static void free_shm_irq(struct shrm_dev *shrm) -{ - free_irq(shrm->ca_wake_irq, shrm); - free_irq(shrm->ac_read_notif_0_irq, shrm); - free_irq(shrm->ac_read_notif_1_irq, shrm); - free_irq(shrm->ca_msg_pending_notif_0_irq, shrm); - free_irq(shrm->ca_msg_pending_notif_1_irq, shrm); -} - - - -#ifdef CONFIG_HIGH_RES_TIMERS -static enum hrtimer_restart callback(struct hrtimer *timer) -{ - return HRTIMER_NORESTART; -} -#endif - -void do_phonet_rcv_tasklet(unsigned long unused) -{ - ssize_t ret; - struct shrm_dev *shrm = (struct shrm_dev *)unused; - - dev_dbg(shrm->dev, "%s IN\n", __func__); - for (;;) { - ret = shrm_net_receive(shrm->ndev); - if (ret == 0) { - dev_dbg(shrm->dev, "len is zero, queue empty\n"); - break; - } - if (ret < 0) { - dev_err(shrm->dev, "len < 0 !!! error!!!\n"); - break; - } - } - dev_dbg(shrm->dev, "%s OUT\n", __func__); -} - -static int shrm_probe(struct platform_device *pdev) -{ - int err = 0; - struct resource *res; - struct shrm_dev *shrm = NULL; - - shrm = kzalloc(sizeof(struct shrm_dev), GFP_KERNEL); - if (shrm == NULL) { - dev_err(&pdev->dev, - "Could not allocate memory for struct shm_dev\n"); - return -ENOMEM; - } - - shrm->dev = &pdev->dev; - shrm->modem = modem_get(shrm->dev, "u8500-shrm-modem"); - if (shrm->modem == NULL) { - dev_err(shrm->dev, " Could not retrieve the modem.\n"); - return -ENODEV; - } - - /* initialise the SHM */ - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(shrm->dev, - "Unable to map Ca Wake up interrupt\n"); - err = -EBUSY; - goto rollback_intr; - } - shrm->ca_wake_irq = res->start; - res = platform_get_resource(pdev, IORESOURCE_IRQ, 1); - - if (!res) { - dev_err(shrm->dev, - "Unable to map APE_Read_notif_common IRQ base\n"); - err = -EBUSY; - goto rollback_intr; - } - shrm->ac_read_notif_0_irq = res->start; - res = platform_get_resource(pdev, IORESOURCE_IRQ, 2); - - if (!res) { - dev_err(shrm->dev, - "Unable to map APE_Read_notif_audio IRQ base\n"); - err = -EBUSY; - goto rollback_intr; - } - shrm->ac_read_notif_1_irq = res->start; - res = platform_get_resource(pdev, IORESOURCE_IRQ, 3); - - if (!res) { - dev_err(shrm->dev, - "Unable to map Cmt_msg_pending_notif_common IRQbase\n"); - err = -EBUSY; - goto rollback_intr; - } - shrm->ca_msg_pending_notif_0_irq = res->start; - res = platform_get_resource(pdev, IORESOURCE_IRQ, 4); - - if (!res) { - dev_err(shrm->dev, - "Unable to map Cmt_msg_pending_notif_audio IRQ base\n"); - err = -EBUSY; - goto rollback_intr; - } - shrm->ca_msg_pending_notif_1_irq = res->start; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - if (!res) { - dev_err(shrm->dev, - "Could not get SHM IO memory information\n"); - err = -ENODEV; - goto rollback_intr; - } - shrm->intr_base = (void __iomem *)ioremap_nocache(res->start, - res->end - res->start + 1); - if (!(shrm->intr_base)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_intr; - } - shrm->ape_common_fifo_base_phy = - (u32 *)U8500_SHM_FIFO_APE_COMMON_BASE; - shrm->ape_common_fifo_base = - (void __iomem *)ioremap_nocache( - U8500_SHM_FIFO_APE_COMMON_BASE, - SHM_FIFO_0_SIZE); - shrm->ape_common_fifo_size = (SHM_FIFO_0_SIZE)/4; - - if (!(shrm->ape_common_fifo_base)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_ape_common_fifo_base; - } - shrm->cmt_common_fifo_base_phy = - (u32 *)U8500_SHM_FIFO_CMT_COMMON_BASE; - shrm->cmt_common_fifo_base = - (void __iomem *)ioremap_nocache( - U8500_SHM_FIFO_CMT_COMMON_BASE, SHM_FIFO_0_SIZE); - shrm->cmt_common_fifo_size = (SHM_FIFO_0_SIZE)/4; - - if (!(shrm->cmt_common_fifo_base)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_cmt_common_fifo_base; - } - shrm->ape_audio_fifo_base_phy = - (u32 *)U8500_SHM_FIFO_APE_AUDIO_BASE; - shrm->ape_audio_fifo_base = - (void __iomem *)ioremap_nocache(U8500_SHM_FIFO_APE_AUDIO_BASE, - SHM_FIFO_1_SIZE); - shrm->ape_audio_fifo_size = (SHM_FIFO_1_SIZE)/4; - - if (!(shrm->ape_audio_fifo_base)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_ape_audio_fifo_base; - } - shrm->cmt_audio_fifo_base_phy = - (u32 *)U8500_SHM_FIFO_CMT_AUDIO_BASE; - shrm->cmt_audio_fifo_base = - (void __iomem *)ioremap_nocache(U8500_SHM_FIFO_CMT_AUDIO_BASE, - SHM_FIFO_1_SIZE); - shrm->cmt_audio_fifo_size = (SHM_FIFO_1_SIZE)/4; - - if (!(shrm->cmt_audio_fifo_base)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_cmt_audio_fifo_base; - } - shrm->ac_common_shared_wptr = - (void __iomem *)ioremap(SHM_ACFIFO_0_WRITE_AMCU, SHM_PTR_SIZE); - - if (!(shrm->ac_common_shared_wptr)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_ac_common_shared_wptr; - } - shrm->ac_common_shared_rptr = - (void __iomem *)ioremap(SHM_ACFIFO_0_READ_AMCU, SHM_PTR_SIZE); - - if (!(shrm->ac_common_shared_rptr)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_map; - } - shrm->ca_common_shared_wptr = - (void __iomem *)ioremap(SHM_CAFIFO_0_WRITE_AMCU, SHM_PTR_SIZE); - - if (!(shrm->ca_common_shared_wptr)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_map; - } - shrm->ca_common_shared_rptr = - (void __iomem *)ioremap(SHM_CAFIFO_0_READ_AMCU, SHM_PTR_SIZE); - - if (!(shrm->ca_common_shared_rptr)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_map; - } - shrm->ac_audio_shared_wptr = - (void __iomem *)ioremap(SHM_ACFIFO_1_WRITE_AMCU, SHM_PTR_SIZE); - - if (!(shrm->ac_audio_shared_wptr)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_map; - } - shrm->ac_audio_shared_rptr = - (void __iomem *)ioremap(SHM_ACFIFO_1_READ_AMCU, SHM_PTR_SIZE); - - if (!(shrm->ac_audio_shared_rptr)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_map; - } - shrm->ca_audio_shared_wptr = - (void __iomem *)ioremap(SHM_CAFIFO_1_WRITE_AMCU, SHM_PTR_SIZE); - - if (!(shrm->ca_audio_shared_wptr)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_map; - } - shrm->ca_audio_shared_rptr = - (void __iomem *)ioremap(SHM_CAFIFO_1_READ_AMCU, SHM_PTR_SIZE); - - if (!(shrm->ca_audio_shared_rptr)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_map; - } - - if (isa_init(shrm) != 0) { - dev_err(shrm->dev, "Driver Initialization Error\n"); - err = -EBUSY; - } - /* install handlers and tasklets */ - if (shm_initialise_irq(shrm)) { - dev_err(shrm->dev, - "shm error in interrupt registration\n"); - goto rollback_irq; - } -#ifdef CONFIG_HIGH_RES_TIMERS - hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - timer.function = callback; - hrtimer_start(&timer, ktime_set(0, 2*NSEC_PER_MSEC), HRTIMER_MODE_REL); -#endif - err = shrm_register_netdev(shrm); - if (err < 0) - goto rollback_irq; - - tasklet_init(&phonet_rcv_tasklet, do_phonet_rcv_tasklet, 0); - phonet_rcv_tasklet.data = (unsigned long)shrm; - - platform_set_drvdata(pdev, shrm); - - return err; -rollback_irq: - free_shm_irq(shrm); -rollback_map: - iounmap(shrm->ac_common_shared_wptr); - iounmap(shrm->ac_common_shared_rptr); - iounmap(shrm->ca_common_shared_wptr); - iounmap(shrm->ca_common_shared_rptr); - iounmap(shrm->ac_audio_shared_wptr); - iounmap(shrm->ac_audio_shared_rptr); - iounmap(shrm->ca_audio_shared_wptr); - iounmap(shrm->ca_audio_shared_rptr); -rollback_ac_common_shared_wptr: - iounmap(shrm->cmt_audio_fifo_base); -rollback_cmt_audio_fifo_base: - iounmap(shrm->ape_audio_fifo_base); -rollback_ape_audio_fifo_base: - iounmap(shrm->cmt_common_fifo_base); -rollback_cmt_common_fifo_base: - iounmap(shrm->ape_common_fifo_base); -rollback_ape_common_fifo_base: - iounmap(shrm->intr_base); -rollback_intr: - kfree(shrm); - return err; -} - -static int __exit shrm_remove(struct platform_device *pdev) -{ - struct shrm_dev *shrm = platform_get_drvdata(pdev); - - free_shm_irq(shrm); - iounmap(shrm->intr_base); - iounmap(shrm->ape_common_fifo_base); - iounmap(shrm->cmt_common_fifo_base); - iounmap(shrm->ape_audio_fifo_base); - iounmap(shrm->cmt_audio_fifo_base); - iounmap(shrm->ac_common_shared_wptr); - iounmap(shrm->ac_common_shared_rptr); - iounmap(shrm->ca_common_shared_wptr); - iounmap(shrm->ca_common_shared_rptr); - iounmap(shrm->ac_audio_shared_wptr); - iounmap(shrm->ac_audio_shared_rptr); - iounmap(shrm->ca_audio_shared_wptr); - iounmap(shrm->ca_audio_shared_rptr); - shrm_unregister_netdev(shrm); - isa_exit(shrm); - kfree(shrm); - - return 0; -} - -#ifdef CONFIG_PM -/** - * u8500_shrm_suspend() - This routine puts the SHRM in to sustend state. - * @dev: pointer to device structure. - * - * This routine checks the current ongoing communication with Modem by - * examining the ca_wake state and prevents suspend if modem communication - * is on-going. - * If ca_wake = 1 (high), modem comm. is on-going; don't suspend - * If ca_wake = 0 (low), no comm. with modem on-going.Allow suspend - */ -int u8500_shrm_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct shrm_dev *shrm = platform_get_drvdata(pdev); - int err; - - dev_dbg(&pdev->dev, "%s called...\n", __func__); - dev_dbg(&pdev->dev, "ca_wake_req_state = %x\n", - get_ca_wake_req_state()); - - /* if ca_wake_req is high, prevent system suspend */ - if (!get_ca_wake_req_state()) { - err = shrm_suspend_netdev(shrm->ndev); - return err; - } else - return -EBUSY; -} - -/** - * u8500_shrm_resume() - This routine resumes the SHRM from suspend state. - * @dev: pointer to device structure - * - * This routine restore back the current state of the SHRM - */ -int u8500_shrm_resume(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct shrm_dev *shrm = platform_get_drvdata(pdev); - int err; - - dev_dbg(&pdev->dev, "%s called...\n", __func__); - err = shrm_resume_netdev(shrm->ndev); - - return err; -} - -static const struct dev_pm_ops shrm_dev_pm_ops = { - .suspend_noirq = u8500_shrm_suspend, - .resume_noirq = u8500_shrm_resume, -}; -#endif - -static struct platform_driver shrm_driver = { - .remove = __exit_p(shrm_remove), - .driver = { - .name = "u8500_shrm", - .owner = THIS_MODULE, -#ifdef CONFIG_PM - .pm = &shrm_dev_pm_ops, -#endif - }, -}; - -static int __init shrm_driver_init(void) -{ - return platform_driver_probe(&shrm_driver, shrm_probe); -} - -static void __exit shrm_driver_exit(void) -{ - platform_driver_unregister(&shrm_driver); -} - -module_init(shrm_driver_init); -module_exit(shrm_driver_exit); - -MODULE_AUTHOR("Biju Das, Kumar Sanghvi, Arun Murthy"); -MODULE_DESCRIPTION("Shared Memory Modem Driver Interface"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/shrm/shrm_driver.c b/drivers/misc/shrm/shrm_driver.c deleted file mode 100644 index 6277794608a..00000000000 --- a/drivers/misc/shrm/shrm_driver.c +++ /dev/null @@ -1,1439 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Biju Das for ST-Ericsson - * Author: Kumar Sanghvi for ST-Ericsson - * Author: Arun Murthy for ST-Ericsson - * License terms: GNU General Public License (GPL) version 2 - */ - -#define DEBUG - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - - -#ifdef CONFIG_HIGH_RES_TIMERS -#include -static struct hrtimer timer; -#endif - - -#define NAME "IPC_ISA" -#define ISA_DEVICES 4 -/**debug functionality*/ -#define ISA_DEBUG 0 - -#define ISI_MESSAGING (0) -#define RPC_MESSAGING (1) -#define AUDIO_MESSAGING (2) -#define SECURITY_MESSAGING (3) - -#define SIZE_OF_FIFO (512*1024) - -static u8 message_fifo[4][SIZE_OF_FIFO]; - -static u8 wr_isi_msg[10*1024]; -static u8 wr_rpc_msg[10*1024]; -static u8 wr_sec_msg[10*1024]; -static u8 wr_audio_msg[10*1024]; - -/* global data */ -/* - * int major:This variable is exported to user as module_param to specify - * major number at load time - */ -static int major; -module_param(major, int, 0); -MODULE_PARM_DESC(major, "Major device number"); -/* global fops mutex */ -static DEFINE_MUTEX(isa_lock); -rx_cb common_rx; -rx_cb audio_rx; - - -static int isi_receive(struct shrm_dev *shrm, void *data, u32 n_bytes); -static int rpc_receive(struct shrm_dev *shrm, void *data, u32 n_bytes); -static int audio_receive(struct shrm_dev *shrm, void *data, u32 n_bytes); -static int security_receive(struct shrm_dev *shrm, - void *data, u32 n_bytes); - -static void rx_common_l2msg_handler(u8 l2_header, - void *msg, u32 length, - struct shrm_dev *shrm) -{ - int ret = 0; -#ifdef CONFIG_U8500_SHRM_LOOP_BACK - u8 *pdata; -#endif - dev_dbg(shrm->dev, "%s IN\n", __func__); - - switch (l2_header) { - case ISI_MESSAGING: - ret = isi_receive(shrm, msg, length); - if (ret < 0) - dev_err(shrm->dev, "isi receive failed\n"); - break; - case RPC_MESSAGING: - ret = rpc_receive(shrm, msg, length); - if (ret < 0) - dev_err(shrm->dev, "rpc receive failed\n"); - break; - case SECURITY_MESSAGING: - ret = security_receive(shrm, msg, length); - if (ret < 0) - dev_err(shrm->dev, - "security receive failed\n"); - break; -#ifdef CONFIG_U8500_SHRM_LOOP_BACK - case COMMMON_LOOPBACK_MESSAGING: - pdata = (u8 *)msg; - if ((*pdata == 0x50) || (*pdata == 0xAF)) { - ret = isi_receive(shrm, msg, length); - if (ret < 0) - dev_err(shrm->dev, "isi receive failed\n"); - } else if ((*pdata == 0x0A) || (*pdata == 0xF5)) { - ret = rpc_receive(shrm, msg, length); - if (ret < 0) - dev_err(shrm->dev, "rpc receive failed\n"); - } else if ((*pdata == 0xFF) || (*pdata == 0x00)) { - ret = security_receive(shrm, msg, length); - if (ret < 0) - dev_err(shrm->dev, - "security receive failed\n"); - } - break; -#endif - default: - break; - } - dev_dbg(shrm->dev, "%s OUT\n", __func__); -} - -static void rx_audio_l2msg_handler(u8 l2_header, - void *msg, u32 length, - struct shrm_dev *shrm) -{ - int ret = 0; - - dev_dbg(shrm->dev, "%s IN\n", __func__); - audio_receive(shrm, msg, length); - if (ret < 0) - dev_err(shrm->dev, "audio receive failed\n"); - dev_dbg(shrm->dev, "%s OUT\n", __func__); -} - -static int __init shm_initialise_irq(struct shrm_dev *shrm) -{ - int err = 0; - - shrm_protocol_init(shrm, - rx_common_l2msg_handler, rx_audio_l2msg_handler); - - err = request_irq(shrm->ca_wake_irq, - ca_wake_irq_handler, IRQF_TRIGGER_RISING, - "ca_wake-up", shrm); - if (err < 0) { - dev_err(shrm->dev, - "Unable to allocate shm tx interrupt line\n"); - return err; - } - - err = request_irq(shrm->ac_read_notif_0_irq, - ac_read_notif_0_irq_handler, 0, - "ac_read_notif_0", shrm); - if (err < 0) { - dev_err(shrm->dev, - "error ac_read_notif_0_irq interrupt line\n"); - goto irq_err1; - } - - err = request_irq(shrm->ac_read_notif_1_irq, - ac_read_notif_1_irq_handler, 0, - "ac_read_notif_1", shrm); - if (err < 0) { - dev_err(shrm->dev, - "error ac_read_notif_1_irq interrupt line\n"); - goto irq_err2; - } - - err = request_irq(shrm->ca_msg_pending_notif_0_irq, - ca_msg_pending_notif_0_irq_handler, 0, - "ca_msg_pending_notif_0", shrm); - if (err < 0) { - dev_err(shrm->dev, - "error ca_msg_pending_notif_0_irq line\n"); - goto irq_err3; - } - - err = request_irq(shrm->ca_msg_pending_notif_1_irq, - ca_msg_pending_notif_1_irq_handler, 0, - "ca_msg_pending_notif_1", shrm); - if (err < 0) { - dev_err(shrm->dev, - "error ca_msg_pending_notif_1_irq interrupt line\n"); - goto irq_err4; - } - - return err; - -irq_err4: - free_irq(shrm->ca_msg_pending_notif_0_irq, shrm); -irq_err3: - free_irq(shrm->ac_read_notif_1_irq, shrm); -irq_err2: - free_irq(shrm->ac_read_notif_0_irq, shrm); -irq_err1: - free_irq(shrm->ca_wake_irq, shrm); - return err; -} - -static void free_shm_irq(struct shrm_dev *shrm) -{ - free_irq(shrm->ca_wake_irq, shrm); - free_irq(shrm->ac_read_notif_0_irq, shrm); - free_irq(shrm->ac_read_notif_1_irq, shrm); - free_irq(shrm->ca_msg_pending_notif_0_irq, shrm); - free_irq(shrm->ca_msg_pending_notif_1_irq, shrm); -} - -/** - * create_queue() - To create FIFO for Tx and Rx message buffering. - * @q: message queue. - * @devicetype: device type 0-isi,1-rpc,2-audio,3-security. - * - * This function creates a FIFO buffer of n_bytes size using - * dma_alloc_coherent(). It also initializes all queue handling - * locks, queue management pointers. It also initializes message list - * which occupies this queue. - * - * It return -ENOMEM in case of no memory. - */ -static int create_queue(struct message_queue *q, u32 devicetype, - struct shrm_dev *shrm) -{ - q->fifo_base = (u8 *)&message_fifo[devicetype]; - q->size = SIZE_OF_FIFO; - q->readptr = 0; - q->writeptr = 0; - q->no = 0; - q->shrm = shrm; - spin_lock_init(&q->update_lock); - INIT_LIST_HEAD(&q->msg_list); - init_waitqueue_head(&q->wq_readable); - atomic_set(&q->q_rp, 0); - - return 0; -} -/** - * delete_queue() - To delete FIFO and assiciated memory. - * @q: message queue - * - * This function deletes FIFO created using create_queue() function. - * It resets queue management pointers. - */ -static void delete_queue(struct message_queue *q) -{ - q->size = 0; - q->readptr = 0; - q->writeptr = 0; -} - -/** - * add_msg_to_queue() - Add a message inside inside queue - * - * @q: message queue - * @size: size in bytes - * - * This function tries to allocate n_bytes of size in FIFO q. - * It returns negative number when no memory can be allocated - * currently. - */ -int add_msg_to_queue(struct message_queue *q, u32 size) -{ - struct queue_element *new_msg = NULL; - struct shrm_dev *shrm = q->shrm; - - dev_dbg(shrm->dev, "%s IN q->writeptr=%d\n", - __func__, q->writeptr); - new_msg = kmalloc(sizeof(struct queue_element), - GFP_KERNEL|GFP_ATOMIC); - - if (new_msg == NULL) { - dev_err(shrm->dev, "memory overflow inside while(1)\n"); - return -ENOMEM; - } - new_msg->offset = q->writeptr; - new_msg->size = size; - new_msg->no = q->no++; - - /* check for overflow condition */ - if (q->readptr <= q->writeptr) { - if (((q->writeptr-q->readptr) + size) >= q->size) { - dev_err(shrm->dev, "Buffer overflow !!\n"); - BUG_ON(((q->writeptr-q->readptr) + size) >= q->size); - } - } else { - if ((q->writeptr + size) >= q->readptr) { - dev_err(shrm->dev, "Buffer overflow !!\n"); - BUG_ON((q->writeptr + size) >= q->readptr); - } - } - q->writeptr = (q->writeptr + size) % q->size; - if (list_empty(&q->msg_list)) { - list_add_tail(&new_msg->entry, &q->msg_list); - /* There can be 2 blocking calls read and another select */ - - atomic_set(&q->q_rp, 1); - wake_up_interruptible(&q->wq_readable); - } else - list_add_tail(&new_msg->entry, &q->msg_list); - - dev_dbg(shrm->dev, "%s OUT\n", __func__); - return 0; -} - -/** - * remove_msg_from_queue() - To remove a message from the msg queue. - * - * @q: message queue - * - * This function delets a message from the message list associated with message - * queue q and also updates read ptr. - * If the message list is empty, then, event is set to block the select and - * read calls of the paricular queue. - * - * The message list is FIFO style and message is always added to tail and - * removed from head. - */ - -int remove_msg_from_queue(struct message_queue *q) -{ - struct queue_element *old_msg = NULL; - struct shrm_dev *shrm = q->shrm; - struct list_head *msg; - - dev_dbg(shrm->dev, "%s IN q->readptr %d\n", - __func__, q->readptr); - - list_for_each(msg, &q->msg_list) { - old_msg = list_entry(msg, struct queue_element, entry); - if (old_msg == NULL) { - dev_err(shrm->dev, ":no message found\n"); - return -EFAULT; - } - break; - } - list_del(msg); - q->readptr = (q->readptr + old_msg->size) % q->size; - if (list_empty(&q->msg_list)) { - dev_dbg(shrm->dev, "List is empty setting RP= 0\n"); - atomic_set(&q->q_rp, 0); - } - kfree(old_msg); - - dev_dbg(shrm->dev, "%s OUT\n", __func__); - return 0; -} - -/** - * get_size_of_new_msg() - retrieve new message from message list - * - * @q: message queue - * - * This function will retrieve most recent message from the corresponding - * queue list. New message is always retrieved from head side. - * It returns new message no, offset if FIFO and size. - */ -int get_size_of_new_msg(struct message_queue *q) -{ - struct queue_element *new_msg = NULL; - struct list_head *msg_list; - struct shrm_dev *shrm = q->shrm; - - dev_dbg(shrm->dev, "%s IN\n", __func__); - - spin_lock_bh(&q->update_lock); - list_for_each(msg_list, &q->msg_list) { - new_msg = list_entry(msg_list, struct queue_element, entry); - if (new_msg == NULL) { - spin_unlock_bh(&q->update_lock); - dev_err(shrm->dev, "no message found\n"); - return -1; - } - break; - } - spin_unlock_bh(&q->update_lock); - - dev_dbg(shrm->dev, "%s OUT\n", __func__); - return new_msg->size; -} - -/** - * isi_receive() - Rx Completion callback - * - * @data:message pointer - * @n_bytes:message size - * - * This function is a callback to indicate ISI message reception is complete. - * It updates Writeptr of the Fifo - */ -static int isi_receive(struct shrm_dev *shrm, - void *data, u32 n_bytes) -{ - u32 size = 0; - int ret = 0; - u8 *psrc; - struct message_queue *q; - struct isadev_context *isidev = &shrm->isa_context->isadev[0]; - - dev_dbg(shrm->dev, "%s IN\n", __func__); - q = &isidev->dl_queue; - spin_lock(&q->update_lock); - /* Memcopy RX data first */ - if ((q->writeptr+n_bytes) >= q->size) { - dev_dbg(shrm->dev, "Inside Loop Back\n"); - psrc = (u8 *)data; - size = (q->size-q->writeptr); - /* Copy First Part of msg */ - memcpy((q->fifo_base+q->writeptr), psrc, size); - psrc += size; - /* Copy Second Part of msg at the top of fifo */ - memcpy(q->fifo_base, psrc, (n_bytes-size)); - } else { - memcpy((q->fifo_base+q->writeptr), data, n_bytes); - } - ret = add_msg_to_queue(q, n_bytes); - if (ret < 0) - dev_err(shrm->dev, "Adding msg to message queue failed\n"); - spin_unlock(&q->update_lock); - dev_dbg(shrm->dev, "%s OUT\n", __func__); - return ret; -} - -/** - * rpc_receive() - Rx Completion callback - * - * @data:message pointer - * @n_bytes:message size - * - * This function is a callback to indicate RPC message reception is complete. - * It updates Writeptr of the Fifo - */ -static int rpc_receive(struct shrm_dev *shrm, - void *data, u32 n_bytes) -{ - u32 size = 0; - int ret = 0; - u8 *psrc; - struct message_queue *q; - struct isadev_context *rpcdev = &shrm->isa_context->isadev[1]; - - dev_dbg(shrm->dev, "%s IN\n", __func__); - q = &rpcdev->dl_queue; - spin_lock(&q->update_lock); - /* Memcopy RX data first */ - if ((q->writeptr+n_bytes) >= q->size) { - psrc = (u8 *)data; - size = (q->size-q->writeptr); - /* Copy First Part of msg */ - memcpy((q->fifo_base+q->writeptr), psrc, size); - psrc += size; - /* Copy Second Part of msg at the top of fifo */ - memcpy(q->fifo_base, psrc, (n_bytes-size)); - } else { - memcpy((q->fifo_base+q->writeptr), data, n_bytes); - } - - ret = add_msg_to_queue(q, n_bytes); - if (ret < 0) - dev_err(shrm->dev, "Adding msg to message queue failed\n"); - spin_unlock(&q->update_lock); - dev_dbg(shrm->dev, "%s OUT\n", __func__); - return ret; -} - -/** - * audio_receive() - Rx Completion callback - * - * @data:message pointer - * @n_bytes:message size - * - * This function is a callback to indicate audio message reception is complete. - * It updates Writeptr of the Fifo - */ -static int audio_receive(struct shrm_dev *shrm, - void *data, u32 n_bytes) -{ - u32 size = 0; - int ret = 0; - u8 *psrc; - struct message_queue *q; - struct isadev_context *audiodev = &shrm->isa_context->isadev[2]; - - dev_dbg(shrm->dev, "%s IN\n", __func__); - q = &audiodev->dl_queue; - spin_lock(&q->update_lock); - /* Memcopy RX data first */ - if ((q->writeptr+n_bytes) >= q->size) { - psrc = (u8 *)data; - size = (q->size-q->writeptr); - /* Copy First Part of msg */ - memcpy((q->fifo_base+q->writeptr), psrc, size); - psrc += size; - /* Copy Second Part of msg at the top of fifo */ - memcpy(q->fifo_base, psrc, (n_bytes-size)); - } else { - memcpy((q->fifo_base+q->writeptr), data, n_bytes); - } - ret = add_msg_to_queue(q, n_bytes); - if (ret < 0) - dev_err(shrm->dev, "Adding msg to message queue failed\n"); - spin_unlock(&q->update_lock); - dev_dbg(shrm->dev, "%s OUT\n", __func__); - return ret; -} - -/** - * security_receive() - Rx Completion callback - * - * @data:message pointer - * @n_bytes: message size - * - * This function is a callback to indicate security message reception - * is complete.It updates Writeptr of the Fifo - */ -static int security_receive(struct shrm_dev *shrm, - void *data, u32 n_bytes) -{ - u32 size = 0; - int ret = 0; - u8 *psrc; - struct message_queue *q; - struct isadev_context *secdev = &shrm->isa_context->isadev[3]; - - dev_dbg(shrm->dev, "%s IN\n", __func__); - q = &secdev->dl_queue; - spin_lock(&q->update_lock); - /* Memcopy RX data first */ - if ((q->writeptr+n_bytes) >= q->size) { - psrc = (u8 *)data; - size = (q->size-q->writeptr); - /* Copy First Part of msg */ - memcpy((q->fifo_base+q->writeptr), psrc, size); - psrc += size; - /* Copy Second Part of msg at the top of fifo */ - memcpy(q->fifo_base, psrc, (n_bytes-size)); - } else { - memcpy((q->fifo_base+q->writeptr), data, n_bytes); - } - ret = add_msg_to_queue(q, n_bytes); - if (ret < 0) - dev_err(shrm->dev, "Adding msg to message queue failed\n"); - spin_unlock(&q->update_lock); - dev_dbg(shrm->dev, "%s OUT\n", __func__); - return ret; -} - - -/** - * isa_select() - Select Interface - * - * @filp:file descriptor pointer - * @wait:poll_table_struct pointer - * - * This function is used to perform non-blocking read operations. It allows - * a process to determine whether it can read from one or more open files - * without blocking. These calls can also block a process until any of a - * given set of file descriptors becomes available for reading. - * If a file is ready to read, POLLIN | POLLRDNORM bitmask is returned. - * The driver method is called whenever the user-space program performs a select - * system call involving a file descriptor associated with the driver. - */ -static u32 isa_select(struct file *filp, - struct poll_table_struct *wait) -{ - struct isadev_context *isadev = filp->private_data; - struct shrm_dev *shrm = isadev->dl_queue.shrm; - struct message_queue *q; - u32 mask = 0; - u32 m = iminor(filp->f_path.dentry->d_inode); - - dev_dbg(shrm->dev, "%s IN\n", __func__); - - if (isadev->device_id != m) - return -1; - q = &isadev->dl_queue; - poll_wait(filp, &q->wq_readable, wait); - if (atomic_read(&q->q_rp) == 1) - mask = POLLIN | POLLRDNORM; - dev_dbg(shrm->dev, "%s OUT\n", __func__); - return mask; -} - -/** - * isa_read() - Read from device - * - * @filp:file descriptor - * @buf:user buffer pointer - * @len:size of requested data transfer - * @ppos:not used - * - * This function is called whenever user calls read() system call. - * It reads a oldest message from queue and copies it into user buffer and - * returns its size. - * If there is no message present in queue, then it blocks until new data is - * available. - */ -ssize_t isa_read(struct file *filp, char __user *buf, - size_t len, loff_t *ppos) -{ - struct isadev_context *isadev = (struct isadev_context *) - filp->private_data; - struct shrm_dev *shrm = isadev->dl_queue.shrm; - struct message_queue *q; - char *psrc; - u32 msgsize; - u32 size = 0; - int ret = 0; - - dev_dbg(shrm->dev, "%s IN\n", __func__); - - if (len <= 0) - return -EFAULT; - q = &isadev->dl_queue; - - spin_lock_bh(&q->update_lock); - if (list_empty(&q->msg_list)) { - spin_unlock_bh(&q->update_lock); - if (wait_event_interruptible(q->wq_readable, - atomic_read(&q->q_rp) == 1)) { - return -ERESTARTSYS; - } - } else - spin_unlock_bh(&q->update_lock); - - msgsize = get_size_of_new_msg(q); - if ((q->readptr+msgsize) >= q->size) { - dev_dbg(shrm->dev, "Inside Loop Back\n"); - psrc = (char *)buf; - size = (q->size-q->readptr); - /* Copy First Part of msg */ - if (copy_to_user(psrc, - (u8 *)(q->fifo_base+q->readptr), - size)) { - dev_err(shrm->dev, "copy_to_user failed\n"); - return -EFAULT; - } - psrc += size; - /* Copy Second Part of msg at the top of fifo */ - if (copy_to_user(psrc, - (u8 *)(q->fifo_base), - (msgsize-size))) { - dev_err(shrm->dev, "copy_to_user failed\n"); - return -EFAULT; - } - } else { - if (copy_to_user(buf, - (u8 *)(q->fifo_base+q->readptr), - msgsize)) { - dev_err(shrm->dev, "copy_to_user failed\n"); - return -EFAULT; - } - } - - spin_lock_bh(&q->update_lock); - ret = remove_msg_from_queue(q); - if (ret < 0) { - dev_err(shrm->dev, - "Removing msg from message queue failed\n"); - msgsize = ret; - } - spin_unlock_bh(&q->update_lock); - dev_dbg(shrm->dev, "%s OUT\n", __func__); - return msgsize; -} -/** - * isa_write() - Write to device - * - * @filp:file descriptor - * @buf:user buffer pointer - * @len:size of requested data transfer - * @ppos:not used - * - * This function is called whenever user calls write() system call. - * It checks if there is space available in queue, and copies the message - * inside queue. If there is no space, it blocks until space becomes available. - * It also schedules transfer thread to transmit the newly added message. - */ -static ssize_t isa_write(struct file *filp, const char __user *buf, - size_t len, loff_t *ppos) -{ - struct isadev_context *isadev = filp->private_data; - struct shrm_dev *shrm = isadev->dl_queue.shrm; - struct message_queue *q; - int err, ret; - void *addr = 0; - u8 l2_header = 0; - - dev_dbg(shrm->dev, "%s IN\n", __func__); - if (len <= 0) - return -EFAULT; - q = &isadev->dl_queue; - - switch (isadev->device_id) { - case ISI_MESSAGING: - dev_dbg(shrm->dev, "ISI\n"); - addr = (void *)wr_isi_msg; -#ifdef CONFIG_U8500_SHRM_LOOP_BACK - dev_dbg(shrm->dev, "Loopback\n"); - l2_header = COMMON_LOOPBACK_MESSAGING; -#else - l2_header = isadev->device_id; -#endif - break; - case RPC_MESSAGING: - dev_dbg(shrm->dev, "RPC\n"); - addr = (void *)wr_rpc_msg; -#ifdef CONFIG_U8500_SHRM_LOOP_BACK - l2_header = COMMON_LOOPBACK_MESSAGING; -#else - l2_header = isadev->device_id; -#endif - break; - case AUDIO_MESSAGING: - dev_dbg(shrm->dev, "Audio\n"); - addr = (void *)wr_audio_msg; -#ifdef CONFIG_U8500_SHRM_LOOP_BACK - l2_header = AUDIO_LOOPBACK_MESSAGING; -#else - l2_header = isadev->device_id; -#endif - - break; - case SECURITY_MESSAGING: - dev_dbg(shrm->dev, "Security\n"); - addr = (void *)wr_sec_msg; -#ifdef CONFIG_U8500_SHRM_LOOP_BACK - l2_header = COMMON_LOOPBACK_MESSAGING; -#else - l2_header = isadev->device_id; -#endif - break; - default: - dev_dbg(shrm->dev, "Wrong device\n"); - return -EFAULT; - } - - if (copy_from_user(addr, buf, len)) { - dev_err(shrm->dev, "copy_from_user failed\n"); - return -EFAULT; - } - - /* Write msg to Fifo */ - if (isadev->device_id == 2) { - mutex_lock(&shrm->isa_context->tx_audio_mutex); - err = shm_write_msg(shrm, l2_header, addr, len); - if (!err) - ret = len; - else - ret = err; - mutex_unlock(&shrm->isa_context->tx_audio_mutex); - } else { - spin_lock_bh(&shrm->isa_context->common_tx); - err = shm_write_msg(shrm, l2_header, addr, len); - if (!err) - ret = len; - else - ret = err; - spin_unlock_bh(&shrm->isa_context->common_tx); - } - - dev_dbg(shrm->dev, "%s OUT\n", __func__); - return ret; -} - -/** - * isa_ioctl() - To handle different ioctl commands supported by driver. - * - * @inode: structure is used by the kernel internally to represent files - * @filp:file descriptor pointer - * @cmd:ioctl command - * @arg:input param - * - * Following ioctls are supported by this driver. - * DLP_IOCTL_ALLOCATE_BUFFER - To allocate buffer for new uplink message. - * This ioctl is called with required message size. It returns offset for - * the allocates space in the queue. DLP_IOCTL_PUT_MESSAGE - To indicate - * new uplink message available in queuq for transmission. Message is copied - * from offset location returned by previous ioctl before calling this ioctl. - * DLP_IOCTL_GET_MESSAGE - To check if any downlink message is available in - * queue. It returns offset for new message inside queue. - * DLP_IOCTL_DEALLOCATE_BUFFER - To deallocate any buffer allocate for - * downlink message once the message is copied. Message is copied from offset - * location returned by previous ioctl before calling this ioctl. - */ -static int isa_ioctl(struct inode *inode, struct file *filp, - unsigned cmd, unsigned long arg) -{ - int err = 0; - struct isadev_context *isadev = filp->private_data; - struct shrm_dev *shrm = isadev->dl_queue.shrm; - u32 m = iminor(inode); - - if (isadev->device_id != m) - return -1; - - switch (cmd) { - case DLP_IOC_ALLOCATE_BUFFER: - dev_dbg(shrm->dev, "DLP_IOC_ALLOCATE_BUFFER\n"); - break; - case DLP_IOC_PUT_MESSAGE: - dev_dbg(shrm->dev, "DLP_IOC_PUT_MESSAGE\n"); - break; - case DLP_IOC_GET_MESSAGE: - dev_dbg(shrm->dev, "DLP_IOC_GET_MESSAGE\n"); - break; - case DLP_IOC_DEALLOCATE_BUFFER: - dev_dbg(shrm->dev, "DLP_IOC_DEALLOCATE_BUFFER\n"); - break; - default: - dev_dbg(shrm->dev, "Unknown IOCTL\n"); - err = -1; - break; - } - return err; -} -/** - * isa_mmap() - Maps kernel queue memory to user space. - * - * @filp:file descriptor pointer - * @vma:virtual area memory structure. - * - * This function maps kernel FIFO into user space. This function - * shall be called twice to map both uplink and downlink buffers. - */ -static int isa_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct isadev_context *isadev = filp->private_data; - struct shrm_dev *shrm = isadev->dl_queue.shrm; - - u32 m = iminor(filp->f_path.dentry->d_inode); - dev_dbg(shrm->dev, "%s %dIN\n", __func__, m); - - isadev = (struct isadev_context *)filp->private_data; - return 0; -} - -/** - * isa_close() - Close device file - * - * @inode:structure is used by the kernel internally to represent files - * @filp:device file descriptor - * - * This function deletes structues associated with this file, deletes - * queues, flushes and destroys workqueus and closes this file. - * It also unregisters itself from l2mux driver. - */ -static int isa_close(struct inode *inode, struct file *filp) -{ - struct isadev_context *isadev = filp->private_data; - struct shrm_dev *shrm = isadev->dl_queue.shrm; - struct isa_driver_context *isa_context = shrm->isa_context; - u8 m; - - mutex_lock(&isa_lock); - m = iminor(filp->f_path.dentry->d_inode); - dev_dbg(shrm->dev, "%s IN %d", __func__, m); - - if (atomic_dec_and_test(&isa_context->is_open[m])) { - atomic_inc(&isa_context->is_open[m]); - dev_err(shrm->dev, "Device not opened yet\n"); - mutex_unlock(&isa_lock); - return -ENODEV; - } - atomic_set(&isa_context->is_open[m], 1); - - dev_dbg(shrm->dev, "isadev->device_id %d", isadev->device_id); - dev_dbg(shrm->dev, "Closed %d device\n", m); - - if (m == ISI_MESSAGING) - dev_dbg(shrm->dev, "Closed ISI_MESSAGING Device\n"); - else if (m == RPC_MESSAGING) - dev_dbg(shrm->dev, "Closed RPC_MESSAGING Device\n"); - else if (m == AUDIO_MESSAGING) - dev_dbg(shrm->dev, "Closed AUDIO_MESSAGING Device\n"); - else if (m == SECURITY_MESSAGING) - dev_dbg(shrm->dev, "Closed SECURITY_MESSAGING Device\n"); - else - dev_dbg(shrm->dev, NAME ":No such device present\n"); - - mutex_unlock(&isa_lock); - return 0; -} -/** - * isa_open() - Open device file - * - * @inode: structure is used by the kernel internally to represent files - * @filp: device file descriptor - * - * This function performs initialization tasks needed to open SHM channel. - * Following tasks are performed. - * -return if device is already opened - * -create uplink FIFO - * -create downlink FIFO - * -init delayed workqueue thread - * -register to l2mux driver - */ -static int isa_open(struct inode *inode, struct file *filp) -{ - int err = 0; - u8 m; - struct isadev_context *isadev; - struct isa_driver_context *isa_context = container_of( - inode->i_cdev, - struct isa_driver_context, - cdev); - struct shrm_dev *shrm = isa_context->isadev->dl_queue.shrm; - - dev_dbg(shrm->dev, "%s IN\n", __func__); - - if (get_boot_state() != BOOT_DONE) { - dev_err(shrm->dev, "Boot is not done\n"); - return -EBUSY; - } - mutex_lock(&isa_lock); - m = iminor(inode); - - if ((m != ISI_MESSAGING) && (m != RPC_MESSAGING) && - (m != AUDIO_MESSAGING) && (m != SECURITY_MESSAGING)) { - dev_err(shrm->dev, "No such device present\n"); - mutex_unlock(&isa_lock); - return -ENODEV; - } - if (!atomic_dec_and_test(&isa_context->is_open[m])) { - atomic_inc(&isa_context->is_open[m]); - dev_err(shrm->dev, "Device already opened\n"); - mutex_unlock(&isa_lock); - return -EBUSY; - } - - if (m == ISI_MESSAGING) - dev_dbg(shrm->dev, "Open ISI_MESSAGING Device\n"); - else if (m == RPC_MESSAGING) - dev_dbg(shrm->dev, "Open RPC_MESSAGING Device\n"); - else if (m == AUDIO_MESSAGING) - dev_dbg(shrm->dev, "Open AUDIO_MESSAGING Device\n"); - else if (m == SECURITY_MESSAGING) - dev_dbg(shrm->dev, "Open SECURITY_MESSAGING Device\n"); - else - dev_dbg(shrm->dev, ":No such device present\n"); - - isadev = &isa_context->isadev[m]; - if (filp != NULL) - filp->private_data = isadev; - - mutex_unlock(&isa_lock); - dev_dbg(shrm->dev, "%s OUT\n", __func__); - return err; -} - -const struct file_operations isa_fops = { - .owner = THIS_MODULE, - .open = isa_open, - .release = isa_close, - .ioctl = isa_ioctl, - .mmap = isa_mmap, - .read = isa_read, - .write = isa_write, - .poll = isa_select, -}; - -/** - * isa_init() - module insertion function - * - * This function registers module as a character driver using - * register_chrdev_region() or alloc_chrdev_region. It adds this - * driver to system using cdev_add() call. Major number is dynamically - * allocated using alloc_chrdev_region() by default or left to user to specify - * it during load time. For this variable major is used as module_param - * Nodes to be created using - * mknod /dev/isi c $major 0 - * mknod /dev/rpc c $major 1 - * mknod /dev/audio c $major 2 - * mknod /dev/sec c $major 3 - */ -int isa_init(struct shrm_dev *shrm) -{ - dev_t dev_id; - int retval, no_dev; - struct isadev_context *isadev; - struct isa_driver_context *isa_context; - - isa_context = kzalloc(sizeof(struct isa_driver_context), - GFP_KERNEL); - shrm->isa_context = isa_context; - if (isa_context == NULL) { - dev_err(shrm->dev, "Failed to alloc memory\n"); - return -ENOMEM; - } - - if (major) { - dev_id = MKDEV(major, 0); - retval = register_chrdev_region(dev_id, ISA_DEVICES, NAME); - } else { - retval = alloc_chrdev_region(&dev_id, 0, ISA_DEVICES, NAME); - major = MAJOR(dev_id); - } - - dev_dbg(shrm->dev, "major %d\n", major); - - cdev_init(&isa_context->cdev, &isa_fops); - isa_context->cdev.owner = THIS_MODULE; - retval = cdev_add(&isa_context->cdev, dev_id, ISA_DEVICES); - if (retval) { - dev_err(shrm->dev, "Failed to add char device\n"); - return retval; - } - - for (no_dev = 0; no_dev < ISA_DEVICES; no_dev++) - atomic_set(&isa_context->is_open[no_dev], 1); - - isa_context->isadev = kzalloc(sizeof - (struct isadev_context)*ISA_DEVICES, - GFP_KERNEL); - if (isa_context->isadev == NULL) { - dev_err(shrm->dev, "Failed to alloc memory\n"); - return -ENOMEM; - } - for (no_dev = 0; no_dev < ISA_DEVICES; no_dev++) { - isadev = &isa_context->isadev[no_dev]; - isadev->device_id = no_dev; - retval = create_queue(&isadev->dl_queue, - isadev->device_id, shrm); - if (retval < 0) { - dev_err(shrm->dev, "create dl_queue failed\n"); - delete_queue(&isadev->dl_queue); - kfree(isadev); - return retval; - } - } - mutex_init(&isa_context->tx_audio_mutex); - spin_lock_init(&isa_context->common_tx); - - dev_err(shrm->dev, "SHRM char driver added\n"); - - return retval; -} - -void isa_exit(struct shrm_dev *shrm) -{ - int no_dev; - struct isadev_context *isadev; - struct isa_driver_context *isa_context = shrm->isa_context; - dev_t dev_id = MKDEV(major, 0); - - for (no_dev = 0; no_dev < ISA_DEVICES; no_dev++) { - isadev = &isa_context->isadev[no_dev]; - delete_queue(&isadev->dl_queue); - kfree(isadev); - } - - cdev_del(&isa_context->cdev); - unregister_chrdev_region(dev_id, ISA_DEVICES); - kfree(isa_context); - - dev_err(shrm->dev, "SHRM char driver removed\n"); -} - -#ifdef CONFIG_HIGH_RES_TIMERS -static enum hrtimer_restart callback(struct hrtimer *timer) -{ - return HRTIMER_NORESTART; -} -#endif - - -static int __init shrm_probe(struct platform_device *pdev) -{ - int err = 0; - struct resource *res; - struct shrm_dev *shrm = NULL; - - if (pdev == NULL) { - dev_err(shrm->dev, - "No device/platform_data found on shm device\n"); - return -ENODEV; - } - - - shrm = kzalloc(sizeof(struct shrm_dev), GFP_KERNEL); - if (shrm == NULL) { - dev_err(shrm->dev, - "Could not allocate memory for struct shm_dev\n"); - return -ENOMEM; - } - shrm->dev = &pdev->dev; - - /* initialise the SHM */ - - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(shrm->dev, "Unable to map Ca Wake up interrupt\n"); - err = -EBUSY; - goto rollback_intr; - } - shrm->ca_wake_irq = res->start; - - res = platform_get_resource(pdev, IORESOURCE_IRQ, 1); - if (!res) { - dev_err(shrm->dev, - "Unable to map APE_Read_notif_common IRQ base\n"); - err = -EBUSY; - goto rollback_intr; - } - shrm->ac_read_notif_0_irq = res->start; - - res = platform_get_resource(pdev, IORESOURCE_IRQ, 2); - if (!res) { - dev_err(shrm->dev, - "Unable to map APE_Read_notif_audio IRQ base\n"); - err = -EBUSY; - goto rollback_intr; - } - shrm->ac_read_notif_1_irq = res->start; - - res = platform_get_resource(pdev, IORESOURCE_IRQ, 3); - if (!res) { - dev_err(shrm->dev, - "Unable to map Cmt_msg_pending_notif_common IRQ base\n"); - err = -EBUSY; - goto rollback_intr; - } - shrm->ca_msg_pending_notif_0_irq = res->start; - - res = platform_get_resource(pdev, IORESOURCE_IRQ, 4); - if (!res) { - dev_err(shrm->dev, - "Unable to map Cmt_msg_pending_notif_audio IRQ base\n"); - err = -EBUSY; - goto rollback_intr; - } - shrm->ca_msg_pending_notif_1_irq = res->start; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(shrm->dev, - "Could not get SHM IO memory information\n"); - err = -ENODEV; - goto rollback_intr; - } - - shrm->intr_base = (void __iomem *)ioremap_nocache(res->start, - res->end - res->start + 1); - - if (!(shrm->intr_base)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_intr; - } - - shrm->ape_common_fifo_base_phy = - (u32 *)U8500_SHM_FIFO_APE_COMMON_BASE; - shrm->ape_common_fifo_base = - (void __iomem *)ioremap_nocache( - U8500_SHM_FIFO_APE_COMMON_BASE, - SHM_FIFO_0_SIZE); - shrm->ape_common_fifo_size = (SHM_FIFO_0_SIZE)/4; - - if (!(shrm->ape_common_fifo_base)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_ape_common_fifo_base; - } - - shrm->cmt_common_fifo_base_phy = - (u32 *)U8500_SHM_FIFO_CMT_COMMON_BASE; - - shrm->cmt_common_fifo_base = - (void __iomem *)ioremap_nocache( - U8500_SHM_FIFO_CMT_COMMON_BASE, SHM_FIFO_0_SIZE); - shrm->cmt_common_fifo_size = (SHM_FIFO_0_SIZE)/4; - - if (!(shrm->cmt_common_fifo_base)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_cmt_common_fifo_base; - } - - shrm->ape_audio_fifo_base_phy = - (u32 *)U8500_SHM_FIFO_APE_AUDIO_BASE; - shrm->ape_audio_fifo_base = - (void __iomem *)ioremap_nocache(U8500_SHM_FIFO_APE_AUDIO_BASE, - SHM_FIFO_1_SIZE); - shrm->ape_audio_fifo_size = (SHM_FIFO_1_SIZE)/4; - - if (!(shrm->ape_audio_fifo_base)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_ape_audio_fifo_base; - } - - shrm->cmt_audio_fifo_base_phy = - (u32 *)U8500_SHM_FIFO_CMT_AUDIO_BASE; - shrm->cmt_audio_fifo_base = - (void __iomem *)ioremap_nocache(U8500_SHM_FIFO_CMT_AUDIO_BASE, - SHM_FIFO_1_SIZE); - shrm->cmt_audio_fifo_size = (SHM_FIFO_1_SIZE)/4; - - if (!(shrm->cmt_audio_fifo_base)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_cmt_audio_fifo_base; - } - - shrm->ac_common_shared_wptr = - (void __iomem *)ioremap(SHM_ACFIFO_0_WRITE_AMCU, SHM_PTR_SIZE); - - if (!(shrm->ac_common_shared_wptr)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_ac_common_shared_wptr; - } - - shrm->ac_common_shared_rptr = - (void __iomem *)ioremap(SHM_ACFIFO_0_READ_AMCU, SHM_PTR_SIZE); - - if (!(shrm->ac_common_shared_rptr)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_map; - } - - - shrm->ca_common_shared_wptr = - (void __iomem *)ioremap(SHM_CAFIFO_0_WRITE_AMCU, SHM_PTR_SIZE); - - if (!(shrm->ca_common_shared_wptr)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_map; - } - - shrm->ca_common_shared_rptr = - (void __iomem *)ioremap(SHM_CAFIFO_0_READ_AMCU, SHM_PTR_SIZE); - - if (!(shrm->ca_common_shared_rptr)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_map; - } - - - shrm->ac_audio_shared_wptr = - (void __iomem *)ioremap(SHM_ACFIFO_1_WRITE_AMCU, SHM_PTR_SIZE); - - if (!(shrm->ac_audio_shared_wptr)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_map; - } - - - shrm->ac_audio_shared_rptr = - (void __iomem *)ioremap(SHM_ACFIFO_1_READ_AMCU, SHM_PTR_SIZE); - - if (!(shrm->ac_audio_shared_rptr)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_map; - } - - - shrm->ca_audio_shared_wptr = - (void __iomem *)ioremap(SHM_CAFIFO_1_WRITE_AMCU, SHM_PTR_SIZE); - - if (!(shrm->ca_audio_shared_wptr)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_map; - } - - - shrm->ca_audio_shared_rptr = - (void __iomem *)ioremap(SHM_CAFIFO_1_READ_AMCU, SHM_PTR_SIZE); - - if (!(shrm->ca_audio_shared_rptr)) { - dev_err(shrm->dev, "Unable to map register base\n"); - err = -EBUSY; - goto rollback_map; - } - - - if (isa_init(shrm) != 0) { - dev_err(shrm->dev, "Driver Initialization Error\n"); - err = -EBUSY; - } - /* install handlers and tasklets */ - if (shm_initialise_irq(shrm)) { - dev_err(shrm->dev, "shm error in interrupt registration\n"); - goto rollback_irq; - } - -#ifdef CONFIG_HIGH_RES_TIMERS - hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - timer.function = callback; - - hrtimer_start(&timer, ktime_set(0, 2*NSEC_PER_MSEC), HRTIMER_MODE_REL); -#endif - - return err; - -rollback_irq: - free_shm_irq(shrm); -rollback_map: - iounmap(shrm->ac_common_shared_wptr); - iounmap(shrm->ac_common_shared_rptr); - iounmap(shrm->ca_common_shared_wptr); - iounmap(shrm->ca_common_shared_rptr); - iounmap(shrm->ac_audio_shared_wptr); - iounmap(shrm->ac_audio_shared_rptr); - iounmap(shrm->ca_audio_shared_wptr); - iounmap(shrm->ca_audio_shared_rptr); -rollback_ac_common_shared_wptr: - iounmap(shrm->cmt_audio_fifo_base); -rollback_cmt_audio_fifo_base: - iounmap(shrm->ape_audio_fifo_base); -rollback_ape_audio_fifo_base: - iounmap(shrm->cmt_common_fifo_base); -rollback_cmt_common_fifo_base: - iounmap(shrm->ape_common_fifo_base); -rollback_ape_common_fifo_base: - iounmap(shrm->intr_base); -rollback_intr: - kfree(shrm); - return err; -} - -static int __exit shrm_remove(struct platform_device *pdev) -{ - struct shrm_dev *shrm = platform_get_drvdata(pdev); - - free_shm_irq(shrm); - iounmap(shrm->intr_base); - iounmap(shrm->ape_common_fifo_base); - iounmap(shrm->cmt_common_fifo_base); - iounmap(shrm->ape_audio_fifo_base); - iounmap(shrm->cmt_audio_fifo_base); - iounmap(shrm->ac_common_shared_wptr); - iounmap(shrm->ac_common_shared_rptr); - iounmap(shrm->ca_common_shared_wptr); - iounmap(shrm->ca_common_shared_rptr); - iounmap(shrm->ac_audio_shared_wptr); - iounmap(shrm->ac_audio_shared_rptr); - iounmap(shrm->ca_audio_shared_wptr); - iounmap(shrm->ca_audio_shared_rptr); - kfree(shrm); - isa_exit(shrm); - - return 0; -} -#ifdef CONFIG_PM - -/** - * u8500_shrm_suspend() - This routine puts the SHRM in to sustend state. - * @pdev: platform device. - * - * This routine checks the current ongoing communication with Modem by - * examining the ca_wake state and prevents suspend if modem communication - * is on-going. - * If ca_wake = 1 (high), modem comm. is on-going; don't suspend - * If ca_wake = 0 (low), no comm. with modem on-going.Allow suspend - */ -int u8500_shrm_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct shrm_dev *shrm = platform_get_drvdata(pdev); - - dev_dbg(shrm->dev, "%s called...\n", __func__); - dev_dbg(shrm->dev, "\n ca_wake_req_state = %x\n", - get_ca_wake_req_state()); - /* if ca_wake_req is high, prevent system suspend */ - if (get_ca_wake_req_state()) - return -EBUSY; - else - return 0; -} - -/** - * u8500_shrm_resume() - This routine resumes the SHRM from sustend state. - * @pdev: platform device. - * - * This routine restore back the current state of the SHRM - */ -int u8500_shrm_resume(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct shrm_dev *shrm = platform_get_drvdata(pdev); - - dev_dbg(shrm->dev, "%s called...\n", __func__); - /* TODO: - * As of now, no state save takes place in suspend. - * So, nothing to restore in resume. - * Simply return as of now. - * State saved in suspend should be restored here. - */ - - return 0; -} - -static const struct dev_pm_ops shrm_dev_pm_ops = { - .suspend = u8500_shrm_suspend, - .resume = u8500_shrm_resume, -}; -#endif - -static struct platform_driver shrm_driver = { - .remove = __exit_p(shrm_remove), - .driver = { - .name = "u8500_shrm", - .owner = THIS_MODULE, -#ifdef CONFIG_PM - .pm = &shrm_dev_pm_ops, -#endif - }, -}; - -static int __init shrm_driver_init(void) -{ - return platform_driver_probe(&shrm_driver, shrm_probe); -} - -static void __exit shrm_driver_exit(void) -{ - platform_driver_unregister(&shrm_driver); -} - -module_init(shrm_driver_init); -module_exit(shrm_driver_exit); - -MODULE_AUTHOR("Biju Das"); -MODULE_DESCRIPTION("Shared Memory Modem Driver Interface"); -MODULE_LICENSE("GPL"); diff --git a/drivers/misc/shrm/shrm_fifo.c b/drivers/misc/shrm/shrm_fifo.c deleted file mode 100644 index cbe0949a56d..00000000000 --- a/drivers/misc/shrm/shrm_fifo.c +++ /dev/null @@ -1,827 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Biju Das for ST-Ericsson - * Author: Kumar Sanghavi for ST-Ericsson - * Author: Arun Murthy for ST-Ericsson - * License terms: GNU General Public License (GPL) version 2 - */ - -#include -#include -#include -#include - -#define L1_BOOT_INFO_REQ 1 -#define L1_BOOT_INFO_RESP 2 -#define L1_NORMAL_MSG 3 -#define L1_HEADER_MASK 28 -#define L1_MAPID_MASK 0xF0000000 -#define CONFIG_OFFSET 8 -#define COUNTER_OFFSET 20 -#define L2_HEADER_SIZE 4 -#define L2_HEADER_OFFSET 24 -#define MASK_0_15_BIT 0xFF -#define MASK_16_31_BIT 0xFF00 -#define MASK_16_27_BIT 0xFFF0000 -#define MASK_0_39_BIT 0xFFFFF -#define MASK_40_55_BIT 0xFF00000 -#define MASK_8_16_BIT 0x0000FF00 -#define MSG_LEN_OFFSET 16 -#define SHRM_VER 2 -#define ca_ist_inactivity_timer 100 /*100ms */ -#define ca_csc_inactivity_timer 100 /*100ms */ - -static u8 msg_audio_counter; -static u8 msg_common_counter; - -struct fifo_write_params ape_shm_fifo_0; -struct fifo_write_params ape_shm_fifo_1; -struct fifo_read_params cmt_shm_fifo_0; -struct fifo_read_params cmt_shm_fifo_1; - - -static u8 cmt_read_notif_0_send; -static u8 cmt_read_notif_1_send; - -void shm_fifo_init(struct shrm_dev *shrm) -{ - ape_shm_fifo_0.writer_local_wptr = 0; - ape_shm_fifo_0.writer_local_rptr = 0; - *((u32 *)shrm->ac_common_shared_wptr) = 0; - *((u32 *)shrm->ac_common_shared_rptr) = 0; - ape_shm_fifo_0.shared_wptr = 0; - ape_shm_fifo_0.shared_rptr = 0; - ape_shm_fifo_0.availablesize = shrm->ape_common_fifo_size; - ape_shm_fifo_0.end_addr_fifo = shrm->ape_common_fifo_size; - ape_shm_fifo_0.fifo_virtual_addr = shrm->ape_common_fifo_base; - spin_lock_init(&ape_shm_fifo_0.fifo_update_lock); - - - cmt_shm_fifo_0.reader_local_rptr = 0; - cmt_shm_fifo_0.reader_local_wptr = 0; - cmt_shm_fifo_0.shared_wptr = - *((u32 *)shrm->ca_common_shared_wptr); - cmt_shm_fifo_0.shared_rptr = - *((u32 *)shrm->ca_common_shared_rptr); - cmt_shm_fifo_0.availablesize = shrm->cmt_common_fifo_size; - cmt_shm_fifo_0.end_addr_fifo = shrm->cmt_common_fifo_size; - cmt_shm_fifo_0.fifo_virtual_addr = shrm->cmt_common_fifo_base; - - ape_shm_fifo_1.writer_local_wptr = 0; - ape_shm_fifo_1.writer_local_rptr = 0; - ape_shm_fifo_1.shared_wptr = 0; - ape_shm_fifo_1.shared_rptr = 0; - *((u32 *)shrm->ac_audio_shared_wptr) = 0; - *((u32 *)shrm->ac_audio_shared_rptr) = 0; - ape_shm_fifo_1.availablesize = shrm->ape_audio_fifo_size; - ape_shm_fifo_1.end_addr_fifo = shrm->ape_audio_fifo_size; - ape_shm_fifo_1.fifo_virtual_addr = shrm->ape_audio_fifo_base; - spin_lock_init(&ape_shm_fifo_1.fifo_update_lock); - - cmt_shm_fifo_1.reader_local_rptr = 0; - cmt_shm_fifo_1.reader_local_wptr = 0; - cmt_shm_fifo_1.shared_wptr = - *((u32 *)shrm->ca_audio_shared_wptr); - cmt_shm_fifo_1.shared_rptr = - *((u32 *)shrm->ca_audio_shared_rptr); - cmt_shm_fifo_1.availablesize = shrm->cmt_audio_fifo_size; - cmt_shm_fifo_1.end_addr_fifo = shrm->cmt_audio_fifo_size; - cmt_shm_fifo_1.fifo_virtual_addr = shrm->cmt_audio_fifo_base; - msg_audio_counter = 0; - msg_common_counter = 0; -} - -u8 read_boot_info_req(struct shrm_dev *shrm, - u32 *config, - u32 *version) -{ - struct fifo_read_params *fifo = &cmt_shm_fifo_0; - u32 *msg; - u32 header = 0; - u8 msgtype; - - /* Read L1 header read content of reader_local_rptr */ - msg = (u32 *) - (fifo->reader_local_rptr + fifo->fifo_virtual_addr); - header = *msg; - msgtype = (header & L1_MAPID_MASK) >> L1_MSG_MAPID_OFFSET; - if (msgtype != L1_BOOT_INFO_REQ) { - dev_err(shrm->dev, "Read_Boot_Info_Req Fatal ERROR\n"); - BUG(); - } - *config = (header >> CONFIG_OFFSET) & MASK_0_15_BIT; - *version = header & MASK_0_15_BIT; - fifo->reader_local_rptr += 1; - - return 1; -} - -void write_boot_info_resp(struct shrm_dev *shrm, u32 config, - u32 version) -{ - struct fifo_write_params *fifo = &ape_shm_fifo_0; - u32 *msg; - u8 msg_length; - version = SHRM_VER; - - spin_lock_bh(&fifo->fifo_update_lock); - /* Read L1 header read content of reader_local_rptr */ - msg = (u32 *) - (fifo->writer_local_wptr+fifo->fifo_virtual_addr); - if (version < 1) { - *msg = ((L1_BOOT_INFO_RESP << L1_MSG_MAPID_OFFSET) | - ((config << CONFIG_OFFSET) & MASK_16_31_BIT) - | (version & MASK_0_15_BIT)); - msg_length = 1; - } else { - *msg = ((L1_BOOT_INFO_RESP << L1_MSG_MAPID_OFFSET) | - ((0x8 << MSG_LEN_OFFSET) & MASK_16_27_BIT) | - ((config << CONFIG_OFFSET) & MASK_8_16_BIT)| - version); - msg++; - *msg = ca_ist_inactivity_timer; - msg++; - *msg = ca_csc_inactivity_timer; - msg_length = L1_NORMAL_MSG; - } - fifo->writer_local_wptr += msg_length; - fifo->availablesize -= msg_length; - spin_unlock_bh(&fifo->fifo_update_lock); -} - -/** - * shm_write_msg_to_fifo() - write message to FIFO - * @shrm: pointer to shrm device information structure - * @channel: audio or common channel - * @l2header: L2 header or device ID - * @addr: pointer to write buffer address - * @length: length of mst to write - * - * Function Which Writes the data into Fifo in IPC zone - * It is called from shm_write_msg. This function will copy the msg - * from the kernel buffer to FIFO. There are 4 kernel buffers from where - * the data is to copied to FIFO one for each of the messages ISI, RPC, - * AUDIO and SECURITY. ISI, RPC and SECURITY messages are pushed to FIFO - * in commmon channel and AUDIO message is pushed onto audio channel FIFO. - */ -int shm_write_msg_to_fifo(struct shrm_dev *shrm, u8 channel, - u8 l2header, void *addr, u32 length) -{ - struct fifo_write_params *fifo = NULL; - u32 l1_header = 0, l2_header = 0; - u32 requiredsize; - u32 size = 0; - u32 *msg; - u8 *src; - - if (channel == COMMON_CHANNEL) - fifo = &ape_shm_fifo_0; - else if (channel == AUDIO_CHANNEL) - fifo = &ape_shm_fifo_1; - else { - dev_err(shrm->dev, "invalid channel\n"); - return -EINVAL; - } - - /* L2 size in 32b */ - requiredsize = ((length + 3) / 4); - /* Add size of L1 & L2 header */ - requiredsize += 2; - - /* if availablesize = or < requiredsize then error */ - if (fifo->availablesize <= requiredsize) { - /* Fatal ERROR - should never happens */ - dev_dbg(shrm->dev, "wr_wptr= %x\n", - fifo->writer_local_wptr); - dev_dbg(shrm->dev, "wr_rptr= %x\n", - fifo->writer_local_rptr); - dev_dbg(shrm->dev, "shared_wptr= %x\n", - fifo->shared_wptr); - dev_dbg(shrm->dev, "shared_rptr= %x\n", - fifo->shared_rptr); - dev_dbg(shrm->dev, "availsize= %x\n", - fifo->availablesize); - dev_dbg(shrm->dev, "end__fifo= %x\n", - fifo->end_addr_fifo); - dev_warn(shrm->dev, "Modem is busy, please wait." - " c_cnt = %d; a_cnt = %d\n", msg_common_counter, - msg_audio_counter); - if (channel == COMMON_CHANNEL) { - dev_warn(shrm->dev, - "Modem is lagging behind in reading." - "Stopping n/w dev queue\n"); - shrm_stop_netdev(shrm->ndev); - } - - return -EAGAIN; - } - - if (channel == COMMON_CHANNEL) { - /* build L1 header */ - l1_header = ((L1_NORMAL_MSG << L1_MSG_MAPID_OFFSET) | - (((msg_common_counter++) << COUNTER_OFFSET) - & MASK_40_55_BIT) | - ((length + L2_HEADER_SIZE) & MASK_0_39_BIT)); - } else if (channel == AUDIO_CHANNEL) { - /* build L1 header */ - l1_header = ((L1_NORMAL_MSG << L1_MSG_MAPID_OFFSET) | - (((msg_audio_counter++) << COUNTER_OFFSET) - & MASK_40_55_BIT) | - ((length + L2_HEADER_SIZE) & MASK_0_39_BIT)); - } - - /* - * Need to take care race condition for fifo->availablesize - * & fifo->writer_local_rptr with Ac_Read_notification interrupt. - * One option could be use stack variable for LocalRptr and recompute - * fifo->availablesize,based on flag enabled in the - * Ac_read_notification - */ - l2_header = ((l2header << L2_HEADER_OFFSET) | - ((length) & MASK_0_39_BIT)); - spin_lock_bh(&fifo->fifo_update_lock); - /* Check Local Rptr is less than or equal to Local WPtr */ - if (fifo->writer_local_rptr <= fifo->writer_local_wptr) { - msg = (u32 *) - (fifo->fifo_virtual_addr+fifo->writer_local_wptr); - - /* check enough place bewteen writer_local_wptr & end of FIFO */ - if ((fifo->end_addr_fifo-fifo->writer_local_wptr) >= - requiredsize) { - /* Add L1 header and L2 header */ - *msg = l1_header; - msg++; - *msg = l2_header; - msg++; - - /* copy the l2 message in 1 memcpy */ - memcpy((void *)msg, addr, length); - /* UpdateWptr */ - fifo->writer_local_wptr += requiredsize; - fifo->availablesize -= requiredsize; - fifo->writer_local_wptr %= fifo->end_addr_fifo; - } else { - /* - * message is split between and of FIFO and beg of FIFO - * copy first part from writer_local_wptr to end of FIFO - */ - size = fifo->end_addr_fifo-fifo->writer_local_wptr; - - if (size == 1) { - /* Add L1 header */ - *msg = l1_header; - msg++; - /* UpdateWptr */ - fifo->writer_local_wptr = 0; - fifo->availablesize -= size; - /* - * copy second part from beg of FIFO - * with remaining part of msg - */ - msg = (u32 *) - fifo->fifo_virtual_addr; - *msg = l2_header; - msg++; - - /* copy the l3 message in 1 memcpy */ - memcpy((void *)msg, addr, length); - /* UpdateWptr */ - fifo->writer_local_wptr += - requiredsize-size; - fifo->availablesize -= - (requiredsize-size); - } else if (size == 2) { - /* Add L1 header and L2 header */ - *msg = l1_header; - msg++; - *msg = l2_header; - msg++; - - /* UpdateWptr */ - fifo->writer_local_wptr = 0; - fifo->availablesize -= size; - - /* - * copy second part from beg of FIFO - * with remaining part of msg - */ - msg = (u32 *) - fifo->fifo_virtual_addr; - /* copy the l3 message in 1 memcpy */ - memcpy((void *)msg, addr, length); - - /* UpdateWptr */ - fifo->writer_local_wptr += - requiredsize-size; - fifo->availablesize -= - (requiredsize-size); - } else { - /* Add L1 header and L2 header */ - *msg = l1_header; - msg++; - *msg = l2_header; - msg++; - - /* copy the l2 message in 1 memcpy */ - memcpy((void *)msg, addr, (size-2)*4); - - - /* UpdateWptr */ - fifo->writer_local_wptr = 0; - fifo->availablesize -= size; - - /* - * copy second part from beg of FIFO - * with remaining part of msg - */ - msg = (u32 *)fifo->fifo_virtual_addr; - src = (u8 *)addr+((size - 2) * 4); - memcpy((void *)msg, src, - (length-((size - 2) * 4))); - - /* UpdateWptr */ - fifo->writer_local_wptr += - requiredsize-size; - fifo->availablesize -= - (requiredsize-size); - } - - } - } else { - /* writer_local_rptr > writer_local_wptr */ - msg = (u32 *) - (fifo->fifo_virtual_addr+fifo->writer_local_wptr); - /* Add L1 header and L2 header */ - *msg = l1_header; - msg++; - *msg = l2_header; - msg++; - /* - * copy message possbile between writer_local_wptr up - * to writer_local_rptr copy the l3 message in 1 memcpy - */ - memcpy((void *)msg, addr, length); - - /* UpdateWptr */ - fifo->writer_local_wptr += requiredsize; - fifo->availablesize -= requiredsize; - - } - spin_unlock_bh(&fifo->fifo_update_lock); - return length; -} - -/** - * read_one_l2msg_common() - read message from common channel - * @shrm: pointer to shrm device information structure - * @l2_msg: pointer to the read L2 message buffer - * @len: message length - * - * This function read one message from the FIFO and returns l2 header type - */ -u8 read_one_l2msg_common(struct shrm_dev *shrm, - u8 *l2_msg, u32 *len) -{ - struct fifo_read_params *fifo = &cmt_shm_fifo_0; - - u32 *msg; - u32 l1_header = 0; - u32 l2_header = 0; - u32 length; - u8 msgtype; - u32 msg_size; - u32 size = 0; - - /* Read L1 header read content of reader_local_rptr */ - msg = (u32 *) - (fifo->reader_local_rptr+fifo->fifo_virtual_addr); - l1_header = *msg++; - msgtype = (l1_header & 0xF0000000) >> L1_HEADER_MASK; - - if (msgtype != L1_NORMAL_MSG) { - /* Fatal ERROR - should never happens */ - dev_dbg(shrm->dev, "wr_wptr= %x\n", - fifo->reader_local_wptr); - dev_dbg(shrm->dev, "wr_rptr= %x\n", - fifo->reader_local_rptr); - dev_dbg(shrm->dev, "shared_wptr= %x\n", - fifo->shared_wptr); - dev_dbg(shrm->dev, "shared_rptr= %x\n", - fifo->shared_rptr); - dev_dbg(shrm->dev, "availsize= %x\n", - fifo->availablesize); - dev_dbg(shrm->dev, "end_fifo= %x\n", - fifo->end_addr_fifo); - /* Fatal ERROR - should never happens */ - dev_crit(shrm->dev, "Fatal ERROR - should never happen\n"); - BUG(); - } - if (fifo->reader_local_rptr == (fifo->end_addr_fifo-1)) { - l2_header = (*((u32 *)fifo->fifo_virtual_addr)); - length = l2_header & MASK_0_39_BIT; - } else { - /* Read L2 header,Msg size & content of reader_local_rptr */ - l2_header = *msg; - length = l2_header & MASK_0_39_BIT; - } - - *len = length; - msg_size = ((length + 3) / 4); - msg_size += 2; - - if (fifo->reader_local_rptr + msg_size <= - fifo->end_addr_fifo) { - /* Skip L2 header */ - msg++; - - /* read msg between reader_local_rptr and end of FIFO */ - memcpy((void *)l2_msg, (void *)msg, length); - /* UpdateLocalRptr */ - fifo->reader_local_rptr += msg_size; - fifo->reader_local_rptr %= fifo->end_addr_fifo; - } else { - /* - * msg split between end of FIFO and beg copy first - * part of msg read msg between reader_local_rptr - * and end of FIFO - */ - size = fifo->end_addr_fifo-fifo->reader_local_rptr; - if (size == 1) { - msg = (u32 *)(fifo->fifo_virtual_addr); - /* Skip L2 header */ - msg++; - memcpy((void *)l2_msg, (void *)(msg), length); - } else if (size == 2) { - /* Skip L2 header */ - msg++; - msg = (u32 *)(fifo->fifo_virtual_addr); - memcpy((void *)l2_msg, - (void *)(msg), length); - } else { - /* Skip L2 header */ - msg++; - memcpy((void *)l2_msg, (void *)msg, ((size - 2) * 4)); - /* copy second part of msg */ - l2_msg += ((size - 2) * 4); - msg = (u32 *)(fifo->fifo_virtual_addr); - memcpy((void *)l2_msg, (void *)(msg), - (length-((size - 2) * 4))); - } - fifo->reader_local_rptr = - (fifo->reader_local_rptr+msg_size) % - fifo->end_addr_fifo; - } - return (l2_header>>L2_HEADER_OFFSET) & MASK_0_15_BIT; - } - -u8 read_remaining_messages_common() -{ - struct fifo_read_params *fifo = &cmt_shm_fifo_0; - /* - * There won't be any Race condition reader_local_rptr & - * fifo->reader_local_wptr with CaMsgpending Notification Interrupt - */ - return ((fifo->reader_local_rptr != fifo->reader_local_wptr) ? 1 : 0); -} - -u8 read_one_l2msg_audio(struct shrm_dev *shrm, - u8 *l2_msg, u32 *len) -{ - struct fifo_read_params *fifo = &cmt_shm_fifo_1; - - u32 *msg; - u32 l1_header = 0; - u32 l2_header = 0; - u32 length; - u8 msgtype; - u32 msg_size; - u32 size = 0; - - /* Read L1 header read content of reader_local_rptr */ - msg = (u32 *) - (fifo->reader_local_rptr+fifo->fifo_virtual_addr); - l1_header = *msg++; - msgtype = (l1_header & 0xF0000000) >> L1_HEADER_MASK; - - if (msgtype != L1_NORMAL_MSG) { - /* Fatal ERROR - should never happens */ - dev_dbg(shrm->dev, "wr_local_wptr= %x\n", - fifo->reader_local_wptr); - dev_dbg(shrm->dev, "wr_local_rptr= %x\n", - fifo->reader_local_rptr); - dev_dbg(shrm->dev, "shared_wptr= %x\n", - fifo->shared_wptr); - dev_dbg(shrm->dev, "shared_rptr= %x\n", - fifo->shared_rptr); - dev_dbg(shrm->dev, "availsize=%x\n", - fifo->availablesize); - dev_dbg(shrm->dev, "end_fifo= %x\n", - fifo->end_addr_fifo); - /* Fatal ERROR - should never happens */ - dev_crit(shrm->dev, "Fatal ERROR - should never happen\n"); - BUG(); - } - if (fifo->reader_local_rptr == (fifo->end_addr_fifo-1)) { - l2_header = (*((u32 *)fifo->fifo_virtual_addr)); - length = l2_header & MASK_0_39_BIT; - } else { - /* Read L2 header,Msg size & content of reader_local_rptr */ - l2_header = *msg; - length = l2_header & MASK_0_39_BIT; - } - - *len = length; - msg_size = ((length + 3) / 4); - msg_size += 2; - - if (fifo->reader_local_rptr + msg_size <= - fifo->end_addr_fifo) { - /* Skip L2 header */ - msg++; - /* read msg between reader_local_rptr and end of FIFO */ - memcpy((void *)l2_msg, (void *)msg, length); - /* UpdateLocalRptr */ - fifo->reader_local_rptr += msg_size; - fifo->reader_local_rptr %= fifo->end_addr_fifo; - } else { - - /* - * msg split between end of FIFO and beg - * copy first part of msg - * read msg between reader_local_rptr and end of FIFO - */ - size = fifo->end_addr_fifo-fifo->reader_local_rptr; - if (size == 1) { - msg = (u32 *)(fifo->fifo_virtual_addr); - /* Skip L2 header */ - msg++; - memcpy((void *)l2_msg, (void *)(msg), length); - } else if (size == 2) { - /* Skip L2 header */ - msg++; - msg = (u32 *)(fifo->fifo_virtual_addr); - memcpy((void *)l2_msg, (void *)(msg), length); - } else { - /* Skip L2 header */ - msg++; - memcpy((void *)l2_msg, (void *)msg, ((size - 2) * 4)); - /* copy second part of msg */ - l2_msg += ((size - 2) * 4); - msg = (u32 *)(fifo->fifo_virtual_addr); - memcpy((void *)l2_msg, (void *)(msg), - (length-((size - 2) * 4))); - } - fifo->reader_local_rptr = - (fifo->reader_local_rptr+msg_size) % - fifo->end_addr_fifo; - - } - return (l2_header>>L2_HEADER_OFFSET) & MASK_0_15_BIT; - } - -u8 read_remaining_messages_audio() -{ - struct fifo_read_params *fifo = &cmt_shm_fifo_1; - - return ((fifo->reader_local_rptr != fifo->reader_local_wptr) ? - 1 : 0); -} - -u8 is_the_only_one_unread_message(struct shrm_dev *shrm, - u8 channel, u32 length) -{ - struct fifo_write_params *fifo = NULL; - u32 messagesize = 0; - u8 is_only_one_unread_msg = 0; - - if (channel == COMMON_CHANNEL) - fifo = &ape_shm_fifo_0; - else /* channel = AUDIO_CHANNEL */ - fifo = &ape_shm_fifo_1; - - /* L3 size in 32b */ - messagesize = ((length + 3) / 4); - /* Add size of L1 & L2 header */ - messagesize += 2; - /* - * possibility of race condition with Ac Read notification interrupt. - * need to check ? - */ - if (fifo->writer_local_wptr > fifo->writer_local_rptr) - is_only_one_unread_msg = - ((fifo->writer_local_rptr + messagesize) == - fifo->writer_local_wptr) ? 1 : 0; - else - /* Msg split between end of fifo and starting of Fifo */ - is_only_one_unread_msg = - (((fifo->writer_local_rptr + messagesize) % - fifo->end_addr_fifo) == fifo->writer_local_wptr) ? - 1 : 0; - - return is_only_one_unread_msg; -} - -void update_ca_common_local_wptr(struct shrm_dev *shrm) -{ - /* - * update CA common reader local write pointer with the - * shared write pointer - */ - struct fifo_read_params *fifo = &cmt_shm_fifo_0; - - fifo->shared_wptr = - (*((u32 *)shrm->ca_common_shared_wptr)); - fifo->reader_local_wptr = fifo->shared_wptr; -} - -void update_ca_audio_local_wptr(struct shrm_dev *shrm) -{ - /* - * update CA audio reader local write pointer with the - * shared write pointer - */ - struct fifo_read_params *fifo = &cmt_shm_fifo_1; - - fifo->shared_wptr = - (*((u32 *)shrm->ca_audio_shared_wptr)); - fifo->reader_local_wptr = fifo->shared_wptr; -} - -void update_ac_common_local_rptr(struct shrm_dev *shrm) -{ - /* - * update AC common writer local read pointer with the - * shared read pointer - */ - struct fifo_write_params *fifo; - u32 free_space = 0; - - fifo = &ape_shm_fifo_0; - - spin_lock_bh(&fifo->fifo_update_lock); - fifo->shared_rptr = - (*((u32 *)shrm->ac_common_shared_rptr)); - - if (fifo->shared_rptr >= fifo->writer_local_rptr) - free_space = - (fifo->shared_rptr-fifo->writer_local_rptr); - else { - free_space = - (fifo->end_addr_fifo-fifo->writer_local_rptr); - free_space += fifo->shared_rptr; - } - - /* Chance of race condition of below variables with write_msg */ - fifo->availablesize += free_space; - fifo->writer_local_rptr = fifo->shared_rptr; - spin_unlock_bh(&fifo->fifo_update_lock); -} - -void update_ac_audio_local_rptr(struct shrm_dev *shrm) -{ - /* - * update AC audio writer local read pointer with the - * shared read pointer - */ - struct fifo_write_params *fifo; - u32 free_space = 0; - - fifo = &ape_shm_fifo_1; - spin_lock_bh(&fifo->fifo_update_lock); - fifo->shared_rptr = - (*((u32 *)shrm->ac_audio_shared_rptr)); - - if (fifo->shared_rptr >= fifo->writer_local_rptr) - free_space = - (fifo->shared_rptr-fifo->writer_local_rptr); - else { - free_space = - (fifo->end_addr_fifo-fifo->writer_local_rptr); - free_space += fifo->shared_rptr; - } - - /* Chance of race condition of below variables with write_msg */ - fifo->availablesize += free_space; - fifo->writer_local_rptr = fifo->shared_rptr; - spin_unlock_bh(&fifo->fifo_update_lock); -} - -void update_ac_common_shared_wptr(struct shrm_dev *shrm) -{ - /* - * update AC common shared write pointer with the - * local write pointer - */ - struct fifo_write_params *fifo; - - fifo = &ape_shm_fifo_0; - spin_lock_bh(&fifo->fifo_update_lock); - /* Update shared pointer fifo offset of the IPC zone */ - (*((u32 *)shrm->ac_common_shared_wptr)) = - fifo->writer_local_wptr; - - fifo->shared_wptr = fifo->writer_local_wptr; - spin_unlock_bh(&fifo->fifo_update_lock); -} - -void update_ac_audio_shared_wptr(struct shrm_dev *shrm) -{ - /* - * update AC audio shared write pointer with the - * local write pointer - */ - struct fifo_write_params *fifo; - - fifo = &ape_shm_fifo_1; - spin_lock_bh(&fifo->fifo_update_lock); - /* Update shared pointer fifo offset of the IPC zone */ - (*((u32 *)shrm->ac_audio_shared_wptr)) = - fifo->writer_local_wptr; - fifo->shared_wptr = fifo->writer_local_wptr; - spin_unlock_bh(&fifo->fifo_update_lock); -} - -void update_ca_common_shared_rptr(struct shrm_dev *shrm) -{ - /* - * update CA common shared read pointer with the - * local read pointer - */ - struct fifo_read_params *fifo; - - fifo = &cmt_shm_fifo_0; - - /* Update shared pointer fifo offset of the IPC zone */ - (*((u32 *)shrm->ca_common_shared_rptr)) = - fifo->reader_local_rptr; - fifo->shared_rptr = fifo->reader_local_rptr; -} - -void update_ca_audio_shared_rptr(struct shrm_dev *shrm) -{ - /* - * update CA audio shared read pointer with the - * local read pointer - */ - struct fifo_read_params *fifo; - - fifo = &cmt_shm_fifo_1; - - /* Update shared pointer fifo offset of the IPC zone */ - (*((u32 *)shrm->ca_audio_shared_rptr)) = - fifo->reader_local_rptr; - fifo->shared_rptr = fifo->reader_local_rptr; -} - -void get_reader_pointers(u8 channel_type, u32 *reader_local_rptr, - u32 *reader_local_wptr, u32 *shared_rptr) -{ - struct fifo_read_params *fifo = NULL; - - if (channel_type == COMMON_CHANNEL) - fifo = &cmt_shm_fifo_0; - else /* channel_type = AUDIO_CHANNEL */ - fifo = &cmt_shm_fifo_1; - - *reader_local_rptr = fifo->reader_local_rptr; - *reader_local_wptr = fifo->reader_local_wptr; - *shared_rptr = fifo->shared_rptr; -} - -void get_writer_pointers(u8 channel_type, u32 *writer_local_rptr, - u32 *writer_local_wptr, u32 *shared_wptr) -{ - struct fifo_write_params *fifo = NULL; - - if (channel_type == COMMON_CHANNEL) - fifo = &ape_shm_fifo_0; - else /* channel_type = AUDIO_CHANNEL */ - fifo = &ape_shm_fifo_1; - - spin_lock_bh(&fifo->fifo_update_lock); - *writer_local_rptr = fifo->writer_local_rptr; - *writer_local_wptr = fifo->writer_local_wptr; - *shared_wptr = fifo->shared_wptr; - spin_unlock_bh(&fifo->fifo_update_lock); -} - -void set_ca_msg_0_read_notif_send(u8 val) -{ - cmt_read_notif_0_send = val; -} - -u8 get_ca_msg_0_read_notif_send(void) -{ - return cmt_read_notif_0_send; -} - -void set_ca_msg_1_read_notif_send(u8 val) -{ - cmt_read_notif_1_send = val; -} - -u8 get_ca_msg_1_read_notif_send(void) -{ - return cmt_read_notif_1_send; -} diff --git a/drivers/misc/shrm/shrm_protocol.c b/drivers/misc/shrm/shrm_protocol.c deleted file mode 100644 index 686bebcb451..00000000000 --- a/drivers/misc/shrm/shrm_protocol.c +++ /dev/null @@ -1,1191 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Biju Das for ST-Ericsson - * Author: Kumar Sanghvi for ST-Ericsson - * Author: Arun Murthy for ST-Ericsson - * License terms: GNU General Public License (GPL) version 2 - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define L2_HEADER_ISI 0x0 -#define L2_HEADER_RPC 0x1 -#define L2_HEADER_AUDIO 0x2 -#define L2_HEADER_SECURITY 0x3 -#define L2_HEADER_COMMON_SIMPLE_LOOPBACK 0xC0 -#define L2_HEADER_COMMON_ADVANCED_LOOPBACK 0xC1 -#define L2_HEADER_AUDIO_SIMPLE_LOOPBACK 0x80 -#define L2_HEADER_AUDIO_ADVANCED_LOOPBACK 0x81 -#define MAX_PAYLOAD 1024 - -static u8 boot_state = BOOT_INIT; -static u8 recieve_common_msg[8*1024]; -static u8 recieve_audio_msg[8*1024]; -static received_msg_handler rx_common_handler; -static received_msg_handler rx_audio_handler; -static struct hrtimer timer; -struct sock *shrm_nl_sk; - -static char shrm_common_tx_state = SHRM_SLEEP_STATE; -static char shrm_common_rx_state = SHRM_SLEEP_STATE; -static char shrm_audio_tx_state = SHRM_SLEEP_STATE; -static char shrm_audio_rx_state = SHRM_SLEEP_STATE; - -static atomic_t ac_sleep_disable_count = ATOMIC_INIT(0); -static struct shrm_dev *shm_dev; - -/* Spin lock and tasklet declaration */ -DECLARE_TASKLET(shm_ca_0_tasklet, shm_ca_msgpending_0_tasklet, 0); -DECLARE_TASKLET(shm_ca_1_tasklet, shm_ca_msgpending_1_tasklet, 0); -DECLARE_TASKLET(shm_ac_read_0_tasklet, shm_ac_read_notif_0_tasklet, 0); -DECLARE_TASKLET(shm_ac_read_1_tasklet, shm_ac_read_notif_1_tasklet, 0); - -static DEFINE_MUTEX(ac_state_mutex); - -static DEFINE_SPINLOCK(ca_common_lock); -static DEFINE_SPINLOCK(ca_audio_lock); -static DEFINE_SPINLOCK(ca_wake_req_lock); -static DEFINE_SPINLOCK(boot_lock); - -enum shrm_nl { - SHRM_NL_MOD_RESET = 1, - SHRM_NL_MOD_QUERY_STATE, - SHRM_NL_USER_MOD_RESET, - SHRM_NL_STATUS_MOD_ONLINE, - SHRM_NL_STATUS_MOD_OFFLINE, -}; - -static void shm_ac_sleep_req_work(struct work_struct *work) -{ - mutex_lock(&ac_state_mutex); - if (atomic_read(&ac_sleep_disable_count) == 0) - modem_release(shm_dev->modem); - mutex_unlock(&ac_state_mutex); -} - -static void shm_ac_wake_req_work(struct work_struct *work) -{ - mutex_lock(&ac_state_mutex); - modem_request(shm_dev->modem); - mutex_unlock(&ac_state_mutex); -} - -static u32 get_host_accessport_val(void) -{ - u32 prcm_hostaccess; - - prcm_hostaccess = readl(PRCM_HOSTACCESS_REQ); - wmb(); - prcm_hostaccess = prcm_hostaccess & 0x01; - - return prcm_hostaccess; -} -static enum hrtimer_restart callback(struct hrtimer *timer) -{ - unsigned long flags; - - spin_lock_irqsave(&ca_wake_req_lock, flags); - if (((shrm_common_rx_state == SHRM_IDLE) || - (shrm_common_rx_state == SHRM_SLEEP_STATE)) - && ((shrm_common_tx_state == SHRM_IDLE) || - (shrm_common_tx_state == SHRM_SLEEP_STATE)) - && ((shrm_audio_rx_state == SHRM_IDLE) || - (shrm_audio_rx_state == SHRM_SLEEP_STATE)) - && ((shrm_audio_tx_state == SHRM_IDLE) || - (shrm_audio_tx_state == SHRM_SLEEP_STATE))) { - - shrm_common_rx_state = SHRM_SLEEP_STATE; - shrm_audio_rx_state = SHRM_SLEEP_STATE; - shrm_common_tx_state = SHRM_SLEEP_STATE; - shrm_audio_tx_state = SHRM_SLEEP_STATE; - - queue_work(shm_dev->shm_ac_sleep_wq, - &shm_dev->shm_ac_sleep_req); - - } - spin_unlock_irqrestore(&ca_wake_req_lock, flags); - - return HRTIMER_NORESTART; -} - -int nl_send_multicast_message(int msg, gfp_t gfp_mask) -{ - struct sk_buff *skb = NULL; - struct nlmsghdr *nlh = NULL; - int err; - - /* prepare netlink message */ - skb = alloc_skb(NLMSG_SPACE(MAX_PAYLOAD), gfp_mask); - if (!skb) { - dev_err(shm_dev->dev, "%s:alloc_skb failed\n", __func__); - err = -ENOMEM; - goto out; - } - - nlh = (struct nlmsghdr *)skb->data; - nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD); - dev_dbg(shm_dev->dev, "nlh->nlmsg_len = %d\n", nlh->nlmsg_len); - - nlh->nlmsg_pid = 0; /* from kernel */ - nlh->nlmsg_flags = 0; - *(int *)NLMSG_DATA(nlh) = msg; - skb_put(skb, MAX_PAYLOAD); - /* sender is in group 1<<0 */ - NETLINK_CB(skb).pid = 0; /* from kernel */ - /* to mcast group 1<<0 */ - NETLINK_CB(skb).dst_group = 1; - - /*multicast the message to all listening processes*/ - err = netlink_broadcast(shrm_nl_sk, skb, 0, 1, gfp_mask); - dev_dbg(shm_dev->dev, "ret val from nl-multicast = %d\n", err); - -out: - return err; -} - -static void nl_send_unicast_message(int dst_pid) -{ - struct sk_buff *skb = NULL; - struct nlmsghdr *nlh = NULL; - int err; - int bt_state; - unsigned long flags; - - dev_info(shm_dev->dev, "Sending unicast message\n"); - - /* prepare the NL message for unicast */ - skb = alloc_skb(NLMSG_SPACE(MAX_PAYLOAD), GFP_KERNEL); - if (!skb) { - dev_err(shm_dev->dev, "%s:alloc_skb failed\n", __func__); - return; - } - - nlh = (struct nlmsghdr *)skb->data; - nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD); - dev_dbg(shm_dev->dev, "nlh->nlmsg_len = %d\n", nlh->nlmsg_len); - - nlh->nlmsg_pid = 0; /* from kernel */ - nlh->nlmsg_flags = 0; - - spin_lock_irqsave(&boot_lock, flags); - bt_state = boot_state; - spin_unlock_irqrestore(&boot_lock, flags); - - if (bt_state == BOOT_DONE) - *(int *)NLMSG_DATA(nlh) = SHRM_NL_STATUS_MOD_ONLINE; - else - *(int *)NLMSG_DATA(nlh) = SHRM_NL_STATUS_MOD_OFFLINE; - - skb_put(skb, MAX_PAYLOAD); - /* sender is in group 1<<0 */ - NETLINK_CB(skb).pid = 0; /* from kernel */ - NETLINK_CB(skb).dst_group = 0; - - /*unicast the message to the querying processes*/ - err = netlink_unicast(shrm_nl_sk, skb, dst_pid, MSG_DONTWAIT); - dev_dbg(shm_dev->dev, "ret val from nl-unicast = %d\n", err); -} - - -static int check_modem_in_reset(void) -{ - u8 bt_state; - unsigned long flags; - - spin_lock_irqsave(&boot_lock, flags); - bt_state = boot_state; - spin_unlock_irqrestore(&boot_lock, flags); - -#ifdef CONFIG_U8500_SHRM_MODEM_SILENT_RESET - if (bt_state != BOOT_UNKNOWN) - return 0; - else - return -ENODEV; -#else - /* - * this check won't be applicable and won't work correctly - * if modem-silent-feature is not enabled - * so, simply return 0 - */ - return 0; -#endif -} - -void shm_ca_msgpending_0_tasklet(unsigned long tasklet_data) -{ - struct shrm_dev *shrm = (struct shrm_dev *)tasklet_data; - u32 reader_local_rptr; - u32 reader_local_wptr; - u32 shared_rptr; - u32 config = 0, version = 0; - unsigned long flags; - - dev_dbg(shrm->dev, "%s IN\n", __func__); - - /* Interprocess locking */ - spin_lock(&ca_common_lock); - - /* Update_reader_local_wptr with shared_wptr */ - update_ca_common_local_wptr(shrm); - get_reader_pointers(COMMON_CHANNEL, &reader_local_rptr, - &reader_local_wptr, &shared_rptr); - - set_ca_msg_0_read_notif_send(0); - - if (boot_state == BOOT_DONE) { - shrm_common_rx_state = SHRM_PTR_FREE; - - if (reader_local_rptr != shared_rptr) - ca_msg_read_notification_0(shrm); - if (reader_local_rptr != reader_local_wptr) - receive_messages_common(shrm); - get_reader_pointers(COMMON_CHANNEL, &reader_local_rptr, - &reader_local_wptr, &shared_rptr); - if (reader_local_rptr == reader_local_wptr) - shrm_common_rx_state = SHRM_IDLE; - } else { - /* BOOT phase.only a BOOT_RESP should be in FIFO */ - if (boot_state != BOOT_INFO_SYNC) { - if (!read_boot_info_req(shrm, &config, &version)) { - dev_err(shrm->dev, - "Unable to read boot state\n"); - BUG(); - } - /* SendReadNotification */ - ca_msg_read_notification_0(shrm); - /* - * Check the version number before - * sending Boot info response - */ - - /* send MsgPending notification */ - write_boot_info_resp(shrm, config, version); - spin_lock_irqsave(&boot_lock, flags); - boot_state = BOOT_INFO_SYNC; - spin_unlock_irqrestore(&boot_lock, flags); - dev_info(shrm->dev, "BOOT_INFO_SYNC\n"); - queue_work(shrm->shm_common_ch_wr_wq, - &shrm->send_ac_msg_pend_notify_0); - } else { - ca_msg_read_notification_0(shrm); - dev_info(shrm->dev, - "BOOT_INFO_SYNC\n"); - } - } - /* Interprocess locking */ - spin_unlock(&ca_common_lock); - dev_dbg(shrm->dev, "%s OUT\n", __func__); -} - -void shm_ca_msgpending_1_tasklet(unsigned long tasklet_data) -{ - struct shrm_dev *shrm = (struct shrm_dev *)tasklet_data; - u32 reader_local_rptr; - u32 reader_local_wptr; - u32 shared_rptr; - - /* - * This function is called when CaMsgPendingNotification Trigerred - * by CMU. It means that CMU has wrote a message into Ca Audio FIFO - */ - - dev_dbg(shrm->dev, "%s IN\n", __func__); - - if (check_modem_in_reset()) { - dev_err(shrm->dev, "%s:Modem state reset or unknown\n", - __func__); - return; - } - - /* Interprocess locking */ - spin_lock(&ca_audio_lock); - - /* Update_reader_local_wptr(with shared_wptr) */ - update_ca_audio_local_wptr(shrm); - get_reader_pointers(AUDIO_CHANNEL, &reader_local_rptr, - &reader_local_wptr, &shared_rptr); - - set_ca_msg_1_read_notif_send(0); - - if (boot_state != BOOT_DONE) { - dev_err(shrm->dev, "Boot Error\n"); - return; - } - shrm_audio_rx_state = SHRM_PTR_FREE; - /* Check we already read the message */ - if (reader_local_rptr != shared_rptr) - ca_msg_read_notification_1(shrm); - if (reader_local_rptr != reader_local_wptr) - receive_messages_audio(shrm); - - get_reader_pointers(AUDIO_CHANNEL, &reader_local_rptr, - &reader_local_wptr, &shared_rptr); - if (reader_local_rptr == reader_local_wptr) - shrm_audio_rx_state = SHRM_IDLE; - - /* Interprocess locking */ - spin_unlock(&ca_audio_lock); - dev_dbg(shrm->dev, "%s OUT\n", __func__); -} - -void shm_ac_read_notif_0_tasklet(unsigned long tasklet_data) -{ - struct shrm_dev *shrm = (struct shrm_dev *)tasklet_data; - u32 writer_local_rptr; - u32 writer_local_wptr; - u32 shared_wptr; - unsigned long flags; - - dev_dbg(shrm->dev, "%s IN\n", __func__); - - /* Update writer_local_rptrwith shared_rptr */ - update_ac_common_local_rptr(shrm); - get_writer_pointers(COMMON_CHANNEL, &writer_local_rptr, - &writer_local_wptr, &shared_wptr); - - if (check_modem_in_reset()) { - dev_err(shrm->dev, "%s:Modem state reset or unknown\n", - __func__); - return; - } - - if (boot_state == BOOT_INFO_SYNC) { - /* BOOT_RESP sent by APE has been received by CMT */ - spin_lock_irqsave(&boot_lock, flags); - boot_state = BOOT_DONE; - spin_unlock_irqrestore(&boot_lock, flags); - dev_info(shrm->dev, "IPC_ISA BOOT_DONE\n"); - - if (shrm->msr_flag) { - shrm_start_netdev(shrm->ndev); - shrm->msr_flag = 0; - - /* multicast that modem is online */ - nl_send_multicast_message(SHRM_NL_STATUS_MOD_ONLINE, GFP_ATOMIC); - } - - } else if (boot_state == BOOT_DONE) { - if (writer_local_rptr != writer_local_wptr) { - shrm_common_tx_state = SHRM_PTR_FREE; - queue_work(shrm->shm_common_ch_wr_wq, - &shrm->send_ac_msg_pend_notify_0); - } else { - shrm_common_tx_state = SHRM_IDLE; - shrm_restart_netdev(shrm->ndev); - } - } else { - dev_err(shrm->dev, "Invalid boot state\n"); - } - /* start timer here */ - hrtimer_start(&timer, ktime_set(0, 10*NSEC_PER_MSEC), - HRTIMER_MODE_REL); - atomic_dec(&ac_sleep_disable_count); - - dev_dbg(shrm->dev, "%s OUT\n", __func__); -} - -void shm_ac_read_notif_1_tasklet(unsigned long tasklet_data) -{ - struct shrm_dev *shrm = (struct shrm_dev *)tasklet_data; - u32 writer_local_rptr; - u32 writer_local_wptr; - u32 shared_wptr; - - dev_dbg(shrm->dev, "%s IN\n", __func__); - - if (check_modem_in_reset()) { - dev_err(shrm->dev, "%s:Modem state reset or unknown\n", - __func__); - return; - } - - /* Update writer_local_rptr(with shared_rptr) */ - update_ac_audio_local_rptr(shrm); - get_writer_pointers(AUDIO_CHANNEL, &writer_local_rptr, - &writer_local_wptr, &shared_wptr); - if (boot_state != BOOT_DONE) { - dev_err(shrm->dev, "Error Case in boot state\n"); - return; - } - if (writer_local_rptr != writer_local_wptr) { - shrm_audio_tx_state = SHRM_PTR_FREE; - queue_work(shrm->shm_audio_ch_wr_wq, - &shrm->send_ac_msg_pend_notify_1); - } else { - shrm_audio_tx_state = SHRM_IDLE; - } - /* start timer here */ - hrtimer_start(&timer, ktime_set(0, 10*NSEC_PER_MSEC), - HRTIMER_MODE_REL); - atomic_dec(&ac_sleep_disable_count); - - dev_dbg(shrm->dev, "%s OUT\n", __func__); -} - -void shm_ca_sleep_req_work(struct work_struct *work) -{ - dev_dbg(shm_dev->dev, "%s:IRQ_PRCMU_CA_SLEEP\n", __func__); - - shrm_common_rx_state = SHRM_IDLE; - shrm_audio_rx_state = SHRM_IDLE; - - writel((1<intr_base + GOP_SET_REGISTER_BASE); - - hrtimer_start(&timer, ktime_set(0, 10*NSEC_PER_MSEC), - HRTIMER_MODE_REL); -#ifdef CONFIG_UX500_SUSPEND - suspend_unblock_sleep(); -#endif - atomic_dec(&ac_sleep_disable_count); -} - -void shm_ca_wake_req_work(struct work_struct *work) -{ - struct shrm_dev *shrm = container_of(work, - struct shrm_dev, shm_ca_wake_req); - - /* initialize the FIFO Variables */ - if (boot_state == BOOT_INIT) - shm_fifo_init(shrm); - - mutex_lock(&ac_state_mutex); - modem_request(shrm->modem); - mutex_unlock(&ac_state_mutex); - - /* send ca_wake_ack_interrupt to CMU */ - if (!get_host_accessport_val()) - BUG(); - writel((1<intr_base + GOP_SET_REGISTER_BASE); -} -#ifdef CONFIG_U8500_SHRM_MODEM_SILENT_RESET -static int shrm_modem_reset_sequence(void) -{ - int err; - unsigned long flags; - - /* - * disable irqs - * very much needed for user-space initiated - * modem-reset - */ - disable_irq_nosync(shm_dev->ac_read_notif_0_irq); - disable_irq_nosync(shm_dev->ac_read_notif_1_irq); - disable_irq_nosync(shm_dev->ca_msg_pending_notif_0_irq); - disable_irq_nosync(shm_dev->ca_msg_pending_notif_1_irq); - disable_irq_nosync(IRQ_PRCMU_CA_WAKE); - disable_irq_nosync(IRQ_PRCMU_CA_SLEEP); - - - /* update the boot_state */ - spin_lock_irqsave(&boot_lock, flags); - boot_state = BOOT_UNKNOWN; - - /* - * put a barrier over here to make sure boot_state is updated - * else, it is seen that some of already executing modem - * irqs or tasklets fail the protocol checks and will ultimately - * try to acces the modem causing system to hang. - * This is particularly seen with user-space initiated modem reset - */ - wmb(); - spin_unlock_irqrestore(&boot_lock, flags); - - hrtimer_cancel(&timer); - - /* - * keep the count to 0 so that we can bring down the line - * for normal ac-wake and ac-sleep logic - */ - atomic_set(&ac_sleep_disable_count, 0); - - /* workaround for MSR */ - queue_work(shm_dev->shm_ac_wake_wq, - &shm_dev->shm_ac_wake_req); - - /* stop network queue */ - shrm_stop_netdev(shm_dev->ndev); - - /* reset char device queues */ - shrm_char_reset_queues(shm_dev); - - /* reset protocol states */ - shrm_common_tx_state = SHRM_SLEEP_STATE; - shrm_common_rx_state = SHRM_SLEEP_STATE; - shrm_audio_tx_state = SHRM_SLEEP_STATE; - shrm_audio_rx_state = SHRM_SLEEP_STATE; - - /* set the msr flag */ - shm_dev->msr_flag = 1; - - /* multicast that modem is going to reset */ - err = nl_send_multicast_message(SHRM_NL_MOD_RESET, GFP_ATOMIC); - - /* reset the boot state */ - spin_lock_irqsave(&boot_lock, flags); - boot_state = BOOT_INIT; - spin_unlock_irqrestore(&boot_lock, flags); - - /* re-enable irqs */ - enable_irq(shm_dev->ac_read_notif_0_irq); - enable_irq(shm_dev->ac_read_notif_1_irq); - enable_irq(shm_dev->ca_msg_pending_notif_0_irq); - enable_irq(shm_dev->ca_msg_pending_notif_1_irq); - enable_irq(IRQ_PRCMU_CA_WAKE); - enable_irq(IRQ_PRCMU_CA_SLEEP); - - return err; -} -#endif - -static void shrm_modem_reset_callback(unsigned long irq) -{ - dev_err(shm_dev->dev, "Received mod_reset_req interrupt\n"); - -#ifdef CONFIG_U8500_SHRM_MODEM_SILENT_RESET - { - int err; - dev_info(shm_dev->dev, "Initiating Modem silent reset\n"); - - err = shrm_modem_reset_sequence(); - if (err) - dev_err(shm_dev->dev, - "Failed multicast of modem reset\n"); - } -#else - dev_info(shm_dev->dev, "Modem in reset loop, doing System reset\n"); - - /* Call the PRCMU reset API */ - prcmu_system_reset(SW_RESET_NO_ARGUMENT); -#endif -} - -DECLARE_TASKLET(shrm_sw_reset_callback, shrm_modem_reset_callback, - IRQ_PRCMU_MODEM_SW_RESET_REQ); - -static irqreturn_t shrm_prcmu_irq_handler(int irq, void *data) -{ - struct shrm_dev *shrm = data; - - switch (irq) { - case IRQ_PRCMU_CA_WAKE: -#ifdef CONFIG_UX500_SUSPEND - suspend_block_sleep(); -#endif - if (shrm->msr_flag) - atomic_set(&ac_sleep_disable_count, 0); - atomic_inc(&ac_sleep_disable_count); - queue_work(shrm->shm_ca_wake_wq, &shrm->shm_ca_wake_req); - break; - case IRQ_PRCMU_CA_SLEEP: - queue_work(shrm->shm_ca_wake_wq, &shrm->shm_ca_sleep_req); - break; - case IRQ_PRCMU_MODEM_SW_RESET_REQ: - tasklet_schedule(&shrm_sw_reset_callback); - break; - default: - dev_err(shrm->dev, "%s: => IRQ %d\n", __func__, irq); - return IRQ_NONE; - } - return IRQ_HANDLED; -} - -static void send_ac_msg_pend_notify_0_work(struct work_struct *work) -{ - struct shrm_dev *shrm = container_of(work, struct shrm_dev, - send_ac_msg_pend_notify_0); - - dev_dbg(shrm->dev, "%s IN\n", __func__); - update_ac_common_shared_wptr(shrm); - - mutex_lock(&ac_state_mutex); - atomic_inc(&ac_sleep_disable_count); - modem_request(shrm->modem); - mutex_unlock(&ac_state_mutex); - - if (!get_host_accessport_val()) - BUG(); - - /* Trigger AcMsgPendingNotification to CMU */ - writel((1<intr_base + GOP_SET_REGISTER_BASE); - - if (shrm_common_tx_state == SHRM_PTR_FREE) - shrm_common_tx_state = SHRM_PTR_BUSY; - - dev_dbg(shrm->dev, "%s OUT\n", __func__); -} - -static void send_ac_msg_pend_notify_1_work(struct work_struct *work) -{ - struct shrm_dev *shrm = container_of(work, struct shrm_dev, - send_ac_msg_pend_notify_1); - - dev_dbg(shrm->dev, "%s IN\n", __func__); - /* Update shared_wptr with writer_local_wptr) */ - update_ac_audio_shared_wptr(shrm); - - mutex_lock(&ac_state_mutex); - atomic_inc(&ac_sleep_disable_count); - modem_request(shrm->modem); - mutex_unlock(&ac_state_mutex); - - if (!get_host_accessport_val()) - BUG(); - - /* Trigger AcMsgPendingNotification to CMU */ - writel((1<intr_base + GOP_SET_REGISTER_BASE); - - if (shrm_audio_tx_state == SHRM_PTR_FREE) - shrm_audio_tx_state = SHRM_PTR_BUSY; - - dev_dbg(shrm->dev, "%s OUT\n", __func__); -} - -void shm_nl_receive(struct sk_buff *skb) -{ - struct nlmsghdr *nlh = NULL; - int msg; - - dev_dbg(shm_dev->dev, "Received NL msg from user-space\n"); - - nlh = (struct nlmsghdr *)skb->data; - msg = *((int *)(NLMSG_DATA(nlh))); - switch (msg) { - case SHRM_NL_MOD_QUERY_STATE: - dev_info(shm_dev->dev, "mod-query-state from user-space\n"); - nl_send_unicast_message(nlh->nlmsg_pid); - break; - - case SHRM_NL_USER_MOD_RESET: - dev_info(shm_dev->dev, "user-space inited mod-reset-req\n"); - dev_info(shm_dev->dev, "PCRMU resets modem\n"); - prcmu_modem_reset(); - break; - - default: - dev_err(shm_dev->dev, "Invalid NL msg from user-space\n"); - break; - }; -} - -int shrm_protocol_init(struct shrm_dev *shrm, - received_msg_handler common_rx_handler, - received_msg_handler audio_rx_handler) -{ - int err; - - shm_dev = shrm; - boot_state = BOOT_INIT; - dev_info(shrm->dev, "IPC_ISA BOOT_INIT\n"); - rx_common_handler = common_rx_handler; - rx_audio_handler = audio_rx_handler; - atomic_set(&ac_sleep_disable_count, 0); - - hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - timer.function = callback; - - shrm->shm_common_ch_wr_wq = create_singlethread_workqueue - ("shm_common_channel_irq"); - if (!shrm->shm_common_ch_wr_wq) { - dev_err(shrm->dev, "failed to create work queue\n"); - return -ENOMEM; - } - shrm->shm_audio_ch_wr_wq = create_singlethread_workqueue - ("shm_audio_channel_irq"); - if (!shrm->shm_audio_ch_wr_wq) { - dev_err(shrm->dev, "failed to create work queue\n"); - err = -ENOMEM; - goto free_wq1; - } - shrm->shm_ac_wake_wq = create_singlethread_workqueue("shm_ac_wake_req"); - if (!shrm->shm_ac_wake_wq) { - dev_err(shrm->dev, "failed to create work queue\n"); - err = -ENOMEM; - goto free_wq2; - } - shrm->shm_ca_wake_wq = create_singlethread_workqueue("shm_ca_wake_req"); - if (!shrm->shm_ac_wake_wq) { - dev_err(shrm->dev, "failed to create work queue\n"); - err = -ENOMEM; - goto free_wq3; - } - shrm->shm_ac_sleep_wq = create_singlethread_workqueue - ("shm_ac_sleep_req"); - if (!shrm->shm_ac_sleep_wq) { - dev_err(shrm->dev, "failed to create work queue\n"); - err = -ENOMEM; - goto free_wq4; - } - INIT_WORK(&shrm->send_ac_msg_pend_notify_0, - send_ac_msg_pend_notify_0_work); - INIT_WORK(&shrm->send_ac_msg_pend_notify_1, - send_ac_msg_pend_notify_1_work); - INIT_WORK(&shrm->shm_ca_wake_req, shm_ca_wake_req_work); - INIT_WORK(&shrm->shm_ca_sleep_req, shm_ca_sleep_req_work); - INIT_WORK(&shrm->shm_ac_sleep_req, shm_ac_sleep_req_work); - INIT_WORK(&shrm->shm_ac_wake_req, shm_ac_wake_req_work); - - /* set tasklet data */ - shm_ca_0_tasklet.data = (unsigned long)shrm; - shm_ca_1_tasklet.data = (unsigned long)shrm; - - err = request_irq(IRQ_PRCMU_CA_SLEEP, shrm_prcmu_irq_handler, - IRQF_NO_SUSPEND, "ca-sleep", shrm); - if (err < 0) { - dev_err(shm_dev->dev, "Failed alloc IRQ_PRCMU_CA_SLEEP.\n"); - goto free_wq5; - } - - err = request_irq(IRQ_PRCMU_CA_WAKE, shrm_prcmu_irq_handler, - IRQF_NO_SUSPEND, "ca-wake", shrm); - if (err < 0) { - dev_err(shm_dev->dev, "Failed alloc IRQ_PRCMU_CA_WAKE.\n"); - goto drop2; - } - - err = request_irq(IRQ_PRCMU_MODEM_SW_RESET_REQ, shrm_prcmu_irq_handler, - IRQF_NO_SUSPEND, "modem-sw-reset-req", shrm); - if (err < 0) { - dev_err(shm_dev->dev, - "Failed alloc IRQ_PRCMU_MODEM_SW_RESET_REQ.\n"); - goto drop1; - } - -#ifdef CONFIG_U8500_SHRM_MODEM_SILENT_RESET - /* init netlink socket for user-space communication */ - shrm_nl_sk = netlink_kernel_create(NULL, NETLINK_SHRM, 1, - shm_nl_receive, NULL, THIS_MODULE); - - if (!shrm_nl_sk) { - dev_err(shm_dev->dev, "netlink socket creation failed\n"); - goto drop; - } -#endif - return 0; - -#ifdef CONFIG_U8500_SHRM_MODEM_SILENT_RESET -drop: - free_irq(IRQ_PRCMU_MODEM_SW_RESET_REQ, NULL); -#endif -drop1: - free_irq(IRQ_PRCMU_CA_WAKE, NULL); -drop2: - free_irq(IRQ_PRCMU_CA_SLEEP, NULL); -free_wq5: - destroy_workqueue(shrm->shm_ac_sleep_wq); -free_wq4: - destroy_workqueue(shrm->shm_ca_wake_wq); -free_wq3: - destroy_workqueue(shrm->shm_ac_wake_wq); -free_wq2: - destroy_workqueue(shrm->shm_audio_ch_wr_wq); -free_wq1: - destroy_workqueue(shrm->shm_common_ch_wr_wq); - return err; -} - -void shrm_protocol_deinit(struct shrm_dev *shrm) -{ - free_irq(IRQ_PRCMU_CA_SLEEP, NULL); - free_irq(IRQ_PRCMU_CA_WAKE, NULL); - free_irq(IRQ_PRCMU_MODEM_SW_RESET_REQ, NULL); - flush_scheduled_work(); - destroy_workqueue(shrm->shm_common_ch_wr_wq); - destroy_workqueue(shrm->shm_audio_ch_wr_wq); - destroy_workqueue(shrm->shm_ac_wake_wq); - destroy_workqueue(shrm->shm_ca_wake_wq); - destroy_workqueue(shrm->shm_ac_sleep_wq); - modem_put(shrm->modem); -} - -int get_ca_wake_req_state(void) -{ - return ((atomic_read(&ac_sleep_disable_count) > 0) || - modem_get_usage(shm_dev->modem)); -} - -irqreturn_t ca_wake_irq_handler(int irq, void *ctrlr) -{ - struct shrm_dev *shrm = ctrlr; - - dev_dbg(shrm->dev, "%s IN\n", __func__); - /* initialize the FIFO Variables */ - if (boot_state == BOOT_INIT) - shm_fifo_init(shrm); - - dev_dbg(shrm->dev, "Inside ca_wake_irq_handler\n"); - - /* Clear the interrupt */ - writel((1 << GOP_CA_WAKE_REQ_BIT), - shrm->intr_base + GOP_CLEAR_REGISTER_BASE); - - /* send ca_wake_ack_interrupt to CMU */ - writel((1 << GOP_CA_WAKE_ACK_BIT), - shrm->intr_base + GOP_SET_REGISTER_BASE); - - - dev_dbg(shrm->dev, "%s OUT\n", __func__); - return IRQ_HANDLED; -} - - -irqreturn_t ac_read_notif_0_irq_handler(int irq, void *ctrlr) -{ - struct shrm_dev *shrm = ctrlr; - - dev_dbg(shrm->dev, "%s IN\n", __func__); - - if (check_modem_in_reset()) { - dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", - __func__); - return IRQ_HANDLED; - } - - shm_ac_read_0_tasklet.data = (unsigned long)shrm; - tasklet_schedule(&shm_ac_read_0_tasklet); - - if (check_modem_in_reset()) { - dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", - __func__); - return IRQ_HANDLED; - } - - /* Clear the interrupt */ - writel((1 << GOP_COMMON_AC_READ_NOTIFICATION_BIT), - shrm->intr_base + GOP_CLEAR_REGISTER_BASE); - - dev_dbg(shrm->dev, "%s OUT\n", __func__); - return IRQ_HANDLED; -} - -irqreturn_t ac_read_notif_1_irq_handler(int irq, void *ctrlr) -{ - struct shrm_dev *shrm = ctrlr; - - dev_dbg(shrm->dev, "%s IN+\n", __func__); - - if (check_modem_in_reset()) { - dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", - __func__); - return IRQ_HANDLED; - } - - shm_ac_read_1_tasklet.data = (unsigned long)shrm; - tasklet_schedule(&shm_ac_read_1_tasklet); - - if (check_modem_in_reset()) { - dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", - __func__); - return IRQ_HANDLED; - } - - /* Clear the interrupt */ - writel((1 << GOP_AUDIO_AC_READ_NOTIFICATION_BIT), - shrm->intr_base + GOP_CLEAR_REGISTER_BASE); - - dev_dbg(shrm->dev, "%s OUT\n", __func__); - return IRQ_HANDLED; -} - -irqreturn_t ca_msg_pending_notif_0_irq_handler(int irq, void *ctrlr) -{ - struct shrm_dev *shrm = ctrlr; - - dev_dbg(shrm->dev, "%s IN\n", __func__); - - if (check_modem_in_reset()) { - dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", - __func__); - return IRQ_HANDLED; - } - - tasklet_schedule(&shm_ca_0_tasklet); - - if (check_modem_in_reset()) { - dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", - __func__); - return IRQ_HANDLED; - } - - /* Clear the interrupt */ - writel((1 << GOP_COMMON_CA_MSG_PENDING_NOTIFICATION_BIT), - shrm->intr_base + GOP_CLEAR_REGISTER_BASE); - - dev_dbg(shrm->dev, "%s OUT\n", __func__); - return IRQ_HANDLED; -} - -irqreturn_t ca_msg_pending_notif_1_irq_handler(int irq, void *ctrlr) -{ - struct shrm_dev *shrm = ctrlr; - - dev_dbg(shrm->dev, "%s IN\n", __func__); - - if (check_modem_in_reset()) { - dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", - __func__); - return IRQ_HANDLED; - } - - tasklet_schedule(&shm_ca_1_tasklet); - - if (check_modem_in_reset()) { - dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", - __func__); - return IRQ_HANDLED; - } - - /* Clear the interrupt */ - writel((1<intr_base+GOP_CLEAR_REGISTER_BASE); - - dev_dbg(shrm->dev, "%s OUT\n", __func__); - return IRQ_HANDLED; - -} - -/** - * shm_write_msg() - write message to shared memory - * @shrm: pointer to the shrm device information structure - * @l2_header: L2 header - * @addr: pointer to the message - * @length: length of the message to be written - * - * This function is called from net or char interface driver write operation. - * Prior to calling this function the message is copied from the user space - * buffer to the kernel buffer. This function based on the l2 header routes - * the message to the respective channel and FIFO. Then makes a call to the - * fifo write function where the message is written to the physical device. - */ -int shm_write_msg(struct shrm_dev *shrm, u8 l2_header, - void *addr, u32 length) -{ - u8 channel = 0; - int ret; - - dev_dbg(shrm->dev, "%s IN\n", __func__); - - if (boot_state != BOOT_DONE) { - dev_err(shrm->dev, - "error after boot done call this fn\n"); - ret = -ENODEV; - goto out; - } - - if ((l2_header == L2_HEADER_ISI) || - (l2_header == L2_HEADER_RPC) || - (l2_header == L2_HEADER_SECURITY) || - (l2_header == L2_HEADER_COMMON_SIMPLE_LOOPBACK) || - (l2_header == L2_HEADER_COMMON_ADVANCED_LOOPBACK)) { - channel = 0; - if (shrm_common_tx_state == SHRM_SLEEP_STATE) - shrm_common_tx_state = SHRM_PTR_FREE; - else if (shrm_common_tx_state == SHRM_IDLE) - shrm_common_tx_state = SHRM_PTR_FREE; - - } else if ((l2_header == L2_HEADER_AUDIO) || - (l2_header == L2_HEADER_AUDIO_SIMPLE_LOOPBACK) || - (l2_header == L2_HEADER_AUDIO_ADVANCED_LOOPBACK)) { - if (shrm_audio_tx_state == SHRM_SLEEP_STATE) - shrm_audio_tx_state = SHRM_PTR_FREE; - else if (shrm_audio_tx_state == SHRM_IDLE) - shrm_audio_tx_state = SHRM_PTR_FREE; - - channel = 1; - } else { - ret = -ENODEV; - goto out; - } - ret = shm_write_msg_to_fifo(shrm, channel, l2_header, addr, length); - if (ret < 0) { - dev_err(shrm->dev, "write message to fifo failed\n"); - return ret; - } - /* - * notify only if new msg copied is the only unread one - * otherwise it means that reading process is ongoing - */ - if (is_the_only_one_unread_message(shrm, channel, length)) { - - /* Send Message Pending Noitication to CMT */ - if (channel == 0) - queue_work(shrm->shm_common_ch_wr_wq, - &shrm->send_ac_msg_pend_notify_0); - else - queue_work(shrm->shm_audio_ch_wr_wq, - &shrm->send_ac_msg_pend_notify_1); - - } - - dev_dbg(shrm->dev, "%s OUT\n", __func__); - return 0; - -out: - return ret; -} - -void ca_msg_read_notification_0(struct shrm_dev *shrm) -{ - dev_dbg(shrm->dev, "%s IN\n", __func__); - - if (get_ca_msg_0_read_notif_send() == 0) { - update_ca_common_shared_rptr(shrm); - - if (check_modem_in_reset()) { - dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", - __func__); - return; - } - - /* Trigger CaMsgReadNotification to CMU */ - writel((1 << GOP_COMMON_CA_READ_NOTIFICATION_BIT), - shrm->intr_base + GOP_SET_REGISTER_BASE); - set_ca_msg_0_read_notif_send(1); - shrm_common_rx_state = SHRM_PTR_BUSY; - } - - dev_dbg(shrm->dev, "%s OUT\n", __func__); -} - -void ca_msg_read_notification_1(struct shrm_dev *shrm) -{ - dev_dbg(shrm->dev, "%s IN\n", __func__); - - if (get_ca_msg_1_read_notif_send() == 0) { - update_ca_audio_shared_rptr(shrm); - - if (check_modem_in_reset()) { - dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", - __func__); - return; - } - - /* Trigger CaMsgReadNotification to CMU */ - writel((1<intr_base+GOP_SET_REGISTER_BASE); - set_ca_msg_1_read_notif_send(1); - shrm_audio_rx_state = SHRM_PTR_BUSY; - } - dev_dbg(shrm->dev, "%s OUT\n", __func__); -} - -/** - * receive_messages_common - receive common channnel msg from - * CMT(Cellular Mobile Terminal) - * @shrm: pointer to shrm device information structure - * - * The messages sent from CMT to APE are written to the respective FIFO - * and an interrupt is triggered by the CMT. This ca message pending - * interrupt calls this function. This function sends a read notification - * acknowledgement to the CMT and calls the common channel receive handler - * where the messsage is copied to the respective(ISI, RPC, SECURIT) queue - * based on the message l2 header. - */ -void receive_messages_common(struct shrm_dev *shrm) -{ - u8 l2_header; - u32 len; - - if (check_modem_in_reset()) { - dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", - __func__); - return; - } - - l2_header = read_one_l2msg_common(shrm, recieve_common_msg, &len); - /* Send Recieve_Call_back to Upper Layer */ - if (!rx_common_handler) { - dev_err(shrm->dev, "common_rx_handler is Null\n"); - BUG(); - } - (*rx_common_handler)(l2_header, &recieve_common_msg, len, - shrm); - /* SendReadNotification */ - ca_msg_read_notification_0(shrm); - - while (read_remaining_messages_common()) { - if (check_modem_in_reset()) { - dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", - __func__); - return; - } - - l2_header = read_one_l2msg_common(shrm, recieve_common_msg, - &len); - /* Send Recieve_Call_back to Upper Layer */ - (*rx_common_handler)(l2_header, - &recieve_common_msg, len, - shrm); - } -} - -/** - * receive_messages_audio() - receive audio message from CMT - * @shrm: pointer to shrm device information structure - * - * The messages sent from CMT to APE are written to the respective FIFO - * and an interrupt is triggered by the CMT. This ca message pending - * interrupt calls this function. This function sends a read notification - * acknowledgement to the CMT and calls the common channel receive handler - * where the messsage is copied to the audio queue. - */ -void receive_messages_audio(struct shrm_dev *shrm) -{ - u8 l2_header; - u32 len; - - if (check_modem_in_reset()) { - dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", - __func__); - return; - } - - l2_header = read_one_l2msg_audio(shrm, recieve_audio_msg, &len); - /* Send Recieve_Call_back to Upper Layer */ - - if (!rx_audio_handler) { - dev_crit(shrm->dev, "audio_rx_handler is Null\n"); - BUG(); - } - (*rx_audio_handler)(l2_header, &recieve_audio_msg, - len, shrm); - - /* SendReadNotification */ - ca_msg_read_notification_1(shrm); - while (read_remaining_messages_audio()) { - if (check_modem_in_reset()) { - dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", - __func__); - return; - } - - l2_header = read_one_l2msg_audio(shrm, - recieve_audio_msg, &len); - /* Send Recieve_Call_back to Upper Layer */ - (*rx_audio_handler)(l2_header, - &recieve_audio_msg, len, - shrm); - } -} - -u8 get_boot_state() -{ - return boot_state; -} diff --git a/drivers/modem/Kconfig b/drivers/modem/Kconfig index a23b5c90e5e..95d4307775b 100644 --- a/drivers/modem/Kconfig +++ b/drivers/modem/Kconfig @@ -18,3 +18,5 @@ config MODEM_U8500 Application processor. If unsure, say N. + +source "drivers/modem/shrm/Kconfig" diff --git a/drivers/modem/Makefile b/drivers/modem/Makefile index 57b50916c79..e1b3f376cce 100644 --- a/drivers/modem/Makefile +++ b/drivers/modem/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_MODEM) := modem_access.o obj-$(CONFIG_MODEM_U8500) += modem_u8500.o +obj-$(CONFIG_U8500_SHRM) += shrm/ diff --git a/drivers/modem/shrm/Kconfig b/drivers/modem/shrm/Kconfig new file mode 100644 index 00000000000..465c8bb10a1 --- /dev/null +++ b/drivers/modem/shrm/Kconfig @@ -0,0 +1,43 @@ +# +# SHM HW kernel configuration +# +config U8500_SHRM + bool "U8500 SHRM hardware driver" + depends on ARCH_U8500 && PHONET && MODEM_U8500 + default Y + ---help--- + If you say Y here, you will enable the STN8500 SHM hardware driver. + + If unsure, say N. +choice + prompt "Modem Image Version" + depends on U8500_SHRM + default SHRM_V1_UPDATES_VERSION + + config SHRM_V1_UPDATES_VERSION + depends on U8500_SHRM + bool "SHRM V1 UPDATES" + help + Modem Images with V1 Updates + +endchoice + +config U8500_SHRM_LOOP_BACK + bool "U8500 SHRM loopback" + depends on U8500_SHRM + default n + ---help--- + If you say Y here, you will enable the shm loopback + + If unsure, say N. + +config U8500_SHRM_MODEM_SILENT_RESET + bool "U8500 SHRM Modem Silent Reset" + depends on U8500_SHRM + default n + ---help--- + If you say Y here, you will enable the modem silent reset feature + + If unsure, say N. + + diff --git a/drivers/modem/shrm/Makefile b/drivers/modem/shrm/Makefile new file mode 100644 index 00000000000..8115c24920b --- /dev/null +++ b/drivers/modem/shrm/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for SHRM drivers +# + +ifdef CONFIG_PHONET +u8500_shrm-objs := modem_shrm_driver.o shrm_fifo.o shrm_protocol.o +else +u8500_shrm-objs := shrm_driver.o shrm_fifo.o shrm_protocol.o +endif + +obj-$(CONFIG_U8500_SHRM) += u8500_shrm.o diff --git a/drivers/modem/shrm/modem_shrm_driver.c b/drivers/modem/shrm/modem_shrm_driver.c new file mode 100644 index 00000000000..5e986ab9a2c --- /dev/null +++ b/drivers/modem/shrm/modem_shrm_driver.c @@ -0,0 +1,669 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Biju Das for ST-Ericsson + * Author: Kumar Sanghvi for ST-Ericsson + * Author: Arun Murthy for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HIGH_RES_TIMERS +#include +static struct hrtimer timer; +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include +/* debug functionality */ +#define ISA_DEBUG 0 + +#define PHONET_TASKLET +#define MAX_RCV_LEN 2048 + +static void do_phonet_rcv_tasklet(unsigned long unused); +struct tasklet_struct phonet_rcv_tasklet; + +/** + * audio_receive() - Receive audio channel completion callback + * @shrm: pointer to shrm device information structure + * @data: message pointer + * @n_bytes: message size + * @l2_header: L2 header/device ID 2->audio, 5->audio_loopback + * + * This fucntion is called from the audio receive handler. Copies the audio + * message from the FIFO to the AUDIO queue. The message is later copied from + * this queue to the user buffer through the char or net interface read + * operation. + */ +static int audio_receive(struct shrm_dev *shrm, void *data, + u32 n_bytes, u8 l2_header) +{ + u32 size = 0; + int ret = 0; + int idx; + u8 *psrc; + struct message_queue *q; + struct isadev_context *audiodev; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + idx = shrm_get_cdev_index(l2_header); + if (idx < 0) { + dev_err(shrm->dev, "failed to get index\n"); + return idx; + } + audiodev = &shrm->isa_context->isadev[idx]; + q = &audiodev->dl_queue; + spin_lock(&q->update_lock); + /* Memcopy RX data first */ + if ((q->writeptr+n_bytes) >= q->size) { + psrc = (u8 *)data; + size = (q->size-q->writeptr); + /* Copy First Part of msg */ + memcpy((q->fifo_base+q->writeptr), psrc, size); + psrc += size; + /* Copy Second Part of msg at the top of fifo */ + memcpy(q->fifo_base, psrc, (n_bytes-size)); + } else { + memcpy((q->fifo_base+q->writeptr), data, n_bytes); + } + ret = add_msg_to_queue(q, n_bytes); + spin_unlock(&q->update_lock); + if (ret < 0) + dev_err(shrm->dev, "Adding a msg to message queue failed"); + dev_dbg(shrm->dev, "%s OUT\n", __func__); + return ret; +} + +/** + * common_receive() - Receive common channel completion callback + * @shrm: pointer to the shrm device information structure + * @data: message pointer + * @n_bytes: message size + * @l2_header: L2 header / device ID + * + * This function is called from the receive handler to copy the respective + * ISI, RPC, SECURITY message to its respective queue. The message is then + * copied from queue to the user buffer on char net interface read operation. + */ +static int common_receive(struct shrm_dev *shrm, void *data, + u32 n_bytes, u8 l2_header) +{ + u32 size = 0; + int ret = 0; + int idx; + u8 *psrc; + struct message_queue *q; + struct isadev_context *isa_dev; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + idx = shrm_get_cdev_index(l2_header); + if (idx < 0) { + dev_err(shrm->dev, "failed to get index\n"); + return idx; + } + isa_dev = &shrm->isa_context->isadev[idx]; + q = &isa_dev->dl_queue; + spin_lock(&q->update_lock); + /* Memcopy RX data first */ + if ((q->writeptr+n_bytes) >= q->size) { + dev_dbg(shrm->dev, "Inside Loop Back\n"); + psrc = (u8 *)data; + size = (q->size-q->writeptr); + /* Copy First Part of msg */ + memcpy((q->fifo_base+q->writeptr), psrc, size); + psrc += size; + /* Copy Second Part of msg at the top of fifo */ + memcpy(q->fifo_base, psrc, (n_bytes-size)); + } else { + memcpy((q->fifo_base+q->writeptr), data, n_bytes); + } + ret = add_msg_to_queue(q, n_bytes); + spin_unlock(&q->update_lock); + if (ret < 0) { + dev_err(shrm->dev, "Adding a msg to message queue failed"); + return ret; + } + + + if (l2_header == ISI_MESSAGING) { + if (shrm->netdev_flag_up) { + dev_dbg(shrm->dev, + "scheduling the phonet tasklet from %s!\n", + __func__); + tasklet_schedule(&phonet_rcv_tasklet); + } + dev_dbg(shrm->dev, + "Out of phonet tasklet %s!!!\n", __func__); + } + dev_dbg(shrm->dev, "%s OUT\n", __func__); + return ret; +} + +/** + * rx_common_l2msg_handler() - common channel receive handler + * @l2_header: L2 header + * @msg: pointer to the receive buffer + * @length: length of the msg to read + * @shrm: pointer to shrm device information structure + * + * This function is called to receive the message from CaMsgPendingNotification + * interrupt handler. + */ +static void rx_common_l2msg_handler(u8 l2_header, + void *msg, u32 length, + struct shrm_dev *shrm) +{ + int ret = 0; + dev_dbg(shrm->dev, "%s IN\n", __func__); + + ret = common_receive(shrm, msg, length, l2_header); + if (ret < 0) + dev_err(shrm->dev, + "common receive with l2 header %d failed\n", l2_header); + + dev_dbg(shrm->dev, "%s OUT\n", __func__); +} + +/** + * rx_audio_l2msg_handler() - audio channel receive handler + * @l2_header: L2 header + * @msg: pointer to the receive buffer + * @length: length of the msg to read + * @shrm: pointer to shrm device information structure + * + * This function is called to receive the message from CaMsgPendingNotification + * interrupt handler. + */ +static void rx_audio_l2msg_handler(u8 l2_header, + void *msg, u32 length, + struct shrm_dev *shrm) +{ + int ret = 0; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + ret = audio_receive(shrm, msg, length, l2_header); + if (ret < 0) + dev_err(shrm->dev, "audio receive failed\n"); + dev_dbg(shrm->dev, "%s OUT\n", __func__); +} + +static int __init shm_initialise_irq(struct shrm_dev *shrm) +{ + int err = 0; + + err = shrm_protocol_init(shrm, + rx_common_l2msg_handler, rx_audio_l2msg_handler); + if (err < 0) { + dev_err(shrm->dev, "SHM Protocol Init Failure\n"); + return err; + } + + err = request_irq(shrm->ca_wake_irq, + ca_wake_irq_handler, IRQF_TRIGGER_RISING, + "ca_wake-up", shrm); + if (err < 0) { + dev_err(shrm->dev, + "Unable to allocate shm tx interrupt line\n"); + free_irq(shrm->ca_wake_irq, shrm); + return err; + } + + err = request_irq(shrm->ac_read_notif_0_irq, + ac_read_notif_0_irq_handler, 0, + "ac_read_notif_0", shrm); + + if (err < 0) { + dev_err(shrm->dev, + "error ac_read_notif_0_irq interrupt line\n"); + goto irq_err1; + } + + err = request_irq(shrm->ac_read_notif_1_irq, + ac_read_notif_1_irq_handler, 0, + "ac_read_notif_1", shrm); + + if (err < 0) { + dev_err(shrm->dev, + "error ac_read_notif_1_irq interrupt line\n"); + goto irq_err2; + } + + err = request_irq(shrm->ca_msg_pending_notif_0_irq, + ca_msg_pending_notif_0_irq_handler, 0, + "ca_msg_pending_notif_0", shrm); + + if (err < 0) { + dev_err(shrm->dev, + "error ca_msg_pending_notif_0_irq line\n"); + goto irq_err3; + } + + err = request_irq(shrm->ca_msg_pending_notif_1_irq, + ca_msg_pending_notif_1_irq_handler, 0, + "ca_msg_pending_notif_1", shrm); + + if (err < 0) { + dev_err(shrm->dev, + "error ca_msg_pending_notif_1_irq interrupt line\n"); + goto irq_err4; + } + return err; +irq_err4: + free_irq(shrm->ca_msg_pending_notif_0_irq, shrm); +irq_err3: + free_irq(shrm->ac_read_notif_1_irq, shrm); +irq_err2: + free_irq(shrm->ac_read_notif_0_irq, shrm); +irq_err1: + free_irq(shrm->ca_wake_irq, shrm); + return err; +} + +static void free_shm_irq(struct shrm_dev *shrm) +{ + free_irq(shrm->ca_wake_irq, shrm); + free_irq(shrm->ac_read_notif_0_irq, shrm); + free_irq(shrm->ac_read_notif_1_irq, shrm); + free_irq(shrm->ca_msg_pending_notif_0_irq, shrm); + free_irq(shrm->ca_msg_pending_notif_1_irq, shrm); +} + + + +#ifdef CONFIG_HIGH_RES_TIMERS +static enum hrtimer_restart callback(struct hrtimer *timer) +{ + return HRTIMER_NORESTART; +} +#endif + +void do_phonet_rcv_tasklet(unsigned long unused) +{ + ssize_t ret; + struct shrm_dev *shrm = (struct shrm_dev *)unused; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + for (;;) { + ret = shrm_net_receive(shrm->ndev); + if (ret == 0) { + dev_dbg(shrm->dev, "len is zero, queue empty\n"); + break; + } + if (ret < 0) { + dev_err(shrm->dev, "len < 0 !!! error!!!\n"); + break; + } + } + dev_dbg(shrm->dev, "%s OUT\n", __func__); +} + +static int shrm_probe(struct platform_device *pdev) +{ + int err = 0; + struct resource *res; + struct shrm_dev *shrm = NULL; + + shrm = kzalloc(sizeof(struct shrm_dev), GFP_KERNEL); + if (shrm == NULL) { + dev_err(&pdev->dev, + "Could not allocate memory for struct shm_dev\n"); + return -ENOMEM; + } + + shrm->dev = &pdev->dev; + shrm->modem = modem_get(shrm->dev, "u8500-shrm-modem"); + if (shrm->modem == NULL) { + dev_err(shrm->dev, " Could not retrieve the modem.\n"); + return -ENODEV; + } + + /* initialise the SHM */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(shrm->dev, + "Unable to map Ca Wake up interrupt\n"); + err = -EBUSY; + goto rollback_intr; + } + shrm->ca_wake_irq = res->start; + res = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + + if (!res) { + dev_err(shrm->dev, + "Unable to map APE_Read_notif_common IRQ base\n"); + err = -EBUSY; + goto rollback_intr; + } + shrm->ac_read_notif_0_irq = res->start; + res = platform_get_resource(pdev, IORESOURCE_IRQ, 2); + + if (!res) { + dev_err(shrm->dev, + "Unable to map APE_Read_notif_audio IRQ base\n"); + err = -EBUSY; + goto rollback_intr; + } + shrm->ac_read_notif_1_irq = res->start; + res = platform_get_resource(pdev, IORESOURCE_IRQ, 3); + + if (!res) { + dev_err(shrm->dev, + "Unable to map Cmt_msg_pending_notif_common IRQbase\n"); + err = -EBUSY; + goto rollback_intr; + } + shrm->ca_msg_pending_notif_0_irq = res->start; + res = platform_get_resource(pdev, IORESOURCE_IRQ, 4); + + if (!res) { + dev_err(shrm->dev, + "Unable to map Cmt_msg_pending_notif_audio IRQ base\n"); + err = -EBUSY; + goto rollback_intr; + } + shrm->ca_msg_pending_notif_1_irq = res->start; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!res) { + dev_err(shrm->dev, + "Could not get SHM IO memory information\n"); + err = -ENODEV; + goto rollback_intr; + } + shrm->intr_base = (void __iomem *)ioremap_nocache(res->start, + res->end - res->start + 1); + if (!(shrm->intr_base)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_intr; + } + shrm->ape_common_fifo_base_phy = + (u32 *)U8500_SHM_FIFO_APE_COMMON_BASE; + shrm->ape_common_fifo_base = + (void __iomem *)ioremap_nocache( + U8500_SHM_FIFO_APE_COMMON_BASE, + SHM_FIFO_0_SIZE); + shrm->ape_common_fifo_size = (SHM_FIFO_0_SIZE)/4; + + if (!(shrm->ape_common_fifo_base)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_ape_common_fifo_base; + } + shrm->cmt_common_fifo_base_phy = + (u32 *)U8500_SHM_FIFO_CMT_COMMON_BASE; + shrm->cmt_common_fifo_base = + (void __iomem *)ioremap_nocache( + U8500_SHM_FIFO_CMT_COMMON_BASE, SHM_FIFO_0_SIZE); + shrm->cmt_common_fifo_size = (SHM_FIFO_0_SIZE)/4; + + if (!(shrm->cmt_common_fifo_base)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_cmt_common_fifo_base; + } + shrm->ape_audio_fifo_base_phy = + (u32 *)U8500_SHM_FIFO_APE_AUDIO_BASE; + shrm->ape_audio_fifo_base = + (void __iomem *)ioremap_nocache(U8500_SHM_FIFO_APE_AUDIO_BASE, + SHM_FIFO_1_SIZE); + shrm->ape_audio_fifo_size = (SHM_FIFO_1_SIZE)/4; + + if (!(shrm->ape_audio_fifo_base)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_ape_audio_fifo_base; + } + shrm->cmt_audio_fifo_base_phy = + (u32 *)U8500_SHM_FIFO_CMT_AUDIO_BASE; + shrm->cmt_audio_fifo_base = + (void __iomem *)ioremap_nocache(U8500_SHM_FIFO_CMT_AUDIO_BASE, + SHM_FIFO_1_SIZE); + shrm->cmt_audio_fifo_size = (SHM_FIFO_1_SIZE)/4; + + if (!(shrm->cmt_audio_fifo_base)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_cmt_audio_fifo_base; + } + shrm->ac_common_shared_wptr = + (void __iomem *)ioremap(SHM_ACFIFO_0_WRITE_AMCU, SHM_PTR_SIZE); + + if (!(shrm->ac_common_shared_wptr)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_ac_common_shared_wptr; + } + shrm->ac_common_shared_rptr = + (void __iomem *)ioremap(SHM_ACFIFO_0_READ_AMCU, SHM_PTR_SIZE); + + if (!(shrm->ac_common_shared_rptr)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_map; + } + shrm->ca_common_shared_wptr = + (void __iomem *)ioremap(SHM_CAFIFO_0_WRITE_AMCU, SHM_PTR_SIZE); + + if (!(shrm->ca_common_shared_wptr)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_map; + } + shrm->ca_common_shared_rptr = + (void __iomem *)ioremap(SHM_CAFIFO_0_READ_AMCU, SHM_PTR_SIZE); + + if (!(shrm->ca_common_shared_rptr)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_map; + } + shrm->ac_audio_shared_wptr = + (void __iomem *)ioremap(SHM_ACFIFO_1_WRITE_AMCU, SHM_PTR_SIZE); + + if (!(shrm->ac_audio_shared_wptr)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_map; + } + shrm->ac_audio_shared_rptr = + (void __iomem *)ioremap(SHM_ACFIFO_1_READ_AMCU, SHM_PTR_SIZE); + + if (!(shrm->ac_audio_shared_rptr)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_map; + } + shrm->ca_audio_shared_wptr = + (void __iomem *)ioremap(SHM_CAFIFO_1_WRITE_AMCU, SHM_PTR_SIZE); + + if (!(shrm->ca_audio_shared_wptr)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_map; + } + shrm->ca_audio_shared_rptr = + (void __iomem *)ioremap(SHM_CAFIFO_1_READ_AMCU, SHM_PTR_SIZE); + + if (!(shrm->ca_audio_shared_rptr)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_map; + } + + if (isa_init(shrm) != 0) { + dev_err(shrm->dev, "Driver Initialization Error\n"); + err = -EBUSY; + } + /* install handlers and tasklets */ + if (shm_initialise_irq(shrm)) { + dev_err(shrm->dev, + "shm error in interrupt registration\n"); + goto rollback_irq; + } +#ifdef CONFIG_HIGH_RES_TIMERS + hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + timer.function = callback; + hrtimer_start(&timer, ktime_set(0, 2*NSEC_PER_MSEC), HRTIMER_MODE_REL); +#endif + err = shrm_register_netdev(shrm); + if (err < 0) + goto rollback_irq; + + tasklet_init(&phonet_rcv_tasklet, do_phonet_rcv_tasklet, 0); + phonet_rcv_tasklet.data = (unsigned long)shrm; + + platform_set_drvdata(pdev, shrm); + + return err; +rollback_irq: + free_shm_irq(shrm); +rollback_map: + iounmap(shrm->ac_common_shared_wptr); + iounmap(shrm->ac_common_shared_rptr); + iounmap(shrm->ca_common_shared_wptr); + iounmap(shrm->ca_common_shared_rptr); + iounmap(shrm->ac_audio_shared_wptr); + iounmap(shrm->ac_audio_shared_rptr); + iounmap(shrm->ca_audio_shared_wptr); + iounmap(shrm->ca_audio_shared_rptr); +rollback_ac_common_shared_wptr: + iounmap(shrm->cmt_audio_fifo_base); +rollback_cmt_audio_fifo_base: + iounmap(shrm->ape_audio_fifo_base); +rollback_ape_audio_fifo_base: + iounmap(shrm->cmt_common_fifo_base); +rollback_cmt_common_fifo_base: + iounmap(shrm->ape_common_fifo_base); +rollback_ape_common_fifo_base: + iounmap(shrm->intr_base); +rollback_intr: + kfree(shrm); + return err; +} + +static int __exit shrm_remove(struct platform_device *pdev) +{ + struct shrm_dev *shrm = platform_get_drvdata(pdev); + + free_shm_irq(shrm); + iounmap(shrm->intr_base); + iounmap(shrm->ape_common_fifo_base); + iounmap(shrm->cmt_common_fifo_base); + iounmap(shrm->ape_audio_fifo_base); + iounmap(shrm->cmt_audio_fifo_base); + iounmap(shrm->ac_common_shared_wptr); + iounmap(shrm->ac_common_shared_rptr); + iounmap(shrm->ca_common_shared_wptr); + iounmap(shrm->ca_common_shared_rptr); + iounmap(shrm->ac_audio_shared_wptr); + iounmap(shrm->ac_audio_shared_rptr); + iounmap(shrm->ca_audio_shared_wptr); + iounmap(shrm->ca_audio_shared_rptr); + shrm_unregister_netdev(shrm); + isa_exit(shrm); + kfree(shrm); + + return 0; +} + +#ifdef CONFIG_PM +/** + * u8500_shrm_suspend() - This routine puts the SHRM in to sustend state. + * @dev: pointer to device structure. + * + * This routine checks the current ongoing communication with Modem by + * examining the ca_wake state and prevents suspend if modem communication + * is on-going. + * If ca_wake = 1 (high), modem comm. is on-going; don't suspend + * If ca_wake = 0 (low), no comm. with modem on-going.Allow suspend + */ +int u8500_shrm_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct shrm_dev *shrm = platform_get_drvdata(pdev); + int err; + + dev_dbg(&pdev->dev, "%s called...\n", __func__); + dev_dbg(&pdev->dev, "ca_wake_req_state = %x\n", + get_ca_wake_req_state()); + + /* if ca_wake_req is high, prevent system suspend */ + if (!get_ca_wake_req_state()) { + err = shrm_suspend_netdev(shrm->ndev); + return err; + } else + return -EBUSY; +} + +/** + * u8500_shrm_resume() - This routine resumes the SHRM from suspend state. + * @dev: pointer to device structure + * + * This routine restore back the current state of the SHRM + */ +int u8500_shrm_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct shrm_dev *shrm = platform_get_drvdata(pdev); + int err; + + dev_dbg(&pdev->dev, "%s called...\n", __func__); + err = shrm_resume_netdev(shrm->ndev); + + return err; +} + +static const struct dev_pm_ops shrm_dev_pm_ops = { + .suspend_noirq = u8500_shrm_suspend, + .resume_noirq = u8500_shrm_resume, +}; +#endif + +static struct platform_driver shrm_driver = { + .remove = __exit_p(shrm_remove), + .driver = { + .name = "u8500_shrm", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &shrm_dev_pm_ops, +#endif + }, +}; + +static int __init shrm_driver_init(void) +{ + return platform_driver_probe(&shrm_driver, shrm_probe); +} + +static void __exit shrm_driver_exit(void) +{ + platform_driver_unregister(&shrm_driver); +} + +module_init(shrm_driver_init); +module_exit(shrm_driver_exit); + +MODULE_AUTHOR("Biju Das, Kumar Sanghvi, Arun Murthy"); +MODULE_DESCRIPTION("Shared Memory Modem Driver Interface"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/modem/shrm/shrm_driver.c b/drivers/modem/shrm/shrm_driver.c new file mode 100644 index 00000000000..11540831f95 --- /dev/null +++ b/drivers/modem/shrm/shrm_driver.c @@ -0,0 +1,1439 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Biju Das for ST-Ericsson + * Author: Kumar Sanghvi for ST-Ericsson + * Author: Arun Murthy for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#ifdef CONFIG_HIGH_RES_TIMERS +#include +static struct hrtimer timer; +#endif + + +#define NAME "IPC_ISA" +#define ISA_DEVICES 4 +/**debug functionality*/ +#define ISA_DEBUG 0 + +#define ISI_MESSAGING (0) +#define RPC_MESSAGING (1) +#define AUDIO_MESSAGING (2) +#define SECURITY_MESSAGING (3) + +#define SIZE_OF_FIFO (512*1024) + +static u8 message_fifo[4][SIZE_OF_FIFO]; + +static u8 wr_isi_msg[10*1024]; +static u8 wr_rpc_msg[10*1024]; +static u8 wr_sec_msg[10*1024]; +static u8 wr_audio_msg[10*1024]; + +/* global data */ +/* + * int major:This variable is exported to user as module_param to specify + * major number at load time + */ +static int major; +module_param(major, int, 0); +MODULE_PARM_DESC(major, "Major device number"); +/* global fops mutex */ +static DEFINE_MUTEX(isa_lock); +rx_cb common_rx; +rx_cb audio_rx; + + +static int isi_receive(struct shrm_dev *shrm, void *data, u32 n_bytes); +static int rpc_receive(struct shrm_dev *shrm, void *data, u32 n_bytes); +static int audio_receive(struct shrm_dev *shrm, void *data, u32 n_bytes); +static int security_receive(struct shrm_dev *shrm, + void *data, u32 n_bytes); + +static void rx_common_l2msg_handler(u8 l2_header, + void *msg, u32 length, + struct shrm_dev *shrm) +{ + int ret = 0; +#ifdef CONFIG_U8500_SHRM_LOOP_BACK + u8 *pdata; +#endif + dev_dbg(shrm->dev, "%s IN\n", __func__); + + switch (l2_header) { + case ISI_MESSAGING: + ret = isi_receive(shrm, msg, length); + if (ret < 0) + dev_err(shrm->dev, "isi receive failed\n"); + break; + case RPC_MESSAGING: + ret = rpc_receive(shrm, msg, length); + if (ret < 0) + dev_err(shrm->dev, "rpc receive failed\n"); + break; + case SECURITY_MESSAGING: + ret = security_receive(shrm, msg, length); + if (ret < 0) + dev_err(shrm->dev, + "security receive failed\n"); + break; +#ifdef CONFIG_U8500_SHRM_LOOP_BACK + case COMMMON_LOOPBACK_MESSAGING: + pdata = (u8 *)msg; + if ((*pdata == 0x50) || (*pdata == 0xAF)) { + ret = isi_receive(shrm, msg, length); + if (ret < 0) + dev_err(shrm->dev, "isi receive failed\n"); + } else if ((*pdata == 0x0A) || (*pdata == 0xF5)) { + ret = rpc_receive(shrm, msg, length); + if (ret < 0) + dev_err(shrm->dev, "rpc receive failed\n"); + } else if ((*pdata == 0xFF) || (*pdata == 0x00)) { + ret = security_receive(shrm, msg, length); + if (ret < 0) + dev_err(shrm->dev, + "security receive failed\n"); + } + break; +#endif + default: + break; + } + dev_dbg(shrm->dev, "%s OUT\n", __func__); +} + +static void rx_audio_l2msg_handler(u8 l2_header, + void *msg, u32 length, + struct shrm_dev *shrm) +{ + int ret = 0; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + audio_receive(shrm, msg, length); + if (ret < 0) + dev_err(shrm->dev, "audio receive failed\n"); + dev_dbg(shrm->dev, "%s OUT\n", __func__); +} + +static int __init shm_initialise_irq(struct shrm_dev *shrm) +{ + int err = 0; + + shrm_protocol_init(shrm, + rx_common_l2msg_handler, rx_audio_l2msg_handler); + + err = request_irq(shrm->ca_wake_irq, + ca_wake_irq_handler, IRQF_TRIGGER_RISING, + "ca_wake-up", shrm); + if (err < 0) { + dev_err(shrm->dev, + "Unable to allocate shm tx interrupt line\n"); + return err; + } + + err = request_irq(shrm->ac_read_notif_0_irq, + ac_read_notif_0_irq_handler, 0, + "ac_read_notif_0", shrm); + if (err < 0) { + dev_err(shrm->dev, + "error ac_read_notif_0_irq interrupt line\n"); + goto irq_err1; + } + + err = request_irq(shrm->ac_read_notif_1_irq, + ac_read_notif_1_irq_handler, 0, + "ac_read_notif_1", shrm); + if (err < 0) { + dev_err(shrm->dev, + "error ac_read_notif_1_irq interrupt line\n"); + goto irq_err2; + } + + err = request_irq(shrm->ca_msg_pending_notif_0_irq, + ca_msg_pending_notif_0_irq_handler, 0, + "ca_msg_pending_notif_0", shrm); + if (err < 0) { + dev_err(shrm->dev, + "error ca_msg_pending_notif_0_irq line\n"); + goto irq_err3; + } + + err = request_irq(shrm->ca_msg_pending_notif_1_irq, + ca_msg_pending_notif_1_irq_handler, 0, + "ca_msg_pending_notif_1", shrm); + if (err < 0) { + dev_err(shrm->dev, + "error ca_msg_pending_notif_1_irq interrupt line\n"); + goto irq_err4; + } + + return err; + +irq_err4: + free_irq(shrm->ca_msg_pending_notif_0_irq, shrm); +irq_err3: + free_irq(shrm->ac_read_notif_1_irq, shrm); +irq_err2: + free_irq(shrm->ac_read_notif_0_irq, shrm); +irq_err1: + free_irq(shrm->ca_wake_irq, shrm); + return err; +} + +static void free_shm_irq(struct shrm_dev *shrm) +{ + free_irq(shrm->ca_wake_irq, shrm); + free_irq(shrm->ac_read_notif_0_irq, shrm); + free_irq(shrm->ac_read_notif_1_irq, shrm); + free_irq(shrm->ca_msg_pending_notif_0_irq, shrm); + free_irq(shrm->ca_msg_pending_notif_1_irq, shrm); +} + +/** + * create_queue() - To create FIFO for Tx and Rx message buffering. + * @q: message queue. + * @devicetype: device type 0-isi,1-rpc,2-audio,3-security. + * + * This function creates a FIFO buffer of n_bytes size using + * dma_alloc_coherent(). It also initializes all queue handling + * locks, queue management pointers. It also initializes message list + * which occupies this queue. + * + * It return -ENOMEM in case of no memory. + */ +static int create_queue(struct message_queue *q, u32 devicetype, + struct shrm_dev *shrm) +{ + q->fifo_base = (u8 *)&message_fifo[devicetype]; + q->size = SIZE_OF_FIFO; + q->readptr = 0; + q->writeptr = 0; + q->no = 0; + q->shrm = shrm; + spin_lock_init(&q->update_lock); + INIT_LIST_HEAD(&q->msg_list); + init_waitqueue_head(&q->wq_readable); + atomic_set(&q->q_rp, 0); + + return 0; +} +/** + * delete_queue() - To delete FIFO and assiciated memory. + * @q: message queue + * + * This function deletes FIFO created using create_queue() function. + * It resets queue management pointers. + */ +static void delete_queue(struct message_queue *q) +{ + q->size = 0; + q->readptr = 0; + q->writeptr = 0; +} + +/** + * add_msg_to_queue() - Add a message inside inside queue + * + * @q: message queue + * @size: size in bytes + * + * This function tries to allocate n_bytes of size in FIFO q. + * It returns negative number when no memory can be allocated + * currently. + */ +int add_msg_to_queue(struct message_queue *q, u32 size) +{ + struct queue_element *new_msg = NULL; + struct shrm_dev *shrm = q->shrm; + + dev_dbg(shrm->dev, "%s IN q->writeptr=%d\n", + __func__, q->writeptr); + new_msg = kmalloc(sizeof(struct queue_element), + GFP_KERNEL|GFP_ATOMIC); + + if (new_msg == NULL) { + dev_err(shrm->dev, "memory overflow inside while(1)\n"); + return -ENOMEM; + } + new_msg->offset = q->writeptr; + new_msg->size = size; + new_msg->no = q->no++; + + /* check for overflow condition */ + if (q->readptr <= q->writeptr) { + if (((q->writeptr-q->readptr) + size) >= q->size) { + dev_err(shrm->dev, "Buffer overflow !!\n"); + BUG_ON(((q->writeptr-q->readptr) + size) >= q->size); + } + } else { + if ((q->writeptr + size) >= q->readptr) { + dev_err(shrm->dev, "Buffer overflow !!\n"); + BUG_ON((q->writeptr + size) >= q->readptr); + } + } + q->writeptr = (q->writeptr + size) % q->size; + if (list_empty(&q->msg_list)) { + list_add_tail(&new_msg->entry, &q->msg_list); + /* There can be 2 blocking calls read and another select */ + + atomic_set(&q->q_rp, 1); + wake_up_interruptible(&q->wq_readable); + } else + list_add_tail(&new_msg->entry, &q->msg_list); + + dev_dbg(shrm->dev, "%s OUT\n", __func__); + return 0; +} + +/** + * remove_msg_from_queue() - To remove a message from the msg queue. + * + * @q: message queue + * + * This function delets a message from the message list associated with message + * queue q and also updates read ptr. + * If the message list is empty, then, event is set to block the select and + * read calls of the paricular queue. + * + * The message list is FIFO style and message is always added to tail and + * removed from head. + */ + +int remove_msg_from_queue(struct message_queue *q) +{ + struct queue_element *old_msg = NULL; + struct shrm_dev *shrm = q->shrm; + struct list_head *msg; + + dev_dbg(shrm->dev, "%s IN q->readptr %d\n", + __func__, q->readptr); + + list_for_each(msg, &q->msg_list) { + old_msg = list_entry(msg, struct queue_element, entry); + if (old_msg == NULL) { + dev_err(shrm->dev, ":no message found\n"); + return -EFAULT; + } + break; + } + list_del(msg); + q->readptr = (q->readptr + old_msg->size) % q->size; + if (list_empty(&q->msg_list)) { + dev_dbg(shrm->dev, "List is empty setting RP= 0\n"); + atomic_set(&q->q_rp, 0); + } + kfree(old_msg); + + dev_dbg(shrm->dev, "%s OUT\n", __func__); + return 0; +} + +/** + * get_size_of_new_msg() - retrieve new message from message list + * + * @q: message queue + * + * This function will retrieve most recent message from the corresponding + * queue list. New message is always retrieved from head side. + * It returns new message no, offset if FIFO and size. + */ +int get_size_of_new_msg(struct message_queue *q) +{ + struct queue_element *new_msg = NULL; + struct list_head *msg_list; + struct shrm_dev *shrm = q->shrm; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + + spin_lock_bh(&q->update_lock); + list_for_each(msg_list, &q->msg_list) { + new_msg = list_entry(msg_list, struct queue_element, entry); + if (new_msg == NULL) { + spin_unlock_bh(&q->update_lock); + dev_err(shrm->dev, "no message found\n"); + return -1; + } + break; + } + spin_unlock_bh(&q->update_lock); + + dev_dbg(shrm->dev, "%s OUT\n", __func__); + return new_msg->size; +} + +/** + * isi_receive() - Rx Completion callback + * + * @data:message pointer + * @n_bytes:message size + * + * This function is a callback to indicate ISI message reception is complete. + * It updates Writeptr of the Fifo + */ +static int isi_receive(struct shrm_dev *shrm, + void *data, u32 n_bytes) +{ + u32 size = 0; + int ret = 0; + u8 *psrc; + struct message_queue *q; + struct isadev_context *isidev = &shrm->isa_context->isadev[0]; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + q = &isidev->dl_queue; + spin_lock(&q->update_lock); + /* Memcopy RX data first */ + if ((q->writeptr+n_bytes) >= q->size) { + dev_dbg(shrm->dev, "Inside Loop Back\n"); + psrc = (u8 *)data; + size = (q->size-q->writeptr); + /* Copy First Part of msg */ + memcpy((q->fifo_base+q->writeptr), psrc, size); + psrc += size; + /* Copy Second Part of msg at the top of fifo */ + memcpy(q->fifo_base, psrc, (n_bytes-size)); + } else { + memcpy((q->fifo_base+q->writeptr), data, n_bytes); + } + ret = add_msg_to_queue(q, n_bytes); + if (ret < 0) + dev_err(shrm->dev, "Adding msg to message queue failed\n"); + spin_unlock(&q->update_lock); + dev_dbg(shrm->dev, "%s OUT\n", __func__); + return ret; +} + +/** + * rpc_receive() - Rx Completion callback + * + * @data:message pointer + * @n_bytes:message size + * + * This function is a callback to indicate RPC message reception is complete. + * It updates Writeptr of the Fifo + */ +static int rpc_receive(struct shrm_dev *shrm, + void *data, u32 n_bytes) +{ + u32 size = 0; + int ret = 0; + u8 *psrc; + struct message_queue *q; + struct isadev_context *rpcdev = &shrm->isa_context->isadev[1]; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + q = &rpcdev->dl_queue; + spin_lock(&q->update_lock); + /* Memcopy RX data first */ + if ((q->writeptr+n_bytes) >= q->size) { + psrc = (u8 *)data; + size = (q->size-q->writeptr); + /* Copy First Part of msg */ + memcpy((q->fifo_base+q->writeptr), psrc, size); + psrc += size; + /* Copy Second Part of msg at the top of fifo */ + memcpy(q->fifo_base, psrc, (n_bytes-size)); + } else { + memcpy((q->fifo_base+q->writeptr), data, n_bytes); + } + + ret = add_msg_to_queue(q, n_bytes); + if (ret < 0) + dev_err(shrm->dev, "Adding msg to message queue failed\n"); + spin_unlock(&q->update_lock); + dev_dbg(shrm->dev, "%s OUT\n", __func__); + return ret; +} + +/** + * audio_receive() - Rx Completion callback + * + * @data:message pointer + * @n_bytes:message size + * + * This function is a callback to indicate audio message reception is complete. + * It updates Writeptr of the Fifo + */ +static int audio_receive(struct shrm_dev *shrm, + void *data, u32 n_bytes) +{ + u32 size = 0; + int ret = 0; + u8 *psrc; + struct message_queue *q; + struct isadev_context *audiodev = &shrm->isa_context->isadev[2]; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + q = &audiodev->dl_queue; + spin_lock(&q->update_lock); + /* Memcopy RX data first */ + if ((q->writeptr+n_bytes) >= q->size) { + psrc = (u8 *)data; + size = (q->size-q->writeptr); + /* Copy First Part of msg */ + memcpy((q->fifo_base+q->writeptr), psrc, size); + psrc += size; + /* Copy Second Part of msg at the top of fifo */ + memcpy(q->fifo_base, psrc, (n_bytes-size)); + } else { + memcpy((q->fifo_base+q->writeptr), data, n_bytes); + } + ret = add_msg_to_queue(q, n_bytes); + if (ret < 0) + dev_err(shrm->dev, "Adding msg to message queue failed\n"); + spin_unlock(&q->update_lock); + dev_dbg(shrm->dev, "%s OUT\n", __func__); + return ret; +} + +/** + * security_receive() - Rx Completion callback + * + * @data:message pointer + * @n_bytes: message size + * + * This function is a callback to indicate security message reception + * is complete.It updates Writeptr of the Fifo + */ +static int security_receive(struct shrm_dev *shrm, + void *data, u32 n_bytes) +{ + u32 size = 0; + int ret = 0; + u8 *psrc; + struct message_queue *q; + struct isadev_context *secdev = &shrm->isa_context->isadev[3]; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + q = &secdev->dl_queue; + spin_lock(&q->update_lock); + /* Memcopy RX data first */ + if ((q->writeptr+n_bytes) >= q->size) { + psrc = (u8 *)data; + size = (q->size-q->writeptr); + /* Copy First Part of msg */ + memcpy((q->fifo_base+q->writeptr), psrc, size); + psrc += size; + /* Copy Second Part of msg at the top of fifo */ + memcpy(q->fifo_base, psrc, (n_bytes-size)); + } else { + memcpy((q->fifo_base+q->writeptr), data, n_bytes); + } + ret = add_msg_to_queue(q, n_bytes); + if (ret < 0) + dev_err(shrm->dev, "Adding msg to message queue failed\n"); + spin_unlock(&q->update_lock); + dev_dbg(shrm->dev, "%s OUT\n", __func__); + return ret; +} + + +/** + * isa_select() - Select Interface + * + * @filp:file descriptor pointer + * @wait:poll_table_struct pointer + * + * This function is used to perform non-blocking read operations. It allows + * a process to determine whether it can read from one or more open files + * without blocking. These calls can also block a process until any of a + * given set of file descriptors becomes available for reading. + * If a file is ready to read, POLLIN | POLLRDNORM bitmask is returned. + * The driver method is called whenever the user-space program performs a select + * system call involving a file descriptor associated with the driver. + */ +static u32 isa_select(struct file *filp, + struct poll_table_struct *wait) +{ + struct isadev_context *isadev = filp->private_data; + struct shrm_dev *shrm = isadev->dl_queue.shrm; + struct message_queue *q; + u32 mask = 0; + u32 m = iminor(filp->f_path.dentry->d_inode); + + dev_dbg(shrm->dev, "%s IN\n", __func__); + + if (isadev->device_id != m) + return -1; + q = &isadev->dl_queue; + poll_wait(filp, &q->wq_readable, wait); + if (atomic_read(&q->q_rp) == 1) + mask = POLLIN | POLLRDNORM; + dev_dbg(shrm->dev, "%s OUT\n", __func__); + return mask; +} + +/** + * isa_read() - Read from device + * + * @filp:file descriptor + * @buf:user buffer pointer + * @len:size of requested data transfer + * @ppos:not used + * + * This function is called whenever user calls read() system call. + * It reads a oldest message from queue and copies it into user buffer and + * returns its size. + * If there is no message present in queue, then it blocks until new data is + * available. + */ +ssize_t isa_read(struct file *filp, char __user *buf, + size_t len, loff_t *ppos) +{ + struct isadev_context *isadev = (struct isadev_context *) + filp->private_data; + struct shrm_dev *shrm = isadev->dl_queue.shrm; + struct message_queue *q; + char *psrc; + u32 msgsize; + u32 size = 0; + int ret = 0; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + + if (len <= 0) + return -EFAULT; + q = &isadev->dl_queue; + + spin_lock_bh(&q->update_lock); + if (list_empty(&q->msg_list)) { + spin_unlock_bh(&q->update_lock); + if (wait_event_interruptible(q->wq_readable, + atomic_read(&q->q_rp) == 1)) { + return -ERESTARTSYS; + } + } else + spin_unlock_bh(&q->update_lock); + + msgsize = get_size_of_new_msg(q); + if ((q->readptr+msgsize) >= q->size) { + dev_dbg(shrm->dev, "Inside Loop Back\n"); + psrc = (char *)buf; + size = (q->size-q->readptr); + /* Copy First Part of msg */ + if (copy_to_user(psrc, + (u8 *)(q->fifo_base+q->readptr), + size)) { + dev_err(shrm->dev, "copy_to_user failed\n"); + return -EFAULT; + } + psrc += size; + /* Copy Second Part of msg at the top of fifo */ + if (copy_to_user(psrc, + (u8 *)(q->fifo_base), + (msgsize-size))) { + dev_err(shrm->dev, "copy_to_user failed\n"); + return -EFAULT; + } + } else { + if (copy_to_user(buf, + (u8 *)(q->fifo_base+q->readptr), + msgsize)) { + dev_err(shrm->dev, "copy_to_user failed\n"); + return -EFAULT; + } + } + + spin_lock_bh(&q->update_lock); + ret = remove_msg_from_queue(q); + if (ret < 0) { + dev_err(shrm->dev, + "Removing msg from message queue failed\n"); + msgsize = ret; + } + spin_unlock_bh(&q->update_lock); + dev_dbg(shrm->dev, "%s OUT\n", __func__); + return msgsize; +} +/** + * isa_write() - Write to device + * + * @filp:file descriptor + * @buf:user buffer pointer + * @len:size of requested data transfer + * @ppos:not used + * + * This function is called whenever user calls write() system call. + * It checks if there is space available in queue, and copies the message + * inside queue. If there is no space, it blocks until space becomes available. + * It also schedules transfer thread to transmit the newly added message. + */ +static ssize_t isa_write(struct file *filp, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct isadev_context *isadev = filp->private_data; + struct shrm_dev *shrm = isadev->dl_queue.shrm; + struct message_queue *q; + int err, ret; + void *addr = 0; + u8 l2_header = 0; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + if (len <= 0) + return -EFAULT; + q = &isadev->dl_queue; + + switch (isadev->device_id) { + case ISI_MESSAGING: + dev_dbg(shrm->dev, "ISI\n"); + addr = (void *)wr_isi_msg; +#ifdef CONFIG_U8500_SHRM_LOOP_BACK + dev_dbg(shrm->dev, "Loopback\n"); + l2_header = COMMON_LOOPBACK_MESSAGING; +#else + l2_header = isadev->device_id; +#endif + break; + case RPC_MESSAGING: + dev_dbg(shrm->dev, "RPC\n"); + addr = (void *)wr_rpc_msg; +#ifdef CONFIG_U8500_SHRM_LOOP_BACK + l2_header = COMMON_LOOPBACK_MESSAGING; +#else + l2_header = isadev->device_id; +#endif + break; + case AUDIO_MESSAGING: + dev_dbg(shrm->dev, "Audio\n"); + addr = (void *)wr_audio_msg; +#ifdef CONFIG_U8500_SHRM_LOOP_BACK + l2_header = AUDIO_LOOPBACK_MESSAGING; +#else + l2_header = isadev->device_id; +#endif + + break; + case SECURITY_MESSAGING: + dev_dbg(shrm->dev, "Security\n"); + addr = (void *)wr_sec_msg; +#ifdef CONFIG_U8500_SHRM_LOOP_BACK + l2_header = COMMON_LOOPBACK_MESSAGING; +#else + l2_header = isadev->device_id; +#endif + break; + default: + dev_dbg(shrm->dev, "Wrong device\n"); + return -EFAULT; + } + + if (copy_from_user(addr, buf, len)) { + dev_err(shrm->dev, "copy_from_user failed\n"); + return -EFAULT; + } + + /* Write msg to Fifo */ + if (isadev->device_id == 2) { + mutex_lock(&shrm->isa_context->tx_audio_mutex); + err = shm_write_msg(shrm, l2_header, addr, len); + if (!err) + ret = len; + else + ret = err; + mutex_unlock(&shrm->isa_context->tx_audio_mutex); + } else { + spin_lock_bh(&shrm->isa_context->common_tx); + err = shm_write_msg(shrm, l2_header, addr, len); + if (!err) + ret = len; + else + ret = err; + spin_unlock_bh(&shrm->isa_context->common_tx); + } + + dev_dbg(shrm->dev, "%s OUT\n", __func__); + return ret; +} + +/** + * isa_ioctl() - To handle different ioctl commands supported by driver. + * + * @inode: structure is used by the kernel internally to represent files + * @filp:file descriptor pointer + * @cmd:ioctl command + * @arg:input param + * + * Following ioctls are supported by this driver. + * DLP_IOCTL_ALLOCATE_BUFFER - To allocate buffer for new uplink message. + * This ioctl is called with required message size. It returns offset for + * the allocates space in the queue. DLP_IOCTL_PUT_MESSAGE - To indicate + * new uplink message available in queuq for transmission. Message is copied + * from offset location returned by previous ioctl before calling this ioctl. + * DLP_IOCTL_GET_MESSAGE - To check if any downlink message is available in + * queue. It returns offset for new message inside queue. + * DLP_IOCTL_DEALLOCATE_BUFFER - To deallocate any buffer allocate for + * downlink message once the message is copied. Message is copied from offset + * location returned by previous ioctl before calling this ioctl. + */ +static int isa_ioctl(struct inode *inode, struct file *filp, + unsigned cmd, unsigned long arg) +{ + int err = 0; + struct isadev_context *isadev = filp->private_data; + struct shrm_dev *shrm = isadev->dl_queue.shrm; + u32 m = iminor(inode); + + if (isadev->device_id != m) + return -1; + + switch (cmd) { + case DLP_IOC_ALLOCATE_BUFFER: + dev_dbg(shrm->dev, "DLP_IOC_ALLOCATE_BUFFER\n"); + break; + case DLP_IOC_PUT_MESSAGE: + dev_dbg(shrm->dev, "DLP_IOC_PUT_MESSAGE\n"); + break; + case DLP_IOC_GET_MESSAGE: + dev_dbg(shrm->dev, "DLP_IOC_GET_MESSAGE\n"); + break; + case DLP_IOC_DEALLOCATE_BUFFER: + dev_dbg(shrm->dev, "DLP_IOC_DEALLOCATE_BUFFER\n"); + break; + default: + dev_dbg(shrm->dev, "Unknown IOCTL\n"); + err = -1; + break; + } + return err; +} +/** + * isa_mmap() - Maps kernel queue memory to user space. + * + * @filp:file descriptor pointer + * @vma:virtual area memory structure. + * + * This function maps kernel FIFO into user space. This function + * shall be called twice to map both uplink and downlink buffers. + */ +static int isa_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct isadev_context *isadev = filp->private_data; + struct shrm_dev *shrm = isadev->dl_queue.shrm; + + u32 m = iminor(filp->f_path.dentry->d_inode); + dev_dbg(shrm->dev, "%s %dIN\n", __func__, m); + + isadev = (struct isadev_context *)filp->private_data; + return 0; +} + +/** + * isa_close() - Close device file + * + * @inode:structure is used by the kernel internally to represent files + * @filp:device file descriptor + * + * This function deletes structues associated with this file, deletes + * queues, flushes and destroys workqueus and closes this file. + * It also unregisters itself from l2mux driver. + */ +static int isa_close(struct inode *inode, struct file *filp) +{ + struct isadev_context *isadev = filp->private_data; + struct shrm_dev *shrm = isadev->dl_queue.shrm; + struct isa_driver_context *isa_context = shrm->isa_context; + u8 m; + + mutex_lock(&isa_lock); + m = iminor(filp->f_path.dentry->d_inode); + dev_dbg(shrm->dev, "%s IN %d", __func__, m); + + if (atomic_dec_and_test(&isa_context->is_open[m])) { + atomic_inc(&isa_context->is_open[m]); + dev_err(shrm->dev, "Device not opened yet\n"); + mutex_unlock(&isa_lock); + return -ENODEV; + } + atomic_set(&isa_context->is_open[m], 1); + + dev_dbg(shrm->dev, "isadev->device_id %d", isadev->device_id); + dev_dbg(shrm->dev, "Closed %d device\n", m); + + if (m == ISI_MESSAGING) + dev_dbg(shrm->dev, "Closed ISI_MESSAGING Device\n"); + else if (m == RPC_MESSAGING) + dev_dbg(shrm->dev, "Closed RPC_MESSAGING Device\n"); + else if (m == AUDIO_MESSAGING) + dev_dbg(shrm->dev, "Closed AUDIO_MESSAGING Device\n"); + else if (m == SECURITY_MESSAGING) + dev_dbg(shrm->dev, "Closed SECURITY_MESSAGING Device\n"); + else + dev_dbg(shrm->dev, NAME ":No such device present\n"); + + mutex_unlock(&isa_lock); + return 0; +} +/** + * isa_open() - Open device file + * + * @inode: structure is used by the kernel internally to represent files + * @filp: device file descriptor + * + * This function performs initialization tasks needed to open SHM channel. + * Following tasks are performed. + * -return if device is already opened + * -create uplink FIFO + * -create downlink FIFO + * -init delayed workqueue thread + * -register to l2mux driver + */ +static int isa_open(struct inode *inode, struct file *filp) +{ + int err = 0; + u8 m; + struct isadev_context *isadev; + struct isa_driver_context *isa_context = container_of( + inode->i_cdev, + struct isa_driver_context, + cdev); + struct shrm_dev *shrm = isa_context->isadev->dl_queue.shrm; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + + if (get_boot_state() != BOOT_DONE) { + dev_err(shrm->dev, "Boot is not done\n"); + return -EBUSY; + } + mutex_lock(&isa_lock); + m = iminor(inode); + + if ((m != ISI_MESSAGING) && (m != RPC_MESSAGING) && + (m != AUDIO_MESSAGING) && (m != SECURITY_MESSAGING)) { + dev_err(shrm->dev, "No such device present\n"); + mutex_unlock(&isa_lock); + return -ENODEV; + } + if (!atomic_dec_and_test(&isa_context->is_open[m])) { + atomic_inc(&isa_context->is_open[m]); + dev_err(shrm->dev, "Device already opened\n"); + mutex_unlock(&isa_lock); + return -EBUSY; + } + + if (m == ISI_MESSAGING) + dev_dbg(shrm->dev, "Open ISI_MESSAGING Device\n"); + else if (m == RPC_MESSAGING) + dev_dbg(shrm->dev, "Open RPC_MESSAGING Device\n"); + else if (m == AUDIO_MESSAGING) + dev_dbg(shrm->dev, "Open AUDIO_MESSAGING Device\n"); + else if (m == SECURITY_MESSAGING) + dev_dbg(shrm->dev, "Open SECURITY_MESSAGING Device\n"); + else + dev_dbg(shrm->dev, ":No such device present\n"); + + isadev = &isa_context->isadev[m]; + if (filp != NULL) + filp->private_data = isadev; + + mutex_unlock(&isa_lock); + dev_dbg(shrm->dev, "%s OUT\n", __func__); + return err; +} + +const struct file_operations isa_fops = { + .owner = THIS_MODULE, + .open = isa_open, + .release = isa_close, + .ioctl = isa_ioctl, + .mmap = isa_mmap, + .read = isa_read, + .write = isa_write, + .poll = isa_select, +}; + +/** + * isa_init() - module insertion function + * + * This function registers module as a character driver using + * register_chrdev_region() or alloc_chrdev_region. It adds this + * driver to system using cdev_add() call. Major number is dynamically + * allocated using alloc_chrdev_region() by default or left to user to specify + * it during load time. For this variable major is used as module_param + * Nodes to be created using + * mknod /dev/isi c $major 0 + * mknod /dev/rpc c $major 1 + * mknod /dev/audio c $major 2 + * mknod /dev/sec c $major 3 + */ +int isa_init(struct shrm_dev *shrm) +{ + dev_t dev_id; + int retval, no_dev; + struct isadev_context *isadev; + struct isa_driver_context *isa_context; + + isa_context = kzalloc(sizeof(struct isa_driver_context), + GFP_KERNEL); + shrm->isa_context = isa_context; + if (isa_context == NULL) { + dev_err(shrm->dev, "Failed to alloc memory\n"); + return -ENOMEM; + } + + if (major) { + dev_id = MKDEV(major, 0); + retval = register_chrdev_region(dev_id, ISA_DEVICES, NAME); + } else { + retval = alloc_chrdev_region(&dev_id, 0, ISA_DEVICES, NAME); + major = MAJOR(dev_id); + } + + dev_dbg(shrm->dev, "major %d\n", major); + + cdev_init(&isa_context->cdev, &isa_fops); + isa_context->cdev.owner = THIS_MODULE; + retval = cdev_add(&isa_context->cdev, dev_id, ISA_DEVICES); + if (retval) { + dev_err(shrm->dev, "Failed to add char device\n"); + return retval; + } + + for (no_dev = 0; no_dev < ISA_DEVICES; no_dev++) + atomic_set(&isa_context->is_open[no_dev], 1); + + isa_context->isadev = kzalloc(sizeof + (struct isadev_context)*ISA_DEVICES, + GFP_KERNEL); + if (isa_context->isadev == NULL) { + dev_err(shrm->dev, "Failed to alloc memory\n"); + return -ENOMEM; + } + for (no_dev = 0; no_dev < ISA_DEVICES; no_dev++) { + isadev = &isa_context->isadev[no_dev]; + isadev->device_id = no_dev; + retval = create_queue(&isadev->dl_queue, + isadev->device_id, shrm); + if (retval < 0) { + dev_err(shrm->dev, "create dl_queue failed\n"); + delete_queue(&isadev->dl_queue); + kfree(isadev); + return retval; + } + } + mutex_init(&isa_context->tx_audio_mutex); + spin_lock_init(&isa_context->common_tx); + + dev_err(shrm->dev, "SHRM char driver added\n"); + + return retval; +} + +void isa_exit(struct shrm_dev *shrm) +{ + int no_dev; + struct isadev_context *isadev; + struct isa_driver_context *isa_context = shrm->isa_context; + dev_t dev_id = MKDEV(major, 0); + + for (no_dev = 0; no_dev < ISA_DEVICES; no_dev++) { + isadev = &isa_context->isadev[no_dev]; + delete_queue(&isadev->dl_queue); + kfree(isadev); + } + + cdev_del(&isa_context->cdev); + unregister_chrdev_region(dev_id, ISA_DEVICES); + kfree(isa_context); + + dev_err(shrm->dev, "SHRM char driver removed\n"); +} + +#ifdef CONFIG_HIGH_RES_TIMERS +static enum hrtimer_restart callback(struct hrtimer *timer) +{ + return HRTIMER_NORESTART; +} +#endif + + +static int __init shrm_probe(struct platform_device *pdev) +{ + int err = 0; + struct resource *res; + struct shrm_dev *shrm = NULL; + + if (pdev == NULL) { + dev_err(shrm->dev, + "No device/platform_data found on shm device\n"); + return -ENODEV; + } + + + shrm = kzalloc(sizeof(struct shrm_dev), GFP_KERNEL); + if (shrm == NULL) { + dev_err(shrm->dev, + "Could not allocate memory for struct shm_dev\n"); + return -ENOMEM; + } + shrm->dev = &pdev->dev; + + /* initialise the SHM */ + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(shrm->dev, "Unable to map Ca Wake up interrupt\n"); + err = -EBUSY; + goto rollback_intr; + } + shrm->ca_wake_irq = res->start; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + if (!res) { + dev_err(shrm->dev, + "Unable to map APE_Read_notif_common IRQ base\n"); + err = -EBUSY; + goto rollback_intr; + } + shrm->ac_read_notif_0_irq = res->start; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 2); + if (!res) { + dev_err(shrm->dev, + "Unable to map APE_Read_notif_audio IRQ base\n"); + err = -EBUSY; + goto rollback_intr; + } + shrm->ac_read_notif_1_irq = res->start; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 3); + if (!res) { + dev_err(shrm->dev, + "Unable to map Cmt_msg_pending_notif_common IRQbase\n"); + err = -EBUSY; + goto rollback_intr; + } + shrm->ca_msg_pending_notif_0_irq = res->start; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 4); + if (!res) { + dev_err(shrm->dev, + "Unable to map Cmt_msg_pending_notif_audio IRQ base\n"); + err = -EBUSY; + goto rollback_intr; + } + shrm->ca_msg_pending_notif_1_irq = res->start; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(shrm->dev, + "Could not get SHM IO memory information\n"); + err = -ENODEV; + goto rollback_intr; + } + + shrm->intr_base = (void __iomem *)ioremap_nocache(res->start, + res->end - res->start + 1); + + if (!(shrm->intr_base)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_intr; + } + + shrm->ape_common_fifo_base_phy = + (u32 *)U8500_SHM_FIFO_APE_COMMON_BASE; + shrm->ape_common_fifo_base = + (void __iomem *)ioremap_nocache( + U8500_SHM_FIFO_APE_COMMON_BASE, + SHM_FIFO_0_SIZE); + shrm->ape_common_fifo_size = (SHM_FIFO_0_SIZE)/4; + + if (!(shrm->ape_common_fifo_base)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_ape_common_fifo_base; + } + + shrm->cmt_common_fifo_base_phy = + (u32 *)U8500_SHM_FIFO_CMT_COMMON_BASE; + + shrm->cmt_common_fifo_base = + (void __iomem *)ioremap_nocache( + U8500_SHM_FIFO_CMT_COMMON_BASE, SHM_FIFO_0_SIZE); + shrm->cmt_common_fifo_size = (SHM_FIFO_0_SIZE)/4; + + if (!(shrm->cmt_common_fifo_base)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_cmt_common_fifo_base; + } + + shrm->ape_audio_fifo_base_phy = + (u32 *)U8500_SHM_FIFO_APE_AUDIO_BASE; + shrm->ape_audio_fifo_base = + (void __iomem *)ioremap_nocache(U8500_SHM_FIFO_APE_AUDIO_BASE, + SHM_FIFO_1_SIZE); + shrm->ape_audio_fifo_size = (SHM_FIFO_1_SIZE)/4; + + if (!(shrm->ape_audio_fifo_base)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_ape_audio_fifo_base; + } + + shrm->cmt_audio_fifo_base_phy = + (u32 *)U8500_SHM_FIFO_CMT_AUDIO_BASE; + shrm->cmt_audio_fifo_base = + (void __iomem *)ioremap_nocache(U8500_SHM_FIFO_CMT_AUDIO_BASE, + SHM_FIFO_1_SIZE); + shrm->cmt_audio_fifo_size = (SHM_FIFO_1_SIZE)/4; + + if (!(shrm->cmt_audio_fifo_base)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_cmt_audio_fifo_base; + } + + shrm->ac_common_shared_wptr = + (void __iomem *)ioremap(SHM_ACFIFO_0_WRITE_AMCU, SHM_PTR_SIZE); + + if (!(shrm->ac_common_shared_wptr)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_ac_common_shared_wptr; + } + + shrm->ac_common_shared_rptr = + (void __iomem *)ioremap(SHM_ACFIFO_0_READ_AMCU, SHM_PTR_SIZE); + + if (!(shrm->ac_common_shared_rptr)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_map; + } + + + shrm->ca_common_shared_wptr = + (void __iomem *)ioremap(SHM_CAFIFO_0_WRITE_AMCU, SHM_PTR_SIZE); + + if (!(shrm->ca_common_shared_wptr)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_map; + } + + shrm->ca_common_shared_rptr = + (void __iomem *)ioremap(SHM_CAFIFO_0_READ_AMCU, SHM_PTR_SIZE); + + if (!(shrm->ca_common_shared_rptr)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_map; + } + + + shrm->ac_audio_shared_wptr = + (void __iomem *)ioremap(SHM_ACFIFO_1_WRITE_AMCU, SHM_PTR_SIZE); + + if (!(shrm->ac_audio_shared_wptr)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_map; + } + + + shrm->ac_audio_shared_rptr = + (void __iomem *)ioremap(SHM_ACFIFO_1_READ_AMCU, SHM_PTR_SIZE); + + if (!(shrm->ac_audio_shared_rptr)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_map; + } + + + shrm->ca_audio_shared_wptr = + (void __iomem *)ioremap(SHM_CAFIFO_1_WRITE_AMCU, SHM_PTR_SIZE); + + if (!(shrm->ca_audio_shared_wptr)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_map; + } + + + shrm->ca_audio_shared_rptr = + (void __iomem *)ioremap(SHM_CAFIFO_1_READ_AMCU, SHM_PTR_SIZE); + + if (!(shrm->ca_audio_shared_rptr)) { + dev_err(shrm->dev, "Unable to map register base\n"); + err = -EBUSY; + goto rollback_map; + } + + + if (isa_init(shrm) != 0) { + dev_err(shrm->dev, "Driver Initialization Error\n"); + err = -EBUSY; + } + /* install handlers and tasklets */ + if (shm_initialise_irq(shrm)) { + dev_err(shrm->dev, "shm error in interrupt registration\n"); + goto rollback_irq; + } + +#ifdef CONFIG_HIGH_RES_TIMERS + hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + timer.function = callback; + + hrtimer_start(&timer, ktime_set(0, 2*NSEC_PER_MSEC), HRTIMER_MODE_REL); +#endif + + return err; + +rollback_irq: + free_shm_irq(shrm); +rollback_map: + iounmap(shrm->ac_common_shared_wptr); + iounmap(shrm->ac_common_shared_rptr); + iounmap(shrm->ca_common_shared_wptr); + iounmap(shrm->ca_common_shared_rptr); + iounmap(shrm->ac_audio_shared_wptr); + iounmap(shrm->ac_audio_shared_rptr); + iounmap(shrm->ca_audio_shared_wptr); + iounmap(shrm->ca_audio_shared_rptr); +rollback_ac_common_shared_wptr: + iounmap(shrm->cmt_audio_fifo_base); +rollback_cmt_audio_fifo_base: + iounmap(shrm->ape_audio_fifo_base); +rollback_ape_audio_fifo_base: + iounmap(shrm->cmt_common_fifo_base); +rollback_cmt_common_fifo_base: + iounmap(shrm->ape_common_fifo_base); +rollback_ape_common_fifo_base: + iounmap(shrm->intr_base); +rollback_intr: + kfree(shrm); + return err; +} + +static int __exit shrm_remove(struct platform_device *pdev) +{ + struct shrm_dev *shrm = platform_get_drvdata(pdev); + + free_shm_irq(shrm); + iounmap(shrm->intr_base); + iounmap(shrm->ape_common_fifo_base); + iounmap(shrm->cmt_common_fifo_base); + iounmap(shrm->ape_audio_fifo_base); + iounmap(shrm->cmt_audio_fifo_base); + iounmap(shrm->ac_common_shared_wptr); + iounmap(shrm->ac_common_shared_rptr); + iounmap(shrm->ca_common_shared_wptr); + iounmap(shrm->ca_common_shared_rptr); + iounmap(shrm->ac_audio_shared_wptr); + iounmap(shrm->ac_audio_shared_rptr); + iounmap(shrm->ca_audio_shared_wptr); + iounmap(shrm->ca_audio_shared_rptr); + kfree(shrm); + isa_exit(shrm); + + return 0; +} +#ifdef CONFIG_PM + +/** + * u8500_shrm_suspend() - This routine puts the SHRM in to sustend state. + * @pdev: platform device. + * + * This routine checks the current ongoing communication with Modem by + * examining the ca_wake state and prevents suspend if modem communication + * is on-going. + * If ca_wake = 1 (high), modem comm. is on-going; don't suspend + * If ca_wake = 0 (low), no comm. with modem on-going.Allow suspend + */ +int u8500_shrm_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct shrm_dev *shrm = platform_get_drvdata(pdev); + + dev_dbg(shrm->dev, "%s called...\n", __func__); + dev_dbg(shrm->dev, "\n ca_wake_req_state = %x\n", + get_ca_wake_req_state()); + /* if ca_wake_req is high, prevent system suspend */ + if (get_ca_wake_req_state()) + return -EBUSY; + else + return 0; +} + +/** + * u8500_shrm_resume() - This routine resumes the SHRM from sustend state. + * @pdev: platform device. + * + * This routine restore back the current state of the SHRM + */ +int u8500_shrm_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct shrm_dev *shrm = platform_get_drvdata(pdev); + + dev_dbg(shrm->dev, "%s called...\n", __func__); + /* TODO: + * As of now, no state save takes place in suspend. + * So, nothing to restore in resume. + * Simply return as of now. + * State saved in suspend should be restored here. + */ + + return 0; +} + +static const struct dev_pm_ops shrm_dev_pm_ops = { + .suspend = u8500_shrm_suspend, + .resume = u8500_shrm_resume, +}; +#endif + +static struct platform_driver shrm_driver = { + .remove = __exit_p(shrm_remove), + .driver = { + .name = "u8500_shrm", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &shrm_dev_pm_ops, +#endif + }, +}; + +static int __init shrm_driver_init(void) +{ + return platform_driver_probe(&shrm_driver, shrm_probe); +} + +static void __exit shrm_driver_exit(void) +{ + platform_driver_unregister(&shrm_driver); +} + +module_init(shrm_driver_init); +module_exit(shrm_driver_exit); + +MODULE_AUTHOR("Biju Das"); +MODULE_DESCRIPTION("Shared Memory Modem Driver Interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/modem/shrm/shrm_fifo.c b/drivers/modem/shrm/shrm_fifo.c new file mode 100644 index 00000000000..883966d4ab5 --- /dev/null +++ b/drivers/modem/shrm/shrm_fifo.c @@ -0,0 +1,827 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Biju Das for ST-Ericsson + * Author: Kumar Sanghavi for ST-Ericsson + * Author: Arun Murthy for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include + +#define L1_BOOT_INFO_REQ 1 +#define L1_BOOT_INFO_RESP 2 +#define L1_NORMAL_MSG 3 +#define L1_HEADER_MASK 28 +#define L1_MAPID_MASK 0xF0000000 +#define CONFIG_OFFSET 8 +#define COUNTER_OFFSET 20 +#define L2_HEADER_SIZE 4 +#define L2_HEADER_OFFSET 24 +#define MASK_0_15_BIT 0xFF +#define MASK_16_31_BIT 0xFF00 +#define MASK_16_27_BIT 0xFFF0000 +#define MASK_0_39_BIT 0xFFFFF +#define MASK_40_55_BIT 0xFF00000 +#define MASK_8_16_BIT 0x0000FF00 +#define MSG_LEN_OFFSET 16 +#define SHRM_VER 2 +#define ca_ist_inactivity_timer 100 /*100ms */ +#define ca_csc_inactivity_timer 100 /*100ms */ + +static u8 msg_audio_counter; +static u8 msg_common_counter; + +struct fifo_write_params ape_shm_fifo_0; +struct fifo_write_params ape_shm_fifo_1; +struct fifo_read_params cmt_shm_fifo_0; +struct fifo_read_params cmt_shm_fifo_1; + + +static u8 cmt_read_notif_0_send; +static u8 cmt_read_notif_1_send; + +void shm_fifo_init(struct shrm_dev *shrm) +{ + ape_shm_fifo_0.writer_local_wptr = 0; + ape_shm_fifo_0.writer_local_rptr = 0; + *((u32 *)shrm->ac_common_shared_wptr) = 0; + *((u32 *)shrm->ac_common_shared_rptr) = 0; + ape_shm_fifo_0.shared_wptr = 0; + ape_shm_fifo_0.shared_rptr = 0; + ape_shm_fifo_0.availablesize = shrm->ape_common_fifo_size; + ape_shm_fifo_0.end_addr_fifo = shrm->ape_common_fifo_size; + ape_shm_fifo_0.fifo_virtual_addr = shrm->ape_common_fifo_base; + spin_lock_init(&ape_shm_fifo_0.fifo_update_lock); + + + cmt_shm_fifo_0.reader_local_rptr = 0; + cmt_shm_fifo_0.reader_local_wptr = 0; + cmt_shm_fifo_0.shared_wptr = + *((u32 *)shrm->ca_common_shared_wptr); + cmt_shm_fifo_0.shared_rptr = + *((u32 *)shrm->ca_common_shared_rptr); + cmt_shm_fifo_0.availablesize = shrm->cmt_common_fifo_size; + cmt_shm_fifo_0.end_addr_fifo = shrm->cmt_common_fifo_size; + cmt_shm_fifo_0.fifo_virtual_addr = shrm->cmt_common_fifo_base; + + ape_shm_fifo_1.writer_local_wptr = 0; + ape_shm_fifo_1.writer_local_rptr = 0; + ape_shm_fifo_1.shared_wptr = 0; + ape_shm_fifo_1.shared_rptr = 0; + *((u32 *)shrm->ac_audio_shared_wptr) = 0; + *((u32 *)shrm->ac_audio_shared_rptr) = 0; + ape_shm_fifo_1.availablesize = shrm->ape_audio_fifo_size; + ape_shm_fifo_1.end_addr_fifo = shrm->ape_audio_fifo_size; + ape_shm_fifo_1.fifo_virtual_addr = shrm->ape_audio_fifo_base; + spin_lock_init(&ape_shm_fifo_1.fifo_update_lock); + + cmt_shm_fifo_1.reader_local_rptr = 0; + cmt_shm_fifo_1.reader_local_wptr = 0; + cmt_shm_fifo_1.shared_wptr = + *((u32 *)shrm->ca_audio_shared_wptr); + cmt_shm_fifo_1.shared_rptr = + *((u32 *)shrm->ca_audio_shared_rptr); + cmt_shm_fifo_1.availablesize = shrm->cmt_audio_fifo_size; + cmt_shm_fifo_1.end_addr_fifo = shrm->cmt_audio_fifo_size; + cmt_shm_fifo_1.fifo_virtual_addr = shrm->cmt_audio_fifo_base; + msg_audio_counter = 0; + msg_common_counter = 0; +} + +u8 read_boot_info_req(struct shrm_dev *shrm, + u32 *config, + u32 *version) +{ + struct fifo_read_params *fifo = &cmt_shm_fifo_0; + u32 *msg; + u32 header = 0; + u8 msgtype; + + /* Read L1 header read content of reader_local_rptr */ + msg = (u32 *) + (fifo->reader_local_rptr + fifo->fifo_virtual_addr); + header = *msg; + msgtype = (header & L1_MAPID_MASK) >> L1_MSG_MAPID_OFFSET; + if (msgtype != L1_BOOT_INFO_REQ) { + dev_err(shrm->dev, "Read_Boot_Info_Req Fatal ERROR\n"); + BUG(); + } + *config = (header >> CONFIG_OFFSET) & MASK_0_15_BIT; + *version = header & MASK_0_15_BIT; + fifo->reader_local_rptr += 1; + + return 1; +} + +void write_boot_info_resp(struct shrm_dev *shrm, u32 config, + u32 version) +{ + struct fifo_write_params *fifo = &ape_shm_fifo_0; + u32 *msg; + u8 msg_length; + version = SHRM_VER; + + spin_lock_bh(&fifo->fifo_update_lock); + /* Read L1 header read content of reader_local_rptr */ + msg = (u32 *) + (fifo->writer_local_wptr+fifo->fifo_virtual_addr); + if (version < 1) { + *msg = ((L1_BOOT_INFO_RESP << L1_MSG_MAPID_OFFSET) | + ((config << CONFIG_OFFSET) & MASK_16_31_BIT) + | (version & MASK_0_15_BIT)); + msg_length = 1; + } else { + *msg = ((L1_BOOT_INFO_RESP << L1_MSG_MAPID_OFFSET) | + ((0x8 << MSG_LEN_OFFSET) & MASK_16_27_BIT) | + ((config << CONFIG_OFFSET) & MASK_8_16_BIT)| + version); + msg++; + *msg = ca_ist_inactivity_timer; + msg++; + *msg = ca_csc_inactivity_timer; + msg_length = L1_NORMAL_MSG; + } + fifo->writer_local_wptr += msg_length; + fifo->availablesize -= msg_length; + spin_unlock_bh(&fifo->fifo_update_lock); +} + +/** + * shm_write_msg_to_fifo() - write message to FIFO + * @shrm: pointer to shrm device information structure + * @channel: audio or common channel + * @l2header: L2 header or device ID + * @addr: pointer to write buffer address + * @length: length of mst to write + * + * Function Which Writes the data into Fifo in IPC zone + * It is called from shm_write_msg. This function will copy the msg + * from the kernel buffer to FIFO. There are 4 kernel buffers from where + * the data is to copied to FIFO one for each of the messages ISI, RPC, + * AUDIO and SECURITY. ISI, RPC and SECURITY messages are pushed to FIFO + * in commmon channel and AUDIO message is pushed onto audio channel FIFO. + */ +int shm_write_msg_to_fifo(struct shrm_dev *shrm, u8 channel, + u8 l2header, void *addr, u32 length) +{ + struct fifo_write_params *fifo = NULL; + u32 l1_header = 0, l2_header = 0; + u32 requiredsize; + u32 size = 0; + u32 *msg; + u8 *src; + + if (channel == COMMON_CHANNEL) + fifo = &ape_shm_fifo_0; + else if (channel == AUDIO_CHANNEL) + fifo = &ape_shm_fifo_1; + else { + dev_err(shrm->dev, "invalid channel\n"); + return -EINVAL; + } + + /* L2 size in 32b */ + requiredsize = ((length + 3) / 4); + /* Add size of L1 & L2 header */ + requiredsize += 2; + + /* if availablesize = or < requiredsize then error */ + if (fifo->availablesize <= requiredsize) { + /* Fatal ERROR - should never happens */ + dev_dbg(shrm->dev, "wr_wptr= %x\n", + fifo->writer_local_wptr); + dev_dbg(shrm->dev, "wr_rptr= %x\n", + fifo->writer_local_rptr); + dev_dbg(shrm->dev, "shared_wptr= %x\n", + fifo->shared_wptr); + dev_dbg(shrm->dev, "shared_rptr= %x\n", + fifo->shared_rptr); + dev_dbg(shrm->dev, "availsize= %x\n", + fifo->availablesize); + dev_dbg(shrm->dev, "end__fifo= %x\n", + fifo->end_addr_fifo); + dev_warn(shrm->dev, "Modem is busy, please wait." + " c_cnt = %d; a_cnt = %d\n", msg_common_counter, + msg_audio_counter); + if (channel == COMMON_CHANNEL) { + dev_warn(shrm->dev, + "Modem is lagging behind in reading." + "Stopping n/w dev queue\n"); + shrm_stop_netdev(shrm->ndev); + } + + return -EAGAIN; + } + + if (channel == COMMON_CHANNEL) { + /* build L1 header */ + l1_header = ((L1_NORMAL_MSG << L1_MSG_MAPID_OFFSET) | + (((msg_common_counter++) << COUNTER_OFFSET) + & MASK_40_55_BIT) | + ((length + L2_HEADER_SIZE) & MASK_0_39_BIT)); + } else if (channel == AUDIO_CHANNEL) { + /* build L1 header */ + l1_header = ((L1_NORMAL_MSG << L1_MSG_MAPID_OFFSET) | + (((msg_audio_counter++) << COUNTER_OFFSET) + & MASK_40_55_BIT) | + ((length + L2_HEADER_SIZE) & MASK_0_39_BIT)); + } + + /* + * Need to take care race condition for fifo->availablesize + * & fifo->writer_local_rptr with Ac_Read_notification interrupt. + * One option could be use stack variable for LocalRptr and recompute + * fifo->availablesize,based on flag enabled in the + * Ac_read_notification + */ + l2_header = ((l2header << L2_HEADER_OFFSET) | + ((length) & MASK_0_39_BIT)); + spin_lock_bh(&fifo->fifo_update_lock); + /* Check Local Rptr is less than or equal to Local WPtr */ + if (fifo->writer_local_rptr <= fifo->writer_local_wptr) { + msg = (u32 *) + (fifo->fifo_virtual_addr+fifo->writer_local_wptr); + + /* check enough place bewteen writer_local_wptr & end of FIFO */ + if ((fifo->end_addr_fifo-fifo->writer_local_wptr) >= + requiredsize) { + /* Add L1 header and L2 header */ + *msg = l1_header; + msg++; + *msg = l2_header; + msg++; + + /* copy the l2 message in 1 memcpy */ + memcpy((void *)msg, addr, length); + /* UpdateWptr */ + fifo->writer_local_wptr += requiredsize; + fifo->availablesize -= requiredsize; + fifo->writer_local_wptr %= fifo->end_addr_fifo; + } else { + /* + * message is split between and of FIFO and beg of FIFO + * copy first part from writer_local_wptr to end of FIFO + */ + size = fifo->end_addr_fifo-fifo->writer_local_wptr; + + if (size == 1) { + /* Add L1 header */ + *msg = l1_header; + msg++; + /* UpdateWptr */ + fifo->writer_local_wptr = 0; + fifo->availablesize -= size; + /* + * copy second part from beg of FIFO + * with remaining part of msg + */ + msg = (u32 *) + fifo->fifo_virtual_addr; + *msg = l2_header; + msg++; + + /* copy the l3 message in 1 memcpy */ + memcpy((void *)msg, addr, length); + /* UpdateWptr */ + fifo->writer_local_wptr += + requiredsize-size; + fifo->availablesize -= + (requiredsize-size); + } else if (size == 2) { + /* Add L1 header and L2 header */ + *msg = l1_header; + msg++; + *msg = l2_header; + msg++; + + /* UpdateWptr */ + fifo->writer_local_wptr = 0; + fifo->availablesize -= size; + + /* + * copy second part from beg of FIFO + * with remaining part of msg + */ + msg = (u32 *) + fifo->fifo_virtual_addr; + /* copy the l3 message in 1 memcpy */ + memcpy((void *)msg, addr, length); + + /* UpdateWptr */ + fifo->writer_local_wptr += + requiredsize-size; + fifo->availablesize -= + (requiredsize-size); + } else { + /* Add L1 header and L2 header */ + *msg = l1_header; + msg++; + *msg = l2_header; + msg++; + + /* copy the l2 message in 1 memcpy */ + memcpy((void *)msg, addr, (size-2)*4); + + + /* UpdateWptr */ + fifo->writer_local_wptr = 0; + fifo->availablesize -= size; + + /* + * copy second part from beg of FIFO + * with remaining part of msg + */ + msg = (u32 *)fifo->fifo_virtual_addr; + src = (u8 *)addr+((size - 2) * 4); + memcpy((void *)msg, src, + (length-((size - 2) * 4))); + + /* UpdateWptr */ + fifo->writer_local_wptr += + requiredsize-size; + fifo->availablesize -= + (requiredsize-size); + } + + } + } else { + /* writer_local_rptr > writer_local_wptr */ + msg = (u32 *) + (fifo->fifo_virtual_addr+fifo->writer_local_wptr); + /* Add L1 header and L2 header */ + *msg = l1_header; + msg++; + *msg = l2_header; + msg++; + /* + * copy message possbile between writer_local_wptr up + * to writer_local_rptr copy the l3 message in 1 memcpy + */ + memcpy((void *)msg, addr, length); + + /* UpdateWptr */ + fifo->writer_local_wptr += requiredsize; + fifo->availablesize -= requiredsize; + + } + spin_unlock_bh(&fifo->fifo_update_lock); + return length; +} + +/** + * read_one_l2msg_common() - read message from common channel + * @shrm: pointer to shrm device information structure + * @l2_msg: pointer to the read L2 message buffer + * @len: message length + * + * This function read one message from the FIFO and returns l2 header type + */ +u8 read_one_l2msg_common(struct shrm_dev *shrm, + u8 *l2_msg, u32 *len) +{ + struct fifo_read_params *fifo = &cmt_shm_fifo_0; + + u32 *msg; + u32 l1_header = 0; + u32 l2_header = 0; + u32 length; + u8 msgtype; + u32 msg_size; + u32 size = 0; + + /* Read L1 header read content of reader_local_rptr */ + msg = (u32 *) + (fifo->reader_local_rptr+fifo->fifo_virtual_addr); + l1_header = *msg++; + msgtype = (l1_header & 0xF0000000) >> L1_HEADER_MASK; + + if (msgtype != L1_NORMAL_MSG) { + /* Fatal ERROR - should never happens */ + dev_dbg(shrm->dev, "wr_wptr= %x\n", + fifo->reader_local_wptr); + dev_dbg(shrm->dev, "wr_rptr= %x\n", + fifo->reader_local_rptr); + dev_dbg(shrm->dev, "shared_wptr= %x\n", + fifo->shared_wptr); + dev_dbg(shrm->dev, "shared_rptr= %x\n", + fifo->shared_rptr); + dev_dbg(shrm->dev, "availsize= %x\n", + fifo->availablesize); + dev_dbg(shrm->dev, "end_fifo= %x\n", + fifo->end_addr_fifo); + /* Fatal ERROR - should never happens */ + dev_crit(shrm->dev, "Fatal ERROR - should never happen\n"); + BUG(); + } + if (fifo->reader_local_rptr == (fifo->end_addr_fifo-1)) { + l2_header = (*((u32 *)fifo->fifo_virtual_addr)); + length = l2_header & MASK_0_39_BIT; + } else { + /* Read L2 header,Msg size & content of reader_local_rptr */ + l2_header = *msg; + length = l2_header & MASK_0_39_BIT; + } + + *len = length; + msg_size = ((length + 3) / 4); + msg_size += 2; + + if (fifo->reader_local_rptr + msg_size <= + fifo->end_addr_fifo) { + /* Skip L2 header */ + msg++; + + /* read msg between reader_local_rptr and end of FIFO */ + memcpy((void *)l2_msg, (void *)msg, length); + /* UpdateLocalRptr */ + fifo->reader_local_rptr += msg_size; + fifo->reader_local_rptr %= fifo->end_addr_fifo; + } else { + /* + * msg split between end of FIFO and beg copy first + * part of msg read msg between reader_local_rptr + * and end of FIFO + */ + size = fifo->end_addr_fifo-fifo->reader_local_rptr; + if (size == 1) { + msg = (u32 *)(fifo->fifo_virtual_addr); + /* Skip L2 header */ + msg++; + memcpy((void *)l2_msg, (void *)(msg), length); + } else if (size == 2) { + /* Skip L2 header */ + msg++; + msg = (u32 *)(fifo->fifo_virtual_addr); + memcpy((void *)l2_msg, + (void *)(msg), length); + } else { + /* Skip L2 header */ + msg++; + memcpy((void *)l2_msg, (void *)msg, ((size - 2) * 4)); + /* copy second part of msg */ + l2_msg += ((size - 2) * 4); + msg = (u32 *)(fifo->fifo_virtual_addr); + memcpy((void *)l2_msg, (void *)(msg), + (length-((size - 2) * 4))); + } + fifo->reader_local_rptr = + (fifo->reader_local_rptr+msg_size) % + fifo->end_addr_fifo; + } + return (l2_header>>L2_HEADER_OFFSET) & MASK_0_15_BIT; + } + +u8 read_remaining_messages_common() +{ + struct fifo_read_params *fifo = &cmt_shm_fifo_0; + /* + * There won't be any Race condition reader_local_rptr & + * fifo->reader_local_wptr with CaMsgpending Notification Interrupt + */ + return ((fifo->reader_local_rptr != fifo->reader_local_wptr) ? 1 : 0); +} + +u8 read_one_l2msg_audio(struct shrm_dev *shrm, + u8 *l2_msg, u32 *len) +{ + struct fifo_read_params *fifo = &cmt_shm_fifo_1; + + u32 *msg; + u32 l1_header = 0; + u32 l2_header = 0; + u32 length; + u8 msgtype; + u32 msg_size; + u32 size = 0; + + /* Read L1 header read content of reader_local_rptr */ + msg = (u32 *) + (fifo->reader_local_rptr+fifo->fifo_virtual_addr); + l1_header = *msg++; + msgtype = (l1_header & 0xF0000000) >> L1_HEADER_MASK; + + if (msgtype != L1_NORMAL_MSG) { + /* Fatal ERROR - should never happens */ + dev_dbg(shrm->dev, "wr_local_wptr= %x\n", + fifo->reader_local_wptr); + dev_dbg(shrm->dev, "wr_local_rptr= %x\n", + fifo->reader_local_rptr); + dev_dbg(shrm->dev, "shared_wptr= %x\n", + fifo->shared_wptr); + dev_dbg(shrm->dev, "shared_rptr= %x\n", + fifo->shared_rptr); + dev_dbg(shrm->dev, "availsize=%x\n", + fifo->availablesize); + dev_dbg(shrm->dev, "end_fifo= %x\n", + fifo->end_addr_fifo); + /* Fatal ERROR - should never happens */ + dev_crit(shrm->dev, "Fatal ERROR - should never happen\n"); + BUG(); + } + if (fifo->reader_local_rptr == (fifo->end_addr_fifo-1)) { + l2_header = (*((u32 *)fifo->fifo_virtual_addr)); + length = l2_header & MASK_0_39_BIT; + } else { + /* Read L2 header,Msg size & content of reader_local_rptr */ + l2_header = *msg; + length = l2_header & MASK_0_39_BIT; + } + + *len = length; + msg_size = ((length + 3) / 4); + msg_size += 2; + + if (fifo->reader_local_rptr + msg_size <= + fifo->end_addr_fifo) { + /* Skip L2 header */ + msg++; + /* read msg between reader_local_rptr and end of FIFO */ + memcpy((void *)l2_msg, (void *)msg, length); + /* UpdateLocalRptr */ + fifo->reader_local_rptr += msg_size; + fifo->reader_local_rptr %= fifo->end_addr_fifo; + } else { + + /* + * msg split between end of FIFO and beg + * copy first part of msg + * read msg between reader_local_rptr and end of FIFO + */ + size = fifo->end_addr_fifo-fifo->reader_local_rptr; + if (size == 1) { + msg = (u32 *)(fifo->fifo_virtual_addr); + /* Skip L2 header */ + msg++; + memcpy((void *)l2_msg, (void *)(msg), length); + } else if (size == 2) { + /* Skip L2 header */ + msg++; + msg = (u32 *)(fifo->fifo_virtual_addr); + memcpy((void *)l2_msg, (void *)(msg), length); + } else { + /* Skip L2 header */ + msg++; + memcpy((void *)l2_msg, (void *)msg, ((size - 2) * 4)); + /* copy second part of msg */ + l2_msg += ((size - 2) * 4); + msg = (u32 *)(fifo->fifo_virtual_addr); + memcpy((void *)l2_msg, (void *)(msg), + (length-((size - 2) * 4))); + } + fifo->reader_local_rptr = + (fifo->reader_local_rptr+msg_size) % + fifo->end_addr_fifo; + + } + return (l2_header>>L2_HEADER_OFFSET) & MASK_0_15_BIT; + } + +u8 read_remaining_messages_audio() +{ + struct fifo_read_params *fifo = &cmt_shm_fifo_1; + + return ((fifo->reader_local_rptr != fifo->reader_local_wptr) ? + 1 : 0); +} + +u8 is_the_only_one_unread_message(struct shrm_dev *shrm, + u8 channel, u32 length) +{ + struct fifo_write_params *fifo = NULL; + u32 messagesize = 0; + u8 is_only_one_unread_msg = 0; + + if (channel == COMMON_CHANNEL) + fifo = &ape_shm_fifo_0; + else /* channel = AUDIO_CHANNEL */ + fifo = &ape_shm_fifo_1; + + /* L3 size in 32b */ + messagesize = ((length + 3) / 4); + /* Add size of L1 & L2 header */ + messagesize += 2; + /* + * possibility of race condition with Ac Read notification interrupt. + * need to check ? + */ + if (fifo->writer_local_wptr > fifo->writer_local_rptr) + is_only_one_unread_msg = + ((fifo->writer_local_rptr + messagesize) == + fifo->writer_local_wptr) ? 1 : 0; + else + /* Msg split between end of fifo and starting of Fifo */ + is_only_one_unread_msg = + (((fifo->writer_local_rptr + messagesize) % + fifo->end_addr_fifo) == fifo->writer_local_wptr) ? + 1 : 0; + + return is_only_one_unread_msg; +} + +void update_ca_common_local_wptr(struct shrm_dev *shrm) +{ + /* + * update CA common reader local write pointer with the + * shared write pointer + */ + struct fifo_read_params *fifo = &cmt_shm_fifo_0; + + fifo->shared_wptr = + (*((u32 *)shrm->ca_common_shared_wptr)); + fifo->reader_local_wptr = fifo->shared_wptr; +} + +void update_ca_audio_local_wptr(struct shrm_dev *shrm) +{ + /* + * update CA audio reader local write pointer with the + * shared write pointer + */ + struct fifo_read_params *fifo = &cmt_shm_fifo_1; + + fifo->shared_wptr = + (*((u32 *)shrm->ca_audio_shared_wptr)); + fifo->reader_local_wptr = fifo->shared_wptr; +} + +void update_ac_common_local_rptr(struct shrm_dev *shrm) +{ + /* + * update AC common writer local read pointer with the + * shared read pointer + */ + struct fifo_write_params *fifo; + u32 free_space = 0; + + fifo = &ape_shm_fifo_0; + + spin_lock_bh(&fifo->fifo_update_lock); + fifo->shared_rptr = + (*((u32 *)shrm->ac_common_shared_rptr)); + + if (fifo->shared_rptr >= fifo->writer_local_rptr) + free_space = + (fifo->shared_rptr-fifo->writer_local_rptr); + else { + free_space = + (fifo->end_addr_fifo-fifo->writer_local_rptr); + free_space += fifo->shared_rptr; + } + + /* Chance of race condition of below variables with write_msg */ + fifo->availablesize += free_space; + fifo->writer_local_rptr = fifo->shared_rptr; + spin_unlock_bh(&fifo->fifo_update_lock); +} + +void update_ac_audio_local_rptr(struct shrm_dev *shrm) +{ + /* + * update AC audio writer local read pointer with the + * shared read pointer + */ + struct fifo_write_params *fifo; + u32 free_space = 0; + + fifo = &ape_shm_fifo_1; + spin_lock_bh(&fifo->fifo_update_lock); + fifo->shared_rptr = + (*((u32 *)shrm->ac_audio_shared_rptr)); + + if (fifo->shared_rptr >= fifo->writer_local_rptr) + free_space = + (fifo->shared_rptr-fifo->writer_local_rptr); + else { + free_space = + (fifo->end_addr_fifo-fifo->writer_local_rptr); + free_space += fifo->shared_rptr; + } + + /* Chance of race condition of below variables with write_msg */ + fifo->availablesize += free_space; + fifo->writer_local_rptr = fifo->shared_rptr; + spin_unlock_bh(&fifo->fifo_update_lock); +} + +void update_ac_common_shared_wptr(struct shrm_dev *shrm) +{ + /* + * update AC common shared write pointer with the + * local write pointer + */ + struct fifo_write_params *fifo; + + fifo = &ape_shm_fifo_0; + spin_lock_bh(&fifo->fifo_update_lock); + /* Update shared pointer fifo offset of the IPC zone */ + (*((u32 *)shrm->ac_common_shared_wptr)) = + fifo->writer_local_wptr; + + fifo->shared_wptr = fifo->writer_local_wptr; + spin_unlock_bh(&fifo->fifo_update_lock); +} + +void update_ac_audio_shared_wptr(struct shrm_dev *shrm) +{ + /* + * update AC audio shared write pointer with the + * local write pointer + */ + struct fifo_write_params *fifo; + + fifo = &ape_shm_fifo_1; + spin_lock_bh(&fifo->fifo_update_lock); + /* Update shared pointer fifo offset of the IPC zone */ + (*((u32 *)shrm->ac_audio_shared_wptr)) = + fifo->writer_local_wptr; + fifo->shared_wptr = fifo->writer_local_wptr; + spin_unlock_bh(&fifo->fifo_update_lock); +} + +void update_ca_common_shared_rptr(struct shrm_dev *shrm) +{ + /* + * update CA common shared read pointer with the + * local read pointer + */ + struct fifo_read_params *fifo; + + fifo = &cmt_shm_fifo_0; + + /* Update shared pointer fifo offset of the IPC zone */ + (*((u32 *)shrm->ca_common_shared_rptr)) = + fifo->reader_local_rptr; + fifo->shared_rptr = fifo->reader_local_rptr; +} + +void update_ca_audio_shared_rptr(struct shrm_dev *shrm) +{ + /* + * update CA audio shared read pointer with the + * local read pointer + */ + struct fifo_read_params *fifo; + + fifo = &cmt_shm_fifo_1; + + /* Update shared pointer fifo offset of the IPC zone */ + (*((u32 *)shrm->ca_audio_shared_rptr)) = + fifo->reader_local_rptr; + fifo->shared_rptr = fifo->reader_local_rptr; +} + +void get_reader_pointers(u8 channel_type, u32 *reader_local_rptr, + u32 *reader_local_wptr, u32 *shared_rptr) +{ + struct fifo_read_params *fifo = NULL; + + if (channel_type == COMMON_CHANNEL) + fifo = &cmt_shm_fifo_0; + else /* channel_type = AUDIO_CHANNEL */ + fifo = &cmt_shm_fifo_1; + + *reader_local_rptr = fifo->reader_local_rptr; + *reader_local_wptr = fifo->reader_local_wptr; + *shared_rptr = fifo->shared_rptr; +} + +void get_writer_pointers(u8 channel_type, u32 *writer_local_rptr, + u32 *writer_local_wptr, u32 *shared_wptr) +{ + struct fifo_write_params *fifo = NULL; + + if (channel_type == COMMON_CHANNEL) + fifo = &ape_shm_fifo_0; + else /* channel_type = AUDIO_CHANNEL */ + fifo = &ape_shm_fifo_1; + + spin_lock_bh(&fifo->fifo_update_lock); + *writer_local_rptr = fifo->writer_local_rptr; + *writer_local_wptr = fifo->writer_local_wptr; + *shared_wptr = fifo->shared_wptr; + spin_unlock_bh(&fifo->fifo_update_lock); +} + +void set_ca_msg_0_read_notif_send(u8 val) +{ + cmt_read_notif_0_send = val; +} + +u8 get_ca_msg_0_read_notif_send(void) +{ + return cmt_read_notif_0_send; +} + +void set_ca_msg_1_read_notif_send(u8 val) +{ + cmt_read_notif_1_send = val; +} + +u8 get_ca_msg_1_read_notif_send(void) +{ + return cmt_read_notif_1_send; +} diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c new file mode 100644 index 00000000000..99a58aa6f71 --- /dev/null +++ b/drivers/modem/shrm/shrm_protocol.c @@ -0,0 +1,1191 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Biju Das for ST-Ericsson + * Author: Kumar Sanghvi for ST-Ericsson + * Author: Arun Murthy for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define L2_HEADER_ISI 0x0 +#define L2_HEADER_RPC 0x1 +#define L2_HEADER_AUDIO 0x2 +#define L2_HEADER_SECURITY 0x3 +#define L2_HEADER_COMMON_SIMPLE_LOOPBACK 0xC0 +#define L2_HEADER_COMMON_ADVANCED_LOOPBACK 0xC1 +#define L2_HEADER_AUDIO_SIMPLE_LOOPBACK 0x80 +#define L2_HEADER_AUDIO_ADVANCED_LOOPBACK 0x81 +#define MAX_PAYLOAD 1024 + +static u8 boot_state = BOOT_INIT; +static u8 recieve_common_msg[8*1024]; +static u8 recieve_audio_msg[8*1024]; +static received_msg_handler rx_common_handler; +static received_msg_handler rx_audio_handler; +static struct hrtimer timer; +struct sock *shrm_nl_sk; + +static char shrm_common_tx_state = SHRM_SLEEP_STATE; +static char shrm_common_rx_state = SHRM_SLEEP_STATE; +static char shrm_audio_tx_state = SHRM_SLEEP_STATE; +static char shrm_audio_rx_state = SHRM_SLEEP_STATE; + +static atomic_t ac_sleep_disable_count = ATOMIC_INIT(0); +static struct shrm_dev *shm_dev; + +/* Spin lock and tasklet declaration */ +DECLARE_TASKLET(shm_ca_0_tasklet, shm_ca_msgpending_0_tasklet, 0); +DECLARE_TASKLET(shm_ca_1_tasklet, shm_ca_msgpending_1_tasklet, 0); +DECLARE_TASKLET(shm_ac_read_0_tasklet, shm_ac_read_notif_0_tasklet, 0); +DECLARE_TASKLET(shm_ac_read_1_tasklet, shm_ac_read_notif_1_tasklet, 0); + +static DEFINE_MUTEX(ac_state_mutex); + +static DEFINE_SPINLOCK(ca_common_lock); +static DEFINE_SPINLOCK(ca_audio_lock); +static DEFINE_SPINLOCK(ca_wake_req_lock); +static DEFINE_SPINLOCK(boot_lock); + +enum shrm_nl { + SHRM_NL_MOD_RESET = 1, + SHRM_NL_MOD_QUERY_STATE, + SHRM_NL_USER_MOD_RESET, + SHRM_NL_STATUS_MOD_ONLINE, + SHRM_NL_STATUS_MOD_OFFLINE, +}; + +static void shm_ac_sleep_req_work(struct work_struct *work) +{ + mutex_lock(&ac_state_mutex); + if (atomic_read(&ac_sleep_disable_count) == 0) + modem_release(shm_dev->modem); + mutex_unlock(&ac_state_mutex); +} + +static void shm_ac_wake_req_work(struct work_struct *work) +{ + mutex_lock(&ac_state_mutex); + modem_request(shm_dev->modem); + mutex_unlock(&ac_state_mutex); +} + +static u32 get_host_accessport_val(void) +{ + u32 prcm_hostaccess; + + prcm_hostaccess = readl(PRCM_HOSTACCESS_REQ); + wmb(); + prcm_hostaccess = prcm_hostaccess & 0x01; + + return prcm_hostaccess; +} +static enum hrtimer_restart callback(struct hrtimer *timer) +{ + unsigned long flags; + + spin_lock_irqsave(&ca_wake_req_lock, flags); + if (((shrm_common_rx_state == SHRM_IDLE) || + (shrm_common_rx_state == SHRM_SLEEP_STATE)) + && ((shrm_common_tx_state == SHRM_IDLE) || + (shrm_common_tx_state == SHRM_SLEEP_STATE)) + && ((shrm_audio_rx_state == SHRM_IDLE) || + (shrm_audio_rx_state == SHRM_SLEEP_STATE)) + && ((shrm_audio_tx_state == SHRM_IDLE) || + (shrm_audio_tx_state == SHRM_SLEEP_STATE))) { + + shrm_common_rx_state = SHRM_SLEEP_STATE; + shrm_audio_rx_state = SHRM_SLEEP_STATE; + shrm_common_tx_state = SHRM_SLEEP_STATE; + shrm_audio_tx_state = SHRM_SLEEP_STATE; + + queue_work(shm_dev->shm_ac_sleep_wq, + &shm_dev->shm_ac_sleep_req); + + } + spin_unlock_irqrestore(&ca_wake_req_lock, flags); + + return HRTIMER_NORESTART; +} + +int nl_send_multicast_message(int msg, gfp_t gfp_mask) +{ + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh = NULL; + int err; + + /* prepare netlink message */ + skb = alloc_skb(NLMSG_SPACE(MAX_PAYLOAD), gfp_mask); + if (!skb) { + dev_err(shm_dev->dev, "%s:alloc_skb failed\n", __func__); + err = -ENOMEM; + goto out; + } + + nlh = (struct nlmsghdr *)skb->data; + nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD); + dev_dbg(shm_dev->dev, "nlh->nlmsg_len = %d\n", nlh->nlmsg_len); + + nlh->nlmsg_pid = 0; /* from kernel */ + nlh->nlmsg_flags = 0; + *(int *)NLMSG_DATA(nlh) = msg; + skb_put(skb, MAX_PAYLOAD); + /* sender is in group 1<<0 */ + NETLINK_CB(skb).pid = 0; /* from kernel */ + /* to mcast group 1<<0 */ + NETLINK_CB(skb).dst_group = 1; + + /*multicast the message to all listening processes*/ + err = netlink_broadcast(shrm_nl_sk, skb, 0, 1, gfp_mask); + dev_dbg(shm_dev->dev, "ret val from nl-multicast = %d\n", err); + +out: + return err; +} + +static void nl_send_unicast_message(int dst_pid) +{ + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh = NULL; + int err; + int bt_state; + unsigned long flags; + + dev_info(shm_dev->dev, "Sending unicast message\n"); + + /* prepare the NL message for unicast */ + skb = alloc_skb(NLMSG_SPACE(MAX_PAYLOAD), GFP_KERNEL); + if (!skb) { + dev_err(shm_dev->dev, "%s:alloc_skb failed\n", __func__); + return; + } + + nlh = (struct nlmsghdr *)skb->data; + nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD); + dev_dbg(shm_dev->dev, "nlh->nlmsg_len = %d\n", nlh->nlmsg_len); + + nlh->nlmsg_pid = 0; /* from kernel */ + nlh->nlmsg_flags = 0; + + spin_lock_irqsave(&boot_lock, flags); + bt_state = boot_state; + spin_unlock_irqrestore(&boot_lock, flags); + + if (bt_state == BOOT_DONE) + *(int *)NLMSG_DATA(nlh) = SHRM_NL_STATUS_MOD_ONLINE; + else + *(int *)NLMSG_DATA(nlh) = SHRM_NL_STATUS_MOD_OFFLINE; + + skb_put(skb, MAX_PAYLOAD); + /* sender is in group 1<<0 */ + NETLINK_CB(skb).pid = 0; /* from kernel */ + NETLINK_CB(skb).dst_group = 0; + + /*unicast the message to the querying processes*/ + err = netlink_unicast(shrm_nl_sk, skb, dst_pid, MSG_DONTWAIT); + dev_dbg(shm_dev->dev, "ret val from nl-unicast = %d\n", err); +} + + +static int check_modem_in_reset(void) +{ + u8 bt_state; + unsigned long flags; + + spin_lock_irqsave(&boot_lock, flags); + bt_state = boot_state; + spin_unlock_irqrestore(&boot_lock, flags); + +#ifdef CONFIG_U8500_SHRM_MODEM_SILENT_RESET + if (bt_state != BOOT_UNKNOWN) + return 0; + else + return -ENODEV; +#else + /* + * this check won't be applicable and won't work correctly + * if modem-silent-feature is not enabled + * so, simply return 0 + */ + return 0; +#endif +} + +void shm_ca_msgpending_0_tasklet(unsigned long tasklet_data) +{ + struct shrm_dev *shrm = (struct shrm_dev *)tasklet_data; + u32 reader_local_rptr; + u32 reader_local_wptr; + u32 shared_rptr; + u32 config = 0, version = 0; + unsigned long flags; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + + /* Interprocess locking */ + spin_lock(&ca_common_lock); + + /* Update_reader_local_wptr with shared_wptr */ + update_ca_common_local_wptr(shrm); + get_reader_pointers(COMMON_CHANNEL, &reader_local_rptr, + &reader_local_wptr, &shared_rptr); + + set_ca_msg_0_read_notif_send(0); + + if (boot_state == BOOT_DONE) { + shrm_common_rx_state = SHRM_PTR_FREE; + + if (reader_local_rptr != shared_rptr) + ca_msg_read_notification_0(shrm); + if (reader_local_rptr != reader_local_wptr) + receive_messages_common(shrm); + get_reader_pointers(COMMON_CHANNEL, &reader_local_rptr, + &reader_local_wptr, &shared_rptr); + if (reader_local_rptr == reader_local_wptr) + shrm_common_rx_state = SHRM_IDLE; + } else { + /* BOOT phase.only a BOOT_RESP should be in FIFO */ + if (boot_state != BOOT_INFO_SYNC) { + if (!read_boot_info_req(shrm, &config, &version)) { + dev_err(shrm->dev, + "Unable to read boot state\n"); + BUG(); + } + /* SendReadNotification */ + ca_msg_read_notification_0(shrm); + /* + * Check the version number before + * sending Boot info response + */ + + /* send MsgPending notification */ + write_boot_info_resp(shrm, config, version); + spin_lock_irqsave(&boot_lock, flags); + boot_state = BOOT_INFO_SYNC; + spin_unlock_irqrestore(&boot_lock, flags); + dev_info(shrm->dev, "BOOT_INFO_SYNC\n"); + queue_work(shrm->shm_common_ch_wr_wq, + &shrm->send_ac_msg_pend_notify_0); + } else { + ca_msg_read_notification_0(shrm); + dev_info(shrm->dev, + "BOOT_INFO_SYNC\n"); + } + } + /* Interprocess locking */ + spin_unlock(&ca_common_lock); + dev_dbg(shrm->dev, "%s OUT\n", __func__); +} + +void shm_ca_msgpending_1_tasklet(unsigned long tasklet_data) +{ + struct shrm_dev *shrm = (struct shrm_dev *)tasklet_data; + u32 reader_local_rptr; + u32 reader_local_wptr; + u32 shared_rptr; + + /* + * This function is called when CaMsgPendingNotification Trigerred + * by CMU. It means that CMU has wrote a message into Ca Audio FIFO + */ + + dev_dbg(shrm->dev, "%s IN\n", __func__); + + if (check_modem_in_reset()) { + dev_err(shrm->dev, "%s:Modem state reset or unknown\n", + __func__); + return; + } + + /* Interprocess locking */ + spin_lock(&ca_audio_lock); + + /* Update_reader_local_wptr(with shared_wptr) */ + update_ca_audio_local_wptr(shrm); + get_reader_pointers(AUDIO_CHANNEL, &reader_local_rptr, + &reader_local_wptr, &shared_rptr); + + set_ca_msg_1_read_notif_send(0); + + if (boot_state != BOOT_DONE) { + dev_err(shrm->dev, "Boot Error\n"); + return; + } + shrm_audio_rx_state = SHRM_PTR_FREE; + /* Check we already read the message */ + if (reader_local_rptr != shared_rptr) + ca_msg_read_notification_1(shrm); + if (reader_local_rptr != reader_local_wptr) + receive_messages_audio(shrm); + + get_reader_pointers(AUDIO_CHANNEL, &reader_local_rptr, + &reader_local_wptr, &shared_rptr); + if (reader_local_rptr == reader_local_wptr) + shrm_audio_rx_state = SHRM_IDLE; + + /* Interprocess locking */ + spin_unlock(&ca_audio_lock); + dev_dbg(shrm->dev, "%s OUT\n", __func__); +} + +void shm_ac_read_notif_0_tasklet(unsigned long tasklet_data) +{ + struct shrm_dev *shrm = (struct shrm_dev *)tasklet_data; + u32 writer_local_rptr; + u32 writer_local_wptr; + u32 shared_wptr; + unsigned long flags; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + + /* Update writer_local_rptrwith shared_rptr */ + update_ac_common_local_rptr(shrm); + get_writer_pointers(COMMON_CHANNEL, &writer_local_rptr, + &writer_local_wptr, &shared_wptr); + + if (check_modem_in_reset()) { + dev_err(shrm->dev, "%s:Modem state reset or unknown\n", + __func__); + return; + } + + if (boot_state == BOOT_INFO_SYNC) { + /* BOOT_RESP sent by APE has been received by CMT */ + spin_lock_irqsave(&boot_lock, flags); + boot_state = BOOT_DONE; + spin_unlock_irqrestore(&boot_lock, flags); + dev_info(shrm->dev, "IPC_ISA BOOT_DONE\n"); + + if (shrm->msr_flag) { + shrm_start_netdev(shrm->ndev); + shrm->msr_flag = 0; + + /* multicast that modem is online */ + nl_send_multicast_message(SHRM_NL_STATUS_MOD_ONLINE, + GFP_ATOMIC); + } + + } else if (boot_state == BOOT_DONE) { + if (writer_local_rptr != writer_local_wptr) { + shrm_common_tx_state = SHRM_PTR_FREE; + queue_work(shrm->shm_common_ch_wr_wq, + &shrm->send_ac_msg_pend_notify_0); + } else { + shrm_common_tx_state = SHRM_IDLE; + shrm_restart_netdev(shrm->ndev); + } + } else { + dev_err(shrm->dev, "Invalid boot state\n"); + } + /* start timer here */ + hrtimer_start(&timer, ktime_set(0, 10*NSEC_PER_MSEC), + HRTIMER_MODE_REL); + atomic_dec(&ac_sleep_disable_count); + + dev_dbg(shrm->dev, "%s OUT\n", __func__); +} + +void shm_ac_read_notif_1_tasklet(unsigned long tasklet_data) +{ + struct shrm_dev *shrm = (struct shrm_dev *)tasklet_data; + u32 writer_local_rptr; + u32 writer_local_wptr; + u32 shared_wptr; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + + if (check_modem_in_reset()) { + dev_err(shrm->dev, "%s:Modem state reset or unknown\n", + __func__); + return; + } + + /* Update writer_local_rptr(with shared_rptr) */ + update_ac_audio_local_rptr(shrm); + get_writer_pointers(AUDIO_CHANNEL, &writer_local_rptr, + &writer_local_wptr, &shared_wptr); + if (boot_state != BOOT_DONE) { + dev_err(shrm->dev, "Error Case in boot state\n"); + return; + } + if (writer_local_rptr != writer_local_wptr) { + shrm_audio_tx_state = SHRM_PTR_FREE; + queue_work(shrm->shm_audio_ch_wr_wq, + &shrm->send_ac_msg_pend_notify_1); + } else { + shrm_audio_tx_state = SHRM_IDLE; + } + /* start timer here */ + hrtimer_start(&timer, ktime_set(0, 10*NSEC_PER_MSEC), + HRTIMER_MODE_REL); + atomic_dec(&ac_sleep_disable_count); + + dev_dbg(shrm->dev, "%s OUT\n", __func__); +} + +void shm_ca_sleep_req_work(struct work_struct *work) +{ + dev_dbg(shm_dev->dev, "%s:IRQ_PRCMU_CA_SLEEP\n", __func__); + + shrm_common_rx_state = SHRM_IDLE; + shrm_audio_rx_state = SHRM_IDLE; + + writel((1<intr_base + GOP_SET_REGISTER_BASE); + + hrtimer_start(&timer, ktime_set(0, 10*NSEC_PER_MSEC), + HRTIMER_MODE_REL); +#ifdef CONFIG_UX500_SUSPEND + suspend_unblock_sleep(); +#endif + atomic_dec(&ac_sleep_disable_count); +} + +void shm_ca_wake_req_work(struct work_struct *work) +{ + struct shrm_dev *shrm = container_of(work, + struct shrm_dev, shm_ca_wake_req); + + /* initialize the FIFO Variables */ + if (boot_state == BOOT_INIT) + shm_fifo_init(shrm); + + mutex_lock(&ac_state_mutex); + modem_request(shrm->modem); + mutex_unlock(&ac_state_mutex); + + /* send ca_wake_ack_interrupt to CMU */ + if (!get_host_accessport_val()) + BUG(); + writel((1<intr_base + GOP_SET_REGISTER_BASE); +} +#ifdef CONFIG_U8500_SHRM_MODEM_SILENT_RESET +static int shrm_modem_reset_sequence(void) +{ + int err; + unsigned long flags; + + /* + * disable irqs + * very much needed for user-space initiated + * modem-reset + */ + disable_irq_nosync(shm_dev->ac_read_notif_0_irq); + disable_irq_nosync(shm_dev->ac_read_notif_1_irq); + disable_irq_nosync(shm_dev->ca_msg_pending_notif_0_irq); + disable_irq_nosync(shm_dev->ca_msg_pending_notif_1_irq); + disable_irq_nosync(IRQ_PRCMU_CA_WAKE); + disable_irq_nosync(IRQ_PRCMU_CA_SLEEP); + + + /* update the boot_state */ + spin_lock_irqsave(&boot_lock, flags); + boot_state = BOOT_UNKNOWN; + + /* + * put a barrier over here to make sure boot_state is updated + * else, it is seen that some of already executing modem + * irqs or tasklets fail the protocol checks and will ultimately + * try to acces the modem causing system to hang. + * This is particularly seen with user-space initiated modem reset + */ + wmb(); + spin_unlock_irqrestore(&boot_lock, flags); + + hrtimer_cancel(&timer); + + /* + * keep the count to 0 so that we can bring down the line + * for normal ac-wake and ac-sleep logic + */ + atomic_set(&ac_sleep_disable_count, 0); + + /* workaround for MSR */ + queue_work(shm_dev->shm_ac_wake_wq, + &shm_dev->shm_ac_wake_req); + + /* stop network queue */ + shrm_stop_netdev(shm_dev->ndev); + + /* reset char device queues */ + shrm_char_reset_queues(shm_dev); + + /* reset protocol states */ + shrm_common_tx_state = SHRM_SLEEP_STATE; + shrm_common_rx_state = SHRM_SLEEP_STATE; + shrm_audio_tx_state = SHRM_SLEEP_STATE; + shrm_audio_rx_state = SHRM_SLEEP_STATE; + + /* set the msr flag */ + shm_dev->msr_flag = 1; + + /* multicast that modem is going to reset */ + err = nl_send_multicast_message(SHRM_NL_MOD_RESET, GFP_ATOMIC); + + /* reset the boot state */ + spin_lock_irqsave(&boot_lock, flags); + boot_state = BOOT_INIT; + spin_unlock_irqrestore(&boot_lock, flags); + + /* re-enable irqs */ + enable_irq(shm_dev->ac_read_notif_0_irq); + enable_irq(shm_dev->ac_read_notif_1_irq); + enable_irq(shm_dev->ca_msg_pending_notif_0_irq); + enable_irq(shm_dev->ca_msg_pending_notif_1_irq); + enable_irq(IRQ_PRCMU_CA_WAKE); + enable_irq(IRQ_PRCMU_CA_SLEEP); + + return err; +} +#endif + +static void shrm_modem_reset_callback(unsigned long irq) +{ + dev_err(shm_dev->dev, "Received mod_reset_req interrupt\n"); + +#ifdef CONFIG_U8500_SHRM_MODEM_SILENT_RESET + { + int err; + dev_info(shm_dev->dev, "Initiating Modem silent reset\n"); + + err = shrm_modem_reset_sequence(); + if (err) + dev_err(shm_dev->dev, + "Failed multicast of modem reset\n"); + } +#else + dev_info(shm_dev->dev, "Modem in reset loop, doing System reset\n"); + + /* Call the PRCMU reset API */ + prcmu_system_reset(SW_RESET_NO_ARGUMENT); +#endif +} + +DECLARE_TASKLET(shrm_sw_reset_callback, shrm_modem_reset_callback, + IRQ_PRCMU_MODEM_SW_RESET_REQ); + +static irqreturn_t shrm_prcmu_irq_handler(int irq, void *data) +{ + struct shrm_dev *shrm = data; + + switch (irq) { + case IRQ_PRCMU_CA_WAKE: +#ifdef CONFIG_UX500_SUSPEND + suspend_block_sleep(); +#endif + if (shrm->msr_flag) + atomic_set(&ac_sleep_disable_count, 0); + atomic_inc(&ac_sleep_disable_count); + queue_work(shrm->shm_ca_wake_wq, &shrm->shm_ca_wake_req); + break; + case IRQ_PRCMU_CA_SLEEP: + queue_work(shrm->shm_ca_wake_wq, &shrm->shm_ca_sleep_req); + break; + case IRQ_PRCMU_MODEM_SW_RESET_REQ: + tasklet_schedule(&shrm_sw_reset_callback); + break; + default: + dev_err(shrm->dev, "%s: => IRQ %d\n", __func__, irq); + return IRQ_NONE; + } + return IRQ_HANDLED; +} + +static void send_ac_msg_pend_notify_0_work(struct work_struct *work) +{ + struct shrm_dev *shrm = container_of(work, struct shrm_dev, + send_ac_msg_pend_notify_0); + + dev_dbg(shrm->dev, "%s IN\n", __func__); + update_ac_common_shared_wptr(shrm); + + mutex_lock(&ac_state_mutex); + atomic_inc(&ac_sleep_disable_count); + modem_request(shrm->modem); + mutex_unlock(&ac_state_mutex); + + if (!get_host_accessport_val()) + BUG(); + + /* Trigger AcMsgPendingNotification to CMU */ + writel((1<intr_base + GOP_SET_REGISTER_BASE); + + if (shrm_common_tx_state == SHRM_PTR_FREE) + shrm_common_tx_state = SHRM_PTR_BUSY; + + dev_dbg(shrm->dev, "%s OUT\n", __func__); +} + +static void send_ac_msg_pend_notify_1_work(struct work_struct *work) +{ + struct shrm_dev *shrm = container_of(work, struct shrm_dev, + send_ac_msg_pend_notify_1); + + dev_dbg(shrm->dev, "%s IN\n", __func__); + /* Update shared_wptr with writer_local_wptr) */ + update_ac_audio_shared_wptr(shrm); + + mutex_lock(&ac_state_mutex); + atomic_inc(&ac_sleep_disable_count); + modem_request(shrm->modem); + mutex_unlock(&ac_state_mutex); + + if (!get_host_accessport_val()) + BUG(); + + /* Trigger AcMsgPendingNotification to CMU */ + writel((1<intr_base + GOP_SET_REGISTER_BASE); + + if (shrm_audio_tx_state == SHRM_PTR_FREE) + shrm_audio_tx_state = SHRM_PTR_BUSY; + + dev_dbg(shrm->dev, "%s OUT\n", __func__); +} + +void shm_nl_receive(struct sk_buff *skb) +{ + struct nlmsghdr *nlh = NULL; + int msg; + + dev_dbg(shm_dev->dev, "Received NL msg from user-space\n"); + + nlh = (struct nlmsghdr *)skb->data; + msg = *((int *)(NLMSG_DATA(nlh))); + switch (msg) { + case SHRM_NL_MOD_QUERY_STATE: + dev_info(shm_dev->dev, "mod-query-state from user-space\n"); + nl_send_unicast_message(nlh->nlmsg_pid); + break; + + case SHRM_NL_USER_MOD_RESET: + dev_info(shm_dev->dev, "user-space inited mod-reset-req\n"); + dev_info(shm_dev->dev, "PCRMU resets modem\n"); + prcmu_modem_reset(); + break; + + default: + dev_err(shm_dev->dev, "Invalid NL msg from user-space\n"); + break; + }; +} + +int shrm_protocol_init(struct shrm_dev *shrm, + received_msg_handler common_rx_handler, + received_msg_handler audio_rx_handler) +{ + int err; + + shm_dev = shrm; + boot_state = BOOT_INIT; + dev_info(shrm->dev, "IPC_ISA BOOT_INIT\n"); + rx_common_handler = common_rx_handler; + rx_audio_handler = audio_rx_handler; + atomic_set(&ac_sleep_disable_count, 0); + + hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + timer.function = callback; + + shrm->shm_common_ch_wr_wq = create_singlethread_workqueue + ("shm_common_channel_irq"); + if (!shrm->shm_common_ch_wr_wq) { + dev_err(shrm->dev, "failed to create work queue\n"); + return -ENOMEM; + } + shrm->shm_audio_ch_wr_wq = create_singlethread_workqueue + ("shm_audio_channel_irq"); + if (!shrm->shm_audio_ch_wr_wq) { + dev_err(shrm->dev, "failed to create work queue\n"); + err = -ENOMEM; + goto free_wq1; + } + shrm->shm_ac_wake_wq = create_singlethread_workqueue("shm_ac_wake_req"); + if (!shrm->shm_ac_wake_wq) { + dev_err(shrm->dev, "failed to create work queue\n"); + err = -ENOMEM; + goto free_wq2; + } + shrm->shm_ca_wake_wq = create_singlethread_workqueue("shm_ca_wake_req"); + if (!shrm->shm_ac_wake_wq) { + dev_err(shrm->dev, "failed to create work queue\n"); + err = -ENOMEM; + goto free_wq3; + } + shrm->shm_ac_sleep_wq = create_singlethread_workqueue + ("shm_ac_sleep_req"); + if (!shrm->shm_ac_sleep_wq) { + dev_err(shrm->dev, "failed to create work queue\n"); + err = -ENOMEM; + goto free_wq4; + } + INIT_WORK(&shrm->send_ac_msg_pend_notify_0, + send_ac_msg_pend_notify_0_work); + INIT_WORK(&shrm->send_ac_msg_pend_notify_1, + send_ac_msg_pend_notify_1_work); + INIT_WORK(&shrm->shm_ca_wake_req, shm_ca_wake_req_work); + INIT_WORK(&shrm->shm_ca_sleep_req, shm_ca_sleep_req_work); + INIT_WORK(&shrm->shm_ac_sleep_req, shm_ac_sleep_req_work); + INIT_WORK(&shrm->shm_ac_wake_req, shm_ac_wake_req_work); + + /* set tasklet data */ + shm_ca_0_tasklet.data = (unsigned long)shrm; + shm_ca_1_tasklet.data = (unsigned long)shrm; + + err = request_irq(IRQ_PRCMU_CA_SLEEP, shrm_prcmu_irq_handler, + IRQF_NO_SUSPEND, "ca-sleep", shrm); + if (err < 0) { + dev_err(shm_dev->dev, "Failed alloc IRQ_PRCMU_CA_SLEEP.\n"); + goto free_wq5; + } + + err = request_irq(IRQ_PRCMU_CA_WAKE, shrm_prcmu_irq_handler, + IRQF_NO_SUSPEND, "ca-wake", shrm); + if (err < 0) { + dev_err(shm_dev->dev, "Failed alloc IRQ_PRCMU_CA_WAKE.\n"); + goto drop2; + } + + err = request_irq(IRQ_PRCMU_MODEM_SW_RESET_REQ, shrm_prcmu_irq_handler, + IRQF_NO_SUSPEND, "modem-sw-reset-req", shrm); + if (err < 0) { + dev_err(shm_dev->dev, + "Failed alloc IRQ_PRCMU_MODEM_SW_RESET_REQ.\n"); + goto drop1; + } + +#ifdef CONFIG_U8500_SHRM_MODEM_SILENT_RESET + /* init netlink socket for user-space communication */ + shrm_nl_sk = netlink_kernel_create(NULL, NETLINK_SHRM, 1, + shm_nl_receive, NULL, THIS_MODULE); + + if (!shrm_nl_sk) { + dev_err(shm_dev->dev, "netlink socket creation failed\n"); + goto drop; + } +#endif + return 0; + +#ifdef CONFIG_U8500_SHRM_MODEM_SILENT_RESET +drop: + free_irq(IRQ_PRCMU_MODEM_SW_RESET_REQ, NULL); +#endif +drop1: + free_irq(IRQ_PRCMU_CA_WAKE, NULL); +drop2: + free_irq(IRQ_PRCMU_CA_SLEEP, NULL); +free_wq5: + destroy_workqueue(shrm->shm_ac_sleep_wq); +free_wq4: + destroy_workqueue(shrm->shm_ca_wake_wq); +free_wq3: + destroy_workqueue(shrm->shm_ac_wake_wq); +free_wq2: + destroy_workqueue(shrm->shm_audio_ch_wr_wq); +free_wq1: + destroy_workqueue(shrm->shm_common_ch_wr_wq); + return err; +} + +void shrm_protocol_deinit(struct shrm_dev *shrm) +{ + free_irq(IRQ_PRCMU_CA_SLEEP, NULL); + free_irq(IRQ_PRCMU_CA_WAKE, NULL); + free_irq(IRQ_PRCMU_MODEM_SW_RESET_REQ, NULL); + flush_scheduled_work(); + destroy_workqueue(shrm->shm_common_ch_wr_wq); + destroy_workqueue(shrm->shm_audio_ch_wr_wq); + destroy_workqueue(shrm->shm_ac_wake_wq); + destroy_workqueue(shrm->shm_ca_wake_wq); + destroy_workqueue(shrm->shm_ac_sleep_wq); + modem_put(shrm->modem); +} + +int get_ca_wake_req_state(void) +{ + return ((atomic_read(&ac_sleep_disable_count) > 0) || + modem_get_usage(shm_dev->modem)); +} + +irqreturn_t ca_wake_irq_handler(int irq, void *ctrlr) +{ + struct shrm_dev *shrm = ctrlr; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + /* initialize the FIFO Variables */ + if (boot_state == BOOT_INIT) + shm_fifo_init(shrm); + + dev_dbg(shrm->dev, "Inside ca_wake_irq_handler\n"); + + /* Clear the interrupt */ + writel((1 << GOP_CA_WAKE_REQ_BIT), + shrm->intr_base + GOP_CLEAR_REGISTER_BASE); + + /* send ca_wake_ack_interrupt to CMU */ + writel((1 << GOP_CA_WAKE_ACK_BIT), + shrm->intr_base + GOP_SET_REGISTER_BASE); + + + dev_dbg(shrm->dev, "%s OUT\n", __func__); + return IRQ_HANDLED; +} + + +irqreturn_t ac_read_notif_0_irq_handler(int irq, void *ctrlr) +{ + struct shrm_dev *shrm = ctrlr; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + + if (check_modem_in_reset()) { + dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", + __func__); + return IRQ_HANDLED; + } + + shm_ac_read_0_tasklet.data = (unsigned long)shrm; + tasklet_schedule(&shm_ac_read_0_tasklet); + + if (check_modem_in_reset()) { + dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", + __func__); + return IRQ_HANDLED; + } + + /* Clear the interrupt */ + writel((1 << GOP_COMMON_AC_READ_NOTIFICATION_BIT), + shrm->intr_base + GOP_CLEAR_REGISTER_BASE); + + dev_dbg(shrm->dev, "%s OUT\n", __func__); + return IRQ_HANDLED; +} + +irqreturn_t ac_read_notif_1_irq_handler(int irq, void *ctrlr) +{ + struct shrm_dev *shrm = ctrlr; + + dev_dbg(shrm->dev, "%s IN+\n", __func__); + + if (check_modem_in_reset()) { + dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", + __func__); + return IRQ_HANDLED; + } + + shm_ac_read_1_tasklet.data = (unsigned long)shrm; + tasklet_schedule(&shm_ac_read_1_tasklet); + + if (check_modem_in_reset()) { + dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", + __func__); + return IRQ_HANDLED; + } + + /* Clear the interrupt */ + writel((1 << GOP_AUDIO_AC_READ_NOTIFICATION_BIT), + shrm->intr_base + GOP_CLEAR_REGISTER_BASE); + + dev_dbg(shrm->dev, "%s OUT\n", __func__); + return IRQ_HANDLED; +} + +irqreturn_t ca_msg_pending_notif_0_irq_handler(int irq, void *ctrlr) +{ + struct shrm_dev *shrm = ctrlr; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + + if (check_modem_in_reset()) { + dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", + __func__); + return IRQ_HANDLED; + } + + tasklet_schedule(&shm_ca_0_tasklet); + + if (check_modem_in_reset()) { + dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", + __func__); + return IRQ_HANDLED; + } + + /* Clear the interrupt */ + writel((1 << GOP_COMMON_CA_MSG_PENDING_NOTIFICATION_BIT), + shrm->intr_base + GOP_CLEAR_REGISTER_BASE); + + dev_dbg(shrm->dev, "%s OUT\n", __func__); + return IRQ_HANDLED; +} + +irqreturn_t ca_msg_pending_notif_1_irq_handler(int irq, void *ctrlr) +{ + struct shrm_dev *shrm = ctrlr; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + + if (check_modem_in_reset()) { + dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", + __func__); + return IRQ_HANDLED; + } + + tasklet_schedule(&shm_ca_1_tasklet); + + if (check_modem_in_reset()) { + dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", + __func__); + return IRQ_HANDLED; + } + + /* Clear the interrupt */ + writel((1<intr_base+GOP_CLEAR_REGISTER_BASE); + + dev_dbg(shrm->dev, "%s OUT\n", __func__); + return IRQ_HANDLED; + +} + +/** + * shm_write_msg() - write message to shared memory + * @shrm: pointer to the shrm device information structure + * @l2_header: L2 header + * @addr: pointer to the message + * @length: length of the message to be written + * + * This function is called from net or char interface driver write operation. + * Prior to calling this function the message is copied from the user space + * buffer to the kernel buffer. This function based on the l2 header routes + * the message to the respective channel and FIFO. Then makes a call to the + * fifo write function where the message is written to the physical device. + */ +int shm_write_msg(struct shrm_dev *shrm, u8 l2_header, + void *addr, u32 length) +{ + u8 channel = 0; + int ret; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + + if (boot_state != BOOT_DONE) { + dev_err(shrm->dev, + "error after boot done call this fn\n"); + ret = -ENODEV; + goto out; + } + + if ((l2_header == L2_HEADER_ISI) || + (l2_header == L2_HEADER_RPC) || + (l2_header == L2_HEADER_SECURITY) || + (l2_header == L2_HEADER_COMMON_SIMPLE_LOOPBACK) || + (l2_header == L2_HEADER_COMMON_ADVANCED_LOOPBACK)) { + channel = 0; + if (shrm_common_tx_state == SHRM_SLEEP_STATE) + shrm_common_tx_state = SHRM_PTR_FREE; + else if (shrm_common_tx_state == SHRM_IDLE) + shrm_common_tx_state = SHRM_PTR_FREE; + + } else if ((l2_header == L2_HEADER_AUDIO) || + (l2_header == L2_HEADER_AUDIO_SIMPLE_LOOPBACK) || + (l2_header == L2_HEADER_AUDIO_ADVANCED_LOOPBACK)) { + if (shrm_audio_tx_state == SHRM_SLEEP_STATE) + shrm_audio_tx_state = SHRM_PTR_FREE; + else if (shrm_audio_tx_state == SHRM_IDLE) + shrm_audio_tx_state = SHRM_PTR_FREE; + + channel = 1; + } else { + ret = -ENODEV; + goto out; + } + ret = shm_write_msg_to_fifo(shrm, channel, l2_header, addr, length); + if (ret < 0) { + dev_err(shrm->dev, "write message to fifo failed\n"); + return ret; + } + /* + * notify only if new msg copied is the only unread one + * otherwise it means that reading process is ongoing + */ + if (is_the_only_one_unread_message(shrm, channel, length)) { + + /* Send Message Pending Noitication to CMT */ + if (channel == 0) + queue_work(shrm->shm_common_ch_wr_wq, + &shrm->send_ac_msg_pend_notify_0); + else + queue_work(shrm->shm_audio_ch_wr_wq, + &shrm->send_ac_msg_pend_notify_1); + + } + + dev_dbg(shrm->dev, "%s OUT\n", __func__); + return 0; + +out: + return ret; +} + +void ca_msg_read_notification_0(struct shrm_dev *shrm) +{ + dev_dbg(shrm->dev, "%s IN\n", __func__); + + if (get_ca_msg_0_read_notif_send() == 0) { + update_ca_common_shared_rptr(shrm); + + if (check_modem_in_reset()) { + dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", + __func__); + return; + } + + /* Trigger CaMsgReadNotification to CMU */ + writel((1 << GOP_COMMON_CA_READ_NOTIFICATION_BIT), + shrm->intr_base + GOP_SET_REGISTER_BASE); + set_ca_msg_0_read_notif_send(1); + shrm_common_rx_state = SHRM_PTR_BUSY; + } + + dev_dbg(shrm->dev, "%s OUT\n", __func__); +} + +void ca_msg_read_notification_1(struct shrm_dev *shrm) +{ + dev_dbg(shrm->dev, "%s IN\n", __func__); + + if (get_ca_msg_1_read_notif_send() == 0) { + update_ca_audio_shared_rptr(shrm); + + if (check_modem_in_reset()) { + dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", + __func__); + return; + } + + /* Trigger CaMsgReadNotification to CMU */ + writel((1<intr_base+GOP_SET_REGISTER_BASE); + set_ca_msg_1_read_notif_send(1); + shrm_audio_rx_state = SHRM_PTR_BUSY; + } + dev_dbg(shrm->dev, "%s OUT\n", __func__); +} + +/** + * receive_messages_common - receive common channnel msg from + * CMT(Cellular Mobile Terminal) + * @shrm: pointer to shrm device information structure + * + * The messages sent from CMT to APE are written to the respective FIFO + * and an interrupt is triggered by the CMT. This ca message pending + * interrupt calls this function. This function sends a read notification + * acknowledgement to the CMT and calls the common channel receive handler + * where the messsage is copied to the respective(ISI, RPC, SECURIT) queue + * based on the message l2 header. + */ +void receive_messages_common(struct shrm_dev *shrm) +{ + u8 l2_header; + u32 len; + + if (check_modem_in_reset()) { + dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", + __func__); + return; + } + + l2_header = read_one_l2msg_common(shrm, recieve_common_msg, &len); + /* Send Recieve_Call_back to Upper Layer */ + if (!rx_common_handler) { + dev_err(shrm->dev, "common_rx_handler is Null\n"); + BUG(); + } + (*rx_common_handler)(l2_header, &recieve_common_msg, len, + shrm); + /* SendReadNotification */ + ca_msg_read_notification_0(shrm); + + while (read_remaining_messages_common()) { + if (check_modem_in_reset()) { + dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", + __func__); + return; + } + + l2_header = read_one_l2msg_common(shrm, recieve_common_msg, + &len); + /* Send Recieve_Call_back to Upper Layer */ + (*rx_common_handler)(l2_header, + &recieve_common_msg, len, + shrm); + } +} + +/** + * receive_messages_audio() - receive audio message from CMT + * @shrm: pointer to shrm device information structure + * + * The messages sent from CMT to APE are written to the respective FIFO + * and an interrupt is triggered by the CMT. This ca message pending + * interrupt calls this function. This function sends a read notification + * acknowledgement to the CMT and calls the common channel receive handler + * where the messsage is copied to the audio queue. + */ +void receive_messages_audio(struct shrm_dev *shrm) +{ + u8 l2_header; + u32 len; + + if (check_modem_in_reset()) { + dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", + __func__); + return; + } + + l2_header = read_one_l2msg_audio(shrm, recieve_audio_msg, &len); + /* Send Recieve_Call_back to Upper Layer */ + + if (!rx_audio_handler) { + dev_crit(shrm->dev, "audio_rx_handler is Null\n"); + BUG(); + } + (*rx_audio_handler)(l2_header, &recieve_audio_msg, + len, shrm); + + /* SendReadNotification */ + ca_msg_read_notification_1(shrm); + while (read_remaining_messages_audio()) { + if (check_modem_in_reset()) { + dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", + __func__); + return; + } + + l2_header = read_one_l2msg_audio(shrm, + recieve_audio_msg, &len); + /* Send Recieve_Call_back to Upper Layer */ + (*rx_audio_handler)(l2_header, + &recieve_audio_msg, len, + shrm); + } +} + +u8 get_boot_state() +{ + return boot_state; +} diff --git a/drivers/net/u8500_shrm.c b/drivers/net/u8500_shrm.c index 488f96f2c04..0e813bbb3cc 100644 --- a/drivers/net/u8500_shrm.c +++ b/drivers/net/u8500_shrm.c @@ -12,15 +12,15 @@ #include #include #include +#include +#include +#include +#include +#include #include #include #include -#include -#include -#include -#include -#include /** * shrm_net_receive() - receive data and copy to user space buffer diff --git a/include/linux/modem/shrm/shrm.h b/include/linux/modem/shrm/shrm.h new file mode 100644 index 00000000000..6deeeb16ba8 --- /dev/null +++ b/include/linux/modem/shrm/shrm.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Biju Das for ST-Ericsson + * Author: Kumar Sanghavi for ST-Ericsson + * Author: Arun Murthy for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef __SHM_DRIVER_IF_H__ +#define __SHM_DRIVER_IF_H__ + +#include + +/* forward declaration */ +struct shrm_dev; + +typedef void (*rx_cb)(void *data, unsigned int length); +typedef void (*received_msg_handler)(unsigned char l2_header, + void *msg_ptr, unsigned int length, + struct shrm_dev *shrm); + +#endif diff --git a/include/linux/modem/shrm/shrm_config.h b/include/linux/modem/shrm/shrm_config.h new file mode 100644 index 00000000000..a82b35ef77b --- /dev/null +++ b/include/linux/modem/shrm/shrm_config.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Biju Das for ST-Ericsson + * Author: Kumar Sanghavi for ST-Ericsson + * Author: Arun Murthy for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef __SHRM_CONFIG_H +#define __SHRM_CONFIG_H + + +/* +Note: modem need to define IPC as a non-cacheable area. +In Cortex R4 MPU requires that base address of NC area is aligned on a +region-sized boundary.On modem side, only 1 NC area can be defined, hence +the whole IPC area must be defined as NC (at least). + +*/ + +/* cache line size = 32bytes*/ +#define SHM_CACHE_LINE 32 +#define SHM_PTR_SIZE 4 + +/* FIFO 0 address configuration */ +/* ---------------------------- */ +/* 128KB */ +#define SHM_FIFO_0_SIZE (128*1024) + + +/* == APE addresses == */ +#ifdef CONFIG_SHRM_V1_UPDATES_VERSION +#define SHM_IPC_BASE_AMCU 0x06F80000 +#else +#define SHM_IPC_BASE_AMCU 0x06000000 +#endif + +/* offset pointers */ +#define SHM_ACFIFO_0_WRITE_AMCU SHM_IPC_BASE_AMCU +#define SHM_ACFIFO_0_READ_AMCU (SHM_ACFIFO_0_WRITE_AMCU + SHM_PTR_SIZE) +#define SHM_CAFIFO_0_WRITE_AMCU (SHM_ACFIFO_0_WRITE_AMCU + SHM_CACHE_LINE) +#define SHM_CAFIFO_0_READ_AMCU (SHM_CAFIFO_0_WRITE_AMCU + SHM_PTR_SIZE) +/* FIFO start */ +#define SHM_ACFIFO_0_START_AMCU (SHM_CAFIFO_0_WRITE_AMCU + SHM_CACHE_LINE) +#define SHM_CAFIFO_0_START_AMCU (SHM_ACFIFO_0_START_AMCU + SHM_FIFO_0_SIZE) + + +/* == CMT addresses ==*/ +#define SHM_IPC_BASE_CMCU (SHM_IPC_BASE_AMCU+0x08000000) +/* offset pointers */ +#define SHM_ACFIFO_0_WRITE_CMCU SHM_IPC_BASE_CMCU +#define SHM_ACFIFO_0_READ_CMCU (SHM_ACFIFO_0_WRITE_CMCU + SHM_PTR_SIZE) +#define SHM_CAFIFO_0_WRITE_CMCU (SHM_ACFIFO_0_WRITE_CMCU + SHM_CACHE_LINE) +#define SHM_CAFIFO_0_READ_CMCU (SHM_CAFIFO_0_WRITE_CMCU + SHM_PTR_SIZE) +/* FIFO*/ +#define SHM_ACFIFO_0_START_CMCU (SHM_CAFIFO_0_WRITE_CMCU + SHM_CACHE_LINE) +#define SHM_CAFIFO_0_START_CMCU (SHM_ACFIFO_0_START_CMCU + SHM_FIFO_0_SIZE) + + +/* ADSP addresses*/ +#define SHM_ACFIFO_0_START_ADSP 0x0 +#define SHM_CAFIFO_0_START_ADSP 0x0 +#define SHM_ACFIFO_0_WRITE_ADSP 0x0 +#define SHM_ACFIFO_0_READ_ADSP 0x0 +#define SHM_CAFIFO_0_WRITE_ADSP 0x0 +#define SHM_CAFIFO_0_READ_ADSP 0x0 + +/* FIFO 1 address configuration */ +/* ---------------------------- */ + + +/* FIFO 1 - 4K */ +#define SHM_FIFO_1_SIZE (4*1024) + + +/* == APE addresses == */ +#define SHM_ACFIFO_1_WRITE_AMCU (SHM_CAFIFO_0_START_AMCU + SHM_FIFO_0_SIZE) +#define SHM_ACFIFO_1_READ_AMCU (SHM_ACFIFO_1_WRITE_AMCU + SHM_PTR_SIZE) +#define SHM_CAFIFO_1_WRITE_AMCU (SHM_ACFIFO_1_WRITE_AMCU + SHM_CACHE_LINE) +#define SHM_CAFIFO_1_READ_AMCU (SHM_CAFIFO_1_WRITE_AMCU + SHM_PTR_SIZE) +/* FIFO*/ +#define SHM_ACFIFO_1_START_AMCU (SHM_CAFIFO_1_WRITE_AMCU + SHM_CACHE_LINE) +#define SHM_CAFIFO_1_START_AMCU (SHM_ACFIFO_1_START_AMCU + SHM_FIFO_1_SIZE) + + +/* == CMT addresses ==*/ +#define SHM_ACFIFO_1_WRITE_CMCU (SHM_CAFIFO_0_START_CMCU + SHM_FIFO_0_SIZE) +#define SHM_ACFIFO_1_READ_CMCU (SHM_ACFIFO_1_WRITE_CMCU + SHM_PTR_SIZE) +#define SHM_CAFIFO_1_WRITE_CMCU (SHM_ACFIFO_1_WRITE_CMCU + SHM_CACHE_LINE) +#define SHM_CAFIFO_1_READ_CMCU (SHM_CAFIFO_1_WRITE_CMCU + SHM_PTR_SIZE) +/* FIFO1 start */ +#define SHM_ACFIFO_1_START_CMCU (SHM_CAFIFO_1_WRITE_CMCU + SHM_CACHE_LINE) +#define SHM_CAFIFO_1_START_CMCU (SHM_ACFIFO_1_START_CMCU + SHM_FIFO_1_SIZE) + + +/* ADSP addresses*/ +#define SHM_ACFIFO_1_START_ADSP 0x0 +#define SHM_CAFIFO_1_START_ADSP 0x0 +#define SHM_ACFIFO_1_WRITE_ADSP 0x0 +#define SHM_ACFIFO_1_READ_ADSP 0x0 +#define SHM_CAFIFO_1_WRITE_ADSP 0x0 +#define SHM_CAFIFO_1_READ_ADSP 0x0 + + +#define U8500_SHM_FIFO_APE_COMMON_BASE (SHM_ACFIFO_0_START_AMCU) +#define U8500_SHM_FIFO_CMT_COMMON_BASE (SHM_CAFIFO_0_START_AMCU) +#define U8500_SHM_FIFO_APE_AUDIO_BASE (SHM_ACFIFO_1_START_AMCU) +#define U8500_SHM_FIFO_CMT_AUDIO_BASE (SHM_CAFIFO_1_START_AMCU) + +#endif /* __SHRM_CONFIG_H */ diff --git a/include/linux/modem/shrm/shrm_driver.h b/include/linux/modem/shrm/shrm_driver.h new file mode 100644 index 00000000000..f662a0c4533 --- /dev/null +++ b/include/linux/modem/shrm/shrm_driver.h @@ -0,0 +1,202 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Biju Das for ST-Ericsson + * Author: Kumar Sanghavi for ST-Ericsson + * Author: Arun Murthy for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef __SHRM_DRIVER_H__ +#define __SHRM_DRIVER_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ISA_DEVICES 6 + +#define BOOT_INIT (0) +#define BOOT_INFO_SYNC (1) +#define BOOT_DONE (2) +#define BOOT_UNKNOWN (3) + +/** + * struct shrm_dev - shrm device information + * @ca_wake_irq: CMT wake interrupt number + * @ac_read_notif_0_irq: ape-cmt common channel read notify interrupt + * @ac_read_notif_1_irq: ape-cmt audio channel read notify interrupt + * @ca_msg_pending_notif_0_irq: cmt-ape common channel msg pending interrupt + * @ca_msg_pending_notif_1_irq: cmt-ape audio channel msg pending interrupt + * @intr_base: interrupt base register address + * @ape_common_fifo_base: ape side common channel fifo base addr + * @ape_audio_fifo_base: ape side audio channel fifo base addr + * @cmt_common_fifo_base: cmt side common channel fifo base addr + * @cmt_audio_fifo_base: cmt side audio channel fifo base addr + * @ape_common_fifo_base_phy: physical addr of ape common fifo + * @ape_audio_fifo_base_phy: physical addr of ape audio fifo + * @cmt_common_fifo_base_phy: physical addr of cmt common fifo + * @cmt_audio_fifo_base_phy: physical addr of cmt audio fifo + * @ape_common_fifo_size: ape side common channel fifo size + * @ape_audio_fifo_size: ape side audio channel fifo size + * @cmt_common_fifo_size: cmt side common channel fifo size + * @cmt_audio_fifo_size: cmt side audio channel fifo size + * @netdev_flag_up: flag to indicate up/down of netwok device + * @msr_flag: flag to check on-going MSR sequence + * @ac_common_shared_wptr: ape-cmt common channel write pointer + * @ac_common_shared_rptr: ape-cmt common channel read pointer + * @ca_common_shared_wptr: cmt-ape common channel write pointer + * @ca_common_shared_rptr: cmt-ape common channel read pointer + * @ac_audio_shared_wptr: ape-cmt audio channel write pointer + * @ac_audio_shared_rptr: ape-cmt audio channel read pointer + * @ca_audio_shared_wptr: cmt-ape audio channel write pointer + * @ca_audio_shared_rptr: cmt-ape audio channel read pointer + * @dev: pointer to the driver device + * @ndev: pointer to the network device structure + * @isa_context: pointer to t_isa_driver_sontext dtructure + * @shm_common_ch_wr_wq: work queue for writing to common channel + * @shm_audio_ch_wr_wq: workqueue for writing to audio channel + * @shm_ac_wake_wq: workqueue for receiving ape-cmt wake requests + * @shm_ca_wake_wq: workqueue for receiving cmt-ape wake requests + * @shm_ac_sleep_wq: workqueue for recieving ape-cmt sleep requests + * @send_ac_msg_pend_notify_0: work for handling pending message on common + * channel + * @send_ac_msg_pend_notify_1: work for handling pending message on audio + * channel + * @shm_ac_wake_req: work to send ape-cmt wake request + * @shm_ca_wake_req: work to send cmt-ape wake request + * @shm_ca_sleep_req: work to send cmt-ape sleep request + * @shm_ac_sleep_req: work to send ape-cmt sleep request + */ +struct shrm_dev { + u8 ca_wake_irq; + u8 ac_read_notif_0_irq; + u8 ac_read_notif_1_irq; + u8 ca_msg_pending_notif_0_irq; + u8 ca_msg_pending_notif_1_irq; + void __iomem *intr_base; + void __iomem *ape_common_fifo_base; + void __iomem *ape_audio_fifo_base; + void __iomem *cmt_common_fifo_base; + void __iomem *cmt_audio_fifo_base; + + u32 *ape_common_fifo_base_phy; + u32 *ape_audio_fifo_base_phy; + u32 *cmt_common_fifo_base_phy; + u32 *cmt_audio_fifo_base_phy; + + int ape_common_fifo_size; + int ape_audio_fifo_size; + int cmt_common_fifo_size; + int cmt_audio_fifo_size; + int netdev_flag_up; + int msr_flag; + + void __iomem *ac_common_shared_wptr; + void __iomem *ac_common_shared_rptr; + void __iomem *ca_common_shared_wptr; + void __iomem *ca_common_shared_rptr; + + void __iomem *ac_audio_shared_wptr; + void __iomem *ac_audio_shared_rptr; + void __iomem *ca_audio_shared_wptr; + void __iomem *ca_audio_shared_rptr; + + struct device *dev; + struct net_device *ndev; + struct modem *modem; + struct isa_driver_context *isa_context; + struct workqueue_struct *shm_common_ch_wr_wq; + struct workqueue_struct *shm_audio_ch_wr_wq; + struct workqueue_struct *shm_ac_wake_wq; + struct workqueue_struct *shm_ca_wake_wq; + struct workqueue_struct *shm_ac_sleep_wq; + struct work_struct send_ac_msg_pend_notify_0; + struct work_struct send_ac_msg_pend_notify_1; + struct work_struct shm_ac_wake_req; + struct work_struct shm_ca_wake_req; + struct work_struct shm_ca_sleep_req; + struct work_struct shm_ac_sleep_req; +}; + +/** + * struct queue_element - information to add an element to queue + * @entry: list entry + * @offset: message offset + * @size: message size + * @no: total number of messages + */ +struct queue_element { + struct list_head entry; + u32 offset; + u32 size; + u32 no; +}; + +/** + * struct message_queue - ISI, RPC, AUDIO, SECURITY message queue information + * @fifo_base: pointer to the respective fifo base + * @size: size of the data to be read + * @readptr: fifo read pointer + * @writeptr: fifo write pointer + * @no: total number of messages + * @update_lock: spinlock for protecting the queue read operation + * @q_rp: queue write pointer + * @wq_readable: wait queue head + * @msg_list: message list + * @shrm: pointer to shrm device information structure + */ +struct message_queue { + u8 *fifo_base; + u32 size; + u32 readptr; + u32 writeptr; + u32 no; + spinlock_t update_lock; + atomic_t q_rp; + wait_queue_head_t wq_readable; + struct list_head msg_list; + struct shrm_dev *shrm; +}; + +/** + * struct isadev_context - shrm char interface context + * @dl_queue: structre to store the queue related info + * @device_id: message id(ISI, RPC, AUDIO, SECURITY) + * @addr: device addresses. + */ +struct isadev_context { + struct message_queue dl_queue; + u8 device_id; + void *addr; +}; + +/** + * struct isa_driver_context - shrm char interface device information + * @is_open: flag to check the usage of queue + * @isadev: pointer to struct t_isadev_context + * @common_tx: spinlock for protecting common channel + * @tx_audio_mutex: mutex for protecting audio channel + * @cdev: character device structre + * @shm_class: pointer to the class structure + */ +struct isa_driver_context { + atomic_t is_open[ISA_DEVICES]; + struct isadev_context *isadev; + spinlock_t common_tx; + struct mutex tx_audio_mutex; + struct cdev cdev; + struct class *shm_class; +}; + +#endif diff --git a/include/linux/modem/shrm/shrm_net.h b/include/linux/modem/shrm/shrm_net.h new file mode 100644 index 00000000000..a97b276ee15 --- /dev/null +++ b/include/linux/modem/shrm/shrm_net.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) ST-Ericsson SA 2009 + * + * Author: Kumar Sanghvi for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef __SHRM_NET_H +#define __SHRM_NET_H + +#define SHRM_HLEN 1 +#define PHONET_ALEN 1 + +#define PN_PIPE 0xD9 +#define PN_DEV_HOST 0x00 +#define PN_LINK_ADDR 0x26 +#define PN_TX_QUEUE_LEN 3 + +#define RESOURCE_ID_INDEX 3 +#define SRC_OBJ_INDEX 7 +#define MSG_ID_INDEX 9 +#define PIPE_HDL_INDEX 10 +#define NETLINK_SHRM 20 + +/** + * struct shrm_net_iface_priv - shrm net interface device information + * @shrm_device: pointer to the shrm device information structure + * @iface_num: flag used to indicate the up/down of netdev + */ +struct shrm_net_iface_priv { + struct shrm_dev *shrm_device; + unsigned int iface_num; +}; + +int shrm_register_netdev(struct shrm_dev *shrm_dev_data); +int shrm_net_receive(struct net_device *dev); +int shrm_suspend_netdev(struct net_device *dev); +int shrm_resume_netdev(struct net_device *dev); +int shrm_stop_netdev(struct net_device *dev); +int shrm_restart_netdev(struct net_device *dev); +int shrm_start_netdev(struct net_device *dev); +void shrm_unregister_netdev(struct shrm_dev *shrm_dev_data); + +#endif /* __SHRM_NET_H */ diff --git a/include/linux/modem/shrm/shrm_private.h b/include/linux/modem/shrm/shrm_private.h new file mode 100644 index 00000000000..2063f6d6e8e --- /dev/null +++ b/include/linux/modem/shrm/shrm_private.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Biju Das for ST-Ericsson + * Author: Kumar Sanghavi for ST-Ericsson + * Author: Arun Murthy for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef __SHRM_PRIVATE_INCLUDED +#define __SHRM_PRIVATE_INCLUDED + +#include +#include +#include +#include + +#define GOP_OUTPUT_REGISTER_BASE (0x0) +#define GOP_SET_REGISTER_BASE (0x4) +#define GOP_CLEAR_REGISTER_BASE (0x8) +#define GOP_TOGGLE_REGISTER_BASE (0xc) + + +#define GOP_AUDIO_AC_READ_NOTIFICATION_BIT (0) +#define GOP_AUDIO_CA_MSG_PENDING_NOTIFICATION_BIT (1) +#define GOP_COMMON_AC_READ_NOTIFICATION_BIT (2) +#define GOP_COMMON_CA_MSG_PENDING_NOTIFICATION_BIT (3) +#define GOP_CA_WAKE_REQ_BIT (7) +#define GOP_AUDIO_CA_READ_NOTIFICATION_BIT (23) +#define GOP_AUDIO_AC_MSG_PENDING_NOTIFICATION_BIT (24) +#define GOP_COMMON_CA_READ_NOTIFICATION_BIT (25) +#define GOP_COMMON_AC_MSG_PENDING_NOTIFICATION_BIT (26) +#define GOP_CA_WAKE_ACK_BIT (27) + +#define L2_MSG_MAPID_OFFSET (24) +#define L1_MSG_MAPID_OFFSET (28) + +#define SHRM_SLEEP_STATE (0) +#define SHRM_PTR_FREE (1) +#define SHRM_PTR_BUSY (2) +#define SHRM_IDLE (3) + +#define ISI_MESSAGING (0) +#define RPC_MESSAGING (1) +#define AUDIO_MESSAGING (2) +#define SECURITY_MESSAGING (3) +#define COMMON_LOOPBACK_MESSAGING (0xC0) +#define AUDIO_LOOPBACK_MESSAGING (0x80) + +#define COMMON_CHANNEL 0 +#define AUDIO_CHANNEL 1 + +typedef void (*MSG_PENDING_NOTIF)(const u32 Wptr); + +/** + * struct fifo_write_params - parameters used for FIFO write operation. + * @writer_local_rptr: pointer to local read buffer + * @writer_local_wptr: pointer to local write buffer + * @shared_wptr: write pointer shared by cmt and ape + * @shared_rptr: read pointer shared by cmt and ape + * @availablesize: available memory in fifo + * @end_addr_fifo: fifo end addr + * @fifo_virtual_addr: fifo virtual addr + * @fifo_update_lock: spin lock to update fifo. + * + * On writting a message to FIFO the same has to be read by the modem before + * writing the next message to the FIFO. In oder to over come this a local + * write and read pointer is used for internal purpose. + */ +struct fifo_write_params { + u32 writer_local_rptr; + u32 writer_local_wptr; + u32 shared_wptr; + u32 shared_rptr; + u32 availablesize; + u32 end_addr_fifo; + u32 *fifo_virtual_addr; + spinlock_t fifo_update_lock; +} ; + +/** + * struct fifo_read_params - parameters used for FIFO read operation + * @reader_local_rptr: pointer to local read buffer + * @reader_local_wptr: pointer to local write buffer + * @shared_wptr: write pointer shared by cmt and ape + * @shared_rptr: read pointer shared by cmt and ape + * @availablesize: available memory in fifo + * @end_addr_fifo: fifo end add + * @fifo_virtual_addr: fifo virtual addr + */ +struct fifo_read_params{ + u32 reader_local_rptr; + u32 reader_local_wptr; + u32 shared_wptr; + u32 shared_rptr; + u32 availablesize; + u32 end_addr_fifo; + u32 *fifo_virtual_addr; + +} ; + +int shrm_protocol_init(struct shrm_dev *shrm, + received_msg_handler common_rx_handler, + received_msg_handler audio_rx_handler); +void shrm_protocol_deinit(struct shrm_dev *shrm); +void shm_fifo_init(struct shrm_dev *shrm); +int shm_write_msg_to_fifo(struct shrm_dev *shrm, u8 channel, + u8 l2header, void *addr, u32 length); +int shm_write_msg(struct shrm_dev *shrm, + u8 l2_header, void *addr, u32 length); + +u8 is_the_only_one_unread_message(struct shrm_dev *shrm, + u8 channel, u32 length); +u8 read_remaining_messages_common(void); +u8 read_remaining_messages_audio(void); +u8 read_one_l2msg_audio(struct shrm_dev *shrm, + u8 *p_l2_msg, u32 *p_len); +u8 read_one_l2msg_common(struct shrm_dev *shrm, + u8 *p_l2_msg, u32 *p_len); +void receive_messages_common(struct shrm_dev *shrm); +void receive_messages_audio(struct shrm_dev *shrm); + +void update_ac_common_local_rptr(struct shrm_dev *shrm); +void update_ac_audio_local_rptr(struct shrm_dev *shrm); +void update_ca_common_local_wptr(struct shrm_dev *shrm); +void update_ca_audio_local_wptr(struct shrm_dev *shrm); +void update_ac_common_shared_wptr(struct shrm_dev *shrm); +void update_ac_audio_shared_wptr(struct shrm_dev *shrm); +void update_ca_common_shared_rptr(struct shrm_dev *shrm); +void update_ca_audio_shared_rptr(struct shrm_dev *shrm); + + +void get_writer_pointers(u8 msg_type, u32 *WriterLocalRptr, \ + u32 *WriterLocalWptr, u32 *SharedWptr); +void get_reader_pointers(u8 msg_type, u32 *ReaderLocalRptr, \ + u32 *ReaderLocalWptr, u32 *SharedRptr); +u8 read_boot_info_req(struct shrm_dev *shrm, + u32 *pConfig, + u32 *pVersion); +void write_boot_info_resp(struct shrm_dev *shrm, u32 Config, + u32 Version); + +void send_ac_msg_pending_notification_0(struct shrm_dev *shrm); +void send_ac_msg_pending_notification_1(struct shrm_dev *shrm); +void ca_msg_read_notification_0(struct shrm_dev *shrm); +void ca_msg_read_notification_1(struct shrm_dev *shrm); + +void set_ca_msg_0_read_notif_send(u8 val); +u8 get_ca_msg_0_read_notif_send(void); +void set_ca_msg_1_read_notif_send(u8 val); +u8 get_ca_msg_1_read_notif_send(void); + +irqreturn_t ca_wake_irq_handler(int irq, void *ctrlr); +irqreturn_t ac_read_notif_0_irq_handler(int irq, void *ctrlr); +irqreturn_t ac_read_notif_1_irq_handler(int irq, void *ctrlr); +irqreturn_t ca_msg_pending_notif_0_irq_handler(int irq, void *ctrlr); +irqreturn_t ca_msg_pending_notif_1_irq_handler(int irq, void *ctrlr); + +void shm_ca_msgpending_0_tasklet(unsigned long); +void shm_ca_msgpending_1_tasklet(unsigned long); +void shm_ac_read_notif_0_tasklet(unsigned long); +void shm_ac_read_notif_1_tasklet(unsigned long); +void shm_ca_wake_req_tasklet(unsigned long); + +u8 get_boot_state(void); + +int get_ca_wake_req_state(void); + +/* shrm character interface */ +int isa_init(struct shrm_dev *shrm); +void isa_exit(struct shrm_dev *shrm); +int add_msg_to_queue(struct message_queue *q, u32 size); +ssize_t isa_read(struct file *filp, char __user *buf, size_t len, + loff_t *ppos); +int get_size_of_new_msg(struct message_queue *q); +int remove_msg_from_queue(struct message_queue *q); +void shrm_char_reset_queues(struct shrm_dev *shrm); +int shrm_get_cdev_index(u8 l2_header); +int shrm_get_cdev_l2header(u8 idx); + +#endif -- cgit v1.2.3 From 3cbfa821cdbf2c298fa4b76ea71211e8573b5cb8 Mon Sep 17 00:00:00 2001 From: Chris Blair Date: Fri, 4 Nov 2011 13:51:25 +0000 Subject: modem: Add M6718 IPC SPI driver net interface Adds the net device interface from the modem to userspace. ST-Ericsson ID: 369397 ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-12224 ST-Ericsson Linux next: NA Change-Id: Ib1cafdc1558305d808870b0fdafa8e7029ec5a6b Signed-off-by: Chris Blair Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36494 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Jonas ABERG --- drivers/modem/m6718_spi/modem_driver.c | 55 ++++- drivers/net/Makefile | 1 + drivers/net/m6718_modem_net.c | 333 ++++++++++++++++++++++++++++++ include/linux/modem/m6718_spi/modem_net.h | 50 +++++ 4 files changed, 437 insertions(+), 2 deletions(-) create mode 100644 drivers/net/m6718_modem_net.c create mode 100644 include/linux/modem/m6718_spi/modem_net.h (limited to 'drivers/net') diff --git a/drivers/modem/m6718_spi/modem_driver.c b/drivers/modem/m6718_spi/modem_driver.c index d85e6943c23..623a9191d27 100644 --- a/drivers/modem/m6718_spi/modem_driver.c +++ b/drivers/modem/m6718_spi/modem_driver.c @@ -13,9 +13,15 @@ #include #include #include +#include #include #include "modem_protocol.h" +#ifdef CONFIG_PHONET +static void phonet_rcv_tasklet_func(unsigned long); +static struct tasklet_struct phonet_rcv_tasklet; +#endif + static struct modem_spi_dev modem_driver_data = { .dev = NULL, .ndev = NULL, @@ -37,6 +43,10 @@ static struct modem_spi_dev modem_driver_data = { * * Special handling is given to slave-loopback channels where the data is simply * sent back to the modem on the same channel. + * + * Special handling is given to the ISI channel when PHONET is enabled - the + * phonet tasklet is scheduled in order to pump the received data through the + * net device interface. */ int modem_m6718_spi_receive(struct spi_device *sdev, u8 channel, u32 len, void *data) @@ -95,10 +105,37 @@ int modem_m6718_spi_receive(struct spi_device *sdev, u8 channel, dev_err(&sdev->dev, "failed to queue frame!"); return ret; } + +#ifdef CONFIG_PHONET + if (channel == MODEM_M6718_SPI_CHN_ISI && + modem_driver_data.netdev_flag_up) + tasklet_schedule(&phonet_rcv_tasklet); +#endif return ret; } EXPORT_SYMBOL_GPL(modem_m6718_spi_receive); +static void phonet_rcv_tasklet_func(unsigned long unused) +{ + ssize_t result; + + dev_dbg(modem_driver_data.dev, "receiving frames for phonet\n"); + /* continue receiving while there are frames in the queue */ + for (;;) { + result = modem_net_receive(modem_driver_data.ndev); + if (result == 0) { + dev_dbg(modem_driver_data.dev, + "queue is empty, finished receiving\n"); + break; + } + if (result < 0) { + dev_err(modem_driver_data.dev, + "failed to receive frame from queue!\n"); + break; + } + } +} + static int spi_probe(struct spi_device *sdev) { int result = 0; @@ -133,9 +170,22 @@ static int spi_probe(struct spi_device *sdev) "failed to initialise char interface\n"); goto rollback_modem_get; } + + result = modem_net_init(&modem_driver_data); + if (result < 0) { + dev_err(&sdev->dev, + "failed to initialse net interface\n"); + goto rollback_isa_init; + } + +#ifdef CONFIG_PHONET + tasklet_init(&phonet_rcv_tasklet, phonet_rcv_tasklet_func, 0); +#endif } return result; +rollback_isa_init: + modem_isa_exit(&modem_driver_data); rollback_modem_get: modem_put(modem_driver_data.modem); rollback_protocol_init: @@ -147,6 +197,7 @@ rollback: static int __exit spi_remove(struct spi_device *sdev) { modem_protocol_exit(); + modem_net_exit(&modem_driver_data); modem_isa_exit(&modem_driver_data); return 0; } @@ -167,7 +218,7 @@ static int spi_suspend(struct spi_device *sdev, pm_message_t mesg) busy = modem_protocol_is_busy(sdev); dev_dbg(&sdev->dev, "suspend called, protocol busy:%d\n", busy); if (!busy) - return 0; + return modem_net_suspend(modem_driver_data.ndev); else return -EBUSY; } @@ -179,7 +230,7 @@ static int spi_suspend(struct spi_device *sdev, pm_message_t mesg) static int spi_resume(struct spi_device *sdev) { dev_dbg(&sdev->dev, "resume called\n"); - return 0; + return modem_net_resume(modem_driver_data.ndev); } #endif /* CONFIG_PM */ diff --git a/drivers/net/Makefile b/drivers/net/Makefile index c1d8099a34e..a0f2484368b 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -73,4 +73,5 @@ obj-$(CONFIG_HYPERV_NET) += hyperv/ ifdef CONFIG_PHONET obj-$(CONFIG_U8500_SHRM) += u8500_shrm.o +obj-$(CONFIG_MODEM_M6718_SPI) += m6718_modem_net.o endif diff --git a/drivers/net/m6718_modem_net.c b/drivers/net/m6718_modem_net.c new file mode 100644 index 00000000000..f64a775560b --- /dev/null +++ b/drivers/net/m6718_modem_net.c @@ -0,0 +1,333 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Chris Blair for ST-Ericsson + * based on u8500_shrm.c + * + * License terms: GNU General Public License (GPL) version 2 + * + * M6718 modem net device interface. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/** + * modem_net_receive() - receive data and copy to user space buffer + * @dev: pointer to the network device structure + * + * Copy data from ISI queue to the user space buffer. + */ +int modem_net_receive(struct net_device *dev) +{ + struct sk_buff *skb; + struct isa_device_context *isadev; + struct message_queue *q; + u32 msgsize; + u32 size = 0; + struct modem_spi_net_dev *net_iface_priv = + (struct modem_spi_net_dev *)netdev_priv(dev); + struct modem_spi_dev *modem_spi_dev = net_iface_priv->modem_spi_dev; + + isadev = &modem_spi_dev->isa_context->isadev[MODEM_M6718_SPI_CHN_ISI]; + q = &isadev->dl_queue; + + spin_lock_bh(&q->update_lock); + if (list_empty(&q->msg_list)) { + spin_unlock_bh(&q->update_lock); + dev_dbg(modem_spi_dev->dev, "empty queue!\n"); + return 0; + } + spin_unlock_bh(&q->update_lock); + + msgsize = modem_isa_msg_size(q); + if (msgsize <= 0) + return msgsize; + + /* + * The packet has been retrieved from the transmission + * medium. Build an skb around it, so upper layers can handle it + */ + skb = dev_alloc_skb(msgsize); + if (!skb) { + pr_notice_ratelimited("isa rx: low on mem - packet dropped\n"); + dev->stats.rx_dropped++; + return -ENOMEM; + } + + if ((q->readptr + msgsize) >= q->size) { + size = (q->size - q->readptr); + /* copy first part of msg */ + skb_copy_to_linear_data(skb, + (u8 *)(q->fifo_base + q->readptr), size); + skb_put(skb, size); + + /* copy second part of msg at the top of fifo */ + skb_copy_to_linear_data_offset(skb, size, + (u8 *)(q->fifo_base), (msgsize - size)); + skb_put(skb, msgsize - size); + + } else { + skb_copy_to_linear_data(skb, + (u8 *)(q->fifo_base + q->readptr), msgsize); + skb_put(skb, msgsize); + } + + spin_lock_bh(&q->update_lock); + modem_isa_unqueue_msg(q); + spin_unlock_bh(&q->update_lock); + + skb_reset_mac_header(skb); + __skb_pull(skb, dev->hard_header_len); + /* write metadata and then pass to the receive level */ + skb->dev = dev; + skb->protocol = htons(ETH_P_PHONET); + skb->priority = 0; + skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */ + if (likely(netif_rx_ni(skb) == NET_RX_SUCCESS)) { + dev->stats.rx_packets++; + dev->stats.rx_bytes += msgsize; + } else { + dev->stats.rx_dropped++; + } + + return msgsize; +} +EXPORT_SYMBOL_GPL(modem_net_receive); + +static int netdev_isa_open(struct net_device *dev) +{ + struct modem_spi_net_dev *net_iface_priv = + (struct modem_spi_net_dev *)netdev_priv(dev); + struct modem_spi_dev *modem_spi_dev = net_iface_priv->modem_spi_dev; + + modem_spi_dev->netdev_flag_up = 1; + if (!netif_carrier_ok(dev)) + netif_carrier_on(dev); + netif_wake_queue(dev); + return 0; +} + +static int netdev_isa_close(struct net_device *dev) +{ + struct modem_spi_net_dev *net_iface_priv = + (struct modem_spi_net_dev *)netdev_priv(dev); + struct modem_spi_dev *modem_spi_dev = net_iface_priv->modem_spi_dev; + + modem_spi_dev->netdev_flag_up = 0; + netif_stop_queue(dev); + netif_carrier_off(dev); + return 0; +} + +static int netdev_isa_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct if_phonet_req *req = (struct if_phonet_req *)ifr; + + switch (cmd) { + case SIOCPNGAUTOCONF: + req->ifr_phonet_autoconf.device = PN_DEV_HOST; + return 0; + } + return -ENOIOCTLCMD; +} + +static struct net_device_stats *netdev_isa_stats(struct net_device *dev) +{ + return &dev->stats; +} + +/** + * netdev_isa_write() - write through the net interface + * @skb: pointer to the socket buffer + * @dev: pointer to the network device structure + * + * Copies data(ISI message) from the user buffer to the kernel buffer and + * schedule transfer thread to transmit the message to the modem via FIFO. + */ +static netdev_tx_t netdev_isa_write(struct sk_buff *skb, struct net_device *dev) +{ + int err; + int retval = 0; + struct modem_spi_net_dev *net_iface_priv = + (struct modem_spi_net_dev *)netdev_priv(dev); + struct modem_spi_dev *modem_spi_dev = net_iface_priv->modem_spi_dev; + + /* + * FIXME: + * U8500 modem requires that Pipe created/enabled Indication should + * be sent from the port corresponding to GPRS socket. + * Also, the U8500 modem does not implement Pipe controller + * which takes care of port manipulations for GPRS traffic. + * + * Now, APE has GPRS socket and the socket for sending + * Indication msgs bound to different ports. + * Phonet stack does not allow an indication msg to be sent + * from GPRS socket, since Phonet stack assumes the presence + * of Pipe controller in modem. + * + * So, due to lack of Pipe controller implementation in the + * U8500 modem, carry out the port manipulation related to + * GPRS traffic here. + * Ideally, it should be done either by Pipe controller in + * modem OR some implementation of Pipe controller on APE side + */ + if (skb->data[RESOURCE_ID_INDEX] == PN_PIPE) { + if ((skb->data[MSG_ID_INDEX] == PNS_PIPE_CREATED_IND) || + (skb->data[MSG_ID_INDEX] == PNS_PIPE_ENABLED_IND) || + (skb->data[MSG_ID_INDEX] == PNS_PIPE_DISABLED_IND)) + skb->data[SRC_OBJ_INDEX] = skb->data[PIPE_HDL_INDEX]; + } + + spin_lock_bh(&modem_spi_dev->isa_context->common_tx_lock); + err = modem_m6718_spi_send(modem_spi_dev, MODEM_M6718_SPI_CHN_ISI, + skb->len, skb->data); + if (!err) { + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + retval = NETDEV_TX_OK; + dev_kfree_skb(skb); + } else { + dev->stats.tx_dropped++; + retval = NETDEV_TX_BUSY; + } + spin_unlock_bh(&modem_spi_dev->isa_context->common_tx_lock); + + return retval; +} + +static const struct net_device_ops modem_netdev_ops = { + .ndo_open = netdev_isa_open, + .ndo_stop = netdev_isa_close, + .ndo_do_ioctl = netdev_isa_ioctl, + .ndo_start_xmit = netdev_isa_write, + .ndo_get_stats = netdev_isa_stats, +}; + +static void net_device_init(struct net_device *dev) +{ + struct modem_spi_net_dev *net_iface_priv; + + dev->netdev_ops = &modem_netdev_ops; + dev->header_ops = &phonet_header_ops; + dev->type = ARPHRD_PHONET; + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + dev->mtu = PHONET_MAX_MTU; + dev->hard_header_len = MODEM_HLEN; + dev->addr_len = PHONET_ALEN; + dev->tx_queue_len = PN_TX_QUEUE_LEN; + dev->destructor = free_netdev; + dev->dev_addr[0] = PN_LINK_ADDR; + net_iface_priv = netdev_priv(dev); + memset(net_iface_priv, 0 , sizeof(struct modem_spi_net_dev)); +} + +int modem_net_init(struct modem_spi_dev *modem_spi_dev) +{ + struct net_device *nw_device; + struct modem_spi_net_dev *net_iface_priv; + int err; + /* + * keep the same net device name as U8500 to allow userspace clients + * to remain unchanged and use the same interfaces + */ + char *devname = "shrm%d"; + + /* allocate the net device */ + nw_device = modem_spi_dev->ndev = + alloc_netdev(sizeof(struct modem_spi_net_dev), + devname, net_device_init); + if (nw_device == NULL) { + dev_err(modem_spi_dev->dev, + "failed to allocate modem net device\n"); + return -ENOMEM; + } + err = register_netdev(modem_spi_dev->ndev); + if (err) { + dev_err(modem_spi_dev->dev, + "failed to register modem net device: error %d\n", err); + free_netdev(modem_spi_dev->ndev); + return -ENODEV; + } + dev_dbg(modem_spi_dev->dev, "registered modem net device\n"); + + net_iface_priv = (struct modem_spi_net_dev *)netdev_priv(nw_device); + net_iface_priv->modem_spi_dev = modem_spi_dev; + net_iface_priv->iface_num = 0; + return err; +} +EXPORT_SYMBOL_GPL(modem_net_init); + +int modem_net_stop(struct net_device *dev) +{ + netif_stop_queue(dev); + return 0; +} + +int modem_net_restart(struct net_device *dev) +{ + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + return 0; +} +EXPORT_SYMBOL_GPL(modem_net_restart); + +int modem_net_start(struct net_device *dev) +{ + struct modem_spi_net_dev *net_iface_priv = + (struct modem_spi_net_dev *)netdev_priv(dev); + struct modem_spi_dev *modem_spi_dev = net_iface_priv->modem_spi_dev; + + if (!netif_carrier_ok(dev)) + netif_carrier_on(dev); + netif_start_queue(dev); + modem_spi_dev->netdev_flag_up = 1; + return 0; +} +EXPORT_SYMBOL_GPL(modem_net_start); + +int modem_net_suspend(struct net_device *dev) +{ + if (netif_running(dev)) { + netif_stop_queue(dev); + netif_carrier_off(dev); + } + netif_device_detach(dev); + return 0; +} +EXPORT_SYMBOL_GPL(modem_net_suspend); + +int modem_net_resume(struct net_device *dev) +{ + netif_device_attach(dev); + if (netif_running(dev)) { + netif_carrier_on(dev); + netif_wake_queue(dev); + } + return 0; +} +EXPORT_SYMBOL_GPL(modem_net_resume); + +void modem_net_exit(struct modem_spi_dev *modem_spi_dev) +{ + if (modem_spi_dev && modem_spi_dev->ndev) { + unregister_netdev(modem_spi_dev->ndev); + modem_spi_dev->ndev = NULL; + dev_dbg(modem_spi_dev->dev, "removed modem net device\n"); + } +} +EXPORT_SYMBOL_GPL(modem_net_exit); + +MODULE_AUTHOR("Chris Blair "); +MODULE_DESCRIPTION("M6718 modem IPC net device interface"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/modem/m6718_spi/modem_net.h b/include/linux/modem/m6718_spi/modem_net.h new file mode 100644 index 00000000000..521103bf006 --- /dev/null +++ b/include/linux/modem/m6718_spi/modem_net.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Chris Blair for ST-Ericsson + * based on shrm_net.h + * + * License terms: GNU General Public License (GPL) version 2 + * + * Modem IPC net device interface header. + */ +#ifndef _MODEM_NET_H_ +#define _MODEM_NET_H_ + +#include + +#define MODEM_HLEN (1) +#define PHONET_ALEN (1) + +#define PN_PIPE (0xD9) +#define PN_DEV_HOST (0x00) +#define PN_LINK_ADDR (0x26) +#define PN_TX_QUEUE_LEN (3) + +#define RESOURCE_ID_INDEX (3) +#define SRC_OBJ_INDEX (7) +#define MSG_ID_INDEX (9) +#define PIPE_HDL_INDEX (10) +#define NETLINK_MODEM (20) + +/** + * struct modem_spi_net_dev - modem net interface device information + * @modem_spi_dev: pointer to the modem spi device information structure + * @iface_num: flag used to indicate the up/down of netdev + */ +struct modem_spi_net_dev { + struct modem_spi_dev *modem_spi_dev; + unsigned int iface_num; +}; + +int modem_net_init(struct modem_spi_dev *modem_spi_dev); +void modem_net_exit(struct modem_spi_dev *modem_spi_dev); + +int modem_net_receive(struct net_device *dev); +int modem_net_suspend(struct net_device *dev); +int modem_net_resume(struct net_device *dev); +int modem_net_start(struct net_device *dev); +int modem_net_restart(struct net_device *dev); +int modem_net_stop(struct net_device *dev); + +#endif /* _MODEM_NET_H_ */ -- cgit v1.2.3 From 5885e4a2af9b0f4e4d0fd4decfab42563d24b477 Mon Sep 17 00:00:00 2001 From: Hemant Ramdasi Date: Fri, 27 Jan 2012 13:05:34 +0530 Subject: shrm-net : remove netif_carrier_off/on calls This patch removes netif_carrier_off() call from machine suspend sequence. Also removes netif_carrier_on() call from resume sequence. This error is introduced because of the below behaviour. Important control message PNS_PEP_STATUS_IND message from phonet stack is getting dropped sometimes in the APE sleep-wakeup transition. It happens because, the Qdisc (between Phonet and SHRM) changes to "noop" when APE goes to sleep. The normal Qdisc in operation in pfifo_fast. When APE wakes up, the transition to pfast_fifo is not fast enough as it is done in a workqueue. So before the switch happens, APE receives 3 packets very quickly and must grant credits in any of the 3 packets. This does not happen. Hence modem cannot send any new packet as it has exhausted all its credits and APE cannot send credit-grant message as it will not receive any new packet. This leads to a deadlock situation for IP data transfer. ST-Ericsson ID: 405569 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I94e1d117c4c191e27a08f6a558b26c442a750816 Signed-off-by: Hemant Ramdasi Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/47661 Reviewed-by: Srinidhi KASAGAR Reviewed-by: Dinesh Kumar SHARMA (STE) Reviewed-by: QABUILD Reviewed-by: Durga Prasada Rao BATHINA --- drivers/net/u8500_shrm.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/u8500_shrm.c b/drivers/net/u8500_shrm.c index 0e813bbb3cc..08597623443 100644 --- a/drivers/net/u8500_shrm.c +++ b/drivers/net/u8500_shrm.c @@ -292,23 +292,17 @@ int shrm_start_netdev(struct net_device *dev) int shrm_suspend_netdev(struct net_device *dev) { - if (netif_running(dev)) { + if (netif_running(dev)) netif_stop_queue(dev); - netif_carrier_off(dev); - } netif_device_detach(dev); - return 0; } int shrm_resume_netdev(struct net_device *dev) { netif_device_attach(dev); - if (netif_running(dev)) { - netif_carrier_on(dev); + if (netif_running(dev)) netif_wake_queue(dev); - } - return 0; } -- cgit v1.2.3