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 --- Documentation/DocBook/shrm.tmpl | 139 +++ 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 | 201 ++++ arch/arm/mach-ux500/include/mach/shrm_net.h | 44 + arch/arm/mach-ux500/include/mach/shrm_private.h | 180 +++ drivers/char/Makefile | 4 + drivers/char/shrm_char.c | 858 ++++++++++++++ drivers/misc/shrm/Kconfig | 49 + drivers/misc/shrm/Makefile | 11 + drivers/misc/shrm/modem_shrm_driver.c | 667 +++++++++++ drivers/misc/shrm/shrm_driver.c | 1439 +++++++++++++++++++++++ drivers/misc/shrm/shrm_fifo.c | 829 +++++++++++++ drivers/misc/shrm/shrm_protocol.c | 1177 ++++++++++++++++++ drivers/net/Makefile | 4 + drivers/net/u8500_shrm.c | 336 ++++++ 16 files changed, 6072 insertions(+) create mode 100644 Documentation/DocBook/shrm.tmpl create mode 100644 arch/arm/mach-ux500/include/mach/shrm.h create mode 100644 arch/arm/mach-ux500/include/mach/shrm_config.h create mode 100644 arch/arm/mach-ux500/include/mach/shrm_driver.h create mode 100644 arch/arm/mach-ux500/include/mach/shrm_net.h create mode 100644 arch/arm/mach-ux500/include/mach/shrm_private.h create mode 100644 drivers/char/shrm_char.c create mode 100644 drivers/misc/shrm/Kconfig create mode 100644 drivers/misc/shrm/Makefile create mode 100644 drivers/misc/shrm/modem_shrm_driver.c create mode 100644 drivers/misc/shrm/shrm_driver.c create mode 100644 drivers/misc/shrm/shrm_fifo.c create mode 100644 drivers/misc/shrm/shrm_protocol.c create mode 100644 drivers/net/u8500_shrm.c diff --git a/Documentation/DocBook/shrm.tmpl b/Documentation/DocBook/shrm.tmpl new file mode 100644 index 00000000000..b35e1bc4e0b --- /dev/null +++ b/Documentation/DocBook/shrm.tmpl @@ -0,0 +1,139 @@ + + + + + + Shared Memory + + + Biju + Das + +
+ biju.das@stericsson.com +
+
+
+ + Kumar + Sanghvi + +
+ kumar.sanghvi@stericsson.com +
+
+
+ + Arun + Murthy + +
+ arun.murthy@stericsson.com +
+
+
+
+ + + 2009-2010 + ST-Ericsson + + + + + Linux standard functions + + + + + + + Licence terms: GNU General Public Licence (GPL) version 2. + + +
+ + + + Introduction + + This Documentation describes the ST-Ericsson's adaptation on protocol used for CMT/APE communication when SHaRedMemory is used as IPC link. + + + + + Design + + The APE consists Cortex A9 dual core SMP, a multimedia DSP and PRCMU. Modem consists of 2 Cortex R4 ARM processor. + The exchange of messages between CMT(Cellular Mobile Terminal) and APE includes copying the data to a shared area DDR. This region is accessible by both CMT and APE. The design includes 2 channels common and audio. Common channel is used for exchanging ISI, RPC and SECURITY messages. Audio channel is used for exchanging AUDIO messages. Each channel consists of 2 FIFO. One FIFO for sending message from CMT to APE and other from APE to CMT. Each of these FIFO have write and read pointer shared between APE and CMT. Writer pointer is updated on copying the message to FIFO and reader will read the messages from the read pointer upto the writer pointer. Writer and reader notifications are used to notify the completion of read/write operation(seperate for APE and CMT). Driver includes 4 queues. Once the messages are sent from CMT to APE it resides in the FIFO and then copied to one of the 4 queues based on the message type(ISI, RPC, AUDIO, SECURITY) and then the net/char device interface fetches this message from the queue and copies to the user space buffer. + + + + + Concepts + + The user space application sends ISI/RPC/AUDIO/SECURITY messages. ISI is sent through the phonet to shrm driver. For achieving this there are 2 interfaces to the shrm driver. Net interface used for exchanging the ISI message and char interface for RPC, AUDIO and SECURITY messages. On receiving any of these messages from the user space application, it is copied to a memory in kernel space. From here it is then copied to respective FIFO from where the CMT reads the message. + CMT(Cellular Mobile Terminal) writes messages to the respective FIFO and thereafter to respective queue. The net/char device copies this message from the queue to the user space buffer. + + + + + Known Bugs And Assumptions + + + + None + + + Assumptions + 1. ApeShmFifo#0 is of 128kB in size. As this is used for transmission except CS audio call data. Expected message size is 1.5kB with a max of 16kB. + 2. ApeShmFifo#1 is of 4kB in size. This is used for transmission of CS audio call data. Expected message size is 24kb. + 3. CmtShmFifo#0 is of 128kB in size. As this is used for transmission except CS audio call data. Expected message size is 1.5kB with a max of 16kB. + 4. CmtShmFifo#1 is of 4kB in size. This is used for transmission of CS audio call data. Expected message size is 24kb. + The total size of the FIFO is 264 kB. + + + + + + + + + Public Functions Provided + + This Section lists the API's provided by the SHRM driver to phonet drivers. + +!Edrivers/net/u8500_shrm.c + + This Section lists the API's provided by the SHRM driver used in transmission of RPC, AUDIO and SECURITY messages. + +!Edrivers/char/shrm_char.c + + + + Private Functions + + 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 + + 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 + + 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 + + + + Other Data Structures + + This Section lists some of the Data structure used by the SHRM driver. + +!Iarch/arm/mach-ux500/include/mach/shrm.h +!Iarch/arm/mach-ux500/include/mach/shrm_driver.h +!Iarch/arm/mach-ux500/include/mach/shrm_private.h + +
diff --git a/arch/arm/mach-ux500/include/mach/shrm.h b/arch/arm/mach-ux500/include/mach/shrm.h new file mode 100644 index 00000000000..6deeeb16ba8 --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/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/arch/arm/mach-ux500/include/mach/shrm_config.h b/arch/arm/mach-ux500/include/mach/shrm_config.h new file mode 100644 index 00000000000..a82b35ef77b --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/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/arch/arm/mach-ux500/include/mach/shrm_driver.h b/arch/arm/mach-ux500/include/mach/shrm_driver.h new file mode 100644 index 00000000000..41f518238b3 --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/shrm_driver.h @@ -0,0 +1,201 @@ +/* + * 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 + +#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 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) + */ +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 new file mode 100644 index 00000000000..4e675a98f3d --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/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, unsigned char *data); +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 new file mode 100644 index 00000000000..33a0e18234b --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/shrm_private.h @@ -0,0 +1,180 @@ +/* + * 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 + * + * 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/Makefile b/drivers/char/Makefile index 0dc5d7ce486..bd155f091c4 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -49,6 +49,10 @@ obj-$(CONFIG_NSC_GPIO) += nsc_gpio.o obj-$(CONFIG_GPIO_TB0219) += tb0219.o obj-$(CONFIG_TELCLOCK) += tlclk.o +ifdef CONFIG_PHONET +obj-$(CONFIG_U8500_SHRM) += shrm_char.o +endif + obj-$(CONFIG_MWAVE) += mwave/ obj-$(CONFIG_AGP) += agp/ obj-$(CONFIG_PCMCIA) += pcmcia/ diff --git a/drivers/char/shrm_char.c b/drivers/char/shrm_char.c new file mode 100644 index 00000000000..256add7cdb8 --- /dev/null +++ b/drivers/char/shrm_char.c @@ -0,0 +1,858 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +#define NAME "IPC_ISA" +/* L2 header for common loopback device is 0xc0 and hence 0xc0+1 = 193*/ +#define MAX_L2_HEADERS 193 + +#define SIZE_OF_FIFO (512*1024) + +static u8 message_fifo[ISA_DEVICES][SIZE_OF_FIFO]; + +static u8 wr_rpc_msg[10*1024]; +static u8 wr_sec_msg[10*1024]; +static u8 wr_audio_msg[10*1024]; + +struct map_device { + u8 l2_header; + u8 idx; + char *name; +}; + +static struct map_device map_dev[] = { + {ISI_MESSAGING, 0, "isi"}, + {RPC_MESSAGING, 1, "rpc"}, + {AUDIO_MESSAGING, 2, "modemaudio"}, + {SECURITY_MESSAGING, 3, "sec"}, + {COMMON_LOOPBACK_MESSAGING, 4, "common_loopback"}, + {AUDIO_LOOPBACK_MESSAGING, 5, "audio_loopback"}, +}; + +/* + * 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); + +/** + * shrm_get_cdev_index() - return the index mapped to l2 header + * @l2_header: L2 header + * + * struct map_device maps the index(count) with the device L2 header. + * This function returns the index for the provided L2 header in case + * of success else -ve value. + */ +int shrm_get_cdev_index(u8 l2_header) +{ + u8 cnt; + for (cnt = 0; cnt < ISA_DEVICES; cnt++) { + if (map_dev[cnt].l2_header == l2_header) + return map_dev[cnt].idx; + } + return -EINVAL; +} + +/** + * shrm_get_cdev_l2header() - return l2_header mapped to the index + * @idx: index + * + * struct map_device maps the index(count) with the device L2 header. + * This function returns the L2 header for the given index in case + * of success else -ve value. + */ +int shrm_get_cdev_l2header(u8 idx) +{ + u8 cnt; + for (cnt = 0; cnt < ISA_DEVICES; cnt++) { + if (map_dev[cnt].idx == idx) + return map_dev[cnt].l2_header; + } + return -EINVAL; +} + +void shrm_char_reset_queues(struct shrm_dev *shrm) +{ + struct isadev_context *isadev; + struct isa_driver_context *isa_context; + struct queue_element *cur_msg = NULL; + struct list_head *cur_msg_ptr = NULL; + struct list_head *msg_ptr; + struct message_queue *q; + int no_dev; + + dev_info(shrm->dev, "%s: Resetting char device queues\n", __func__); + isa_context = shrm->isa_context; + for (no_dev = 0 ; no_dev < ISA_DEVICES ; no_dev++) { + isadev = &isa_context->isadev[no_dev]; + q = &isadev->dl_queue; + + /* empty out the msg queue */ + list_for_each_safe(cur_msg_ptr, msg_ptr, &q->msg_list) { + cur_msg = list_entry(cur_msg_ptr, + struct queue_element, entry); + list_del(cur_msg_ptr); + kfree(cur_msg); + } + + /* reset the msg queue pointers */ + q->size = SIZE_OF_FIFO; + q->readptr = 0; + q->writeptr = 0; + q->no = 0; + + /* wake up the blocking read/select */ + atomic_set(&q->q_rp, 1); + wake_up_interruptible(&q->wq_readable); + } +} + +/** + * 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, + * 4-common_loopback, 5-audio_loopback. + * @shrm: pointer to the shrm device information structure + * + * 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. + */ +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; +} + +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 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_ATOMIC); + if (new_msg == NULL) { + dev_err(shrm->dev, "unable to allocate memory\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 -EFAULT; + } + break; + } + spin_unlock_bh(&q->update_lock); + + dev_dbg(shrm->dev, "%s OUT\n", __func__); + return new_msg->size; +} + +/** + * isa_select() - shrm char interface driver 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); + u8 idx = shrm_get_cdev_index(m); + + dev_dbg(shrm->dev, "%s IN\n", __func__); + + if (shrm->msr_flag) + return -ENODEV; + + if (isadev->device_id != idx) + 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 + * + * 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) +{ + u32 size = 0; + int ret; + char *psrc; + struct isadev_context *isadev = (struct isadev_context *) + filp->private_data; + struct shrm_dev *shrm = isadev->dl_queue.shrm; + struct message_queue *q; + u32 msgsize; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + + if (len <= 0) + return -EFAULT; + + q = &isadev->dl_queue; + + if (shrm->msr_flag) { + atomic_set(&q->q_rp, 0); + return -ENODEV; + } + + spin_lock_bh(&q->update_lock); + if (list_empty(&q->msg_list)) { + spin_unlock_bh(&q->update_lock); + dev_dbg(shrm->dev, "Waiting for Data\n"); + if (wait_event_interruptible(q->wq_readable, + atomic_read(&q->q_rp) == 1)) + return -ERESTARTSYS; + } else + spin_unlock_bh(&q->update_lock); + + if (shrm->msr_flag) { + atomic_set(&q->q_rp, 0); + return -ENODEV; + } + + msgsize = get_size_of_new_msg(q); + + if (len < msgsize) + return -EINVAL; + + 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, + "Remove 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 shrm char device + * @filp: file descriptor + * @buf: user buffer pointer + * @len: size of requested data transfer + * @ppos: not used + * + * 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. + */ +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; + void *addr = 0; + int err, l2_header; + int ret = 0; + + dev_dbg(shrm->dev, "%s IN\n", __func__); + + if (len <= 0 || buf == NULL) + return -EFAULT; + q = &isadev->dl_queue; + l2_header = shrm_get_cdev_l2header(isadev->device_id); + if (l2_header < 0) { + dev_err(shrm->dev, "failed to get L2 header\n"); + return l2_header; + } + + switch (l2_header) { + case RPC_MESSAGING: + dev_dbg(shrm->dev, "RPC\n"); + addr = (void *)wr_rpc_msg; + break; + case AUDIO_MESSAGING: + dev_dbg(shrm->dev, "Audio\n"); + addr = (void *)wr_audio_msg; + break; + case SECURITY_MESSAGING: + dev_dbg(shrm->dev, "Security\n"); + addr = (void *)wr_sec_msg; + break; + case COMMON_LOOPBACK_MESSAGING: + dev_dbg(shrm->dev, "Common loopback\n"); + addr = isadev->addr; + break; + case AUDIO_LOOPBACK_MESSAGING: + dev_dbg(shrm->dev, "Audio loopback\n"); + addr = isadev->addr; + 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 ((l2_header == AUDIO_MESSAGING) || + (l2_header == AUDIO_LOOPBACK_MESSAGING)) { + 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); + + isadev = (struct isadev_context *)filp->private_data; + + if (isadev->device_id != m) + return -EINVAL; + + 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 = -EFAULT; + 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 %d\n", __func__, m); + + 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; + int idx; + + mutex_lock(&isa_lock); + m = iminor(filp->f_path.dentry->d_inode); + idx = shrm_get_cdev_index(m); + if (idx < 0) { + dev_err(shrm->dev, "failed to get index\n"); + return idx; + } + dev_dbg(shrm->dev, "isa_close %d", m); + + if (atomic_dec_and_test(&isa_context->is_open[idx])) { + atomic_inc(&isa_context->is_open[idx]); + dev_err(shrm->dev, "Device not opened yet\n"); + mutex_unlock(&isa_lock); + return -ENODEV; + } + atomic_set(&isa_context->is_open[idx], 1); + + switch (m) { + case RPC_MESSAGING: + dev_info(shrm->dev, "Close RPC_MESSAGING Device\n"); + break; + case AUDIO_MESSAGING: + dev_info(shrm->dev, "Close AUDIO_MESSAGING Device\n"); + break; + case SECURITY_MESSAGING: + dev_info(shrm->dev, "CLose SECURITY_MESSAGING Device\n"); + break; + case COMMON_LOOPBACK_MESSAGING: + kfree(isadev->addr); + dev_info(shrm->dev, "Close COMMON_LOOPBACK_MESSAGING Device\n"); + break; + case AUDIO_LOOPBACK_MESSAGING: + kfree(isadev->addr); + dev_info(shrm->dev, "Close AUDIO_LOOPBACK_MESSAGING Device\n"); + break; + default: + dev_info(shrm->dev, "No such device present\n"); + mutex_unlock(&isa_lock); + return -ENODEV; + }; + 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; + int idx; + 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 != RPC_MESSAGING) && + (m != AUDIO_LOOPBACK_MESSAGING) && + (m != COMMON_LOOPBACK_MESSAGING) && + (m != AUDIO_MESSAGING) && + (m != SECURITY_MESSAGING)) { + dev_err(shrm->dev, "No such device present\n"); + mutex_unlock(&isa_lock); + return -ENODEV; + } + idx = shrm_get_cdev_index(m); + if (idx < 0) { + dev_err(shrm->dev, "failed to get index\n"); + return idx; + } + if (!atomic_dec_and_test(&isa_context->is_open[idx])) { + atomic_inc(&isa_context->is_open[idx]); + dev_err(shrm->dev, "Device already opened\n"); + mutex_unlock(&isa_lock); + return -EBUSY; + } + isadev = &isa_context->isadev[idx]; + if (filp != NULL) + filp->private_data = isadev; + + switch (m) { + case RPC_MESSAGING: + dev_info(shrm->dev, "Open RPC_MESSAGING Device\n"); + break; + case AUDIO_MESSAGING: + dev_info(shrm->dev, "Open AUDIO_MESSAGING Device\n"); + break; + case SECURITY_MESSAGING: + dev_info(shrm->dev, "Open SECURITY_MESSAGING Device\n"); + break; + case COMMON_LOOPBACK_MESSAGING: + isadev->addr = kzalloc(10 * 1024, GFP_KERNEL); + if (!isadev->addr) { + mutex_unlock(&isa_lock); + return -ENOMEM; + } + dev_info(shrm->dev, "Open COMMON_LOOPBACK_MESSAGING Device\n"); + break; + case AUDIO_LOOPBACK_MESSAGING: + isadev->addr = kzalloc(10 * 1024, GFP_KERNEL); + if (!isadev->addr) { + mutex_unlock(&isa_lock); + return -ENOMEM; + } + dev_info(shrm->dev, "Open AUDIO_LOOPBACK_MESSAGING Device\n"); + break; + }; + + 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 + * @shrm: pointer to the shrm device information structure + * + * 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); + if (isa_context == NULL) { + dev_err(shrm->dev, "Failed to alloc memory\n"); + return -ENOMEM; + } + shrm->isa_context = isa_context; + if (major) { + dev_id = MKDEV(major, MAX_L2_HEADERS); + retval = register_chrdev_region(dev_id, ISA_DEVICES, NAME); + } else { + /* + * L2 header of loopback device is 192(0xc0). As per the shrm + * protocol the minor id of the deivce is mapped to the + * L2 header. + */ + retval = alloc_chrdev_region(&dev_id, 0, MAX_L2_HEADERS, 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, MAX_L2_HEADERS); + if (retval) { + dev_err(shrm->dev, "Failed to add char device\n"); + return retval; + } + /* create class and device */ + isa_context->shm_class = class_create(THIS_MODULE, NAME); + if (IS_ERR(isa_context->shm_class)) { + dev_err(shrm->dev, "Error creating shrm class\n"); + cdev_del(&isa_context->cdev); + retval = PTR_ERR(isa_context->shm_class); + kfree(isa_context); + return retval; + } + + for (no_dev = 0; no_dev < ISA_DEVICES; no_dev++) { + atomic_set(&isa_context->is_open[no_dev], 1); + device_create(isa_context->shm_class, NULL, + MKDEV(MAJOR(dev_id), + map_dev[no_dev].l2_header), NULL, + map_dev[no_dev].name); + } + + 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_dbg(shrm->dev, " SHM 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++) { + device_destroy(isa_context->shm_class, + MKDEV(MAJOR(dev_id), + map_dev[no_dev].l2_header)); + isadev = &isa_context->isadev[no_dev]; + delete_queue(&isadev->dl_queue); + kfree(isadev); + } + class_destroy(isa_context->shm_class); + cdev_del(&isa_context->cdev); + unregister_chrdev_region(dev_id, ISA_DEVICES); + kfree(isa_context); + dev_dbg(shrm->dev, " SHM Char Driver removed\n"); +} diff --git a/drivers/misc/shrm/Kconfig b/drivers/misc/shrm/Kconfig new file mode 100644 index 00000000000..fffee1c703e --- /dev/null +++ b/drivers/misc/shrm/Kconfig @@ -0,0 +1,49 @@ +# +# 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_ED_V1_VERSION + depends on U8500_SHRM + bool "SHRM ED / V1 " + help + Modem Images with ED/V1 updates + + 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 new file mode 100644 index 00000000000..8115c24920b --- /dev/null +++ b/drivers/misc/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/misc/shrm/modem_shrm_driver.c b/drivers/misc/shrm/modem_shrm_driver.c new file mode 100644 index 00000000000..d94c007098b --- /dev/null +++ b/drivers/misc/shrm/modem_shrm_driver.c @@ -0,0 +1,667 @@ +/* + * 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 + +#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 +static u8 ph_recv_buf[MAX_RCV_LEN]; + +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, ph_recv_buf); + 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; + /* 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 new file mode 100644 index 00000000000..6277794608a --- /dev/null +++ b/drivers/misc/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 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 new file mode 100644 index 00000000000..ecf4b7e4466 --- /dev/null +++ b/drivers/misc/shrm/shrm_fifo.c @@ -0,0 +1,829 @@ +/* + * 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; + + /* 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; + } + spin_lock(&fifo->fifo_update_lock); + fifo->writer_local_wptr += msg_length; + fifo->availablesize -= msg_length; + spin_unlock(&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)); + /* 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 */ + 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 + * 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++; + spin_lock_bh(&fifo->fifo_update_lock); + /* 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); + spin_unlock_bh(&fifo->fifo_update_lock); + } else if (size == 2) { + /* Add L1 header and L2 header */ + *msg = l1_header; + msg++; + *msg = l2_header; + msg++; + + spin_lock_bh(&fifo->fifo_update_lock); + /* 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); + spin_unlock_bh(&fifo->fifo_update_lock); + } 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); + + spin_lock_bh(&fifo->fifo_update_lock); + + /* 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); + spin_unlock_bh(&fifo->fifo_update_lock); + } + + } + } 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); + + spin_lock_bh(&fifo->fifo_update_lock); + /* 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; + + 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 */ + spin_lock(&fifo->fifo_update_lock); + fifo->availablesize += free_space; + fifo->writer_local_rptr = fifo->shared_rptr; + spin_unlock(&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; + 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 */ + spin_lock(&fifo->fifo_update_lock); + fifo->availablesize += free_space; + fifo->writer_local_rptr = fifo->shared_rptr; + spin_unlock(&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; + /* 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; +} + +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; + /* 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; +} + +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; + + *writer_local_rptr = fifo->writer_local_rptr; + *writer_local_wptr = fifo->writer_local_wptr; + *shared_wptr = fifo->shared_wptr; +} + +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 new file mode 100644 index 00000000000..dd52f9e20ea --- /dev/null +++ b/drivers/misc/shrm/shrm_protocol.c @@ -0,0 +1,1177 @@ +/* + * 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 + +#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; +static char is_earlydrop; +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) + prcmu_ac_sleep_req(); + 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); + prcmu_ac_wake_req(); + 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); +} + +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); + + /* reset the state for ac-wake LOW logic */ + atomic_set(&ac_sleep_disable_count, 0); + + /* 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); + + /* reset counter for ac-wake/ac-sleep logic */ + atomic_set(&ac_sleep_disable_count, 0); + + return err; +} + +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(); +#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 + 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); + prcmu_ac_wake_req(); + 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); + prcmu_ac_wake_req(); + 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); + + is_earlydrop = cpu_is_u8500ed(); + if (is_earlydrop != 0x01) { + 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); + + /* 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); +} + +int get_ca_wake_req_state(void) +{ + return ((atomic_read(&ac_sleep_disable_count) > 0) || + prcmu_is_ac_wake_requested()); +} + +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/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 eb4fc74ab6d0434aef59790badb85834f320ba74 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 20 Oct 2011 10:22:50 +0200 Subject: modem: add mloader and trace features Signed-off-by: Philippe Langlais --- arch/arm/mach-ux500/include/mach/mloader-dbx500.h | 48 ++++ arch/arm/mach-ux500/mloader-db8500.c | 82 +++++++ drivers/misc/db8500-modem-trace.c | 273 ++++++++++++++++++++++ drivers/misc/dbx500-mloader.c | 269 +++++++++++++++++++++ include/linux/db8500-modem-trace.h | 24 ++ include/linux/mloader.h | 25 ++ 6 files changed, 721 insertions(+) create mode 100644 arch/arm/mach-ux500/include/mach/mloader-dbx500.h create mode 100644 arch/arm/mach-ux500/mloader-db8500.c create mode 100644 drivers/misc/db8500-modem-trace.c create mode 100644 drivers/misc/dbx500-mloader.c create mode 100644 include/linux/db8500-modem-trace.h create mode 100644 include/linux/mloader.h diff --git a/arch/arm/mach-ux500/include/mach/mloader-dbx500.h b/arch/arm/mach-ux500/include/mach/mloader-dbx500.h new file mode 100644 index 00000000000..68fa55a3f53 --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/mloader-dbx500.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Ludovic Barre for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _MLOADER_UX500_H_ +#define _MLOADER_UX500_H_ + +/** + * struct dbx500_ml_area - data structure for modem memory areas description + * @name: name of the area + * @start: start address of the area + * @size: size of the area + */ +struct dbx500_ml_area { + const char *name; + u32 start; + u32 size; +}; + +/** + * struct dbx500_ml_fw - data stucture for modem firmwares description + * @name: firmware name + * @area: area where firmware is uploaded + * @offset: offset in the area where firmware is uploaded + */ +struct dbx500_ml_fw { + const char *name; + struct dbx500_ml_area *area; + u32 offset; +}; + +/** + * struct dbx500_mloader_pdata - data structure for platform specific data + * @fws: pointer on firmwares table + * @nr_fws: number of firmwares + * @areas: pointer on areas table + * @nr_areas: number of areas + */ +struct dbx500_mloader_pdata { + struct dbx500_ml_fw *fws; + int nr_fws; + struct dbx500_ml_area *areas; + int nr_areas; +}; + +#endif /* _MLOADER_UX500_H_ */ diff --git a/arch/arm/mach-ux500/mloader-db8500.c b/arch/arm/mach-ux500/mloader-db8500.c new file mode 100644 index 00000000000..6171a9db82f --- /dev/null +++ b/arch/arm/mach-ux500/mloader-db8500.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2011 ST-Ericsson + * + * Author: Maxime Coquelin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + */ +#include +#include + +#include + +static struct dbx500_ml_area modem_areas[] = { + { .name = "modem_trace", .start = 0x6000000, .size = 0xf00000 }, + { .name = "modem_shared", .start = 0x6f00000, .size = 0x100000 }, + { .name = "modem_priv", .start = 0x7000000, .size = 0x1000000 }, +}; + +static struct dbx500_ml_fw modem_fws[] = { + { .name = "MODEM", .area = &modem_areas[0], .offset = 0x0 }, + { .name = "IPL", .area = &modem_areas[1], .offset = 0x00 }, +}; + +static struct dbx500_mloader_pdata mloader_fw_data = { + .fws = modem_fws, + .nr_fws = ARRAY_SIZE(modem_fws), + .areas = modem_areas, + .nr_areas = ARRAY_SIZE(modem_areas), +}; + +struct platform_device mloader_fw_device = { + .name = "dbx500_mloader_fw", + .id = -1, + .dev = { + .platform_data = &mloader_fw_data, + }, + .num_resources = 0, +}; + +/* Default areas can be overloaded in cmdline */ +static int __init early_modem_priv(char *p) +{ + struct dbx500_ml_area *area = &modem_areas[2]; + + area->size = memparse(p, &p); + + if (*p == '@') + area->start = memparse(p + 1, &p); + + return 0; +} +early_param("mem_modem", early_modem_priv); + +static int __init early_modem_shared(char *p) +{ + struct dbx500_ml_area *area = &modem_areas[1]; + + area->size = memparse(p, &p); + + if (*p == '@') + area->start = memparse(p + 1, &p); + + return 0; +} +early_param("mem_mshared", early_modem_shared); + +static int __init early_modem_trace(char *p) +{ + struct dbx500_ml_area *area = &modem_areas[0]; + + area->size = memparse(p, &p); + + if (*p == '@') + area->start = memparse(p + 1, &p); + + return 0; +} +early_param("mem_mtrace", early_modem_trace); + diff --git a/drivers/misc/db8500-modem-trace.c b/drivers/misc/db8500-modem-trace.c new file mode 100644 index 00000000000..0d739fb4694 --- /dev/null +++ b/drivers/misc/db8500-modem-trace.c @@ -0,0 +1,273 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Authors: Michel JAOUEN + * Maxime COQUELIN + * 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 + +#define DEVICE_NAME "db8500-modem-trace" + +/* activation of this flag triggers an initialization of 2 buffers + * 4kbytes , id 0xdeadbeef + * and 16Kbytes id 0xfadafada + * we assume that platform provides minimum 20Kbytes. */ + +struct trace { + u32 start; + u32 end; + u32 mdm_base; + u32 ape_base; + void __iomem *area; + /* this spinlock to forbid concurrent access on the same trace buffer */ + spinlock_t lock; + struct device *dev; + struct miscdevice misc_dev; +}; + +struct trace_modem { + u32 phys_addr; + u8 filler; +}; + +static struct trace *trace_priv; + + +/* all this definition are linked to modem interface */ +#define MODEM_MARKER 0x88 +/* free marker is also written on filler */ +#define FREE_MARKER 0xa5 +#define FREE_MARKER_2 0xa5a5 +#define READ_MARKER 0x5a + +struct buffer_header { + u8 pattern; + u8 filler; + u16 head_size; +}; + + +static int trace_read(unsigned long arg) +{ + struct modem_trace_req req; + struct buffer_header *pt; + char tmp_char; + + if (copy_from_user(&req, (struct modem_trace_req *)arg, + sizeof(struct modem_trace_req))) + return -EFAULT; + + /* compute Modem physical address to APE physical address range */ + if (req.phys_addr < trace_priv->mdm_base) { + dev_err(trace_priv->dev, "MODEM ADDR uncorrect\n"); + return -EINVAL; + } + req.phys_addr += trace_priv->ape_base - trace_priv->mdm_base; + + /* check request is in the range and aligned */ + if ((req.phys_addr % 4 != 0) + || (req.phys_addr < trace_priv->start) + || (req.phys_addr + req.size) >= trace_priv->end) { + dev_err(trace_priv->dev, "req out of range %x %x\n", + req.phys_addr, req.size); + return -EINVAL; + } + + /* perform access to memory area */ + pt = (struct buffer_header *)((u32)trace_priv->area + + req.phys_addr - trace_priv->start); + + /* in case of several request coming on same trace buffer take a + * spinlock */ + spin_lock(&trace_priv->lock); + if (pt->pattern != MODEM_MARKER) { + /* pattern and size not matching */ + dev_err(trace_priv->dev, "req not matching filler %x/%x \ + or/and pattern %x\n", req.filler, pt->filler, + pt->pattern); + spin_unlock(&trace_priv->lock); + return -EINVAL; + } + /* mark pattern as read and unlock spin */ + pt->pattern = READ_MARKER; + spin_unlock(&trace_priv->lock); + + req.size -= copy_to_user(req.buff, pt, req.size); + + pt->pattern = FREE_MARKER; + pt->filler = FREE_MARKER; + tmp_char = MODEM_MARKER; + + /* Update marker for trace tool */ + if (copy_to_user(req.buff, &tmp_char, 1)) + return -EFAULT; + + /* Update effective written size */ + if (copy_to_user((struct modem_trace_req *)arg, &req, + sizeof(struct modem_trace_req))) + return -EFAULT; + + return 0; +} + +static int trace_mmapdump(struct file *file, struct vm_area_struct *vma) +{ + unsigned long vma_start = vma->vm_start; + + if (vma->vm_flags & VM_WRITE) + return -EPERM; + + if ((vma->vm_end - vma->vm_start) < + (trace_priv->end - trace_priv->start)) + return -EINVAL; + if (remap_pfn_range(vma, + vma_start, + trace_priv->start >> PAGE_SHIFT, + trace_priv->end - trace_priv->start, + vma->vm_page_prot)) + return -EAGAIN; + return 0; +} + +static long trace_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + long ret = 0; + void __user *argp = (void __user *)arg; + unsigned long size = trace_priv->end-trace_priv->start; + + switch (cmd) { + case TM_GET_DUMPINFO: + ret = put_user(size, (unsigned long *)argp); + break; + case TM_TRACE_REQ: + ret = trace_read(arg); + break; + + default: + ret = -EPERM; + break; + } + return ret; +} + +static const struct file_operations trace_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = trace_ioctl, + .mmap = trace_mmapdump +}; + +static int trace_probe(struct platform_device *pdev) +{ + int rv = 0; + struct db8500_trace_platform_data *pdata = pdev->dev.platform_data; + /* retrieve area descriptor from platform device ressource */ + struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if ((mem->start == 0) && (mem->end == 0)) { + rv = -EINVAL; + goto out; + } + + if ((pdata->ape_base == 0) || (pdata->modem_base == 0)) { + rv = -EINVAL; + goto out; + } + + trace_priv = kzalloc(sizeof(*trace_priv), GFP_ATOMIC); + if (!trace_priv) { + rv = -ENOMEM; + goto out; + } + + trace_priv->dev = &pdev->dev; + trace_priv->misc_dev.minor = MISC_DYNAMIC_MINOR; + trace_priv->misc_dev.name = DEVICE_NAME; + trace_priv->misc_dev.fops = &trace_fops; + trace_priv->area = (void __iomem *)ioremap_nocache(mem->start, + mem->end - mem->start); + if (!trace_priv->area) { + rv = -ENOMEM; + goto outfree; + } + + trace_priv->start = mem->start; + trace_priv->end = mem->end; + + trace_priv->mdm_base = pdata->modem_base; + trace_priv->ape_base = pdata->ape_base; + + /* spin allowing smp access for reading/writing trace buffer header */ + spin_lock_init(&trace_priv->lock); + + rv = misc_register(&trace_priv->misc_dev); + if (rv) { + dev_err(&pdev->dev, "can't misc_register\n"); + goto outunmap; + } + + return rv; + +outunmap: + iounmap(trace_priv->area); +outfree: + kfree(trace_priv); +out: + return rv; + +} + +static int trace_remove(struct platform_device *pdev) +{ + int rv = 0; + + if (trace_priv) { + rv = misc_deregister(&trace_priv->misc_dev); + iounmap(trace_priv->area); + kfree(trace_priv); + } + + return rv; +} + +static struct platform_driver trace_driver = { + .probe = trace_probe, + .remove = trace_remove, + .driver = { + .name = "db8500-modem-trace", + .owner = THIS_MODULE, + }, +}; + +static int trace_init(void) +{ + platform_driver_register(&trace_driver); + return 0; +} +static void trace_exit(void) +{ + platform_driver_unregister(&trace_driver); +} +module_init(trace_init); +module_exit(trace_exit); + +MODULE_AUTHOR("ST-Ericsson"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/dbx500-mloader.c b/drivers/misc/dbx500-mloader.c new file mode 100644 index 00000000000..c3ec8b67983 --- /dev/null +++ b/drivers/misc/dbx500-mloader.c @@ -0,0 +1,269 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Ludovic Barre 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 + +#define DEVICE_NAME "dbx500_mloader_fw" + +struct mloader_priv { + struct platform_device *pdev; + struct dbx500_mloader_pdata *pdata; + struct miscdevice misc_dev; + u32 aeras_size; +}; + +static struct mloader_priv *mloader_priv; + +static int mloader_fw_send(struct dbx500_ml_fw *fw_info) +{ + const struct firmware *fw; + unsigned long size; + unsigned long phys_start; + void *fw_data; + void *vaddr; + void __iomem *ioaddr; + int ret; + + ret = request_firmware(&fw, fw_info->name, &mloader_priv->pdev->dev); + if (ret) { + dev_err(&mloader_priv->pdev->dev, "request firmware failed\n"); + goto out; + } + + if (fw->size > (fw_info->area->size - fw_info->offset)) { + dev_err(&mloader_priv->pdev->dev, + "fw:%s is too big for:%s\n", + fw_info->name, fw_info->area->name); + ret = -EINVAL; + goto err_fw; + } + + size = PAGE_ALIGN(fw->size); + phys_start = fw_info->area->start + fw_info->offset; + phys_start &= PAGE_MASK; + ioaddr = ioremap(phys_start, size); + if (!ioaddr) { + dev_err(&mloader_priv->pdev->dev, + "failed remap memory region.\n"); + ret = -EINVAL; + goto err_fw; + } + + vaddr = ioaddr + (fw_info->offset & ~PAGE_MASK); + fw_data = (void *)fw->data; + memcpy_toio(vaddr, fw_data, fw->size); + iounmap(ioaddr); + +err_fw: + release_firmware(fw); +out: + return ret; +} + +static int mloader_fw_upload(void) +{ + int i, ret; + struct dbx500_mloader_pdata *pdata = mloader_priv->pdata; + + for (i = 0; i < pdata->nr_fws; i++) { + ret = mloader_fw_send(&pdata->fws[i]); + if (ret) + goto err; + } + + return 0; +err: + dev_err(&mloader_priv->pdev->dev, + "Failed to upload %s firmware", pdata->fws[i].name); + return ret; +} + +static int mloader_fw_mmapdump(struct file *file, struct vm_area_struct *vma) +{ + int i; + unsigned long dump_size = 0; + unsigned long vma_start = vma->vm_start; + + if (vma->vm_flags & VM_WRITE) + return -EPERM; + + for (i = 0 ; i < mloader_priv->pdata->nr_areas ; i++) + dump_size += mloader_priv->pdata->areas[i].size; + + if ((vma->vm_end - vma->vm_start) < dump_size) + return -EINVAL; + + for (i = 0 ; i < mloader_priv->pdata->nr_areas ; i++) { + if (remap_pfn_range(vma, + vma_start, + mloader_priv->pdata->areas[i].start >> PAGE_SHIFT, + mloader_priv->pdata->areas[i].size, + vma->vm_page_prot)) + return -EAGAIN; + vma_start += mloader_priv->pdata->areas[i].size; + } + return 0; +} + +static void mloader_fw_dumpinfo(struct dump_image *images) +{ + u32 offset = 0; + int i; + + for (i = 0 ; i < mloader_priv->pdata->nr_areas ; i++) { + strncpy(images[i].name, + mloader_priv->pdata->areas[i].name, MAX_NAME); + images[i].name[MAX_NAME-1] = 0; + images[i].offset = offset; + images[i].size = mloader_priv->pdata->areas[i].size; + offset += mloader_priv->pdata->areas[i].size; + } +} + +static long mloader_fw_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + long ret = 0; + void __user *argp = (void __user *)arg; + + switch (cmd) { + case ML_UPLOAD: + ret = mloader_fw_upload(); + break; + case ML_GET_NBIMAGES: + ret = put_user(mloader_priv->pdata->nr_areas, + (unsigned long __user *)argp); + break; + case ML_GET_DUMPINFO: { + struct dump_image *dump_images; + dump_images = kzalloc(mloader_priv->pdata->nr_areas + * sizeof(struct dump_image), GFP_ATOMIC); + mloader_fw_dumpinfo(dump_images); + ret = copy_to_user(argp, (void *) dump_images, + mloader_priv->pdata->nr_areas + * sizeof(struct dump_image)) ? -EFAULT : 0; + kfree(dump_images); + break; + } + default: + ret = -EPERM; + break; + } + + return ret; +} + +static const struct file_operations modem_fw_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = mloader_fw_ioctl, + .mmap = mloader_fw_mmapdump, +}; + +static int __devinit mloader_fw_probe(struct platform_device *pdev) +{ + int ret = 0; + int i; + + mloader_priv = kzalloc(sizeof(*mloader_priv), GFP_ATOMIC); + if (!mloader_priv) { + ret = -ENOMEM; + goto out; + } + + mloader_priv->pdev = pdev; + mloader_priv->pdata = pdev->dev.platform_data; + + mloader_priv->misc_dev.minor = MISC_DYNAMIC_MINOR; + mloader_priv->misc_dev.name = DEVICE_NAME; + mloader_priv->misc_dev.fops = &modem_fw_fops; + ret = misc_register(&mloader_priv->misc_dev); + if (ret < 0) { + dev_err(&pdev->dev, "can't misc_register\n"); + goto err_free_priv; + } + + dev_info(&mloader_priv->pdev->dev, "mloader device register\n"); + + for (i = 0 ; i < mloader_priv->pdata->nr_areas ; i++) { + dev_dbg(&mloader_priv->pdev->dev, + "Area:%d (name:%s start:%x size:%x)\n", + i, mloader_priv->pdata->areas[i].name, + mloader_priv->pdata->areas[i].start, + mloader_priv->pdata->areas[i].size); + } + + for (i = 0 ; i < mloader_priv->pdata->nr_fws ; i++) { + dev_dbg(&mloader_priv->pdev->dev, + "Firmware:%d (name:%s offset:%x " + "area_name:%s area_start:%x area_size:%x)\n", + i, mloader_priv->pdata->fws[i].name, + mloader_priv->pdata->fws[i].offset, + mloader_priv->pdata->fws[i].area->name, + mloader_priv->pdata->fws[i].area->start, + mloader_priv->pdata->fws[i].area->size); + } + + return ret; + +err_free_priv: + kfree(mloader_priv); +out: + return ret; +} + +static int __devexit mloader_fw_remove(struct platform_device *pdev) +{ + int err; + + err = misc_register(&mloader_priv->misc_dev); + if (err < 0) + dev_err(&pdev->dev, "can't misc_deregister, %d\n", err); + + kfree(mloader_priv); + + return err; +} + +static struct platform_driver mloader_fw_driver = { + .driver.name = DEVICE_NAME, + .driver.owner = THIS_MODULE, + .probe = mloader_fw_probe, + .remove = __devexit_p(mloader_fw_remove), +}; + +static int __init mloader_fw_init(void) +{ + return platform_driver_register(&mloader_fw_driver); +} + +static void __exit mloader_fw_exit(void) +{ + kfree(mloader_priv); + platform_driver_unregister(&mloader_fw_driver); +} + +module_init(mloader_fw_init); +module_exit(mloader_fw_exit); +MODULE_DESCRIPTION("ST-Ericsson modem loader firmware"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Ludovic Barre "); diff --git a/include/linux/db8500-modem-trace.h b/include/linux/db8500-modem-trace.h new file mode 100644 index 00000000000..4863e1a0b03 --- /dev/null +++ b/include/linux/db8500-modem-trace.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Authors: Michel JAOUEN + * Maxime COQUELIN + * for ST-Ericsson + * License terms: GNU General Public License (GPL), version 2 + */ +/* macro for requesting a trace read */ + +struct modem_trace_req { + __u32 phys_addr; + __u8 filler; + __u8 *buff; + __u32 size; +}; + +#define TM_IO_NUMBER 0xfc +#define TM_GET_DUMPINFO _IOR(TM_IO_NUMBER, 1, unsigned long) +#define TM_TRACE_REQ _IOWR(TM_IO_NUMBER, 2, unsigned long) + +struct db8500_trace_platform_data { + unsigned long ape_base; + unsigned long modem_base; +}; diff --git a/include/linux/mloader.h b/include/linux/mloader.h new file mode 100644 index 00000000000..ceca3245856 --- /dev/null +++ b/include/linux/mloader.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Ludovic Barre for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _MLOADER_H_ +#define _MLOADER_H_ + +/* not use in ioctl-number.txt */ +#define ML_IO_NUMBER 0xFE + +#define ML_UPLOAD _IO(ML_IO_NUMBER, 1) +#define ML_GET_NBIMAGES _IOR(ML_IO_NUMBER, 2, int) +#define ML_GET_DUMPINFO _IOR(ML_IO_NUMBER, 3, struct dump_image*) + +#define MAX_NAME 16 + +struct dump_image { + char name[MAX_NAME]; + unsigned int offset; + unsigned int size; +}; + +#endif /* _MLOADER_H_ */ -- cgit v1.2.3 From 7ab0424a93187e672d46db190e62ff745bce65e1 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Wed, 6 Apr 2011 09:39:29 +0200 Subject: shrm: Fix compilation pb after 2.6.38 merge, change ioctl to unlocked_ioctl Signed-off-by: Philippe Langlais --- arch/arm/mach-ux500/include/mach/isa_ioctl.h | 51 ++++++++++++++++++++++++++++ drivers/char/shrm_char.c | 9 +++-- 2 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 arch/arm/mach-ux500/include/mach/isa_ioctl.h diff --git a/arch/arm/mach-ux500/include/mach/isa_ioctl.h b/arch/arm/mach-ux500/include/mach/isa_ioctl.h new file mode 100644 index 00000000000..b05726f8c3c --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/isa_ioctl.h @@ -0,0 +1,51 @@ +/*---------------------------------------------------------------------------*/ +/* Copyright ST Ericsson, 2009. */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2.1 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ +/* See the GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program. If not, see . */ +/*---------------------------------------------------------------------------*/ +#ifndef __MODEM_IPC_INCLUDED +#define __MODEM_IPC_INCLUDED + +#define DLP_IOCTL_MAGIC_NUMBER 'M' +#define COMMON_BUFFER_SIZE (1024*1024) + +/** +DLP Message Structure for Userland +*/ +struct t_dlp_message{ + unsigned int offset; + unsigned int size; +}; + +/** +mmap constants. +*/ +enum t_dlp_mmap_params { + MMAP_DLQUEUE, + MMAP_ULQUEUE +}; + +/** +DLP IOCTLs for Userland +*/ +#define DLP_IOC_ALLOCATE_BUFFER \ + _IOWR(DLP_IOCTL_MAGIC_NUMBER, 0, struct t_dlp_message *) +#define DLP_IOC_DEALLOCATE_BUFFER \ + _IOWR(DLP_IOCTL_MAGIC_NUMBER, 1, struct t_dlp_message *) +#define DLP_IOC_GET_MESSAGE \ + _IOWR(DLP_IOCTL_MAGIC_NUMBER, 2, struct t_dlp_message *) +#define DLP_IOC_PUT_MESSAGE \ + _IOWR(DLP_IOCTL_MAGIC_NUMBER, 3, struct t_dlp_message *) + +#endif /*__MODEM_IPC_INCLUDED*/ + diff --git a/drivers/char/shrm_char.c b/drivers/char/shrm_char.c index 256add7cdb8..2890f152d2d 100644 --- a/drivers/char/shrm_char.c +++ b/drivers/char/shrm_char.c @@ -527,13 +527,12 @@ ssize_t isa_write(struct file *filp, const char __user *buf, * 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) +static long isa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - int err = 0; + long err = 0; struct isadev_context *isadev = filp->private_data; struct shrm_dev *shrm = isadev->dl_queue.shrm; - u32 m = iminor(inode); + u32 m = iminor(filp->f_path.dentry->d_inode); isadev = (struct isadev_context *)filp->private_data; @@ -734,7 +733,7 @@ const struct file_operations isa_fops = { .owner = THIS_MODULE, .open = isa_open, .release = isa_close, - .ioctl = isa_ioctl, + .unlocked_ioctl = isa_ioctl, .mmap = isa_mmap, .read = isa_read, .write = isa_write, -- 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(-) 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 16ad59e9bbd7ae65bdc0e906402a3352842efd17 Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Mon, 28 Feb 2011 16:41:33 +0100 Subject: MISC: SHRM: Fix build warning ST-Ericsson Linux next: Not tested, ask SSM for ER ST-Ericsson ID: - ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ic7f0aecbea1555b78ead3d0dd681eadcca5a2d8e Signed-off-by: Jonas Aaberg Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/17149 --- drivers/misc/shrm/shrm_protocol.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/misc/shrm/shrm_protocol.c b/drivers/misc/shrm/shrm_protocol.c index dd52f9e20ea..6afe423d2cb 100644 --- a/drivers/misc/shrm/shrm_protocol.c +++ b/drivers/misc/shrm/shrm_protocol.c @@ -465,7 +465,7 @@ void shm_ca_wake_req_work(struct work_struct *work) writel((1<intr_base + GOP_SET_REGISTER_BASE); } - +#ifdef CONFIG_U8500_SHRM_MODEM_SILENT_RESET static int shrm_modem_reset_sequence(void) { int err; @@ -539,6 +539,7 @@ static int shrm_modem_reset_sequence(void) return err; } +#endif static void shrm_modem_reset_callback(unsigned long irq) { -- 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(-) 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 0c76d0b51beafe92267895565fdd5ca554c8765b Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Wed, 12 Oct 2011 10:45:02 +0200 Subject: shrm: sw reset: Save SW Reset Reason before reset Converts the reboot reason string received in SYSCALL_DEFINE4 in sys.c into a 2 bytes reset reason code. This 16 bit value is stored in the TCDM Memory at location: tcdm_base + 0xFF8. The string to code mapping structure has been added in file reboot_reasons.h and reboot_reasons.c. The code for translation has been placed in cpu.c. ST-Ericsson Linux next: Tested and reviewed with 2011-03-28 ST-Ericsson ID: 327863 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I5fe83b824c6dbe3f61a3d77671ce845e6f81d87b Signed-off-by: rickard evertsson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/19174 Reviewed-by: Mattias WALLIN Conflicts: arch/arm/mach-ux500/Makefile arch/arm/mach-ux500/cpu.c Conflicts: arch/arm/mach-ux500/Makefile arch/arm/mach-ux500/cpu.c arch/arm/mach-ux500/include/mach/prcmu-fw-api.h arch/arm/mach-ux500/include/mach/system.h arch/arm/mach-ux500/prcmu-db8500.c --- drivers/misc/shrm/shrm_protocol.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/misc/shrm/shrm_protocol.c b/drivers/misc/shrm/shrm_protocol.c index 6afe423d2cb..351f78ecbc2 100644 --- a/drivers/misc/shrm/shrm_protocol.c +++ b/drivers/misc/shrm/shrm_protocol.c @@ -19,6 +19,7 @@ #include #include #include +#include #define L2_HEADER_ISI 0x0 #define L2_HEADER_RPC 0x1 @@ -557,8 +558,9 @@ static void shrm_modem_reset_callback(unsigned long irq) } #else dev_info(shm_dev->dev, "Modem in reset loop, doing System reset\n"); + /* Call the PRCMU reset API */ - prcmu_system_reset(); + prcmu_system_reset(SW_RESET_NO_ARGUMENT); #endif } -- cgit v1.2.3 From 590d975e7b48b8313a0b346dcb9f3014055e1222 Mon Sep 17 00:00:00 2001 From: Kumar Sanghvi Date: Wed, 6 Apr 2011 11:58:13 +0530 Subject: u8500:shrm: Fix null pointer dereference Fixes null pointer dereference in shrm char interface ST-Ericsson Linux next: - ST-Ericsson ID: ER332892 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ia60fa08e61e50c2e2645e22c2d7b9c5df01df7ef Signed-off-by: Kumar Sanghvi Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/20103 Reviewed-by: Hemant-vilas RAMDASI Reviewed-by: Arun MURTHY Reviewed-by: Jonas ABERG --- drivers/char/shrm_char.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/char/shrm_char.c b/drivers/char/shrm_char.c index 714d38e42f1..e2aaf60674f 100644 --- a/drivers/char/shrm_char.c +++ b/drivers/char/shrm_char.c @@ -273,6 +273,7 @@ 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; + int size = 0; dev_dbg(shrm->dev, "%s IN\n", __func__); @@ -284,12 +285,13 @@ int get_size_of_new_msg(struct message_queue *q) dev_err(shrm->dev, "no message found\n"); return -EFAULT; } + size = new_msg->size; break; } spin_unlock_bh(&q->update_lock); dev_dbg(shrm->dev, "%s OUT\n", __func__); - return new_msg->size; + return size; } /** -- cgit v1.2.3 From 3f2bd84ea8f3d966f2abf93ccb5d5427805d3f95 Mon Sep 17 00:00:00 2001 From: Kumar Sanghvi Date: Fri, 8 Apr 2011 19:05:15 +0530 Subject: u8500: shrm: Workaround for MSR prcmu driver APIs like prcmu_ac_wake_req/prcmu_ac_sleep_req currently does not support multiple clients. To make the MSR feature working, this patch puts workaround in shrm driver due to the limitations of prcmu driver. ST-Ericsson ID: ER329867 Change-Id: Ic6092f89752e05c53436ec0e07c2f2945dbd56c5 Signed-off-by: Kumar Sanghvi Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/21077 Reviewed-by: QATEST Reviewed-by: Hemant-vilas RAMDASI Reviewed-by: Sebastien RAULT Tested-by: Sebastien RAULT Reviewed-by: Srinidhi KASAGAR --- drivers/misc/shrm/shrm_protocol.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/drivers/misc/shrm/shrm_protocol.c b/drivers/misc/shrm/shrm_protocol.c index 351f78ecbc2..0831bc8803e 100644 --- a/drivers/misc/shrm/shrm_protocol.c +++ b/drivers/misc/shrm/shrm_protocol.c @@ -77,6 +77,21 @@ static void shm_ac_sleep_req_work(struct work_struct *work) mutex_unlock(&ac_state_mutex); } +static void shm_ac_wake_req_work(struct work_struct *work) +{ + mutex_lock(&ac_state_mutex); + /* make sure that we bring down the ac-wake line + * so that prcmu driver will actually write + * to the PRCM_HOSTACCESS_REQ register on the + * next prcmu_ac_wake_req call + */ + prcmu_ac_sleep_req(); + + atomic_inc(&ac_sleep_disable_count); + prcmu_ac_wake_req(); + mutex_unlock(&ac_state_mutex); +} + static u32 get_host_accessport_val(void) { u32 prcm_hostaccess; @@ -369,6 +384,13 @@ void shm_ac_read_notif_0_tasklet(unsigned long tasklet_data) /* multicast that modem is online */ nl_send_multicast_message(SHRM_NL_STATUS_MOD_ONLINE, GFP_ATOMIC); + + /* This decrement corresponds to increment + * done in MSR sequecne + */ + atomic_dec(&ac_sleep_disable_count); + queue_work(shm_dev->shm_ac_sleep_wq, + &shm_dev->shm_ac_sleep_req); } } else if (boot_state == BOOT_DONE) { @@ -504,6 +526,9 @@ static int shrm_modem_reset_sequence(void) /* reset the state for ac-wake LOW logic */ atomic_set(&ac_sleep_disable_count, 0); + queue_work(shm_dev->shm_ac_wake_wq, + &shm_dev->shm_ac_wake_req); + /* stop network queue */ shrm_stop_netdev(shm_dev->ndev); @@ -535,9 +560,6 @@ static int shrm_modem_reset_sequence(void) enable_irq(IRQ_PRCMU_CA_WAKE); enable_irq(IRQ_PRCMU_CA_SLEEP); - /* reset counter for ac-wake/ac-sleep logic */ - atomic_set(&ac_sleep_disable_count, 0); - return err; } #endif @@ -730,6 +752,7 @@ int shrm_protocol_init(struct shrm_dev *shrm, 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; -- cgit v1.2.3 From ecd3d1c2e397188b5ea04a4ebb1cc8c6f69bf3b3 Mon Sep 17 00:00:00 2001 From: Kumar Sanghvi Date: Tue, 19 Apr 2011 21:07:56 +0530 Subject: u8500: shrm: Update AC_Wake logic and one fix for MSR Updates the AC_Wake logic related to MSR. Also, corrects one NULL pointer dereference. ST-Ericsson ID: ER335373 Change-Id: Ie3f46c2a7bcdde9936a0f9eed52f8dcd0ab5a06b Signed-off-by: Kumar Sanghvi Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/21162 Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR --- drivers/char/shrm_char.c | 13 +++++++------ drivers/misc/shrm/shrm_protocol.c | 23 +++++++---------------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/drivers/char/shrm_char.c b/drivers/char/shrm_char.c index e2aaf60674f..6a6b3d7245a 100644 --- a/drivers/char/shrm_char.c +++ b/drivers/char/shrm_char.c @@ -236,25 +236,26 @@ 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; + struct list_head *msg_ptr = NULL; + struct list_head *old_msg_ptr = NULL; 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); + list_for_each_safe(old_msg_ptr, msg_ptr, &q->msg_list) { + old_msg = list_entry(old_msg_ptr, struct queue_element, entry); if (old_msg == NULL) { dev_err(shrm->dev, "no message found\n"); return -EFAULT; } + list_del(old_msg_ptr); + q->readptr = (q->readptr + old_msg->size)%q->size; + kfree(old_msg); 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; diff --git a/drivers/misc/shrm/shrm_protocol.c b/drivers/misc/shrm/shrm_protocol.c index 0831bc8803e..0598c62812f 100644 --- a/drivers/misc/shrm/shrm_protocol.c +++ b/drivers/misc/shrm/shrm_protocol.c @@ -80,14 +80,6 @@ static void shm_ac_sleep_req_work(struct work_struct *work) static void shm_ac_wake_req_work(struct work_struct *work) { mutex_lock(&ac_state_mutex); - /* make sure that we bring down the ac-wake line - * so that prcmu driver will actually write - * to the PRCM_HOSTACCESS_REQ register on the - * next prcmu_ac_wake_req call - */ - prcmu_ac_sleep_req(); - - atomic_inc(&ac_sleep_disable_count); prcmu_ac_wake_req(); mutex_unlock(&ac_state_mutex); } @@ -384,13 +376,6 @@ void shm_ac_read_notif_0_tasklet(unsigned long tasklet_data) /* multicast that modem is online */ nl_send_multicast_message(SHRM_NL_STATUS_MOD_ONLINE, GFP_ATOMIC); - - /* This decrement corresponds to increment - * done in MSR sequecne - */ - atomic_dec(&ac_sleep_disable_count); - queue_work(shm_dev->shm_ac_sleep_wq, - &shm_dev->shm_ac_sleep_req); } } else if (boot_state == BOOT_DONE) { @@ -523,9 +508,13 @@ static int shrm_modem_reset_sequence(void) hrtimer_cancel(&timer); - /* reset the state for ac-wake LOW logic */ + /* + * 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); @@ -598,6 +587,8 @@ static irqreturn_t shrm_prcmu_irq_handler(int irq, void *data) #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; -- cgit v1.2.3 From 71c53084e41266b6c837f48400dc8d9cd07d6e74 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 20 Oct 2011 10:13:38 +0200 Subject: U5500 : Mailbox logical driver. The Mailbox is an inter-processor communication device. Message passing is possible between different CPU's. The logical driver allows multiplexing of up to 256 logical channels for each physical mailbox.This driver uses the services of Physical driver in mbox.c Mailboxes are only supported on U5500. ST-Ericsson Linux next: 336280 ST-Ericsson ID: AP 274804 ST-Ericsson FOSS-OUT ID: N/00201-FEA 212 8003 Change-Id: I23bf7e76801b69e13e216f34d88ad5371d787683 Signed-off-by: Bibek Basu Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/20652 Reviewed-by: Srinidhi KASAGAR --- .../mach-ux500/include/mach/mbox_channels-db5500.h | 71 ++ drivers/misc/mbox.c | 565 ++++++++++ drivers/misc/mbox_channels-db5500.c | 1141 ++++++++++++++++++++ 3 files changed, 1777 insertions(+) create mode 100644 arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h create mode 100644 drivers/misc/mbox.c create mode 100644 drivers/misc/mbox_channels-db5500.c diff --git a/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h b/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h new file mode 100644 index 00000000000..829b1708bf1 --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * Author: Marcin Mielczarczyk for ST-Ericsson. + * Bibek Basu + * License terms: GNU General Public License (GPL), version 2. + */ + +#ifndef __INC_MBOX_CHANNELS_H +#define __INC_MBOX_CHANNELS_H + +/* Maximum number of datawords which can be send in one PDU */ +#define MAILBOX_NR_OF_DATAWORDS 3 + +/** + * mbox_channel_cb_t - Definition of the mailbox channel callback. + * @data: Pointer to the data. + * @length: Length of the data. + * @priv: The client's private data. + * + * This function will be called upon reception of complete mbox channel PDU + * or after completion of send operation. + */ +typedef void mbox_channel_cb_t (u32 *data, u32 length, void *priv); + +/** + * mbox_channel_msg - Definition of mbox channel message + * @channel: Channel number. + * @data: Pointer to data to be sent. + * @length: Length of data to be sent. + * @cb: Pointer to the callback function to be called when send + * operation will be finished. + * @priv: The client's private data. + * + * This structure describes mailbox channel message. + */ +struct mbox_channel_msg { + u16 channel; + u32 *data; + u8 length; + mbox_channel_cb_t *cb; + void *priv; +}; + +/** mbox_channel_register - Set up a given mailbox channel. + * @channel: Mailbox channel number. + * @cb: Pointer to the callback function to be called when a new message + * is received. + * @priv: Client user data which will be returned in the callback. + * + * Returns 0 on success or a negative error code on error. + */ +int mbox_channel_register(u16 channel, mbox_channel_cb_t *cb, void *priv); + +/** + * mbox_channel_send - Send data on given mailbox channel. + * @msg: Mailbox channel message to be sent. + * + * Returns 0 on success or a negative error code on error. + */ +int mbox_channel_send(struct mbox_channel_msg *msg); + +/** + * mbox_channel_revoke_messages - Revoke messages on given mailbox channel. + * @channel: Mailbox channel number. + * + * Returns 0 on success or a negative error code on error. + */ +int mbox_channel_revoke_messages(u16 channel); + +#endif /*INC_STE_MBOX_H*/ + diff --git a/drivers/misc/mbox.c b/drivers/misc/mbox.c new file mode 100644 index 00000000000..2b2d51caf9d --- /dev/null +++ b/drivers/misc/mbox.c @@ -0,0 +1,565 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Stefan Nilsson for ST-Ericsson. + * Author: Martin Persson for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2. + */ + +/* + * Mailbox nomenclature: + * + * APE MODEM + * mbox pairX + * .......................... + * . . + * . peer . + * . send ---- . + * . --> | | . + * . | | . + * . ---- . + * . . + * . local . + * . rec ---- . + * . | | <-- . + * . | | . + * . ---- . + * ......................... + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MBOX_NAME "mbox" + +#define MBOX_FIFO_DATA 0x000 +#define MBOX_FIFO_ADD 0x004 +#define MBOX_FIFO_REMOVE 0x008 +#define MBOX_FIFO_THRES_FREE 0x00C +#define MBOX_FIFO_THRES_OCCUP 0x010 +#define MBOX_FIFO_STATUS 0x014 + +#define MBOX_DISABLE_IRQ 0x4 +#define MBOX_ENABLE_IRQ 0x0 +#define MBOX_LATCH 1 + +/* Global list of all mailboxes */ +static struct list_head mboxs = LIST_HEAD_INIT(mboxs); + +static struct mbox *get_mbox_with_id(u8 id) +{ + u8 i; + struct list_head *pos = &mboxs; + for (i = 0; i <= id; i++) + pos = pos->next; + + return (struct mbox *) list_entry(pos, struct mbox, list); +} + +int mbox_send(struct mbox *mbox, u32 mbox_msg, bool block) +{ + int res = 0; + + spin_lock(&mbox->lock); + + dev_dbg(&(mbox->pdev->dev), + "About to buffer 0x%X to mailbox 0x%X." + " ri = %d, wi = %d\n", + mbox_msg, (u32)mbox, mbox->read_index, + mbox->write_index); + + /* Check if write buffer is full */ + while (((mbox->write_index + 1) % MBOX_BUF_SIZE) == mbox->read_index) { + if (!block) { + dev_dbg(&(mbox->pdev->dev), + "Buffer full in non-blocking call! " + "Returning -ENOMEM!\n"); + res = -ENOMEM; + goto exit; + } + spin_unlock(&mbox->lock); + dev_dbg(&(mbox->pdev->dev), + "Buffer full in blocking call! Sleeping...\n"); + mbox->client_blocked = 1; + wait_for_completion(&mbox->buffer_available); + dev_dbg(&(mbox->pdev->dev), + "Blocking send was woken up! Trying again...\n"); + spin_lock(&mbox->lock); + } + + mbox->buffer[mbox->write_index] = mbox_msg; + mbox->write_index = (mbox->write_index + 1) % MBOX_BUF_SIZE; + + /* + * Indicate that we want an IRQ as soon as there is a slot + * in the FIFO + */ + writel(MBOX_ENABLE_IRQ, mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); + +exit: + spin_unlock(&mbox->lock); + return res; +} +EXPORT_SYMBOL(mbox_send); + +#if defined(CONFIG_DEBUG_FS) +/* + * Expected input: + * Example: "echo 0xdeadbeef 4 > mbox-node" sends 0xdeadbeef 4 times + */ +static ssize_t mbox_write_fifo(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + unsigned long mbox_mess; + unsigned long nbr_sends; + unsigned long i; + char int_buf[16]; + char *token; + char *val; + + struct mbox *mbox = (struct mbox *) dev->platform_data; + + strncpy((char *) &int_buf, buf, sizeof(int_buf)); + token = (char *) &int_buf; + + /* Parse message */ + val = strsep(&token, " "); + if ((val == NULL) || (strict_strtoul(val, 16, &mbox_mess) != 0)) + mbox_mess = 0xDEADBEEF; + + val = strsep(&token, " "); + if ((val == NULL) || (strict_strtoul(val, 10, &nbr_sends) != 0)) + nbr_sends = 1; + + dev_dbg(dev, "Will write 0x%lX %ld times using data struct at 0x%X\n", + mbox_mess, nbr_sends, (u32) mbox); + + for (i = 0; i < nbr_sends; i++) + mbox_send(mbox, mbox_mess, true); + + return count; +} + +static ssize_t mbox_read_fifo(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int mbox_value; + struct mbox *mbox = (struct mbox *) dev->platform_data; + + if ((readl(mbox->virtbase_local + MBOX_FIFO_STATUS) & 0x7) <= 0) + return sprintf(buf, "Mailbox is empty\n"); + + mbox_value = readl(mbox->virtbase_local + MBOX_FIFO_DATA); + writel(MBOX_LATCH, (mbox->virtbase_local + MBOX_FIFO_REMOVE)); + + return sprintf(buf, "0x%X\n", mbox_value); +} + +static DEVICE_ATTR(fifo, S_IWUGO | S_IRUGO, mbox_read_fifo, mbox_write_fifo); + +static int mbox_show(struct seq_file *s, void *data) +{ + struct list_head *pos; + u8 mbox_index = 0; + + list_for_each(pos, &mboxs) { + struct mbox *m = + (struct mbox *) list_entry(pos, struct mbox, list); + if (m == NULL) { + seq_printf(s, + "Unable to retrieve mailbox %d\n", + mbox_index); + continue; + } + + spin_lock(&m->lock); + if ((m->virtbase_peer == NULL) || (m->virtbase_local == NULL)) { + seq_printf(s, "MAILBOX %d not setup or corrupt\n", + mbox_index); + spin_unlock(&m->lock); + continue; + } + + seq_printf(s, + "===========================\n" + " MAILBOX %d\n" + " PEER MAILBOX DUMP\n" + "---------------------------\n" + "FIFO: 0x%X (%d)\n" + "Free Threshold: 0x%.2X (%d)\n" + "Occupied Threshold: 0x%.2X (%d)\n" + "Status: 0x%.2X (%d)\n" + " Free spaces (ot): %d (%d)\n" + " Occup spaces (ot): %d (%d)\n" + "===========================\n" + " LOCAL MAILBOX DUMP\n" + "---------------------------\n" + "FIFO: 0x%.X (%d)\n" + "Free Threshold: 0x%.2X (%d)\n" + "Occupied Threshold: 0x%.2X (%d)\n" + "Status: 0x%.2X (%d)\n" + " Free spaces (ot): %d (%d)\n" + " Occup spaces (ot): %d (%d)\n" + "===========================\n" + "write_index: %d\n" + "read_index : %d\n" + "===========================\n" + "\n", + mbox_index, + readl(m->virtbase_peer + MBOX_FIFO_DATA), + readl(m->virtbase_peer + MBOX_FIFO_DATA), + readl(m->virtbase_peer + MBOX_FIFO_THRES_FREE), + readl(m->virtbase_peer + MBOX_FIFO_THRES_FREE), + readl(m->virtbase_peer + MBOX_FIFO_THRES_OCCUP), + readl(m->virtbase_peer + MBOX_FIFO_THRES_OCCUP), + readl(m->virtbase_peer + MBOX_FIFO_STATUS), + readl(m->virtbase_peer + MBOX_FIFO_STATUS), + (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 4) & 0x7, + (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 7) & 0x1, + (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 0) & 0x7, + (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 3) & 0x1, + readl(m->virtbase_local + MBOX_FIFO_DATA), + readl(m->virtbase_local + MBOX_FIFO_DATA), + readl(m->virtbase_local + MBOX_FIFO_THRES_FREE), + readl(m->virtbase_local + MBOX_FIFO_THRES_FREE), + readl(m->virtbase_local + MBOX_FIFO_THRES_OCCUP), + readl(m->virtbase_local + MBOX_FIFO_THRES_OCCUP), + readl(m->virtbase_local + MBOX_FIFO_STATUS), + readl(m->virtbase_local + MBOX_FIFO_STATUS), + (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 4) & 0x7, + (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 7) & 0x1, + (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 0) & 0x7, + (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 3) & 0x1, + m->write_index, m->read_index); + mbox_index++; + spin_unlock(&m->lock); + } + + return 0; +} + +static int mbox_open(struct inode *inode, struct file *file) +{ + return single_open(file, mbox_show, NULL); +} + +static const struct file_operations mbox_operations = { + .owner = THIS_MODULE, + .open = mbox_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +static irqreturn_t mbox_irq(int irq, void *arg) +{ + u32 mbox_value; + int nbr_occup; + int nbr_free; + struct mbox *mbox = (struct mbox *) arg; + + spin_lock(&mbox->lock); + + dev_dbg(&(mbox->pdev->dev), + "mbox IRQ [%d] received. ri = %d, wi = %d\n", + irq, mbox->read_index, mbox->write_index); + + /* + * Check if we have any outgoing messages, and if there is space for + * them in the FIFO. + */ + if (mbox->read_index != mbox->write_index) { + /* + * Check by reading FREE for LOCAL since that indicates + * OCCUP for PEER + */ + nbr_free = (readl(mbox->virtbase_local + MBOX_FIFO_STATUS) + >> 4) & 0x7; + dev_dbg(&(mbox->pdev->dev), + "Status indicates %d empty spaces in the FIFO!\n", + nbr_free); + + while ((nbr_free > 0) && + (mbox->read_index != mbox->write_index)) { + /* Write the message and latch it into the FIFO */ + writel(mbox->buffer[mbox->read_index], + (mbox->virtbase_peer + MBOX_FIFO_DATA)); + writel(MBOX_LATCH, + (mbox->virtbase_peer + MBOX_FIFO_ADD)); + dev_dbg(&(mbox->pdev->dev), + "Wrote message 0x%X to addr 0x%X\n", + mbox->buffer[mbox->read_index], + (u32) (mbox->virtbase_peer + MBOX_FIFO_DATA)); + + nbr_free--; + mbox->read_index = + (mbox->read_index + 1) % MBOX_BUF_SIZE; + } + + /* + * Check if we still want IRQ:s when there is free + * space to send + */ + if (mbox->read_index != mbox->write_index) { + dev_dbg(&(mbox->pdev->dev), + "Still have messages to send, but FIFO full. " + "Request IRQ again!\n"); + writel(MBOX_ENABLE_IRQ, + mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); + } else { + dev_dbg(&(mbox->pdev->dev), + "No more messages to send. " + "Do not request IRQ again!\n"); + writel(MBOX_DISABLE_IRQ, + mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); + } + + /* + * Check if we can signal any blocked clients that it is OK to + * start buffering again + */ + if (mbox->client_blocked && + (((mbox->write_index + 1) % MBOX_BUF_SIZE) + != mbox->read_index)) { + dev_dbg(&(mbox->pdev->dev), + "Waking up blocked client\n"); + complete(&mbox->buffer_available); + mbox->client_blocked = 0; + } + } + + /* Check if we have any incoming messages */ + nbr_occup = readl(mbox->virtbase_local + MBOX_FIFO_STATUS) & 0x7; + if (nbr_occup == 0) + goto exit; + + if (mbox->cb == NULL) { + dev_dbg(&(mbox->pdev->dev), "No receive callback registered, " + "leaving %d incoming messages in fifo!\n", nbr_occup); + goto exit; + } + + /* Read and acknowledge the message */ + mbox_value = readl(mbox->virtbase_local + MBOX_FIFO_DATA); + writel(MBOX_LATCH, (mbox->virtbase_local + MBOX_FIFO_REMOVE)); + + /* Notify consumer of new mailbox message */ + dev_dbg(&(mbox->pdev->dev), "Calling callback for message 0x%X!\n", + mbox_value); + mbox->cb(mbox_value, mbox->client_data); + +exit: + dev_dbg(&(mbox->pdev->dev), "Exit mbox IRQ. ri = %d, wi = %d\n", + mbox->read_index, mbox->write_index); + spin_unlock(&mbox->lock); + + return IRQ_HANDLED; +} + +/* Setup is executed once for each mbox pair */ +struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv) +{ + struct resource *resource; + int irq; + int res; + struct mbox *mbox; + + mbox = get_mbox_with_id(mbox_id); + if (mbox == NULL) { + dev_err(&(mbox->pdev->dev), "Incorrect mailbox id: %d!\n", + mbox_id); + goto exit; + } + + /* + * Check if mailbox has been allocated to someone else, + * otherwise allocate it + */ + if (mbox->allocated) { + dev_err(&(mbox->pdev->dev), "Mailbox number %d is busy!\n", + mbox_id); + mbox = NULL; + goto exit; + } + mbox->allocated = true; + + dev_dbg(&(mbox->pdev->dev), "Initiating mailbox number %d: 0x%X...\n", + mbox_id, (u32)mbox); + + mbox->client_data = priv; + mbox->cb = mbox_cb; + + /* Get addr for peer mailbox and ioremap it */ + resource = platform_get_resource_byname(mbox->pdev, + IORESOURCE_MEM, + "mbox_peer"); + if (resource == NULL) { + dev_err(&(mbox->pdev->dev), + "Unable to retrieve mbox peer resource\n"); + mbox = NULL; + goto exit; + } + dev_dbg(&(mbox->pdev->dev), + "Resource name: %s start: 0x%X, end: 0x%X\n", + resource->name, resource->start, resource->end); + mbox->virtbase_peer = ioremap(resource->start, resource_size(resource)); + if (!mbox->virtbase_peer) { + dev_err(&(mbox->pdev->dev), "Unable to ioremap peer mbox\n"); + mbox = NULL; + goto exit; + } + dev_dbg(&(mbox->pdev->dev), + "ioremapped peer physical: (0x%X-0x%X) to virtual: 0x%X\n", + resource->start, resource->end, (u32) mbox->virtbase_peer); + + /* Get addr for local mailbox and ioremap it */ + resource = platform_get_resource_byname(mbox->pdev, + IORESOURCE_MEM, + "mbox_local"); + if (resource == NULL) { + dev_err(&(mbox->pdev->dev), + "Unable to retrieve mbox local resource\n"); + mbox = NULL; + goto exit; + } + dev_dbg(&(mbox->pdev->dev), + "Resource name: %s start: 0x%X, end: 0x%X\n", + resource->name, resource->start, resource->end); + mbox->virtbase_local = ioremap(resource->start, resource_size(resource)); + if (!mbox->virtbase_local) { + dev_err(&(mbox->pdev->dev), "Unable to ioremap local mbox\n"); + mbox = NULL; + goto exit; + } + dev_dbg(&(mbox->pdev->dev), + "ioremapped local physical: (0x%X-0x%X) to virtual: 0x%X\n", + resource->start, resource->end, (u32) mbox->virtbase_peer); + + init_completion(&mbox->buffer_available); + mbox->client_blocked = 0; + + /* Get IRQ for mailbox and allocate it */ + irq = platform_get_irq_byname(mbox->pdev, "mbox_irq"); + if (irq < 0) { + dev_err(&(mbox->pdev->dev), + "Unable to retrieve mbox irq resource\n"); + mbox = NULL; + goto exit; + } + + dev_dbg(&(mbox->pdev->dev), "Allocating irq %d...\n", irq); + res = request_irq(irq, mbox_irq, 0, mbox->name, (void *) mbox); + if (res < 0) { + dev_err(&(mbox->pdev->dev), + "Unable to allocate mbox irq %d\n", irq); + mbox = NULL; + goto exit; + } + + /* Set up mailbox to not launch IRQ on free space in mailbox */ + writel(MBOX_DISABLE_IRQ, mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); + + /* + * Set up mailbox to launch IRQ on new message if we have + * a callback set. If not, do not raise IRQ, but keep message + * in FIFO for manual retrieval + */ + if (mbox_cb != NULL) + writel(MBOX_ENABLE_IRQ, + mbox->virtbase_local + MBOX_FIFO_THRES_OCCUP); + else + writel(MBOX_DISABLE_IRQ, + mbox->virtbase_local + MBOX_FIFO_THRES_OCCUP); + +#if defined(CONFIG_DEBUG_FS) + res = device_create_file(&(mbox->pdev->dev), &dev_attr_fifo); + if (res != 0) + dev_warn(&(mbox->pdev->dev), + "Unable to create mbox sysfs entry"); + + (void) debugfs_create_file("mbox", S_IFREG | S_IRUGO, NULL, + NULL, &mbox_operations); +#endif + + dev_info(&(mbox->pdev->dev), + "Mailbox driver with index %d initiated!\n", mbox_id); + +exit: + return mbox; +} +EXPORT_SYMBOL(mbox_setup); + + +int __init mbox_probe(struct platform_device *pdev) +{ + struct mbox local_mbox; + struct mbox *mbox; + int res = 0; + dev_dbg(&(pdev->dev), "Probing mailbox (pdev = 0x%X)...\n", (u32) pdev); + + memset(&local_mbox, 0x0, sizeof(struct mbox)); + + /* Associate our mbox data with the platform device */ + res = platform_device_add_data(pdev, + (void *) &local_mbox, + sizeof(struct mbox)); + if (res != 0) { + dev_err(&(pdev->dev), + "Unable to allocate driver platform data!\n"); + goto exit; + } + + mbox = (struct mbox *) pdev->dev.platform_data; + mbox->pdev = pdev; + mbox->write_index = 0; + mbox->read_index = 0; + + INIT_LIST_HEAD(&(mbox->list)); + list_add_tail(&(mbox->list), &mboxs); + + sprintf(mbox->name, "%s", MBOX_NAME); + spin_lock_init(&mbox->lock); + + dev_info(&(pdev->dev), "Mailbox driver loaded\n"); + +exit: + return res; +} + +static struct platform_driver mbox_driver = { + .driver = { + .name = MBOX_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init mbox_init(void) +{ + return platform_driver_probe(&mbox_driver, mbox_probe); +} + +module_init(mbox_init); + +void __exit mbox_exit(void) +{ + platform_driver_unregister(&mbox_driver); +} + +module_exit(mbox_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MBOX driver"); diff --git a/drivers/misc/mbox_channels-db5500.c b/drivers/misc/mbox_channels-db5500.c new file mode 100644 index 00000000000..74fdfa7aaae --- /dev/null +++ b/drivers/misc/mbox_channels-db5500.c @@ -0,0 +1,1141 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Mailbox Logical Driver + * + * Author: Marcin Mielczarczyk for ST-Ericsson. + * Bibek Basu ,bibek.basu@stericsson.com> + * License terms: GNU General Public License (GPL), version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Defines start sequence number for given mailbox channel */ +#define CHANNEL_START_SEQUENCE_NUMBER 0x80 + +/* Defines number of channels per mailbox unit */ +#define CHANNELS_PER_MBOX_UNIT 256 + +/* + * This macro builds mbox channel PDU header with following format: + * --------------------------------------------------------------------------- + * | | | | | + * | Sequence nmbr | Type | Length | Destination logical channel number | + * | | | | | + * --------------------------------------------------------------------------- + * 31 24 20 16 0 + * + */ +#define BUILD_HEADER(chan, len, type, seq_no) \ + ((chan) | (((len) & 0xf) << 16) | \ + (((type) & 0xf) << 20) | ((seq_no) << 24)) + +/* Returns type from mbox message header */ +#define GET_TYPE(mbox_msg) (((mbox_msg) >> 20) & 0xf) + +/* Returns channel number from mbox message header */ +#define GET_CHANNEL(mbox_msg) ((mbox_msg) & 0xffff) + +/* Returns length of payload from mbox message header */ +#define GET_LENGTH(mbox_msg) (((mbox_msg) >> 16) & 0xf) + +/* Returns sequence number from mbox message header */ +#define GET_SEQ_NUMBER(mbox_msg) (((mbox_msg) >> 24) + +/* Number of buffers */ +#define NUM_DSP_BUFFER 3 + +/* circular buffer indicator */ +static int buffer_index; + +enum mbox_msg{ + MBOX_CLOSE, + MBOX_OPEN, + MBOX_SEND, + MBOX_CAST, + MBOX_ACK, + MBOX_NAK, +}; + +enum mbox_dir { + MBOX_TX, + MBOX_RX, +}; + +struct mbox_channel_mapping { + u16 chan_base; + u8 mbox_id; + enum mbox_dir direction; +}; + +/* This table maps mbox logical channel to mbox id and direction */ +static struct mbox_channel_mapping channel_mappings[] = { + {0x500, 2, MBOX_RX}, /* channel 5 maps to mbox 0.1, dsp->app (unsec) */ + {0x900, 2, MBOX_TX}, /* channel 9 maps to mbox 0.0, app->dsp (unsec) */ +}; + +/* This table specifies mailbox ids which mbox channels module will use */ +static u8 mbox_ids[] = { + 2, /* app <-> dsp (unsec) */ +}; + +/** + * struct mbox_unit_status - current status of mbox unit + * @mbox_id : holds mbox unit identification number + * @mbox : holds mbox pointer after mbox_register() call + * @tx_chans : holds list of open tx mbox channels + * @tx_lock: lock for tx channel + * @rx_chans : holds list of open rx mbox channels + * @rx_lock: lock for rx channel + */ +struct mbox_unit_status { + u8 mbox_id; + struct mbox *mbox; + struct list_head tx_chans; + spinlock_t tx_lock; + struct list_head rx_chans; + spinlock_t rx_lock; +}; + +static struct { + struct platform_device *pdev; + struct mbox_unit_status mbox_unit[ARRAY_SIZE(mbox_ids)]; +} channels; + +/* This structure describes pending element for mbox tx channel */ +struct pending_elem { + struct list_head list; + u32 *data; + u8 length; +}; + +struct rx_pending_elem { + u32 buffer[MAILBOX_NR_OF_DATAWORDS]; + u8 length; + void *priv; +}; + +struct rx_pending_elem rx_pending[NUM_DSP_BUFFER + 1]; + +/* This structure holds list of pending elements for mbox tx channel */ +struct tx_channel { + struct list_head pending; +}; + +/* Specific status for mbox rx channel */ +struct rx_channel { + struct list_head pending; + spinlock_t lock; + u32 buffer[MAILBOX_NR_OF_DATAWORDS]; + u8 index; + u8 length; +}; + +/* This structure holds status of mbox channel - common for tx and rx */ +struct channel_status { + struct list_head list; + u16 channel; + int state; + mbox_channel_cb_t *cb; + void *priv; + u8 seq_number; + bool with_ack; + struct rx_channel rx; + struct tx_channel tx; + struct work_struct receive_msg; + struct work_struct open_msg; + struct work_struct cast_msg; + struct mutex lock; +}; + +/* Checks if provided channel number is valid */ +static bool check_channel(u16 channel, enum mbox_dir direction) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(channel_mappings); i++) { + if ((channel >= channel_mappings[i].chan_base) && + (channel < channel_mappings[i].chan_base + + CHANNELS_PER_MBOX_UNIT)) { + /* Check if direction of given channel is correct*/ + if (channel_mappings[i].direction == direction) + return true; + else + break; + } + } + return false; +} + +/* get the tx channel corresponding to the given rx channel */ +static u16 get_tx_channel(u16 channel) +{ + int i; + int relative_chan = 0; + int mbox_id = 0xFF; + u16 tx_channel = 0xFF; + + for (i = 0; i < ARRAY_SIZE(channel_mappings); i++) { + if ((channel >= channel_mappings[i].chan_base) && + (channel < channel_mappings[i].chan_base + + CHANNELS_PER_MBOX_UNIT)) { + /* Check if direction of given channel is correct*/ + relative_chan = channel - channel_mappings[i].chan_base; + mbox_id = channel_mappings[i].mbox_id; + + } + } + + for (i = 0; i < ARRAY_SIZE(channel_mappings); i++) { + if ((mbox_id == channel_mappings[i].mbox_id) && + (channel_mappings[i].direction == MBOX_TX)) + tx_channel = channel_mappings[i].chan_base + + relative_chan; + } + return tx_channel; +} + +/* Returns mbox unit id for given mbox channel */ +static int get_mbox_id(u16 channel) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(channel_mappings); i++) { + if ((channel >= channel_mappings[i].chan_base) && + (channel < channel_mappings[i].chan_base + + CHANNELS_PER_MBOX_UNIT)) { + return channel_mappings[i].mbox_id; + } + } + /* There is no mbox unit registered for given channel */ + return -EINVAL; +} + +/* Returns mbox structure saved after mbox_register() call */ +static struct mbox *get_mbox(u16 channel) +{ + int i; + int mbox_id = get_mbox_id(channel); + + if (mbox_id < 0) { + dev_err(&channels.pdev->dev, "couldn't get mbox id\n"); + return NULL; + } + + for (i = 0; i < ARRAY_SIZE(channels.mbox_unit); i++) { + if (channels.mbox_unit[i].mbox_id == mbox_id) + return channels.mbox_unit[i].mbox; + } + return NULL; +} + +/* Returns pointer to rx mbox channels list for given mbox unit */ +static struct list_head *get_rx_list(u8 mbox_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mbox_ids); i++) { + if (channels.mbox_unit[i].mbox_id == mbox_id) + return &channels.mbox_unit[i].rx_chans; + } + return NULL; +} + +/* Returns pointer to tx mbox channels list for given mbox unit */ +static struct list_head *get_tx_list(u8 mbox_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mbox_ids); i++) { + if (channels.mbox_unit[i].mbox_id == mbox_id) + return &channels.mbox_unit[i].tx_chans; + } + return NULL; +} + +static int send_pdu(struct channel_status *chan_status, int command, + u16 channel) +{ + struct mbox *mbox; + u32 header = 0; + int ret = 0; + + /* SEND PDU is not supported */ + if (command == MBOX_SEND) { + dev_err(&channels.pdev->dev, "SEND command not implemented\n"); + ret = -EINVAL; + goto exit; + } + mbox = get_mbox(chan_status->channel); + if (mbox == NULL) { + dev_err(&channels.pdev->dev, "couldn't get mailbox\n"); + ret = -ENOSYS; + goto exit; + } + /* For CAST type send all pending messages */ + if (command == MBOX_CAST) { + struct list_head *pos, *n; + + /* Send all pending messages from TX channel */ + list_for_each_safe(pos, n, &chan_status->tx.pending) { + struct pending_elem *pending = + list_entry(pos, struct pending_elem, list); + int i; + + header = BUILD_HEADER(channel, + pending->length, + command, + chan_status->seq_number); + + ret = mbox_send(mbox, header, true); + if (ret < 0) { + dev_err(&channels.pdev->dev, + "failed to send header, err=%d\n", ret); + goto exit; + } + + for (i = 0; i < pending->length; i++) { + ret = mbox_send(mbox, pending->data[i], true); + if (ret < 0) { + dev_err(&channels.pdev->dev, + "failed to send header, err=%d\n", ret); + goto exit; + } + } + + /* Call client's callback that data is already sent */ + if (chan_status->cb) + chan_status->cb(pending->data, pending->length, + chan_status->priv); + else + dev_err(&channels.pdev->dev, + "%s no callback provided\n", __func__); + + /* Increment sequence number */ + chan_status->seq_number++; + + /* Remove and free element from the list */ + list_del(&pending->list); + kfree(pending); + } + } else { + header = BUILD_HEADER(channel, 0, + command, chan_status->seq_number); + + ret = mbox_send(mbox, header, true); + if (ret < 0) + dev_err(&channels.pdev->dev, "failed to send header\n"); + /* Increment sequence number */ + chan_status->seq_number++; + } + +exit: + return ret; +} + +void mbox_handle_receive_msg(struct work_struct *work) +{ + struct channel_status *rx_chan = container_of(work, + struct channel_status, + receive_msg); + + /* Call client's callback and reset state */ + if (rx_chan->cb) { + static int rx_pending_count; + + if (rx_pending_count == NUM_DSP_BUFFER) + rx_pending_count = 0; + else + rx_pending_count++; + rx_chan->cb(rx_pending[rx_pending_count].buffer, + rx_pending[rx_pending_count].length, + rx_pending[rx_pending_count].priv); + buffer_index--; + } else { + dev_err(&channels.pdev->dev, + "%s no callback provided\n", __func__); + } + +} + +void mbox_handle_open_msg(struct work_struct *work) +{ + struct channel_status *tx_chan = container_of(work, + struct channel_status, + open_msg); + /* Change channel state to OPEN */ + tx_chan->state = MBOX_OPEN; + /* If pending list not empty, start sending data */ + if (!list_empty(&tx_chan->tx.pending)) + send_pdu(tx_chan, MBOX_CAST, tx_chan->channel); +} + +void mbox_handle_cast_msg(struct work_struct *work) +{ + struct channel_status *rx_chan = container_of(work, + struct channel_status, + cast_msg); + /* Check if channel is opened */ + if (rx_chan->state == MBOX_CLOSE) { + /* Peer sent message to closed channel */ + dev_err(&channels.pdev->dev, + "channel in wrong state\n"); + } +} + +static bool handle_receive_msg(u32 mbox_msg, struct list_head *rx_list) +{ + struct list_head *pos; + struct channel_status *tmp; + int i; + static int rx_pending_count; + struct channel_status *rx_chan = NULL; + struct mbox_unit_status *mbox_unit = container_of(rx_list, + struct mbox_unit_status, + rx_chans); + spin_lock(&mbox_unit->rx_lock); + list_for_each(pos, rx_list) { + tmp = list_entry(pos, struct channel_status, list); + if (tmp->state == MBOX_SEND || + tmp->state == MBOX_CAST) + /* Received message is payload */ + rx_chan = tmp; + } + /* Received message is header */ + spin_unlock(&mbox_unit->rx_lock); + if (rx_chan) { + /* Store received data in RX channel buffer */ + rx_chan->rx.buffer[rx_chan->rx.index++] = mbox_msg; + /* Check if it's last data of PDU */ + if (rx_chan->rx.index == rx_chan->rx.length) { + if (rx_pending_count == NUM_DSP_BUFFER) + rx_pending_count = 0; + else + rx_pending_count++; + for (i = 0; i < MAILBOX_NR_OF_DATAWORDS; i++) { + rx_pending[rx_pending_count].buffer[i] = + rx_chan->rx.buffer[i]; + } + rx_pending[rx_pending_count].length = + rx_chan->rx.length; + rx_pending[rx_pending_count].priv = rx_chan->priv; + rx_chan->rx.index = 0; + rx_chan->rx.length = 0; + rx_chan->state = MBOX_OPEN; + rx_chan->seq_number++; + buffer_index++; + if (buffer_index >= NUM_DSP_BUFFER) + dev_err(&channels.pdev->dev, + "rxbuf overflow%d\n", buffer_index); + schedule_work(&rx_chan->receive_msg); + } + dev_dbg(&channels.pdev->dev, "%s OK\n", __func__); + return true; + } + return false; +} + +static void handle_open_msg(u16 channel, u8 mbox_id) +{ + struct list_head *tx_list, *pos; + struct channel_status *tmp; + struct channel_status *tx_chan = NULL; + struct mbox_unit_status *mbox_unit; + channel = get_tx_channel(channel); + dev_dbg(&channels.pdev->dev, "%s mbox_id %d\tchannel %x\n", + __func__, mbox_id, channel); + /* Get TX channesenx for given mbox unit */ + tx_list = get_tx_list(mbox_id); + if (tx_list == NULL) { + dev_err(&channels.pdev->dev, "given mbox id is not valid %d\n", + mbox_id); + return; + } + mbox_unit = container_of(tx_list, struct mbox_unit_status, tx_chans); + /* Search for channel in tx list */ + spin_lock(&mbox_unit->tx_lock); + list_for_each(pos, tx_list) { + tmp = list_entry(pos, struct channel_status, list); + dev_dbg(&channels.pdev->dev, "tmp->channel=%d\n", + tmp->channel); + if (tmp->channel == channel) + tx_chan = tmp; + } + spin_unlock(&mbox_unit->tx_lock); + if (tx_chan) { + schedule_work(&tx_chan->open_msg); + } else { + /* No tx channel found on the list, allocate new element */ + tx_chan = kzalloc(sizeof(*tx_chan), GFP_KERNEL); + if (tx_chan == NULL) { + dev_err(&channels.pdev->dev, + "failed to allocate memory\n"); + return; + } + + /* Fill initial data and add this element to tx list */ + tx_chan->channel = get_tx_channel(channel); + tx_chan->state = MBOX_OPEN; + tx_chan->seq_number = CHANNEL_START_SEQUENCE_NUMBER; + INIT_LIST_HEAD(&tx_chan->tx.pending); + INIT_WORK(&tx_chan->open_msg, mbox_handle_open_msg); + INIT_WORK(&tx_chan->cast_msg, mbox_handle_cast_msg); + INIT_WORK(&tx_chan->receive_msg, mbox_handle_receive_msg); + mutex_init(&tx_chan->lock); + spin_lock(&mbox_unit->tx_lock); + list_add_tail(&tx_chan->list, tx_list); + spin_unlock(&mbox_unit->tx_lock); + } +} + +static void handle_cast_msg(u16 channel, struct list_head *rx_list, + u32 mbox_msg, bool send) +{ + struct list_head *pos; + struct channel_status *tmp; + struct channel_status *rx_chan = NULL; + struct mbox_unit_status *mbox_unit = container_of(rx_list, + struct mbox_unit_status, + rx_chans); + dev_dbg(&channels.pdev->dev, " %s\n", __func__); + /* Search for channel in rx list */ + spin_lock(&mbox_unit->rx_lock); + list_for_each(pos, rx_list) { + tmp = list_entry(pos, struct channel_status, list); + if (tmp->channel == channel) + rx_chan = tmp; + } + spin_unlock(&mbox_unit->rx_lock); + + if (rx_chan) { + rx_chan->rx.buffer[0] = mbox_msg; + rx_chan->with_ack = send; + rx_chan->rx.length = GET_LENGTH(rx_chan->rx.buffer[0]); + if (rx_chan->rx.length <= MAILBOX_NR_OF_DATAWORDS && + rx_chan->rx.length > 0) { + rx_chan->rx.index = 0; + rx_chan->state = MBOX_CAST; + } + schedule_work(&rx_chan->cast_msg); + } else { + /* Channel not found, peer sent wrong message */ + dev_err(&channels.pdev->dev, "channel %d doesn't exist\n", + channel); + } +} + +/* + * This callback is called whenever mbox unit receives data. + * priv parameter holds mbox unit id. + */ +static void mbox_cb(u32 mbox_msg, void *priv) +{ + u8 mbox_id = *(u8 *)priv; + struct list_head *rx_list; + u8 type = GET_TYPE(mbox_msg); + u16 channel = GET_CHANNEL(mbox_msg); + + dev_dbg(&channels.pdev->dev, "%s type %d\t, mbox_msg %x\n", + __func__, type, mbox_msg); + /* Get RX channels list for given mbox unit */ + rx_list = get_rx_list(mbox_id); + if (rx_list == NULL) { + dev_err(&channels.pdev->dev, "given mbox id is not valid %d\n", + mbox_id); + return; + } + + /* If received message is payload this function will take care of it */ + if (handle_receive_msg(mbox_msg, rx_list)) + return; + + /* Received message is header as no RX channel is in SEND/CAST state */ + switch (type) { + case MBOX_CLOSE: + /* Not implemented */ + break; + case MBOX_OPEN: + handle_open_msg(channel, mbox_id); + break; + case MBOX_SEND: + handle_cast_msg(channel, rx_list, mbox_msg, true); + break; + case MBOX_CAST: + handle_cast_msg(channel, rx_list, mbox_msg, false); + break; + case MBOX_ACK: + case MBOX_NAK: + /* Not implemented */ + break; + } +} + +int mbox_channel_register(u16 channel, mbox_channel_cb_t *cb, void *priv) +{ + struct channel_status *rx_chan; + struct list_head *pos, *rx_list; + int res = 0; + struct mbox_unit_status *mbox_unit; + + dev_dbg(&channels.pdev->dev, " %s channel = %d\n", __func__, channel); + /* Closing of channels is not implemented */ + if (cb == NULL) { + dev_err(&channels.pdev->dev, + "channel close is not implemented\n"); + res = -EINVAL; + goto exit; + } + + /* Check if provided channel number is valid */ + if (!check_channel(channel, MBOX_RX)) { + dev_err(&channels.pdev->dev, "wrong mbox channel number %d\n", + channel); + res = -EINVAL; + goto exit; + } + + rx_list = get_rx_list(get_mbox_id(channel)); + if (rx_list == NULL) { + dev_err(&channels.pdev->dev, "given mbox id is not valid\n"); + res = -EINVAL; + goto exit; + } + + mbox_unit = container_of(rx_list, struct mbox_unit_status, rx_chans); + + /* Check if channel is already registered */ + spin_lock(&mbox_unit->rx_lock); + list_for_each(pos, rx_list) { + rx_chan = list_entry(pos, struct channel_status, list); + + if (rx_chan->channel == channel) { + dev_dbg(&channels.pdev->dev, + "channel already registered\n"); + rx_chan->cb = cb; + rx_chan->priv = priv; + spin_unlock(&mbox_unit->rx_lock); + goto exit; + } + } + spin_unlock(&mbox_unit->rx_lock); + + rx_chan = kzalloc(sizeof(*rx_chan), GFP_KERNEL); + if (rx_chan == NULL) { + dev_err(&channels.pdev->dev, + "couldn't allocate channel status\n"); + res = -ENOMEM; + goto exit; + } + + /* Fill out newly allocated element and add it to rx list */ + rx_chan->channel = channel; + rx_chan->cb = cb; + rx_chan->priv = priv; + rx_chan->seq_number = CHANNEL_START_SEQUENCE_NUMBER; + mutex_init(&rx_chan->lock); + INIT_LIST_HEAD(&rx_chan->rx.pending); + INIT_WORK(&rx_chan->open_msg, mbox_handle_open_msg); + INIT_WORK(&rx_chan->cast_msg, mbox_handle_cast_msg); + INIT_WORK(&rx_chan->receive_msg, mbox_handle_receive_msg); + spin_lock(&mbox_unit->rx_lock); + list_add_tail(&rx_chan->list, rx_list); + spin_unlock(&mbox_unit->rx_lock); + + mutex_lock(&rx_chan->lock); + res = send_pdu(rx_chan, MBOX_OPEN, get_tx_channel(rx_chan->channel)); + if (res) { + dev_err(&channels.pdev->dev, "failed to send OPEN command\n"); + spin_lock(&mbox_unit->rx_lock); + list_del(&rx_chan->list); + spin_unlock(&mbox_unit->rx_lock); + kfree(rx_chan); + } else { + rx_chan->seq_number++; + rx_chan->state = MBOX_OPEN; + } + mutex_unlock(&rx_chan->lock); +exit: + return res; +} +EXPORT_SYMBOL(mbox_channel_register); + +int mbox_channel_send(struct mbox_channel_msg *msg) +{ + struct list_head *pos, *tx_list; + struct channel_status *tmp = NULL; + struct channel_status *tx_chan = NULL; + struct pending_elem *pending; + struct mbox_unit_status *mbox_unit; + + if (msg->length > MAILBOX_NR_OF_DATAWORDS || msg->length == 0) { + dev_err(&channels.pdev->dev, "data length incorrect\n"); + return -EINVAL; + } + + if (!check_channel(msg->channel, MBOX_TX)) { + dev_err(&channels.pdev->dev, "wrong channel number %d\n", + msg->channel); + return -EINVAL; + } + + tx_list = get_tx_list(get_mbox_id(msg->channel)); + if (tx_list == NULL) { + dev_err(&channels.pdev->dev, "given mbox id is not valid\n"); + return -EINVAL; + } + + mbox_unit = container_of(tx_list, struct mbox_unit_status, tx_chans); + + spin_lock(&mbox_unit->tx_lock); + dev_dbg(&channels.pdev->dev, "send:tx_list=%x\tmbox_unit=%x\n", + (u32)tx_list, (u32)mbox_unit); + list_for_each(pos, tx_list) { + tmp = list_entry(pos, struct channel_status, list); + if (tmp->channel == msg->channel) + tx_chan = tmp; + } + spin_unlock(&mbox_unit->tx_lock); + /* Allocate pending element and add it to the list */ + pending = kzalloc(sizeof(*pending), GFP_KERNEL); + if (pending == NULL) { + dev_err(&channels.pdev->dev, + "couldn't allocate memory for pending\n"); + return -ENOMEM; + } + pending->data = msg->data; + pending->length = msg->length; + + if (tx_chan) { + mutex_lock(&tx_chan->lock); + list_add_tail(&pending->list, &tx_chan->tx.pending); + tx_chan->cb = msg->cb; + tx_chan->priv = msg->priv; + /* If channel is already opened start sending data */ + if (tx_chan->state == MBOX_OPEN) + send_pdu(tx_chan, MBOX_CAST, tx_chan->channel); + /* Stop processing here */ + mutex_unlock(&tx_chan->lock); + } else { + /* No channel found on the list, allocate new element */ + tx_chan = kzalloc(sizeof(*tx_chan), GFP_KERNEL); + if (tx_chan == NULL) { + dev_err(&channels.pdev->dev, + "couldn't allocate memory for \ + tx_chan\n"); + return -ENOMEM; + } + tx_chan->channel = msg->channel; + tx_chan->cb = msg->cb; + tx_chan->priv = msg->priv; + tx_chan->state = MBOX_CLOSE; + tx_chan->seq_number = CHANNEL_START_SEQUENCE_NUMBER; + INIT_LIST_HEAD(&tx_chan->tx.pending); + INIT_WORK(&tx_chan->open_msg, mbox_handle_open_msg); + INIT_WORK(&tx_chan->cast_msg, mbox_handle_cast_msg); + INIT_WORK(&tx_chan->receive_msg, mbox_handle_receive_msg); + mutex_init(&tx_chan->lock); + spin_lock(&mbox_unit->tx_lock); + list_add_tail(&tx_chan->list, tx_list); + spin_unlock(&mbox_unit->tx_lock); + mutex_lock(&tx_chan->lock); + list_add_tail(&pending->list, &tx_chan->tx.pending); + mutex_unlock(&tx_chan->lock); + } + return 0; +} +EXPORT_SYMBOL(mbox_channel_send); + +static void revoke_pending_msgs(struct channel_status *tx_chan) +{ + struct list_head *pos, *n; + struct pending_elem *pending; + + list_for_each_safe(pos, n, &tx_chan->tx.pending) { + pending = list_entry(pos, struct pending_elem, list); + + if (tx_chan->cb) + tx_chan->cb(pending->data, pending->length, + tx_chan->priv); + else + dev_err(&channels.pdev->dev, + "%s no callback provided\n", __func__); + list_del(&pending->list); + kfree(pending); + } +} + +/* Clear all pending messages from TX channel */ +int mbox_channel_revoke_messages(u16 channel) +{ + struct list_head *pos, *tx_list; + struct channel_status *tmp; + struct channel_status *tx_chan = NULL; + struct mbox_unit_status *mbox_unit; + int res = 0; + + if (!check_channel(channel, MBOX_TX)) { + dev_err(&channels.pdev->dev, + "wrong channel number %d\n", channel); + return -EINVAL; + } + + tx_list = get_tx_list(get_mbox_id(channel)); + if (tx_list == NULL) { + dev_err(&channels.pdev->dev, "given mbox id is not valid\n"); + return -EINVAL; + } + + mbox_unit = container_of(tx_list, struct mbox_unit_status, tx_chans); + + spin_lock(&mbox_unit->tx_lock); + list_for_each(pos, tx_list) { + tmp = list_entry(pos, struct channel_status, list); + if (tmp->channel == channel) + tx_chan = tmp; + } + spin_unlock(&mbox_unit->tx_lock); + + if (tx_chan) { + mutex_lock(&tx_chan->lock); + revoke_pending_msgs(tx_chan); + mutex_unlock(&tx_chan->lock); + dev_dbg(&channels.pdev->dev, "channel %d cleared\n", + channel); + } else { + dev_err(&channels.pdev->dev, "no channel found\n"); + res = -EINVAL; + } + + dev_dbg(&channels.pdev->dev, "%s exiting %d\n", __func__, res); + return res; +} +EXPORT_SYMBOL(mbox_channel_revoke_messages); + +#if defined(CONFIG_DEBUG_FS) +#define MBOXTEST_DEBUG 1 +#ifdef MBOXTEST_DEBUG +#define DBG_TEST(x) x +#else +#define DBG_TEST(x) +#endif + +#define MBOX_TEST_MAX_WORDS 3 +#define MBOX_RX_CHAN 0x500 +#define MBOX_TX_RX_CHANNEL_DIFF 0x400 +#define MBOX_MAX_NUM_TRANSFER 30000 +static int registration_done; +/** + * struct mboxtest_data - mbox test via debugfs information + * @rx_buff: Buffer for incomming data + * @rx_pointer: Ptr to actual RX data buff + * @tx_buff Buffer for outgoing data + * @tx_pointer: Ptr to actual TX data buff + * @tx_done: TX Transfer done indicator + * @rx_done: RX Transfer done indicator + * @received Received words + * @xfer_words: Num of bytes in actual trf + * @xfers: Number of transfers + * @words Number of total words + * @channel: Channel test number + */ +struct mboxtest_data { + unsigned int *rx_buff; + unsigned int *rx_pointer; + unsigned int *tx_buff; + unsigned int *tx_pointer; + struct completion tx_done; + struct completion rx_done; + int received; + int xfer_words; + int xfers; + int words; + int channel; +}; + +static void mboxtest_receive_cb(u32 *data, u32 len, void *arg) +{ + struct mboxtest_data *mboxtest = (struct mboxtest_data *) arg; + int i; + + printk(KERN_INFO "receive_cb.. data.= 0x%X, len = %d\n", + *data, len); + + for (i = 0; i < len; i++) + *(mboxtest->rx_pointer++) = *(data++); + + mboxtest->received += len; + + printk(KERN_INFO "received = %d, words = %d\n", + mboxtest->received, mboxtest->words); + if (mboxtest->received >= mboxtest->words) + complete(&mboxtest->rx_done); + dev_dbg(&channels.pdev->dev, "%s exiting\n", __func__); +} + +static void mboxtest_send_cb(u32 *data, u32 len, void *arg) +{ + struct mboxtest_data *mboxtest = (struct mboxtest_data *) arg; + + printk(KERN_INFO "send_cb.. data.= 0x%X, len = %d\n", + *data, len); + + complete(&mboxtest->tx_done); + dev_dbg(&channels.pdev->dev, "kernel:mboxtest_send_cb exiting\n"); +} + +static int mboxtest_transmit(struct mboxtest_data *mboxtest) +{ + int status = 0; + struct mbox_channel_msg msg; + + dev_dbg(&channels.pdev->dev, "%s entering\n", __func__); + init_completion(&mboxtest->tx_done); + + msg.channel = mboxtest->channel; + msg.data = mboxtest->tx_pointer; + msg.length = mboxtest->words; + msg.cb = mboxtest_send_cb; + msg.priv = mboxtest; + + status = mbox_channel_send(&msg); + if (!status) { + mboxtest->tx_pointer += mboxtest->xfer_words; + wait_for_completion(&mboxtest->tx_done); + } + + dev_dbg(&channels.pdev->dev, "%s exiting %d\n", + __func__, status); + return status; +} + +static int transfer_test(struct mboxtest_data *mboxtest) +{ + int status = 0; + int len = 0; + int i; + + len = mboxtest->words; + + dev_dbg(&channels.pdev->dev, "%s enterring\n", __func__); + /* Allocate buffers */ + mboxtest->rx_buff = kzalloc(sizeof(unsigned int) * len, GFP_KERNEL); + if (!mboxtest->rx_buff) { + DBG_TEST(printk(KERN_INFO + "Cannot allocate mbox rx memory\n")); + status = -ENOMEM; + goto err1; + } + memset(mboxtest->rx_buff, '\0', sizeof(unsigned int) * len); + + mboxtest->tx_buff = kzalloc(sizeof(unsigned int) * len, GFP_KERNEL); + if (!mboxtest->tx_buff) { + DBG_TEST(printk(KERN_INFO + "Cannot allocate mbox tx memory\n")); + status = -ENOMEM; + goto err2; + } + memset(mboxtest->tx_buff, '\0', sizeof(unsigned int) * len); + + /* Generate data */ + get_random_bytes((unsigned char *)mboxtest->tx_buff, + sizeof(unsigned int) * len); + /* Set pointers */ + mboxtest->tx_pointer = mboxtest->tx_buff; + mboxtest->rx_pointer = mboxtest->rx_buff; + mboxtest->received = 0; + init_completion(&mboxtest->rx_done); + + /* Start tx transfer test transfer */ + status = mboxtest_transmit(mboxtest); + DBG_TEST(printk(KERN_INFO "xfer_words=%d\n", + mboxtest->xfer_words)); + if (!status) + wait_for_completion(&mboxtest->rx_done); + for (i = 0; i < len; i++) + DBG_TEST(printk(KERN_INFO "%d -> TX:0x%X, RX:0x%X\n", i, + mboxtest->tx_buff[i], mboxtest->rx_buff[i])); + + dev_dbg(&channels.pdev->dev, "%s exiting %d\n", __func__, status); + return status; +err2: + kfree(mboxtest->rx_buff); +err1: + return status; +} + +static int mboxtest_prepare(struct mboxtest_data *mboxtest) +{ + int err = 0; + + mboxtest->xfers = MBOX_MAX_NUM_TRANSFER; + /* Calculate number of bytes in each transfer */ + mboxtest->xfer_words = mboxtest->words / mboxtest->xfers; + + /* Trim to maxiumum data words per transfer */ + if (mboxtest->xfer_words > MBOX_TEST_MAX_WORDS) { + DBG_TEST(printk(KERN_INFO "Recalculating xfers ...\n")); + mboxtest->xfer_words = MBOX_TEST_MAX_WORDS; + if (mboxtest->words % mboxtest->xfer_words) + mboxtest->xfers = (mboxtest->words / + mboxtest->xfer_words) + 1; + else + mboxtest->xfers = (mboxtest->words / + mboxtest->xfer_words); + } + + DBG_TEST(printk(KERN_INFO "Params: chan=0x%X words=%d, xfers=%d\n", + mboxtest->channel, mboxtest->words, + mboxtest->xfers)); + + if (mbox_channel_register(mboxtest->channel, + mboxtest_receive_cb, mboxtest)) { + DBG_TEST(printk(KERN_INFO "Cannot register mbox channel\n")); + err = -ENOMEM; + goto err; + } + + registration_done = true; + return 0; +err: + kfree(mboxtest); + return err; +} + +struct mboxtest_data mboxtest; +/* + * Expected input: + * Example: "echo 500 2" + */ +static ssize_t mbox_write_channel(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + unsigned long nbr_channel; + unsigned long nbr_word; + char int_buf[16]; + char *token; + char *val; + + strncpy((char *) &int_buf, buf, sizeof(int_buf)); + token = (char *) &int_buf; + + /* Parse message */ + val = strsep(&token, " "); + if ((val == NULL) || (strict_strtoul(val, 16, &nbr_channel) != 0)) + nbr_channel = MBOX_RX_CHAN; + + val = strsep(&token, " "); + if ((val == NULL) || (strict_strtoul(val, 16, &nbr_word) != 0)) + nbr_word = 2; + + dev_dbg(dev, "Will setup logical channel %ld\n", nbr_channel); + mboxtest.channel = nbr_channel; + mboxtest.words = nbr_word; + + if (!registration_done) + mboxtest_prepare(&mboxtest); + else + dev_dbg(&channels.pdev->dev, "already registration done\n"); + + return count; +} + +static ssize_t mbox_read_channel(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + + unsigned long i; + static bool config_done; + + if (!config_done) { + config_done = true; + mboxtest.channel += MBOX_TX_RX_CHANNEL_DIFF; + } + dev_dbg(dev, "Will transfer %d words %d times at channel 0x%x\n", + mboxtest.words, mboxtest.xfers, mboxtest.channel); + for (i = 0; i < mboxtest.xfers; i++) + transfer_test(&mboxtest); + + return 1; +} +static DEVICE_ATTR(channel, S_IWUGO | S_IRUGO, mbox_read_channel, + mbox_write_channel); + +#endif + +static int __init mbox_channel_probe(struct platform_device *pdev) +{ + int i, ret = 0; + struct mbox *mbox; + + dev_dbg(&(pdev->dev), "Probing mailbox (pdev = 0x%X)...\n", (u32)pdev); + + /* Register to given mailbox units (ids) */ + for (i = 0; i < ARRAY_SIZE(mbox_ids); i++) { + mbox = mbox_setup(mbox_ids[i], mbox_cb, &mbox_ids[i]); + if (mbox == NULL) { + dev_err(&(pdev->dev), "Unable to setup mailbox %d\n", + mbox_ids[i]); + ret = -EBUSY; + goto exit; + } + channels.mbox_unit[i].mbox_id = mbox_ids[i]; + channels.mbox_unit[i].mbox = mbox; + INIT_LIST_HEAD(&channels.mbox_unit[i].rx_chans); + INIT_LIST_HEAD(&channels.mbox_unit[i].tx_chans); + spin_lock_init(&channels.mbox_unit[i].rx_lock); + spin_lock_init(&channels.mbox_unit[i].tx_lock); + } + + channels.pdev = pdev; + + dev_dbg(&(pdev->dev), "Mailbox channel driver loaded\n"); +#if defined(CONFIG_DEBUG_FS) + ret = device_create_file(&(pdev->dev), &dev_attr_channel); + if (ret != 0) + dev_warn(&(pdev->dev), + "Unable to create mbox_channel sysfs entry"); + + +#endif +exit: + return ret; +} + +static struct platform_driver mbox_channel_driver = { + .driver = { + .name = "mbox_channel", + .owner = THIS_MODULE, + }, +}; + +static int __init mbox_channel_init(void) +{ + if (!machine_is_u5500()) + return 0; + + platform_device_register_simple("mbox_channel", 0, NULL, 0); + + return platform_driver_probe(&mbox_channel_driver, mbox_channel_probe); +} +module_init(mbox_channel_init); + +static void __exit mbox_channel_exit(void) +{ + platform_driver_unregister(&mbox_channel_driver); +} +module_exit(mbox_channel_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MBOX channels driver"); -- cgit v1.2.3 From 079c8e0087da770a1daa749ad630bb8627d6191e Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 14 Oct 2011 09:42:59 +0200 Subject: kernel-doc: shrm: fix kernel-doc warnings fix the potential kernel-doc warnings ST-Ericsson Linux next: - ST-Ericsson ID: 327425 ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: om prakash Change-Id: Ibaa302ddbaa8e223b81bd047fceaf7c3b97af491 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23345 Reviewed-by: Rabin VINCENT Reviewed-by: Jonas ABERG Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR --- Documentation/DocBook/shrm.tmpl | 2 +- arch/arm/mach-ux500/include/mach/shrm_driver.h | 1 + arch/arm/mach-ux500/include/mach/shrm_private.h | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/DocBook/shrm.tmpl b/Documentation/DocBook/shrm.tmpl index b35e1bc4e0b..7f21cd3f7dd 100644 --- a/Documentation/DocBook/shrm.tmpl +++ b/Documentation/DocBook/shrm.tmpl @@ -109,6 +109,7 @@ This Section lists the API's provided by the SHRM driver used in transmission of RPC, AUDIO and SECURITY messages. !Edrivers/char/shrm_char.c + @@ -132,7 +133,6 @@ This Section lists some of the Data structure used by the SHRM driver. -!Iarch/arm/mach-ux500/include/mach/shrm.h !Iarch/arm/mach-ux500/include/mach/shrm_driver.h !Iarch/arm/mach-ux500/include/mach/shrm_private.h diff --git a/arch/arm/mach-ux500/include/mach/shrm_driver.h b/arch/arm/mach-ux500/include/mach/shrm_driver.h index 41f518238b3..36bb8413ad9 100644 --- a/arch/arm/mach-ux500/include/mach/shrm_driver.h +++ b/arch/arm/mach-ux500/include/mach/shrm_driver.h @@ -173,6 +173,7 @@ struct message_queue { * 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; diff --git a/arch/arm/mach-ux500/include/mach/shrm_private.h b/arch/arm/mach-ux500/include/mach/shrm_private.h index 33a0e18234b..1f09e7bef94 100644 --- a/arch/arm/mach-ux500/include/mach/shrm_private.h +++ b/arch/arm/mach-ux500/include/mach/shrm_private.h @@ -61,6 +61,7 @@ typedef void (*MSG_PENDING_NOTIF)(const u32 Wptr); * @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 -- cgit v1.2.3 From f21deb0ceda2342d187ddaf611583cf636216696 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 14 Oct 2011 09:47:42 +0200 Subject: mach_ux500: Add support for mloader. Add support for mloader platform driver for u4500. ST-Ericsson ID: AP 335929 ST-Ericsson FOSS-OUT ID: NA Change-Id: I0df37c29121097e078277b8989d33dfc9963a1f4 Signed-off-by: Sesahgiri.Holi Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/21012 Reviewed-by: Martin PERSSON Reviewed-by: Preetham-rao K Tested-by: Preetham-rao K Reviewed-by: Srinidhi KASAGAR Conflicts: arch/arm/mach-ux500/Kconfig arch/arm/mach-ux500/Makefile arch/arm/mach-ux500/board-u5500.c --- arch/arm/mach-ux500/mloader-db5500.c | 186 +++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 arch/arm/mach-ux500/mloader-db5500.c diff --git a/arch/arm/mach-ux500/mloader-db5500.c b/arch/arm/mach-ux500/mloader-db5500.c new file mode 100644 index 00000000000..946a0ff490d --- /dev/null +++ b/arch/arm/mach-ux500/mloader-db5500.c @@ -0,0 +1,186 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Authors: Jonas Aaberg + * Paer-Olof Haakansson + * for ST-Ericsson. + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include + +static ssize_t db5500_mloader_sysfs_addr(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t db5500_mloader_sysfs_finalize(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + + +static DEVICE_ATTR(addr, S_IRUSR|S_IRGRP, db5500_mloader_sysfs_addr, NULL); +static DEVICE_ATTR(finalize, S_IWUSR, NULL, db5500_mloader_sysfs_finalize); + +static unsigned int db5500_bootargs_memmap_modem_start; +static unsigned int db5500_bootargs_memmap_modem_total_size; + +static unsigned int db5500_mloader_shm_total_size; +module_param_named(shm_total_size, db5500_mloader_shm_total_size, uint, 0600); +MODULE_PARM_DESC(shm_total_size, "Total Size of SHM shared memory"); + +static int __init db5500_bootargs_memmap(char *str) +{ + char start_val_str[10]; + char *next_val_str; + int ret; + + next_val_str = strchr(str, '$'); + if (next_val_str == NULL) + return -EINVAL; + strncpy(start_val_str, str, next_val_str - str); + ret = strict_strtoul(start_val_str, 0, + &db5500_bootargs_memmap_modem_total_size); + if (ret < 0) + return -EINVAL; + + ret = strict_strtoul(next_val_str + 1, 0, + &db5500_bootargs_memmap_modem_start); + if (ret < 0) + return -EINVAL; + + return 1; +} +__setup("memmap=", db5500_bootargs_memmap); + +static int __init db5500_bootargs_shm_total_size(char *str) +{ + int ret; + ret = strict_strtoul(str, 0, &db5500_mloader_shm_total_size); + if (ret < 0) + return -EINVAL; + return 1; +} +__setup("mloader.shm_total_size=", db5500_bootargs_shm_total_size); + +static int __exit db5500_mloader_remove(struct platform_device *pdev) +{ + sysfs_remove_file(&pdev->dev.kobj, &dev_attr_addr.attr); + sysfs_remove_file(&pdev->dev.kobj, &dev_attr_finalize.attr); + + return 0; +} + + +static struct platform_driver db5500_mloader_driver = { + .driver = { + .name = "db5500_mloader", + }, + .remove = __exit_p(db5500_mloader_remove), +}; + +struct db5500_mloader { + struct work_struct work; + struct platform_device *pdev; +}; + +static void db5500_mloader_clean_up(struct work_struct *work) +{ + struct db5500_mloader *m = container_of(work, + struct db5500_mloader, + work); + + /* Remove this module */ + platform_device_unregister(m->pdev); + + platform_driver_unregister(&db5500_mloader_driver); + kfree(m); + +} + +static ssize_t db5500_mloader_sysfs_addr(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "0x%x 0x%x 0x%x\n", + db5500_bootargs_memmap_modem_start, + db5500_bootargs_memmap_modem_total_size, + db5500_mloader_shm_total_size); +} + +static ssize_t db5500_mloader_sysfs_finalize(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct db5500_mloader *m; + + m = kmalloc(sizeof(struct db5500_mloader), GFP_KERNEL); + + m->pdev = container_of(dev, + struct platform_device, + dev); + + INIT_WORK(&m->work, db5500_mloader_clean_up); + + /* The module can not remove itself while being in a sysfs function, + * it has to use a workqueue. + */ + schedule_work(&m->work); + + return count; +} + +static void db5500_mloader_release(struct device *dev) +{ + /* Nothing to release */ +} + +static int __init db5500_mloader_probe(struct platform_device *pdev) +{ + int ret = 0; + + pdev->dev.release = db5500_mloader_release; + + ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_addr.attr); + if (ret) + return ret; + ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_finalize.attr); + + if (ret) { + sysfs_remove_file(&pdev->dev.kobj, &dev_attr_addr.attr); + return ret; + } + + return 0; + +} + +static int __init db5500_mloader_init(void) +{ +/* + * mloader for Fairbanks. It exports the physical + * address where the modem side ELF should be located in a sysfs + * file to make it available for a user space utility. + * When the mLoader utility has picked up these settings, this module is no + * longer needed and can be removed by writing to sysfs finalize. + * + * The modem side should be loaded via mmap'ed /dev/mem + * + */ + + return platform_driver_probe(&db5500_mloader_driver, + db5500_mloader_probe); +} +module_init(db5500_mloader_init); + + +static void __exit mloader_exit(void) +{ + platform_driver_unregister(&db5500_mloader_driver); +} +module_exit(mloader_exit); + +MODULE_AUTHOR("Jonas Aaberg "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 7fd6e650c4aeacd1221e30082f955baa70f887c7 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 14 Oct 2011 11:13:35 +0200 Subject: u5500 : kernel-doc for Mailbox Logical Driver Kernle-Doc created for u5500 Mailbox Logical Driver ST-Ericsson ID: 277198 Change-Id: I6e1298615a3943177fa05342a9ff8215e521fa72 Signed-off-by: Bibek Basu Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23718 Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR --- Documentation/DocBook/u5500_LogicalMailbox.tmpl | 114 +++++++++++++++++++++ .../mach-ux500/include/mach/mbox_channels-db5500.h | 73 ++++++------- drivers/misc/mbox_channels-db5500.c | 38 ++++++- 3 files changed, 185 insertions(+), 40 deletions(-) create mode 100644 Documentation/DocBook/u5500_LogicalMailbox.tmpl diff --git a/Documentation/DocBook/u5500_LogicalMailbox.tmpl b/Documentation/DocBook/u5500_LogicalMailbox.tmpl new file mode 100644 index 00000000000..71a5d6c7c28 --- /dev/null +++ b/Documentation/DocBook/u5500_LogicalMailbox.tmpl @@ -0,0 +1,114 @@ + + + + + + u5500 Mailbox Logical Driver + + + + Bibek + Basu + +
+ bibek.basu@stericsson.com +
+
+
+
+ + + 2011 + ST-Ericsson + + + + + Linux standard functions + + + + + + + + This documentation is free software; you can redistribute + it and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later + version. + + + + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + + For more details see the file COPYING in the source + distribution of Linux. + + +
+ + + + + Introduction + + This documentation describes the API provided by the U5500 Mailbox Logical Driver. + + + + + Known Bugs And Assumptions + + + + None + + + None. + + + + + + + + + Structures + + This chapter contains the autogenerated documentation of the structures which are + used in the U5500 Mailbox Logical Driver. + +!Iarch/arm/mach-ux500/include/mach/mbox_channels-db5500.h + + + + Public Functions Provided + + List of public interfaces in stmpe driver + +!Edrivers/misc/mbox_channels-db5500.c + + + + Internal Functions Provided + + This chapter contains the autogenerated documentation of the internal functions. + +!Idrivers/misc/mbox_channels-db5500.c + + +
diff --git a/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h b/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h index 829b1708bf1..0b2fc604873 100644 --- a/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h +++ b/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h @@ -12,27 +12,27 @@ #define MAILBOX_NR_OF_DATAWORDS 3 /** - * mbox_channel_cb_t - Definition of the mailbox channel callback. - * @data: Pointer to the data. - * @length: Length of the data. - * @priv: The client's private data. - * - * This function will be called upon reception of complete mbox channel PDU - * or after completion of send operation. - */ + * mbox_channel_cb_t - Definition of the mailbox channel callback. + * @data: Pointer to the data. + * @length: Length of the data. + * @priv: The client's private data. + * + * This function will be called upon reception of complete mbox channel PDU + * or after completion of send operation. + */ typedef void mbox_channel_cb_t (u32 *data, u32 length, void *priv); /** - * mbox_channel_msg - Definition of mbox channel message - * @channel: Channel number. - * @data: Pointer to data to be sent. - * @length: Length of data to be sent. - * @cb: Pointer to the callback function to be called when send - * operation will be finished. - * @priv: The client's private data. - * - * This structure describes mailbox channel message. - */ + * struct mbox_channel_msg - Definition of mbox channel message + * @channel: Channel number. + * @data: Pointer to data to be sent. + * @length: Length of data to be sent. + * @cb: Pointer to the callback function to be called when send + * operation will be finished. + * @priv: The client's private data. + * + * This structure describes mailbox channel message. + */ struct mbox_channel_msg { u16 channel; u32 *data; @@ -41,30 +41,31 @@ struct mbox_channel_msg { void *priv; }; -/** mbox_channel_register - Set up a given mailbox channel. - * @channel: Mailbox channel number. - * @cb: Pointer to the callback function to be called when a new message - * is received. - * @priv: Client user data which will be returned in the callback. - * - * Returns 0 on success or a negative error code on error. - */ +/** + * mbox_channel_register - Set up a given mailbox channel. + * @channel: Mailbox channel number. + * @cb: Pointer to the callback function to be called when a new message + * is received. + * @priv: Client user data which will be returned in the callback. + * + * Returns 0 on success or a negative error code on error. + */ int mbox_channel_register(u16 channel, mbox_channel_cb_t *cb, void *priv); /** - * mbox_channel_send - Send data on given mailbox channel. - * @msg: Mailbox channel message to be sent. - * - * Returns 0 on success or a negative error code on error. - */ + * mbox_channel_send - Send data on given mailbox channel. + * @msg: Mailbox channel message to be sent. + * + * Returns 0 on success or a negative error code on error. + */ int mbox_channel_send(struct mbox_channel_msg *msg); /** - * mbox_channel_revoke_messages - Revoke messages on given mailbox channel. - * @channel: Mailbox channel number. - * - * Returns 0 on success or a negative error code on error. - */ + * mbox_channel_revoke_messages - Revoke messages on given mailbox channel. + * @channel: Mailbox channel number. + * + * Returns 0 on success or a negative error code on error. + */ int mbox_channel_revoke_messages(u16 channel); #endif /*INC_STE_MBOX_H*/ diff --git a/drivers/misc/mbox_channels-db5500.c b/drivers/misc/mbox_channels-db5500.c index 74fdfa7aaae..64aaa29cc4f 100644 --- a/drivers/misc/mbox_channels-db5500.c +++ b/drivers/misc/mbox_channels-db5500.c @@ -579,6 +579,17 @@ static void mbox_cb(u32 mbox_msg, void *priv) } } +/** + * mbox_channel_register() - Registers for a channel + * @channel: Channel Number. + * @cb: Pointer to function pointer mbox_channel_cb_t + * @priv: Pointer to private data + * + * This routine is used to register for a logical channel. + * It first does sanity check on the requested channel availability + * and parameters. Then it prepares internal entry for the channel. + * And send a OPEN request for that channel. + */ int mbox_channel_register(u16 channel, mbox_channel_cb_t *cb, void *priv) { struct channel_status *rx_chan; @@ -668,6 +679,17 @@ exit: } EXPORT_SYMBOL(mbox_channel_register); +/** + * mbox_channel_send() - Send messages + * @msg: Pointer to mbox_channel_msg data structure. + * + * This routine is used to send messages over the registered logical + * TX channel. It first does sanity check on the message paramenters. + * It registered channel is not found then it just registers for that + * channel. If channel found, it puts the message to the pending list. + * If channel is OPEN, it then pushes the message to the mailbox in + * FIFO manner from the pending list. + */ int mbox_channel_send(struct mbox_channel_msg *msg) { struct list_head *pos, *tx_list; @@ -773,7 +795,15 @@ static void revoke_pending_msgs(struct channel_status *tx_chan) } } -/* Clear all pending messages from TX channel */ +/** + * mbox_channel_revoke_messages() - Revoke pending messages + * @channel: Channel on which action to be taken. + * + * This routine Clear all pending messages from TX channel + * It searches for the channel.Checks if there is pending + * messages.Calls if tehre is any registered function. And + * deletes the messages for the pending list. + */ int mbox_channel_revoke_messages(u16 channel) { struct list_head *pos, *tx_list; @@ -837,14 +867,14 @@ static int registration_done; * struct mboxtest_data - mbox test via debugfs information * @rx_buff: Buffer for incomming data * @rx_pointer: Ptr to actual RX data buff - * @tx_buff Buffer for outgoing data + * @tx_buff: Buffer for outgoing data * @tx_pointer: Ptr to actual TX data buff * @tx_done: TX Transfer done indicator * @rx_done: RX Transfer done indicator - * @received Received words + * @received: Received words * @xfer_words: Num of bytes in actual trf * @xfers: Number of transfers - * @words Number of total words + * @words: Number of total words * @channel: Channel test number */ struct mboxtest_data { -- cgit v1.2.3 From b311033ce81d805d2de23d88b1f19bea19258197 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 14 Oct 2011 12:02:54 +0200 Subject: arm: ux500: common dbx500 prcmu driver api This patch updates the PRCMU driver API to be the same (as far as possible) in U8500 and U4500. - has been renamed . - The platform specific APIs have moved to and (but these should not be directly included). - The PRCMU QoS API has been put in . ST Ericsson ID: 334772 ST Ericsson FOSS-OUT ID: trivial ST Ericsson Linux next: 318371 Change-Id: I6ce117ec35ebf2e987178ccacce09afb554d2736 Signed-off-by: Mattias Nilsson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23863 Reviewed-by: Jonas ABERG --- drivers/misc/shrm/shrm_protocol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/shrm/shrm_protocol.c b/drivers/misc/shrm/shrm_protocol.c index 0598c62812f..567c9b93d5f 100644 --- a/drivers/misc/shrm/shrm_protocol.c +++ b/drivers/misc/shrm/shrm_protocol.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include -- cgit v1.2.3 From 90340709abb21e9124846cf39460c4dd9769febb Mon Sep 17 00:00:00 2001 From: Bibek Basu Date: Tue, 31 May 2011 15:49:11 +0530 Subject: u5500 : Change flag to GFP_ATOMIC for memory req In interrupt handler one should request for memory with flag as GFP_ATOMIC ST-Ericsson Linux next: 336280 ST-Ericsson ID: 343229 ST-Ericsson FOSS-OUT ID: N/00201-FEA 212 8003 Signed-off-by: Bibek Basu Change-Id: Ic5510e48f6e4d7acbdfa3ac681ecd2fac1e60e46 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24222 Reviewed-by: QATEST Reviewed-by: Linus WALLEIJ Reviewed-by: Martin PERSSON Reviewed-by: Srinidhi KASAGAR --- drivers/misc/mbox_channels-db5500.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/mbox_channels-db5500.c b/drivers/misc/mbox_channels-db5500.c index 64aaa29cc4f..1fb883f3296 100644 --- a/drivers/misc/mbox_channels-db5500.c +++ b/drivers/misc/mbox_channels-db5500.c @@ -475,7 +475,7 @@ static void handle_open_msg(u16 channel, u8 mbox_id) schedule_work(&tx_chan->open_msg); } else { /* No tx channel found on the list, allocate new element */ - tx_chan = kzalloc(sizeof(*tx_chan), GFP_KERNEL); + tx_chan = kzalloc(sizeof(*tx_chan), GFP_ATOMIC); if (tx_chan == NULL) { dev_err(&channels.pdev->dev, "failed to allocate memory\n"); -- cgit v1.2.3 From 73ca73d5a459a4d52c5d6b7c09e8c3a691dc73da Mon Sep 17 00:00:00 2001 From: "Sesahgiri.Holi" Date: Thu, 2 Jun 2011 10:12:35 +0530 Subject: mach-ux500: Adapt mloader to new mem layout ST-Ericsson ID: AP 340293 ST-Ericsson FOSS-OUT ID: NA Signed-off-by: Sesahgiri.Holi Change-Id: I6e470258e5d0794649bdbfa8fe469984531f8051 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24505 Reviewed-by: Martin PERSSON Reviewed-by: QATEST Reviewed-by: Preetham-rao K Tested-by: Preetham-rao K Reviewed-by: Srinidhi KASAGAR --- arch/arm/mach-ux500/mloader-db5500.c | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/arch/arm/mach-ux500/mloader-db5500.c b/arch/arm/mach-ux500/mloader-db5500.c index 946a0ff490d..7798a1646d4 100644 --- a/arch/arm/mach-ux500/mloader-db5500.c +++ b/arch/arm/mach-ux500/mloader-db5500.c @@ -31,29 +31,15 @@ static unsigned int db5500_mloader_shm_total_size; module_param_named(shm_total_size, db5500_mloader_shm_total_size, uint, 0600); MODULE_PARM_DESC(shm_total_size, "Total Size of SHM shared memory"); -static int __init db5500_bootargs_memmap(char *str) +static int __init db5500_bootargs_modem_memmap(char *p) { - char start_val_str[10]; - char *next_val_str; - int ret; - - next_val_str = strchr(str, '$'); - if (next_val_str == NULL) - return -EINVAL; - strncpy(start_val_str, str, next_val_str - str); - ret = strict_strtoul(start_val_str, 0, - &db5500_bootargs_memmap_modem_total_size); - if (ret < 0) - return -EINVAL; + db5500_bootargs_memmap_modem_total_size = memparse(p, &p); + if (*p == '@') + db5500_bootargs_memmap_modem_start = memparse(p + 1, &p); - ret = strict_strtoul(next_val_str + 1, 0, - &db5500_bootargs_memmap_modem_start); - if (ret < 0) - return -EINVAL; - - return 1; + return 0; } -__setup("memmap=", db5500_bootargs_memmap); +early_param("mem_modem", db5500_bootargs_modem_memmap); static int __init db5500_bootargs_shm_total_size(char *str) { @@ -63,7 +49,7 @@ static int __init db5500_bootargs_shm_total_size(char *str) return -EINVAL; return 1; } -__setup("mloader.shm_total_size=", db5500_bootargs_shm_total_size); +early_param("mloader.shm_total_size", db5500_bootargs_shm_total_size); static int __exit db5500_mloader_remove(struct platform_device *pdev) { -- cgit v1.2.3 From fd98160e672ebc14dc5b86516b8c30357025027a Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Wed, 1 Jun 2011 08:20:32 +0200 Subject: misc: shrm: Remove u8500 ED support ST-Ericsson Linux next: Not tested, ask SSM for ER ST-Ericsson ID: 342987 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I10725c885b3492014f5ee6006e188d569383960b Signed-off-by: Jonas Aaberg Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24278 Reviewed-by: QATEST Reviewed-by: Hemant-vilas RAMDASI Reviewed-by: Kumar A SANGHVI --- drivers/misc/shrm/Kconfig | 6 ------ drivers/misc/shrm/shrm_protocol.c | 8 ++------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/misc/shrm/Kconfig b/drivers/misc/shrm/Kconfig index fffee1c703e..6bafdeec5b9 100644 --- a/drivers/misc/shrm/Kconfig +++ b/drivers/misc/shrm/Kconfig @@ -14,12 +14,6 @@ choice depends on U8500_SHRM default SHRM_V1_UPDATES_VERSION - config SHRM_ED_V1_VERSION - depends on U8500_SHRM - bool "SHRM ED / V1 " - help - Modem Images with ED/V1 updates - config SHRM_V1_UPDATES_VERSION depends on U8500_SHRM bool "SHRM V1 UPDATES" diff --git a/drivers/misc/shrm/shrm_protocol.c b/drivers/misc/shrm/shrm_protocol.c index 567c9b93d5f..315d4b7ff6e 100644 --- a/drivers/misc/shrm/shrm_protocol.c +++ b/drivers/misc/shrm/shrm_protocol.c @@ -37,7 +37,6 @@ 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; -static char is_earlydrop; struct sock *shrm_nl_sk; static char shrm_common_tx_state = SHRM_SLEEP_STATE; @@ -698,11 +697,8 @@ int shrm_protocol_init(struct shrm_dev *shrm, rx_audio_handler = audio_rx_handler; atomic_set(&ac_sleep_disable_count, 0); - is_earlydrop = cpu_is_u8500ed(); - if (is_earlydrop != 0x01) { - hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - timer.function = callback; - } + hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + timer.function = callback; shrm->shm_common_ch_wr_wq = create_singlethread_workqueue ("shm_common_channel_irq"); -- cgit v1.2.3 From 13e73ab3ca41a236ff8574a1f113e5f8ba60c022 Mon Sep 17 00:00:00 2001 From: Robert Marklund Date: Thu, 23 Jun 2011 13:27:23 +0200 Subject: misc: shrm: Update for header file changes 3.0 Change-Id: I7fa83c559bb65115ca3117a8df092d4f169d2390 Signed-off-by: Robert Marklund --- drivers/char/shrm_char.c | 1 + drivers/misc/shrm/modem_shrm_driver.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/shrm_char.c b/drivers/char/shrm_char.c index 6a6b3d7245a..6375d7f1ea3 100644 --- a/drivers/char/shrm_char.c +++ b/drivers/char/shrm_char.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include diff --git a/drivers/misc/shrm/modem_shrm_driver.c b/drivers/misc/shrm/modem_shrm_driver.c index 29368950256..4f6145f9d01 100644 --- a/drivers/misc/shrm/modem_shrm_driver.c +++ b/drivers/misc/shrm/modem_shrm_driver.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 2a418882e47a84ff7b8bcd0204e848db7035aefb Mon Sep 17 00:00:00 2001 From: Kumar Sanghvi Date: Mon, 16 May 2011 20:56:25 +0530 Subject: drivers: Add Modem Access Framework Adds Modem Access Framework, which allows for registering platform specific modem access mechanisms. The framework also exposes APIs for client drivers for getting and releasing access to modem, regardless of the underlying platform specific access mechanism. ST-Ericsson ID: CR329459 Change-Id: I643c56892e4423ec27467cbcfe8a22586c1e3f6a Signed-off-by: Kumar Sanghvi Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23553 Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR --- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/modem/Kconfig | 7 + drivers/modem/Makefile | 2 + drivers/modem/modem_access.c | 416 +++++++++++++++++++++++++++++++++++++ include/linux/modem/modem.h | 63 ++++++ include/linux/modem/modem_client.h | 53 +++++ 7 files changed, 544 insertions(+) create mode 100644 drivers/modem/Kconfig create mode 100644 drivers/modem/Makefile create mode 100644 drivers/modem/modem_access.c create mode 100644 include/linux/modem/modem.h create mode 100644 include/linux/modem/modem_client.h diff --git a/drivers/Kconfig b/drivers/Kconfig index d236aef7e59..ae18ca02e32 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -140,4 +140,6 @@ source "drivers/virt/Kconfig" source "drivers/devfreq/Kconfig" +source "drivers/modem/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 95952c82bf1..831a5d43937 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -99,6 +99,7 @@ obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_CPU_IDLE) += cpuidle/ obj-y += mmc/ obj-$(CONFIG_MEMSTICK) += memstick/ +obj-$(CONFIG_MODEM) += modem/ obj-y += leds/ obj-$(CONFIG_INFINIBAND) += infiniband/ obj-$(CONFIG_SGI_SN) += sn/ diff --git a/drivers/modem/Kconfig b/drivers/modem/Kconfig new file mode 100644 index 00000000000..000181cdb3d --- /dev/null +++ b/drivers/modem/Kconfig @@ -0,0 +1,7 @@ +config MODEM + bool "Modem Access Framework" + default n + help + Add support for Modem Access Framework. It allows different + platform specific drivers to register modem access mechanisms + and allows transparent access to modem to the client drivers. diff --git a/drivers/modem/Makefile b/drivers/modem/Makefile new file mode 100644 index 00000000000..0526714bebd --- /dev/null +++ b/drivers/modem/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_MODEM) := modem_access.o + diff --git a/drivers/modem/modem_access.c b/drivers/modem/modem_access.c new file mode 100644 index 00000000000..7337029c251 --- /dev/null +++ b/drivers/modem/modem_access.c @@ -0,0 +1,416 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * License Terms: GNU General Public License v2 + * Author: Kumar Sanghvi + * + * Heavily adapted from Regulator framework. + * Provides mechanisms for registering platform specific access + * mechanisms for modem. + * Also, exposes APIs for gettng/releasing the access and even + * query the access status, and the modem usage status. + */ +#include +#include +#include +#include + +static DEFINE_MUTEX(modem_list_mutex); +static LIST_HEAD(modem_list); + +struct modem { + struct device *dev; + struct list_head list; + char *modem_name; + struct device_attribute dev_attr; + struct modem_dev *mdev; + atomic_t use; +}; + +static const char *mdev_get_name(struct modem_dev *mdev) +{ + if (mdev->desc->name) + return mdev->desc->name; + else + return ""; +} + +static int _modem_is_requested(struct modem_dev *mdev) +{ + /* If we don't know then assume that the modem is always on */ + if (!mdev->desc->ops->is_requested) + return 0; + + return mdev->desc->ops->is_requested(mdev); +} + +/** + * modem_is_requested - check if modem access is requested + * @modem: modem device + * + * Checks whether modem is accessed or not by querying + * the underlying platform specific modem access + * implementation. + */ +int modem_is_requested(struct modem *modem) +{ + int ret; + + mutex_lock(&modem->mdev->mutex); + ret = _modem_is_requested(modem->mdev); + mutex_unlock(&modem->mdev->mutex); + + return ret; +} +EXPORT_SYMBOL(modem_is_requested); + +static int _modem_request(struct modem_dev *mdev) +{ + int ret; + + if (++mdev->use_count == 1) { + ret = _modem_is_requested(mdev); + if (ret == 0) + mdev->desc->ops->request(mdev); + } + + return 0; +} + +/** + * modem_request - Request access the modem + * @modem: modem device + * + * API to access the modem. It keeps a client + * specific check on whether the particular modem + * requested is accessed or not. + */ +void modem_request(struct modem *modem) +{ + struct modem_dev *mdev = modem->mdev; + int ret = 0; + + + mutex_lock(&mdev->mutex); + if (atomic_read(&modem->use) == 1) { + mutex_unlock(&mdev->mutex); + return; + } + ret = _modem_request(mdev); + if (ret == 0) + atomic_set(&modem->use, 1); + mutex_unlock(&mdev->mutex); +} +EXPORT_SYMBOL(modem_request); + +static int _modem_release(struct modem_dev *mdev) +{ + if (WARN(mdev->use_count <= 0, + "unbalanced releases for %s\n", + mdev_get_name(mdev))) + return -EIO; + + if (--mdev->use_count == 0) + mdev->desc->ops->release(mdev); + + return 0; +} + +/** + * modem_release - Release access to modem + * @modem: modem device + * + * Releases accesss to the modem. It keeps a client + * specific check on whether a particular modem + * is released or not. + */ +void modem_release(struct modem *modem) +{ + struct modem_dev *mdev = modem->mdev; + int ret = 0; + + mutex_lock(&mdev->mutex); + if (atomic_read(&modem->use) == 0) { + mutex_unlock(&mdev->mutex); + return; + } + ret = _modem_release(mdev); + if (ret == 0) + atomic_set(&modem->use, 0); + mutex_unlock(&mdev->mutex); +} +EXPORT_SYMBOL(modem_release); + +/** + * modem_get_usage - Check if particular client is using modem + * @modem: modem device + * + * Checks whether the particular client is using access to modem. + * This API could be used by client drivers in making their + * suspend decisions. + */ +int modem_get_usage(struct modem *modem) +{ + return atomic_read(&modem->use); +} +EXPORT_SYMBOL(modem_get_usage); + +static struct modem *create_modem(struct modem_dev *mdev, + struct device *dev, + const char *id) +{ + struct modem *modem; + + modem = kzalloc(sizeof(*modem), GFP_KERNEL); + if (modem == NULL) + return NULL; + + mutex_lock(&mdev->mutex); + modem->mdev = mdev; + modem->dev = dev; + list_add(&modem->list, &mdev->client_list); + + mutex_unlock(&mdev->mutex); + return modem; + +} + +static struct modem *_modem_get(struct device *dev, const char *id, + int exclusive) +{ + struct modem_dev *mdev_ptr; + struct modem *modem = ERR_PTR(-ENODEV); + int ret; + + if (id == NULL) { + pr_err("modem_get with no identifier\n"); + return modem; + } + + mutex_lock(&modem_list_mutex); + list_for_each_entry(mdev_ptr, &modem_list, modem_list) { + if (strcmp(mdev_get_name(mdev_ptr), id) == 0) + goto found; + } + + return ERR_PTR(-ENODEV); + +found: + if (!try_module_get(mdev_ptr->owner)) + goto out; + + modem = create_modem(mdev_ptr, dev, id); + if (modem == NULL) { + modem = ERR_PTR(-ENOMEM); + module_put(mdev_ptr->owner); + } + + mdev_ptr->open_count++; + ret = _modem_is_requested(mdev_ptr); + if (ret) + mdev_ptr->use_count = 1; + else + mdev_ptr->use_count = 0; + +out: + mutex_unlock(&modem_list_mutex); + return modem; + +} + +/** + * modem_get - Get reference to a particular platform specific modem + * @dev: device + * @id: modem device name + * + * Get reference to a particular modem device. + */ +struct modem *modem_get(struct device *dev, const char *id) +{ + return _modem_get(dev, id, 0); +} +EXPORT_SYMBOL(modem_get); + +/** + * modem_put - Release reference to a modem device + * @modem: modem device + * + * Release reference to a modem device. + */ +void modem_put(struct modem *modem) +{ + struct modem_dev *mdev; + + if (modem == NULL || IS_ERR(modem)) + return; + + mutex_lock(&modem_list_mutex); + mdev = modem->mdev; + + list_del(&modem->list); + kfree(modem); + + mdev->open_count--; + + module_put(mdev->owner); + mutex_unlock(&modem_list_mutex); +} +EXPORT_SYMBOL(modem_put); + +static ssize_t modem_print_state(char *buf, int state) +{ + if (state > 0) + return sprintf(buf, "accessed\n"); + else if (state == 0) + return sprintf(buf, "released\n"); + else + return sprintf(buf, "unknown\n"); +} + +static ssize_t modem_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct modem_dev *mdev = dev_get_drvdata(dev); + ssize_t ret; + + mutex_lock(&mdev->mutex); + ret = modem_print_state(buf, _modem_is_requested(mdev)); + mutex_unlock(&mdev->mutex); + + return ret; +} +static DEVICE_ATTR(state, 0444, modem_state_show, NULL); + +static ssize_t modem_use_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct modem_dev *mdev = dev_get_drvdata(dev); + struct modem *mod; + size_t size = 0; + + list_for_each_entry(mod, &mdev->client_list, list) { + if (mod->dev != NULL) + size += sprintf((buf + size), "%s (%d)\n", + dev_name(mod->dev), atomic_read(&mod->use)); + else + size += sprintf((buf + size), "unknown (%d)\n", + atomic_read(&mod->use)); + } + size += sprintf((buf + size), "\n"); + + return size; +} +static DEVICE_ATTR(use, 0444, modem_use_show, NULL); + +static ssize_t modem_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct modem_dev *mdev = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", mdev_get_name(mdev)); +} +static DEVICE_ATTR(name, 0444, modem_name_show, NULL); + +static ssize_t modem_num_active_users_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct modem_dev *mdev = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", mdev->use_count); +} +static DEVICE_ATTR(num_active_users, 0444, modem_num_active_users_show, NULL); + +static int add_modem_attributes(struct modem_dev *mdev) +{ + struct device *dev = &mdev->dev; + struct modem_ops *ops = mdev->desc->ops; + int status = 0; + + status = device_create_file(dev, &dev_attr_use); + if (status < 0) + return status; + + status = device_create_file(dev, &dev_attr_name); + if (status < 0) + return status; + + status = device_create_file(dev, &dev_attr_num_active_users); + if (status < 0) + return status; + + if (ops->is_requested) { + status = device_create_file(dev, &dev_attr_state); + if (status < 0) + return status; + } + + return 0; +} + +/** + * modem_register - register a modem + * @modem_desc: - description for modem + * @dev: - device + * @driver_data:- driver specific data + * + * Register a modem with the modem access framework, so that + * it could be used by client drivers for accessing the + * modem. + */ +struct modem_dev *modem_register(struct modem_desc *modem_desc, + struct device *dev, + void *driver_data) +{ + static atomic_t modem_no = ATOMIC_INIT(0); + struct modem_dev *mdev; + int ret; + + if (modem_desc == NULL) + return ERR_PTR(-EINVAL); + + if (modem_desc->name == NULL || modem_desc->ops == NULL) + return ERR_PTR(-EINVAL); + + mdev = kzalloc(sizeof(struct modem_dev), GFP_KERNEL); + if (mdev == NULL) + return ERR_PTR(-ENOMEM); + + mutex_lock(&modem_list_mutex); + + mutex_init(&mdev->mutex); + mdev->modem_data = driver_data; + mdev->owner = modem_desc->owner; + mdev->desc = modem_desc; + INIT_LIST_HEAD(&mdev->client_list); + INIT_LIST_HEAD(&mdev->modem_list); + BLOCKING_INIT_NOTIFIER_HEAD(&mdev->notifier); + + /* mdev->dev.class = &modem_class;*/ + mdev->dev.parent = dev; + dev_set_name(&mdev->dev, "modem.%d", atomic_inc_return(&modem_no) - 1); + ret = device_register(&mdev->dev); + if (ret != 0) + goto clean; + + dev_set_drvdata(&mdev->dev, mdev); + + ret = add_modem_attributes(mdev); + if (ret < 0) + goto backoff; + + list_add(&mdev->modem_list, &modem_list); + +out: + mutex_unlock(&modem_list_mutex); + return mdev; + +backoff: + device_unregister(&mdev->dev); + mdev = ERR_PTR(ret); + goto out; + +clean: + kfree(mdev); + mdev = ERR_PTR(ret); + goto out; +} +EXPORT_SYMBOL(modem_register); diff --git a/include/linux/modem/modem.h b/include/linux/modem/modem.h new file mode 100644 index 00000000000..c9614a9b061 --- /dev/null +++ b/include/linux/modem/modem.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * License Terms: GNU General Public License v2 + * Author: Kumar Sanghvi + * + * Heavily adapted from Regulator framework + */ +#ifndef __MODEM_H__ +#define __MODEM_H__ + +#include + +struct modem_dev; + +struct modem_ops { + void (*request)(struct modem_dev *); + void (*release)(struct modem_dev *); + int (*is_requested)(struct modem_dev *); +}; + +struct modem_desc { + const char *name; + int id; + struct modem_ops *ops; + struct module *owner; +}; + +struct modem_dev { + struct modem_desc *desc; + int use_count; + int open_count; + int exclusive; + + struct list_head modem_list; + + struct list_head client_list; + + struct blocking_notifier_head notifier; + struct mutex mutex; + struct module *owner; + struct device dev; + void *modem_data; +}; + +#ifdef CONFIG_MODEM +struct modem_dev *modem_register(struct modem_desc *modem_desc, + struct device *dev, + void *driver_data); +void modem_unregister(struct modem_dev *mdev); + +#else +static inline struct modem_dev *modem_register(struct modem_desc *modem_desc, + struct device *dev, void *driver_data) +{ + return NULL; +} + +static inline void modem_unregister(struct modem_dev *mdev) +{ +} +#endif +#endif /* __MODEM_H__ */ diff --git a/include/linux/modem/modem_client.h b/include/linux/modem/modem_client.h new file mode 100644 index 00000000000..21f04798490 --- /dev/null +++ b/include/linux/modem/modem_client.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * License Terms: GNU General Public License v2 + * Author: Kumar Sanghvi + * + * Heavily adapted from Regulator framework + */ +#ifndef __MODEM_CLIENT_H__ +#define __MODEM_CLIENT_H__ + +#include + +struct modem; + +#ifdef CONFIG_MODEM +struct modem *modem_get(struct device *dev, const char *id); +void modem_put(struct modem *modem); +void modem_request(struct modem *modem); +void modem_release(struct modem *modem); +int modem_is_requested(struct modem *modem); +int modem_get_usage(struct modem *modem); + +#else + +static inline struct modem *modem_get(struct device *dev, const char *id) +{ + return NULL; +} + +static inline void modem_put(struct modem *modem) +{ +} + +static inline void modem_request(struct modem *modem) +{ +} + +static inline void modem_release(struct modem *modem) +{ +} + +static inline int modem_is_requested(struct modem *modem) +{ + return 0; +} + +static inline int modem_get_usage(struct modem *modem) +{ + return 0; +} +#endif +#endif /* __MODEM_CLIENT_H__ */ -- cgit v1.2.3 From 34f084d5cedadd99c5084f6d2e295dfb5eac1d23 Mon Sep 17 00:00:00 2001 From: Kumar Sanghvi Date: Mon, 16 May 2011 21:00:49 +0530 Subject: drivers: modem: Add modem access driver for STE U8500 Adds platform driver which implements mechanism for accessing modem on STE U8500 platform which uses Shared Memory for communicating between Modem and Application processor. The driver also registers itself with the Modem Access framework. ST-Ericsson ID: CR329459 Change-Id: Id5bc18f6f974b026f9be2d3e2756e03417e01e07 Signed-off-by: Kumar Sanghvi Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23554 Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR --- drivers/modem/Kconfig | 19 +++++++-- drivers/modem/Makefile | 3 +- drivers/modem/modem_u8500.c | 94 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 drivers/modem/modem_u8500.c diff --git a/drivers/modem/Kconfig b/drivers/modem/Kconfig index 000181cdb3d..a23b5c90e5e 100644 --- a/drivers/modem/Kconfig +++ b/drivers/modem/Kconfig @@ -2,6 +2,19 @@ config MODEM bool "Modem Access Framework" default n help - Add support for Modem Access Framework. It allows different - platform specific drivers to register modem access mechanisms - and allows transparent access to modem to the client drivers. + Add support for Modem Access Framework. It allows different + platform specific drivers to register modem access mechanisms + and allows transparent access to modem to the client drivers. + + If unsure, say N. + +config MODEM_U8500 + bool "Modem Access driver for STE U8500 platform" + depends on MODEM + default n + help + Add support for Modem Access driver on STE U8500 platform which + uses Shared Memroy as IPC mechanism between Modem processor and + Application processor. + + If unsure, say N. diff --git a/drivers/modem/Makefile b/drivers/modem/Makefile index 0526714bebd..57b50916c79 100644 --- a/drivers/modem/Makefile +++ b/drivers/modem/Makefile @@ -1,2 +1,3 @@ -obj-$(CONFIG_MODEM) := modem_access.o +obj-$(CONFIG_MODEM) := modem_access.o +obj-$(CONFIG_MODEM_U8500) += modem_u8500.o diff --git a/drivers/modem/modem_u8500.c b/drivers/modem/modem_u8500.c new file mode 100644 index 00000000000..20b5fe78ef7 --- /dev/null +++ b/drivers/modem/modem_u8500.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * License Terms: GNU General Public License v2 + * Author: Kumar Sanghvi + * + * Platform driver implementing access mechanisms to modem + * on U8500 which uses Shared Memroy as IPC between Application + * Processor and Modem processor. + */ +#include +#include +#include +#include + +static void u8500_modem_request(struct modem_dev *mdev) +{ + prcmu_ac_wake_req(); +} + +static void u8500_modem_release(struct modem_dev *mdev) +{ + prcmu_ac_sleep_req(); +} + +static int u8500_modem_is_requested(struct modem_dev *mdev) +{ + return prcmu_is_ac_wake_requested(); +} + +static struct modem_ops u8500_modem_ops = { + .request = u8500_modem_request, + .release = u8500_modem_release, + .is_requested = u8500_modem_is_requested, +}; + +static struct modem_desc u8500_modem_desc = { + .name = "u8500-shrm-modem", + .id = 0, + .ops = &u8500_modem_ops, + .owner = THIS_MODULE, +}; + + +static int __devinit u8500_modem_probe(struct platform_device *pdev) +{ + struct modem_dev *mdev; + int err; + + mdev = modem_register(&u8500_modem_desc, &pdev->dev, + NULL); + if (IS_ERR(mdev)) { + err = PTR_ERR(mdev); + pr_err("failed to register %s: err %i\n", + u8500_modem_desc.name, err); + } + + return 0; +} + +static int __devexit u8500_modem_remove(struct platform_device *pdev) +{ + + return 0; +} + +static struct platform_driver u8500_modem_driver = { + .driver = { + .name = "u8500-modem", + .owner = THIS_MODULE, + }, + .probe = u8500_modem_probe, + .remove = __devexit_p(u8500_modem_remove), +}; + +static int __init u8500_modem_init(void) +{ + int ret; + + ret = platform_driver_register(&u8500_modem_driver); + if (ret < 0) { + printk(KERN_ERR "u8500_modem: platform driver reg failed\n"); + return -ENODEV; + } + + return 0; +} + +static void __exit u8500_modem_exit(void) +{ + platform_driver_unregister(&u8500_modem_driver); +} + +arch_initcall(u8500_modem_init); -- cgit v1.2.3 From 99f8c94f9cd6abf8411efecfec038f766ebc6a92 Mon Sep 17 00:00:00 2001 From: Kumar Sanghvi Date: Mon, 16 May 2011 21:06:06 +0530 Subject: u8500: shrm: Adapt to use Modem Access framework Removes calls to prcmu driver APIs. Uses the modem access framework to get/release access to modem. ST-Ericsson ID: CR329459 Change-Id: I7933c0b41863156820a6239ff2cb74e4ed26c3d7 Signed-off-by: Kumar Sanghvi Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23555 Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR --- arch/arm/mach-ux500/include/mach/shrm_driver.h | 2 ++ drivers/misc/shrm/modem_shrm_driver.c | 6 ++++++ drivers/misc/shrm/shrm_protocol.c | 15 ++++++++------- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-ux500/include/mach/shrm_driver.h b/arch/arm/mach-ux500/include/mach/shrm_driver.h index 36bb8413ad9..71b3fbcf229 100644 --- a/arch/arm/mach-ux500/include/mach/shrm_driver.h +++ b/arch/arm/mach-ux500/include/mach/shrm_driver.h @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -115,6 +116,7 @@ struct shrm_dev { 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; diff --git a/drivers/misc/shrm/modem_shrm_driver.c b/drivers/misc/shrm/modem_shrm_driver.c index 4f6145f9d01..a3c8d4bda1b 100644 --- a/drivers/misc/shrm/modem_shrm_driver.c +++ b/drivers/misc/shrm/modem_shrm_driver.c @@ -333,6 +333,12 @@ static int shrm_probe(struct platform_device *pdev) } 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) { diff --git a/drivers/misc/shrm/shrm_protocol.c b/drivers/misc/shrm/shrm_protocol.c index 315d4b7ff6e..686bebcb451 100644 --- a/drivers/misc/shrm/shrm_protocol.c +++ b/drivers/misc/shrm/shrm_protocol.c @@ -20,6 +20,7 @@ #include #include #include +#include #define L2_HEADER_ISI 0x0 #define L2_HEADER_RPC 0x1 @@ -72,14 +73,14 @@ static void shm_ac_sleep_req_work(struct work_struct *work) { mutex_lock(&ac_state_mutex); if (atomic_read(&ac_sleep_disable_count) == 0) - prcmu_ac_sleep_req(); + 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); - prcmu_ac_wake_req(); + modem_request(shm_dev->modem); mutex_unlock(&ac_state_mutex); } @@ -463,7 +464,7 @@ void shm_ca_wake_req_work(struct work_struct *work) shm_fifo_init(shrm); mutex_lock(&ac_state_mutex); - prcmu_ac_wake_req(); + modem_request(shrm->modem); mutex_unlock(&ac_state_mutex); /* send ca_wake_ack_interrupt to CMU */ @@ -614,7 +615,7 @@ static void send_ac_msg_pend_notify_0_work(struct work_struct *work) mutex_lock(&ac_state_mutex); atomic_inc(&ac_sleep_disable_count); - prcmu_ac_wake_req(); + modem_request(shrm->modem); mutex_unlock(&ac_state_mutex); if (!get_host_accessport_val()) @@ -641,7 +642,7 @@ static void send_ac_msg_pend_notify_1_work(struct work_struct *work) mutex_lock(&ac_state_mutex); atomic_inc(&ac_sleep_disable_count); - prcmu_ac_wake_req(); + modem_request(shrm->modem); mutex_unlock(&ac_state_mutex); if (!get_host_accessport_val()) @@ -777,7 +778,6 @@ int shrm_protocol_init(struct shrm_dev *shrm, goto drop; } #endif - return 0; #ifdef CONFIG_U8500_SHRM_MODEM_SILENT_RESET @@ -812,12 +812,13 @@ void shrm_protocol_deinit(struct shrm_dev *shrm) 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) || - prcmu_is_ac_wake_requested()); + modem_get_usage(shm_dev->modem)); } irqreturn_t ca_wake_irq_handler(int irq, void *ctrlr) -- 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 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 8ed330871411fd5fe08c5bdefea9017d21b6250a Mon Sep 17 00:00:00 2001 From: Bibek Basu Date: Wed, 8 Jun 2011 11:07:32 +0530 Subject: Stagging : AB5500 SIM interface Driver SIM Interface driver provides interface to configure various parameters of AB5500 SIM Level Shifter. Support provided are: Configure Pull up on sim lines Configure Operation Mode Notify Sim Insert/Extract Interrupt ST-Ericsson Linux next: 336280 ST-Ericsson ID: 341172 ST-Ericsson FOSS-OUT ID:STETL-FOSS-OUT-10242 Change-Id: I5ef4b92384ca4da0f96839afae7c7468f244978f Signed-off-by: Bibek Basu Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/21582 Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR --- drivers/staging/ab5500_sim/Makefile | 1 + drivers/staging/ab5500_sim/ab5500-sim.c | 299 ++++++++++++++++++++++++++++++++ drivers/staging/ab5500_sim/sysfs-sim | 83 +++++++++ 3 files changed, 383 insertions(+) create mode 100644 drivers/staging/ab5500_sim/Makefile create mode 100644 drivers/staging/ab5500_sim/ab5500-sim.c create mode 100644 drivers/staging/ab5500_sim/sysfs-sim diff --git a/drivers/staging/ab5500_sim/Makefile b/drivers/staging/ab5500_sim/Makefile new file mode 100644 index 00000000000..520717e4dd7 --- /dev/null +++ b/drivers/staging/ab5500_sim/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_AB5500_SIM) += ab5500-sim.o diff --git a/drivers/staging/ab5500_sim/ab5500-sim.c b/drivers/staging/ab5500_sim/ab5500-sim.c new file mode 100644 index 00000000000..788a7984a46 --- /dev/null +++ b/drivers/staging/ab5500_sim/ab5500-sim.c @@ -0,0 +1,299 @@ +/* + * Copyright (C) ST Ericsson SA 2010 + * + * Sim Interface driver for AB5500 + * + * License Terms: GNU General Public License v2 + * Author: Bibek Basu + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USIM_SUP2_REG 0x13 +#define USIM_SUP_REG 0x14 +#define USIM_SIMCTRL_REG 0x17 +#define USIM_SIMCTRL2_REG 0x18 +#define USIM_USBUICC_REG 0x19 +#define USIM_USBUICC2_REG 0x20 +#define SIM_DAT_PULLUP_10K 0x0F +#define SIM_LDO_1_8V 1875000 +#define SIM_LDO_2_8V 2800000 +#define SIM_LDO_2_9V 2900000 + +enum shift { + SHIFT0, + SHIFT1, + SHIFT2, + SHIFT3, + SHIFT4, + SHIFT5, + SHIFT6, + SHIFT7, +}; + +enum mask { + MASK1 = 1, + MASK3 = 3, + MASK7 = 7, +}; + +enum sim_mode { + OFF_MODE, + LOW_PWR, + PWRCTRL, + FULL_PWR, +}; +/** + * struct ab5500_sim - ab5500 Sim Interface device information + * @dev: pointer to the structure device + * @lock: mutex lock + * @sim_int_status: Sim presence status + * @irq_base: Base of the two irqs + */ +struct ab5500_sim { + struct device *dev; + struct mutex lock; + bool sim_int_status; + u8 irq_base; +}; + +/* Exposure to the sysfs interface */ +int ab5500_sim_weak_pulldforce(struct device *dev, + struct device_attribute *attr, + const char *user_buf, size_t count) +{ + unsigned long user_val; + int err; + bool enable; + + err = strict_strtoul(user_buf, 0, &user_val); + if (err) + return -EINVAL; + enable = user_val ? true : false; + err = abx500_mask_and_set(dev, AB5500_BANK_SIM_USBSIM, + USIM_USBUICC2_REG, MASK1 << SHIFT5, user_val << SHIFT5); + if (err) + return -EINVAL; + return count; +} + +int ab5500_sim_load_sel(struct device *dev, + struct device_attribute *attr, + const char *user_buf, size_t count) +{ + unsigned long user_val; + int err; + bool enable; + + err = strict_strtoul(user_buf, 0, &user_val); + if (err) + return -EINVAL; + enable = user_val ? true : false; + err = abx500_mask_and_set(dev, AB5500_BANK_SIM_USBSIM, + USIM_USBUICC_REG, MASK1 << SHIFT1, user_val << SHIFT1); + if (err) + return -EINVAL; + return count; +} + +int ab5500_sim_mode_sel(struct device *dev, + struct device_attribute *attr, + const char *user_buf, size_t count) +{ + unsigned long user_val; + int err; + + err = strict_strtoul(user_buf, 0, &user_val); + if (err) + return -EINVAL; + err = abx500_mask_and_set(dev, AB5500_BANK_SIM_USBSIM, + USIM_SIMCTRL2_REG, MASK3 << SHIFT4, user_val << SHIFT4); + if (err) + return -EINVAL; + return count; +} + +int ab5500_sim_dat_pullup(struct device *dev, + struct device_attribute *attr, + const char *user_buf, size_t count) +{ + unsigned long user_val; + int err; + + err = strict_strtoul(user_buf, 0, &user_val); + if (err) + return -EINVAL; + err = abx500_mask_and_set(dev, AB5500_BANK_SIM_USBSIM, + USIM_SIMCTRL_REG, MASK7, user_val); + if (err) + return -EINVAL; + return count; +} + +int ab5500_sim_enable_pullup(struct device *dev, + struct device_attribute *attr, + const char *user_buf, size_t count) +{ + unsigned long user_val; + int err; + bool enable; + + err = strict_strtoul(user_buf, 0, &user_val); + if (err) + return -EINVAL; + enable = user_val ? true : false; + err = abx500_mask_and_set(dev, AB5500_BANK_SIM_USBSIM, + USIM_SIMCTRL_REG, MASK1 << SHIFT3, enable << SHIFT3); + if (err) + return -EINVAL; + return count; +} + +static ssize_t ab5500_simoff_int(struct device *dev, + struct device_attribute *devattr, char *user_buf) +{ + struct ab5500_sim *di = dev_get_drvdata(dev); + int len; + + mutex_lock(&di->lock); + len = sprintf(user_buf, "%d\n", di->sim_int_status); + mutex_unlock(&di->lock); + return len; +} + +static DEVICE_ATTR(enable_pullup, S_IWUSR, NULL, ab5500_sim_enable_pullup); +static DEVICE_ATTR(dat_pullup, S_IWUSR, NULL, ab5500_sim_dat_pullup); +static DEVICE_ATTR(mode_sel, S_IWUSR, NULL, ab5500_sim_mode_sel); +static DEVICE_ATTR(load_sel, S_IWUSR, NULL, ab5500_sim_load_sel); +static DEVICE_ATTR(weak_pulldforce, S_IWUSR, NULL, ab5500_sim_weak_pulldforce); +static DEVICE_ATTR(simoff_int, S_IRUGO, ab5500_simoff_int, NULL); + +static struct attribute *ab5500_sim_attributes[] = { + &dev_attr_enable_pullup.attr, + &dev_attr_dat_pullup.attr, + &dev_attr_mode_sel.attr, + &dev_attr_load_sel.attr, + &dev_attr_weak_pulldforce.attr, + &dev_attr_simoff_int.attr, + NULL +}; + +static const struct attribute_group ab5500sim_attr_grp = { + .attrs = ab5500_sim_attributes, +}; + +static irqreturn_t ab5500_sim_irq_handler(int irq, void *irq_data) +{ + struct platform_device *pdev = irq_data; + struct ab5500_sim *data = platform_get_drvdata(pdev); + + if (irq == data->irq_base) + data->sim_int_status = true; + else + data->sim_int_status = false; + sysfs_notify(&pdev->dev.kobj, NULL, "simoff_int"); + + return IRQ_HANDLED; +} + +static int __devexit ab5500_sim_remove(struct platform_device *pdev) +{ + struct ab5500_sim *di = platform_get_drvdata(pdev); + int irq = platform_get_irq_byname(pdev, "SIMOFF"); + + if (irq >= 0) { + free_irq(irq, di); + irq++; + free_irq(irq, di); + } + sysfs_remove_group(&pdev->dev.kobj, &ab5500sim_attr_grp); + platform_set_drvdata(pdev, NULL); + kfree(di); + + return 0; +} + +static int __devinit ab5500_sim_probe(struct platform_device *pdev) +{ + int ret = 0; + int irq; + struct ab5500_sim *di = + kzalloc(sizeof(struct ab5500_sim), GFP_KERNEL); + if (!di) + return -ENOMEM; + + dev_info(&pdev->dev, "ab5500_sim_driver PROBE\n"); + irq = platform_get_irq_byname(pdev, "SIMOFF"); + if (irq < 0) { + dev_err(&pdev->dev, "Get irq by name failed\n"); + return irq; + } + di->irq_base = irq; + di->dev = &pdev->dev; + mutex_init(&di->lock); + platform_set_drvdata(pdev, di); + /* sysfs interface to configure sim reg from user space */ + if (sysfs_create_group(&pdev->dev.kobj, &ab5500sim_attr_grp) < 0) { + dev_err(&pdev->dev, " Failed creating sysfs group\n"); + return -ENOMEM; + } + ret = request_threaded_irq(irq, NULL, ab5500_sim_irq_handler, + IRQF_NO_SUSPEND , "ab5500-sim", pdev); + if (ret < 0) { + dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret); + goto exit; + } + /* this is the contiguous irq for sim removal,falling edge */ + irq = irq + 1; + ret = request_threaded_irq(irq, NULL, ab5500_sim_irq_handler, + IRQF_NO_SUSPEND , "ab5500-sim", pdev); + if (ret < 0) { + dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret); + free_irq(--irq, di); + goto exit; + } + return ret; +exit: + sysfs_remove_group(&pdev->dev.kobj, &ab5500sim_attr_grp); + platform_set_drvdata(pdev, NULL); + kfree(di); + return ret; +} + +static struct platform_driver ab5500_sim_driver = { + .probe = ab5500_sim_probe, + .remove = __devexit_p(ab5500_sim_remove), + .driver = { + .name = "ab5500-sim", + .owner = THIS_MODULE, + }, +}; + +static int __init ab5500_sim_init(void) +{ + return platform_driver_register(&ab5500_sim_driver); +} + +static void __exit ab5500_sim_exit(void) +{ + platform_driver_unregister(&ab5500_sim_driver); +} + +module_init(ab5500_sim_init); +module_exit(ab5500_sim_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Bibek Basu"); +MODULE_ALIAS("platform:ab5500-sim"); +MODULE_DESCRIPTION("AB5500 sim interface driver"); diff --git a/drivers/staging/ab5500_sim/sysfs-sim b/drivers/staging/ab5500_sim/sysfs-sim new file mode 100644 index 00000000000..b809b21e39e --- /dev/null +++ b/drivers/staging/ab5500_sim/sysfs-sim @@ -0,0 +1,83 @@ +What: /sys/devices/platform/ab5500-core.0/ab5500-sim.4/ +Date: June 2011 +KernelVersion: 2.6.35 +Contact: Bibek Basu +Description: + The /sys/devices/.../ab5500-sim.4 directory contains attributes + allowing the user space to check and configure ab5500 sim level + shifter interface caracteristics for communication to SIM card + +What: /sys/devices/.../enable_pullup +Date: June 2011 +KernelVersion: 2.6.35 +Contact: Bibek Basu +Description: + The /sys/devices/.../ab5500-sim.4/enable_pullup attribute allows + the user space to configure if internal pull up in SIMIO lines + has to be enabled or disabled. For enabling write 1 to the file + and 0 for disabling + + +What: /sys/devices/.../dat_pullup +Date: June 2011 +KernelVersion: 2.6.35 +Contact: Bibek Basu +Description: + The /sys/devices/.../ab5500-sim.4/dat_pullup attribute allows + the user space to configure the resistance value for internal + pull up in SIMIO lines. Following value can be written on the file + 0 SIM_DAT pull-up disabled + 1 SIM_DAT pull-up 4kOhm + 2 SIM_DAT pull-up 5kOhm + 3 SIM_DAT pull-up 6kOhm + 4 SIM_DAT pull-up 7kOhm + 5 SIM_DAT pull-up 8kOhm + 6 SIM_DAT pull-up 9kOhm + 7 SIM_DAT pull-up 10kOhm + +What: /sys/devices/.../mode_sel +Date: June 2011 +KernelVersion: 2.6.35 +Contact: Bibek Basu +Description: + The /sys/devices/.../ab5500-sim.4/mode_sel attribute allows + the user space to configure the mode at which the level shifter + will work. Following value can be written on the file + 0 TG mode and LI mode off + 1 TG mode on + 2 LI mode on + 3 TG mode and LI mode off + +What: /sys/devices/.../load_sel +Date: June 2011 +KernelVersion: 2.6.35 +Contact: Bibek Basu +Description: + The /sys/devices/.../ab5500-sim.4/load_sel attribute allows + the user space to configure the load on the USBUICC lines. + Following value can be written on the file. + 0 Data line load < 21pF + 1 Data line load 21-30pF + +What: /sys/devices/.../weak_pulldforce +Date: June 2011 +KernelVersion: 2.6.35 +Contact: Bibek Basu +Description: + The /sys/devices/.../ab5500-sim.4/weak_pulldforce attribute allows + the user space to configure the weak pull down on the USBUICC lines. + Following value can be written on the file. + 0 USB-UICC data lines weak pull down active + 1 USB-UICC data lines weak pull down not active + +What: /sys/devices/.../simoff_int +Date: June 2011 +KernelVersion: 2.6.35 +Contact: Bibek Basu +Description: + The /sys/devices/.../ab5500-sim.4/simoff_int attribute allows + the user space to poll this file and get notified in case a sim + hot swap has happened. a zero means sim extracetd and a one means + inserted. + + -- cgit v1.2.3 From 509e35ab0d844f024c32fc7fe2f510d2407d98ab Mon Sep 17 00:00:00 2001 From: Kumar Sanghvi Date: Thu, 19 May 2011 14:14:52 +0530 Subject: u8500: shrm: Add support for CIQ L2Mux channel Adds support for the CIQ L2Mux channel ST-Ericsson ID: CR334399 Change-Id: Ib189a1090c11b3537fafd6f3b9bc8becf1c63c2f Signed-off-by: Kumar Sanghvi Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23373 Reviewed-by: QATEST Tested-by: Sean SANDOVAL Reviewed-by: Srinidhi KASAGAR --- drivers/char/shrm_char.c | 24 +++++++++++++++++++++--- drivers/modem/shrm/shrm_protocol.c | 4 +++- include/linux/modem/shrm/shrm_driver.h | 2 +- include/linux/modem/shrm/shrm_private.h | 1 + 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/char/shrm_char.c b/drivers/char/shrm_char.c index 31f1fedd704..fa44b469b98 100644 --- a/drivers/char/shrm_char.c +++ b/drivers/char/shrm_char.c @@ -25,8 +25,8 @@ #define NAME "IPC_ISA" -/* L2 header for common loopback device is 0xc0 and hence 0xc0+1 = 193*/ -#define MAX_L2_HEADERS 193 +/* L2 header for ciq device is 0xc3 and hence 0xc3+1 = 196*/ +#define MAX_L2_HEADERS 196 #define SIZE_OF_FIFO (512*1024) @@ -49,6 +49,7 @@ static struct map_device map_dev[] = { {SECURITY_MESSAGING, 3, "sec"}, {COMMON_LOOPBACK_MESSAGING, 4, "common_loopback"}, {AUDIO_LOOPBACK_MESSAGING, 5, "audio_loopback"}, + {CIQ_MESSAGING, 6, "ciq"}, }; /* @@ -483,6 +484,10 @@ ssize_t isa_write(struct file *filp, const char __user *buf, dev_dbg(shrm->dev, "Audio loopback\n"); addr = isadev->addr; break; + case CIQ_MESSAGING: + dev_dbg(shrm->dev, "CIQ\n"); + addr = isadev->addr; + break; default: dev_dbg(shrm->dev, "Wrong device\n"); return -EFAULT; @@ -637,6 +642,10 @@ static int isa_close(struct inode *inode, struct file *filp) kfree(isadev->addr); dev_info(shrm->dev, "Close AUDIO_LOOPBACK_MESSAGING Device\n"); break; + case CIQ_MESSAGING: + kfree(isadev->addr); + dev_info(shrm->dev, "Close CIQ_MESSAGING Device\n"); + break; default: dev_info(shrm->dev, "No such device present\n"); mutex_unlock(&isa_lock); @@ -683,7 +692,8 @@ static int isa_open(struct inode *inode, struct file *filp) (m != AUDIO_LOOPBACK_MESSAGING) && (m != COMMON_LOOPBACK_MESSAGING) && (m != AUDIO_MESSAGING) && - (m != SECURITY_MESSAGING)) { + (m != SECURITY_MESSAGING) && + (m != CIQ_MESSAGING)) { dev_err(shrm->dev, "No such device present\n"); mutex_unlock(&isa_lock); return -ENODEV; @@ -729,6 +739,14 @@ static int isa_open(struct inode *inode, struct file *filp) } dev_info(shrm->dev, "Open AUDIO_LOOPBACK_MESSAGING Device\n"); break; + case CIQ_MESSAGING: + isadev->addr = kzalloc(10 * 1024, GFP_KERNEL); + if (!isadev->addr) { + mutex_unlock(&isa_lock); + return -ENOMEM; + } + dev_info(shrm->dev, "Open CIQ_MESSAGING Device\n"); + break; }; mutex_unlock(&isa_lock); diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index 99a58aa6f71..14fe28ca267 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -29,6 +29,7 @@ #define L2_HEADER_COMMON_ADVANCED_LOOPBACK 0xC1 #define L2_HEADER_AUDIO_SIMPLE_LOOPBACK 0x80 #define L2_HEADER_AUDIO_ADVANCED_LOOPBACK 0x81 +#define L2_HEADER_CIQ 0xC3 #define MAX_PAYLOAD 1024 static u8 boot_state = BOOT_INIT; @@ -993,7 +994,8 @@ int shm_write_msg(struct shrm_dev *shrm, u8 l2_header, (l2_header == L2_HEADER_RPC) || (l2_header == L2_HEADER_SECURITY) || (l2_header == L2_HEADER_COMMON_SIMPLE_LOOPBACK) || - (l2_header == L2_HEADER_COMMON_ADVANCED_LOOPBACK)) { + (l2_header == L2_HEADER_COMMON_ADVANCED_LOOPBACK) || + (l2_header == L2_HEADER_CIQ)) { channel = 0; if (shrm_common_tx_state == SHRM_SLEEP_STATE) shrm_common_tx_state = SHRM_PTR_FREE; diff --git a/include/linux/modem/shrm/shrm_driver.h b/include/linux/modem/shrm/shrm_driver.h index f662a0c4533..e7b87005565 100644 --- a/include/linux/modem/shrm/shrm_driver.h +++ b/include/linux/modem/shrm/shrm_driver.h @@ -24,7 +24,7 @@ #include #include -#define ISA_DEVICES 6 +#define ISA_DEVICES 7 #define BOOT_INIT (0) #define BOOT_INFO_SYNC (1) diff --git a/include/linux/modem/shrm/shrm_private.h b/include/linux/modem/shrm/shrm_private.h index 2063f6d6e8e..888a7c200fd 100644 --- a/include/linux/modem/shrm/shrm_private.h +++ b/include/linux/modem/shrm/shrm_private.h @@ -46,6 +46,7 @@ #define SECURITY_MESSAGING (3) #define COMMON_LOOPBACK_MESSAGING (0xC0) #define AUDIO_LOOPBACK_MESSAGING (0x80) +#define CIQ_MESSAGING (0xC3) #define COMMON_CHANNEL 0 #define AUDIO_CHANNEL 1 -- cgit v1.2.3 From 8244975b977a9cda6c8a8ed4e3028e2d114d5456 Mon Sep 17 00:00:00 2001 From: Bibek Basu Date: Mon, 23 May 2011 11:43:41 +0530 Subject: U8500 : driver for awaking modem on sim hot swap activity This driver uses GPIO to detect SIM hot swap and uses Modem Access Framework to wake up modem. ST-Ericsson Linux next: 336280 ST-Ericsson ID: 329459 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ib4b1f67422317850f5d859ddf4c5e457f4cb616c Signed-off-by: Bibek Basu Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23543 Reviewed-by: QATEST Reviewed-by: Jonas ABERG --- arch/arm/mach-ux500/include/mach/sim_detect.h | 16 +++ drivers/misc/sim_detect.c | 163 ++++++++++++++++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 arch/arm/mach-ux500/include/mach/sim_detect.h create mode 100644 drivers/misc/sim_detect.c diff --git a/arch/arm/mach-ux500/include/mach/sim_detect.h b/arch/arm/mach-ux500/include/mach/sim_detect.h new file mode 100644 index 00000000000..8d6e81f1e8a --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/sim_detect.h @@ -0,0 +1,16 @@ +/* + * Copyright ST-Ericsson 2010 SA. + * + * Author: Bibek Basu + * Licensed under GPLv2. + */ + +#ifndef _AB8500_SIM_DETECT_H +#define _AB8500_SIM_DETECT_H + +struct sim_detect_platform_data { + int irq_num; +}; + +#endif + diff --git a/drivers/misc/sim_detect.c b/drivers/misc/sim_detect.c new file mode 100644 index 00000000000..24862edfca7 --- /dev/null +++ b/drivers/misc/sim_detect.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: BIBEK BASU + * License terms: GNU General Public License (GPL) version 2 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* time in millisec */ +#define TIMER_DELAY 10 +struct sim_detect{ + struct work_struct timer_expired; + struct device *dev; + struct modem *modem; + struct hrtimer timer; +}; + +static void inform_modem_release(struct work_struct *work) +{ + struct sim_detect *sim_detect = + container_of(work, struct sim_detect, timer_expired); + + /* call Modem Access Framework api to release modem */ + modem_release(sim_detect->modem); +} + +static enum hrtimer_restart timer_callback(struct hrtimer *timer) +{ + struct sim_detect *sim_detect = + container_of(timer, struct sim_detect, timer); + + schedule_work(&sim_detect->timer_expired); + return HRTIMER_NORESTART; +} + +static irqreturn_t sim_activity_irq(int irq, void *dev) +{ + struct sim_detect *sim_detect = dev; + + /* call Modem Access Framework api to acquire modem */ + modem_request(sim_detect->modem); + /* start the timer for 10ms */ + hrtimer_start(&sim_detect->timer, + ktime_set(0, TIMER_DELAY*NSEC_PER_MSEC), + HRTIMER_MODE_REL); + return IRQ_HANDLED; +} + +#ifdef CONFIG_PM +/** + * sim_detect_suspend() - This routine puts the Sim detect in to sustend state. + * @dev: pointer to device structure. + * + * This routine checks the current ongoing communication with Modem by + * examining the modem_get_usage and work_pending state. + * accordingly prevents suspend if modem communication + * is on-going. + */ +int sim_detect_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sim_detect *sim_detect = platform_get_drvdata(pdev); + + dev_dbg(&pdev->dev, "%s called...\n", __func__); + /* if modem is accessed, event system suspend */ + if (modem_get_usage(sim_detect->modem) + || work_pending(&sim_detect->timer_expired)) + return -EBUSY; + else + return 0; +} + +static const struct dev_pm_ops sim_detect_dev_pm_ops = { + .suspend = sim_detect_suspend, +}; +#endif + +static int __devinit sim_detect_probe(struct platform_device *pdev) +{ + struct sim_detect_platform_data *plat = dev_get_platdata(&pdev->dev); + struct sim_detect *sim_detect; + int ret; + + sim_detect = kzalloc(sizeof(struct sim_detect), GFP_KERNEL); + if (sim_detect == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + sim_detect->dev = &pdev->dev; + INIT_WORK(&sim_detect->timer_expired, inform_modem_release); + hrtimer_init(&sim_detect->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + sim_detect->timer.function = timer_callback; + sim_detect->modem = modem_get(sim_detect->dev, "u8500-shrm-modem"); + if (IS_ERR(sim_detect->modem)) { + ret = PTR_ERR(sim_detect->modem); + dev_err(sim_detect->dev, "Could not retrieve the modem\n"); + goto out_free; + } + platform_set_drvdata(pdev, sim_detect); + ret = request_threaded_irq(plat->irq_num, + NULL, sim_activity_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "sim activity", sim_detect); + if (ret < 0) + goto out_free_irq; +out_free_irq: + modem_put(sim_detect->modem); + platform_set_drvdata(pdev, NULL); +out_free: + kfree(sim_detect); + return ret; +} + +static int __devexit sim_detect_remove(struct platform_device *pdev) +{ + struct sim_detect *sim_detect = platform_get_drvdata(pdev); + + modem_put(sim_detect->modem); + platform_set_drvdata(pdev, NULL); + kfree(sim_detect); + return 0; +} + +static struct platform_driver sim_detect_driver = { + .driver = { + .name = "sim_detect", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &sim_detect_dev_pm_ops, +#endif + }, + .probe = sim_detect_probe, + .remove = __devexit_p(sim_detect_remove), +}; + +static int __init sim_detect_init(void) +{ + return platform_driver_register(&sim_detect_driver); +} +module_init(sim_detect_init); + +static void __exit sim_detect_exit(void) +{ + platform_driver_unregister(&sim_detect_driver); +} +module_exit(sim_detect_exit); + +MODULE_AUTHOR("BIBEK BASU "); +MODULE_DESCRIPTION("Detects SIM Hot Swap and wakes modem"); +MODULE_ALIAS("SIM DETECT INTERRUPT driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 257f38c035bc3be368d2c29a1fdf65286021ba0e Mon Sep 17 00:00:00 2001 From: Bibek Basu Date: Thu, 23 Jun 2011 15:33:21 +0530 Subject: U8500: change GPIO for SIM activity detection driver This patch solves two things. One is to move to AB8500 GPIO for sim activity detection and another is to fix the missing return in driver probe. ST-Ericsson Linux next: 336280 ST-Ericsson ID: 349042 ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Bibek Basu Change-Id: Idd4c2b0877c9d8a1590b4e866efa61377963fb93 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25770 Reviewed-by: QATEST Reviewed-by: Jonas ABERG --- drivers/misc/sim_detect.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/sim_detect.c b/drivers/misc/sim_detect.c index 24862edfca7..c267fe95ee3 100644 --- a/drivers/misc/sim_detect.c +++ b/drivers/misc/sim_detect.c @@ -115,6 +115,7 @@ static int __devinit sim_detect_probe(struct platform_device *pdev) "sim activity", sim_detect); if (ret < 0) goto out_free_irq; + return 0; out_free_irq: modem_put(sim_detect->modem); platform_set_drvdata(pdev, NULL); -- cgit v1.2.3 From 7c3e6677064464512bd693f47c9607a8fcc18212 Mon Sep 17 00:00:00 2001 From: Robert Marklund Date: Fri, 12 Aug 2011 11:46:36 +0200 Subject: Doc: Fixing documentation for html generation Change documentation to align with header files and source file renames and move. Change-Id: I8e67e41e90dfb61255db5e2e84ead09972361c1c Signed-off-by: Robert Marklund --- Documentation/DocBook/shrm.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/DocBook/shrm.tmpl b/Documentation/DocBook/shrm.tmpl index 2773b821bf5..b93bb065172 100644 --- a/Documentation/DocBook/shrm.tmpl +++ b/Documentation/DocBook/shrm.tmpl @@ -104,7 +104,7 @@ This Section lists the API's provided by the SHRM driver to phonet drivers. -!Edrivers/net/u8500_shrm.c +!Edrivers/modem/shrm/shrm_fifo.c This Section lists the API's provided by the SHRM driver used in transmission of RPC, AUDIO and SECURITY messages. -- cgit v1.2.3 From 2b19d5601ba13b02454f7df76ac92db381102c4a Mon Sep 17 00:00:00 2001 From: Bibek Basu Date: Wed, 17 Aug 2011 09:11:17 +0530 Subject: ux500_ing05160_mailboxLD_0.1 : coverity fix in ab5500 core and mailbox commit id : I57e11eeb : U5500 : coverity fix in ab5500 core and mailbox Build and boot test only done for 8500 config Booting will only happend properly if used Initramfs Change-Id: If4f6c8d50499d67128694a91517f7aae8c152653 Signed-off-by: Bibek Basu Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28898 --- drivers/misc/mbox_channels-db5500.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/mbox_channels-db5500.c b/drivers/misc/mbox_channels-db5500.c index 1fb883f3296..0d0e16ebb74 100644 --- a/drivers/misc/mbox_channels-db5500.c +++ b/drivers/misc/mbox_channels-db5500.c @@ -668,12 +668,13 @@ int mbox_channel_register(u16 channel, mbox_channel_cb_t *cb, void *priv) spin_lock(&mbox_unit->rx_lock); list_del(&rx_chan->list); spin_unlock(&mbox_unit->rx_lock); + mutex_unlock(&rx_chan->lock); kfree(rx_chan); } else { rx_chan->seq_number++; rx_chan->state = MBOX_OPEN; + mutex_unlock(&rx_chan->lock); } - mutex_unlock(&rx_chan->lock); exit: return res; } @@ -1036,7 +1037,6 @@ static int mboxtest_prepare(struct mboxtest_data *mboxtest) registration_done = true; return 0; err: - kfree(mboxtest); return err; } -- cgit v1.2.3 From ef96b3f2e01e3941fd9a172220294d203c6e9ef3 Mon Sep 17 00:00:00 2001 From: Bibek Basu Date: Wed, 17 Aug 2011 09:34:34 +0530 Subject: ux500_ing05160_mailboxLD_0.1 : Mailbox Logical driver update Commit ID : I6af14884 :U5500 : Mailbox Logical driver update Build and boot test only done for 8500 config Booting will only happend properly if used Initramfs Signed-off-by: Bibek Basu Change-Id: I9fbd96cd73670649fb11aa467a6a2c23153b3040 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28906 --- drivers/misc/mbox_channels-db5500.c | 216 +++++++++++++++++++++++++----------- 1 file changed, 150 insertions(+), 66 deletions(-) diff --git a/drivers/misc/mbox_channels-db5500.c b/drivers/misc/mbox_channels-db5500.c index 0d0e16ebb74..6bd4afa5fe2 100644 --- a/drivers/misc/mbox_channels-db5500.c +++ b/drivers/misc/mbox_channels-db5500.c @@ -52,10 +52,7 @@ #define GET_SEQ_NUMBER(mbox_msg) (((mbox_msg) >> 24) /* Number of buffers */ -#define NUM_DSP_BUFFER 3 - -/* circular buffer indicator */ -static int buffer_index; +#define NUM_DSP_BUFFER 4 enum mbox_msg{ MBOX_CLOSE, @@ -140,7 +137,22 @@ struct rx_channel { u8 length; }; -/* This structure holds status of mbox channel - common for tx and rx */ +/** + * struct channel_status - status of mbox channel - common for tx and rx + * @list : holds list of channels registered + * @channel : holds channel number + * @state : holds state of channel + * @cb: holds callback function forr rx channel + * @with_ack : holds if ack is needed + * @rx: holds pointer to rx_channel + * @tx : holds pointer to tx_channel + * @receive_wq : holds pointer to receive workqueue_struct + * @cast_wq : holds pointer to cast workqueue_struct + * @open_msg: holds work_struct for open msg + * @receive_msg : holds work_struct for receive msg + * @cast_msg: holds work_struct for cast msg + * @lock: holds lock for channel + */ struct channel_status { struct list_head list; u16 channel; @@ -151,8 +163,10 @@ struct channel_status { bool with_ack; struct rx_channel rx; struct tx_channel tx; - struct work_struct receive_msg; + struct workqueue_struct *receive_wq; + struct workqueue_struct *cast_wq; struct work_struct open_msg; + struct work_struct receive_msg; struct work_struct cast_msg; struct mutex lock; }; @@ -318,7 +332,8 @@ static int send_pdu(struct channel_status *chan_status, int command, chan_status->priv); else dev_err(&channels.pdev->dev, - "%s no callback provided\n", __func__); + "%s no callback provided:header 0x%x\n", + __func__, header); /* Increment sequence number */ chan_status->seq_number++; @@ -351,15 +366,12 @@ void mbox_handle_receive_msg(struct work_struct *work) /* Call client's callback and reset state */ if (rx_chan->cb) { static int rx_pending_count; - + rx_chan->cb(rx_pending[rx_pending_count].buffer, + rx_pending[rx_pending_count].length, + rx_pending[rx_pending_count].priv); + rx_pending_count++; if (rx_pending_count == NUM_DSP_BUFFER) rx_pending_count = 0; - else - rx_pending_count++; - rx_chan->cb(rx_pending[rx_pending_count].buffer, - rx_pending[rx_pending_count].length, - rx_pending[rx_pending_count].priv); - buffer_index--; } else { dev_err(&channels.pdev->dev, "%s no callback provided\n", __func__); @@ -392,35 +404,16 @@ void mbox_handle_cast_msg(struct work_struct *work) } } -static bool handle_receive_msg(u32 mbox_msg, struct list_head *rx_list) +static bool handle_receive_msg(u32 mbox_msg, struct channel_status *rx_chan) { - struct list_head *pos; - struct channel_status *tmp; int i; static int rx_pending_count; - struct channel_status *rx_chan = NULL; - struct mbox_unit_status *mbox_unit = container_of(rx_list, - struct mbox_unit_status, - rx_chans); - spin_lock(&mbox_unit->rx_lock); - list_for_each(pos, rx_list) { - tmp = list_entry(pos, struct channel_status, list); - if (tmp->state == MBOX_SEND || - tmp->state == MBOX_CAST) - /* Received message is payload */ - rx_chan = tmp; - } - /* Received message is header */ - spin_unlock(&mbox_unit->rx_lock); + if (rx_chan) { /* Store received data in RX channel buffer */ rx_chan->rx.buffer[rx_chan->rx.index++] = mbox_msg; /* Check if it's last data of PDU */ if (rx_chan->rx.index == rx_chan->rx.length) { - if (rx_pending_count == NUM_DSP_BUFFER) - rx_pending_count = 0; - else - rx_pending_count++; for (i = 0; i < MAILBOX_NR_OF_DATAWORDS; i++) { rx_pending[rx_pending_count].buffer[i] = rx_chan->rx.buffer[i]; @@ -432,11 +425,11 @@ static bool handle_receive_msg(u32 mbox_msg, struct list_head *rx_list) rx_chan->rx.length = 0; rx_chan->state = MBOX_OPEN; rx_chan->seq_number++; - buffer_index++; - if (buffer_index >= NUM_DSP_BUFFER) - dev_err(&channels.pdev->dev, - "rxbuf overflow%d\n", buffer_index); - schedule_work(&rx_chan->receive_msg); + rx_pending_count++; + if (rx_pending_count == NUM_DSP_BUFFER) + rx_pending_count = 0; + queue_work(rx_chan->receive_wq, + &rx_chan->receive_msg); } dev_dbg(&channels.pdev->dev, "%s OK\n", __func__); return true; @@ -453,7 +446,7 @@ static void handle_open_msg(u16 channel, u8 mbox_id) channel = get_tx_channel(channel); dev_dbg(&channels.pdev->dev, "%s mbox_id %d\tchannel %x\n", __func__, mbox_id, channel); - /* Get TX channesenx for given mbox unit */ + /* Get TX channel for given mbox unit */ tx_list = get_tx_list(mbox_id); if (tx_list == NULL) { dev_err(&channels.pdev->dev, "given mbox id is not valid %d\n", @@ -497,25 +490,10 @@ static void handle_open_msg(u16 channel, u8 mbox_id) } } -static void handle_cast_msg(u16 channel, struct list_head *rx_list, +static void handle_cast_msg(u16 channel, struct channel_status *rx_chan, u32 mbox_msg, bool send) { - struct list_head *pos; - struct channel_status *tmp; - struct channel_status *rx_chan = NULL; - struct mbox_unit_status *mbox_unit = container_of(rx_list, - struct mbox_unit_status, - rx_chans); dev_dbg(&channels.pdev->dev, " %s\n", __func__); - /* Search for channel in rx list */ - spin_lock(&mbox_unit->rx_lock); - list_for_each(pos, rx_list) { - tmp = list_entry(pos, struct channel_status, list); - if (tmp->channel == channel) - rx_chan = tmp; - } - spin_unlock(&mbox_unit->rx_lock); - if (rx_chan) { rx_chan->rx.buffer[0] = mbox_msg; rx_chan->with_ack = send; @@ -525,7 +503,8 @@ static void handle_cast_msg(u16 channel, struct list_head *rx_list, rx_chan->rx.index = 0; rx_chan->state = MBOX_CAST; } - schedule_work(&rx_chan->cast_msg); + queue_work(rx_chan->cast_wq, + &rx_chan->cast_msg); } else { /* Channel not found, peer sent wrong message */ dev_err(&channels.pdev->dev, "channel %d doesn't exist\n", @@ -543,6 +522,11 @@ static void mbox_cb(u32 mbox_msg, void *priv) struct list_head *rx_list; u8 type = GET_TYPE(mbox_msg); u16 channel = GET_CHANNEL(mbox_msg); + struct mbox_unit_status *mbox_unit; + struct list_head *pos; + struct channel_status *tmp; + struct channel_status *rx_chan = NULL; + bool is_Payload = 0; dev_dbg(&channels.pdev->dev, "%s type %d\t, mbox_msg %x\n", __func__, type, mbox_msg); @@ -554,9 +538,31 @@ static void mbox_cb(u32 mbox_msg, void *priv) return; } - /* If received message is payload this function will take care of it */ - if (handle_receive_msg(mbox_msg, rx_list)) - return; + mbox_unit = container_of(rx_list, struct mbox_unit_status, rx_chans); + /* Search for channel in rx list */ + spin_lock(&mbox_unit->rx_lock); + list_for_each(pos, rx_list) { + tmp = list_entry(pos, struct channel_status, list); + if (tmp->state == MBOX_SEND || + tmp->state == MBOX_CAST) { + /* Received message is payload */ + is_Payload = 1; + rx_chan = tmp; + } else + if (tmp->channel == channel) + rx_chan = tmp; + } + spin_unlock(&mbox_unit->rx_lock); + /* if callback is present for that RX channel */ + if (rx_chan && rx_chan->cb) { + /* If received message is payload this + * function will take care of it + */ + if ((is_Payload) && (handle_receive_msg(mbox_msg, rx_chan))) + return; + } else + dev_err(&channels.pdev->dev, "callback not present:msg 0x%x " + "rx_chan 0x%x\n", mbox_msg, (u32)rx_chan); /* Received message is header as no RX channel is in SEND/CAST state */ switch (type) { @@ -567,10 +573,14 @@ static void mbox_cb(u32 mbox_msg, void *priv) handle_open_msg(channel, mbox_id); break; case MBOX_SEND: - handle_cast_msg(channel, rx_list, mbox_msg, true); + /* if callback is present for that RX channel */ + if (rx_chan && rx_chan->cb) + handle_cast_msg(channel, rx_chan, mbox_msg, true); break; case MBOX_CAST: - handle_cast_msg(channel, rx_list, mbox_msg, false); + /* if callback is present for that RX channel */ + if (rx_chan && rx_chan->cb) + handle_cast_msg(channel, rx_chan, mbox_msg, false); break; case MBOX_ACK: case MBOX_NAK: @@ -598,10 +608,10 @@ int mbox_channel_register(u16 channel, mbox_channel_cb_t *cb, void *priv) struct mbox_unit_status *mbox_unit; dev_dbg(&channels.pdev->dev, " %s channel = %d\n", __func__, channel); - /* Closing of channels is not implemented */ + /* Check for callback fcn */ if (cb == NULL) { dev_err(&channels.pdev->dev, - "channel close is not implemented\n"); + "channel callback missing:channel %d\n", channel); res = -EINVAL; goto exit; } @@ -654,6 +664,18 @@ int mbox_channel_register(u16 channel, mbox_channel_cb_t *cb, void *priv) rx_chan->seq_number = CHANNEL_START_SEQUENCE_NUMBER; mutex_init(&rx_chan->lock); INIT_LIST_HEAD(&rx_chan->rx.pending); + rx_chan->cast_wq = create_singlethread_workqueue("mbox_cast_msg"); + if (!rx_chan->cast_wq) { + dev_err(&channels.pdev->dev, "failed to create work queue\n"); + res = -ENOMEM; + goto error_cast_wq; + } + rx_chan->receive_wq = create_singlethread_workqueue("mbox_receive_msg"); + if (!rx_chan->receive_wq) { + dev_err(&channels.pdev->dev, "failed to create work queue\n"); + res = -ENOMEM; + goto error_recv_wq; + } INIT_WORK(&rx_chan->open_msg, mbox_handle_open_msg); INIT_WORK(&rx_chan->cast_msg, mbox_handle_cast_msg); INIT_WORK(&rx_chan->receive_msg, mbox_handle_receive_msg); @@ -669,17 +691,79 @@ int mbox_channel_register(u16 channel, mbox_channel_cb_t *cb, void *priv) list_del(&rx_chan->list); spin_unlock(&mbox_unit->rx_lock); mutex_unlock(&rx_chan->lock); - kfree(rx_chan); + goto error_send_pdu; } else { rx_chan->seq_number++; rx_chan->state = MBOX_OPEN; mutex_unlock(&rx_chan->lock); + return res; } +error_send_pdu: + flush_workqueue(rx_chan->receive_wq); +error_recv_wq: + flush_workqueue(rx_chan->cast_wq); +error_cast_wq: + kfree(rx_chan); exit: return res; } EXPORT_SYMBOL(mbox_channel_register); +/** + * mbox_channel_deregister() - DeRegisters for a channel + * @channel: Channel Number. + * + * This routine is used to deregister for a logical channel. + * It first does sanity check on the requested channel availability + * and parameters. Then it deletes the channel + */ +int mbox_channel_deregister(u16 channel) +{ + struct channel_status *rx_chan = NULL; + struct list_head *pos, *rx_list; + int res = 0; + struct mbox_unit_status *mbox_unit; + + dev_dbg(&channels.pdev->dev, " %s channel = %d\n", __func__, channel); + /* Check if provided channel number is valid */ + if (!check_channel(channel, MBOX_RX)) { + dev_err(&channels.pdev->dev, "wrong mbox channel number %d\n", + channel); + res = -EINVAL; + goto exit; + } + + rx_list = get_rx_list(get_mbox_id(channel)); + if (rx_list == NULL) { + dev_err(&channels.pdev->dev, "given mbox id is not valid\n"); + res = -EINVAL; + goto exit; + } + + mbox_unit = container_of(rx_list, struct mbox_unit_status, rx_chans); + + /* Check if channel is already registered */ + spin_lock(&mbox_unit->rx_lock); + list_for_each(pos, rx_list) { + rx_chan = list_entry(pos, struct channel_status, list); + + if (rx_chan->channel == channel) { + dev_dbg(&channels.pdev->dev, + "channel found\n"); + rx_chan->cb = NULL; + } + } + list_del(&rx_chan->list); + spin_unlock(&mbox_unit->rx_lock); + flush_workqueue(rx_chan->cast_wq); + flush_workqueue(rx_chan->receive_wq); + kfree(rx_chan); + +exit: + return res; +} +EXPORT_SYMBOL(mbox_channel_deregister); + /** * mbox_channel_send() - Send messages * @msg: Pointer to mbox_channel_msg data structure. -- cgit v1.2.3 From afb68c88f151dfa304db91e6f9951dade0a464e6 Mon Sep 17 00:00:00 2001 From: Bibek Basu Date: Wed, 17 Aug 2011 13:45:01 +0530 Subject: ux500_ing05160_ab5500_sim_0.1: fix the resource leak Commit id : I70c297af: staging ab5500-sim: fix the resource leak Build and boot test only done for 8500 config Booting will only happend properly if used Initramfs Signed-off-by: Bibek Basu Change-Id: I8edbdb1dbf4282b8bfa4e6b5a3f4b0ca2b460138 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28916 --- drivers/staging/ab5500_sim/ab5500-sim.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/staging/ab5500_sim/ab5500-sim.c b/drivers/staging/ab5500_sim/ab5500-sim.c index 788a7984a46..3f4ae7cd34a 100644 --- a/drivers/staging/ab5500_sim/ab5500-sim.c +++ b/drivers/staging/ab5500_sim/ab5500-sim.c @@ -230,14 +230,16 @@ static int __devinit ab5500_sim_probe(struct platform_device *pdev) int irq; struct ab5500_sim *di = kzalloc(sizeof(struct ab5500_sim), GFP_KERNEL); - if (!di) - return -ENOMEM; - + if (!di) { + ret = -ENOMEM; + goto error_alloc; + } dev_info(&pdev->dev, "ab5500_sim_driver PROBE\n"); irq = platform_get_irq_byname(pdev, "SIMOFF"); if (irq < 0) { dev_err(&pdev->dev, "Get irq by name failed\n"); - return irq; + ret = irq; + goto exit; } di->irq_base = irq; di->dev = &pdev->dev; @@ -246,13 +248,14 @@ static int __devinit ab5500_sim_probe(struct platform_device *pdev) /* sysfs interface to configure sim reg from user space */ if (sysfs_create_group(&pdev->dev.kobj, &ab5500sim_attr_grp) < 0) { dev_err(&pdev->dev, " Failed creating sysfs group\n"); - return -ENOMEM; + ret = -ENOMEM; + goto error_sysfs; } ret = request_threaded_irq(irq, NULL, ab5500_sim_irq_handler, IRQF_NO_SUSPEND , "ab5500-sim", pdev); if (ret < 0) { dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret); - goto exit; + goto error_irq; } /* this is the contiguous irq for sim removal,falling edge */ irq = irq + 1; @@ -261,13 +264,16 @@ static int __devinit ab5500_sim_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret); free_irq(--irq, di); - goto exit; + goto error_irq; } return ret; -exit: +error_irq: sysfs_remove_group(&pdev->dev.kobj, &ab5500sim_attr_grp); +error_sysfs: platform_set_drvdata(pdev, NULL); +exit: kfree(di); +error_alloc: return ret; } -- cgit v1.2.3 From 9d7261cb0bc7a3239b909f1611d32bb40680ff26 Mon Sep 17 00:00:00 2001 From: Bibek Basu Date: Wed, 17 Aug 2011 15:54:00 +0530 Subject: ux500_ing05160_mailboxPD_0.1:Lockdeb correctness fix for modem_irq Commit ID: Ib94dcbf6: U5500 : Lockdeb correctness fix for modem_irq Build and boot test only done for 8500 config Booting will only happend properly if used Initramfs Change-Id: I5da03baa7b8ee8f32de5712b7136442fbe2cec1c Signed-off-by: Bibek Basu Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28923 --- arch/arm/mach-ux500/modem-irq-db5500.c | 6 ++++-- drivers/misc/mbox.c | 13 ++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-ux500/modem-irq-db5500.c b/arch/arm/mach-ux500/modem-irq-db5500.c index 6b86416c94c..52bca4eb8b6 100644 --- a/arch/arm/mach-ux500/modem-irq-db5500.c +++ b/arch/arm/mach-ux500/modem-irq-db5500.c @@ -81,7 +81,7 @@ static irqreturn_t modem_cpu_irq_handler(int irq, void *data) virt_irq); if (virt_irq != 0) - generic_handle_irq(virt_irq); + handle_nested_irq(virt_irq); pr_debug("modem_irq: Done handling virtual IRQ %d!\n", virt_irq); @@ -91,6 +91,7 @@ static irqreturn_t modem_cpu_irq_handler(int irq, void *data) static void create_virtual_irq(int irq, struct irq_chip *modem_irq_chip) { irq_set_chip_and_handler(irq, modem_irq_chip, handle_simple_irq); + irq_set_nested_thread(irq, 1); set_irq_flags(irq, IRQF_VALID); pr_debug("modem_irq: Created virtual IRQ %d\n", irq); @@ -131,7 +132,8 @@ static int modem_irq_init(void) create_virtual_irq(MBOX_PAIR2_VIRT_IRQ, &modem_irq_chip); err = request_threaded_irq(IRQ_DB5500_MODEM, NULL, - modem_cpu_irq_handler, IRQF_ONESHOT, + modem_cpu_irq_handler, + IRQF_ONESHOT | IRQF_ONESHOT, "modem_irq", mi); if (err) pr_err("modem_irq: Could not register IRQ %d\n", diff --git a/drivers/misc/mbox.c b/drivers/misc/mbox.c index 2b2d51caf9d..b2810718822 100644 --- a/drivers/misc/mbox.c +++ b/drivers/misc/mbox.c @@ -69,8 +69,9 @@ static struct mbox *get_mbox_with_id(u8 id) int mbox_send(struct mbox *mbox, u32 mbox_msg, bool block) { int res = 0; + unsigned long flag; - spin_lock(&mbox->lock); + spin_lock_irqsave(&mbox->lock, flag); dev_dbg(&(mbox->pdev->dev), "About to buffer 0x%X to mailbox 0x%X." @@ -87,14 +88,14 @@ int mbox_send(struct mbox *mbox, u32 mbox_msg, bool block) res = -ENOMEM; goto exit; } - spin_unlock(&mbox->lock); + spin_unlock_irqrestore(&mbox->lock, flag); dev_dbg(&(mbox->pdev->dev), "Buffer full in blocking call! Sleeping...\n"); mbox->client_blocked = 1; wait_for_completion(&mbox->buffer_available); dev_dbg(&(mbox->pdev->dev), "Blocking send was woken up! Trying again...\n"); - spin_lock(&mbox->lock); + spin_lock_irqsave(&mbox->lock, flag); } mbox->buffer[mbox->write_index] = mbox_msg; @@ -107,7 +108,7 @@ int mbox_send(struct mbox *mbox, u32 mbox_msg, bool block) writel(MBOX_ENABLE_IRQ, mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); exit: - spin_unlock(&mbox->lock); + spin_unlock_irqrestore(&mbox->lock, flag); return res; } EXPORT_SYMBOL(mbox_send); @@ -462,7 +463,9 @@ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv) } dev_dbg(&(mbox->pdev->dev), "Allocating irq %d...\n", irq); - res = request_irq(irq, mbox_irq, 0, mbox->name, (void *) mbox); + res = request_threaded_irq(irq, NULL, mbox_irq, + IRQF_NO_SUSPEND | IRQF_ONESHOT, + mbox->name, (void *) mbox); if (res < 0) { dev_err(&(mbox->pdev->dev), "Unable to allocate mbox irq %d\n", irq); -- cgit v1.2.3 From 9b451288d934c7117c1a3802ceeabf928d6b5ca4 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Mon, 27 Jun 2011 11:07:30 +0530 Subject: u8500-shrm: memory leakage in probe function Memory leakage if getting modem access framework fails during probe. ST-Ericsson Linux next: Not Tested ST-Ericsson ID: ER349208 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I1827581aa5bdabeaf32f09b8eb1f482525785cc0 Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25869 Reviewed-by: QATEST Reviewed-by: Jonas ABERG Reviewed-by: Srinidhi KASAGAR --- drivers/modem/shrm/modem_shrm_driver.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/modem/shrm/modem_shrm_driver.c b/drivers/modem/shrm/modem_shrm_driver.c index 5e986ab9a2c..f46b86bd22e 100644 --- a/drivers/modem/shrm/modem_shrm_driver.c +++ b/drivers/modem/shrm/modem_shrm_driver.c @@ -334,7 +334,8 @@ static int shrm_probe(struct platform_device *pdev) 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; + err = -ENODEV; + goto rollback_intr; } /* initialise the SHM */ -- cgit v1.2.3 From bd003374c1653bf3c749c34e0211c4e2ded1b57b Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Tue, 28 Jun 2011 15:33:28 +0530 Subject: u8500-shrm: display useful error message in case of failure In case of failure, prints the parameters causing the failure, which helps in debugging. ST-Ericsson Linux next: Not Tested ST-Ericsson ID: ER349776 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ie76025473740cfa7ffbfba0c41342d90ae7ff98e Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/26013 Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR --- drivers/modem/shrm/shrm_fifo.c | 26 ++++++++++++++------------ drivers/modem/shrm/shrm_protocol.c | 3 ++- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/drivers/modem/shrm/shrm_fifo.c b/drivers/modem/shrm/shrm_fifo.c index 883966d4ab5..9e6564aa6bc 100644 --- a/drivers/modem/shrm/shrm_fifo.c +++ b/drivers/modem/shrm/shrm_fifo.c @@ -108,6 +108,7 @@ u8 read_boot_info_req(struct shrm_dev *shrm, 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"); + dev_err(shrm->dev, "Received msgtype is %d\n", msgtype); BUG(); } *config = (header >> CONFIG_OFFSET) & MASK_0_15_BIT; @@ -401,17 +402,17 @@ u8 read_one_l2msg_common(struct shrm_dev *shrm, if (msgtype != L1_NORMAL_MSG) { /* Fatal ERROR - should never happens */ - dev_dbg(shrm->dev, "wr_wptr= %x\n", + dev_info(shrm->dev, "wr_wptr= %x\n", fifo->reader_local_wptr); - dev_dbg(shrm->dev, "wr_rptr= %x\n", + dev_info(shrm->dev, "wr_rptr= %x\n", fifo->reader_local_rptr); - dev_dbg(shrm->dev, "shared_wptr= %x\n", + dev_info(shrm->dev, "shared_wptr= %x\n", fifo->shared_wptr); - dev_dbg(shrm->dev, "shared_rptr= %x\n", + dev_info(shrm->dev, "shared_rptr= %x\n", fifo->shared_rptr); - dev_dbg(shrm->dev, "availsize= %x\n", + dev_info(shrm->dev, "availsize= %x\n", fifo->availablesize); - dev_dbg(shrm->dev, "end_fifo= %x\n", + dev_info(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"); @@ -506,18 +507,19 @@ u8 read_one_l2msg_audio(struct shrm_dev *shrm, if (msgtype != L1_NORMAL_MSG) { /* Fatal ERROR - should never happens */ - dev_dbg(shrm->dev, "wr_local_wptr= %x\n", + dev_info(shrm->dev, "wr_local_wptr= %x\n", fifo->reader_local_wptr); - dev_dbg(shrm->dev, "wr_local_rptr= %x\n", + dev_info(shrm->dev, "wr_local_rptr= %x\n", fifo->reader_local_rptr); - dev_dbg(shrm->dev, "shared_wptr= %x\n", + dev_info(shrm->dev, "shared_wptr= %x\n", fifo->shared_wptr); - dev_dbg(shrm->dev, "shared_rptr= %x\n", + dev_info(shrm->dev, "shared_rptr= %x\n", fifo->shared_rptr); - dev_dbg(shrm->dev, "availsize=%x\n", + dev_info(shrm->dev, "availsize=%x\n", fifo->availablesize); - dev_dbg(shrm->dev, "end_fifo= %x\n", + dev_info(shrm->dev, "end_fifo= %x\n", fifo->end_addr_fifo); + dev_info(shrm->dev, "Received msgtype is &d\n", msgtype); /* Fatal ERROR - should never happens */ dev_crit(shrm->dev, "Fatal ERROR - should never happen\n"); BUG(); diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index 14fe28ca267..d2efbc6e5fd 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -985,7 +985,8 @@ int shm_write_msg(struct shrm_dev *shrm, u8 l2_header, if (boot_state != BOOT_DONE) { dev_err(shrm->dev, - "error after boot done call this fn\n"); + "error:after boot done call this fn, L2Header = %d\n", + l2_header); ret = -ENODEV; goto out; } -- cgit v1.2.3 From c9dd7d5b6135bd2ef42f8202cdf72aeb5358fab6 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Wed, 17 Aug 2011 16:27:51 +0530 Subject: modem: u8500-shrm: Error case not handled properly After creation of single threaded workqueue, typo in checking the error condition. ST-Ericsson Linux next: NA ST-Ericsson ID: 356913 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Idc89353a2848a251a71999ffb0fe61fe3f11f08a Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28928 Reviewed-by: QATOOLS Reviewed-by: Rabin VINCENT --- drivers/modem/shrm/shrm_protocol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index d2efbc6e5fd..990591faee1 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -722,7 +722,7 @@ int shrm_protocol_init(struct shrm_dev *shrm, goto free_wq2; } shrm->shm_ca_wake_wq = create_singlethread_workqueue("shm_ca_wake_req"); - if (!shrm->shm_ac_wake_wq) { + if (!shrm->shm_ca_wake_wq) { dev_err(shrm->dev, "failed to create work queue\n"); err = -ENOMEM; goto free_wq3; -- cgit v1.2.3 From 5343fdf6aa71df71978803b0a826a1f2e0b3b921 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Mon, 17 Oct 2011 08:48:46 +0200 Subject: ab5500: move to separate header --- drivers/staging/ab5500_sim/ab5500-sim.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/ab5500_sim/ab5500-sim.c b/drivers/staging/ab5500_sim/ab5500-sim.c index 3f4ae7cd34a..d222a22ed24 100644 --- a/drivers/staging/ab5500_sim/ab5500-sim.c +++ b/drivers/staging/ab5500_sim/ab5500-sim.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include -- cgit v1.2.3 From af5a752b3b1ff460c1ff3162d74c6f1de4be4691 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 20 Oct 2011 10:16:38 +0200 Subject: drivers: misc: Modem Audio Driver Mad is a Character Device Driver to read and write from special shared memory between APE and ACC side. ST-Ericsson Linux next: Not tested, ER 332152 ST-Ericsson ID: 332152 ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-10006 Change-Id: I087f6eb799f87ebb9ed38b4c011da5f53803b161 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/26545 Reviewed-by: Amaresh MULAGE Tested-by: Amaresh MULAGE Reviewed-by: QATEST Reviewed-by: Henrik CARLING --- drivers/misc/modem_audio/Kconfig | 6 + drivers/misc/modem_audio/Makefile | 2 + drivers/misc/modem_audio/mad.c | 486 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 494 insertions(+) create mode 100644 drivers/misc/modem_audio/Kconfig create mode 100644 drivers/misc/modem_audio/Makefile create mode 100644 drivers/misc/modem_audio/mad.c diff --git a/drivers/misc/modem_audio/Kconfig b/drivers/misc/modem_audio/Kconfig new file mode 100644 index 00000000000..5396868a9de --- /dev/null +++ b/drivers/misc/modem_audio/Kconfig @@ -0,0 +1,6 @@ +config MODEM_AUDIO_DRIVER + bool "Modem Audio Driver" + depends on (U5500_MBOX && UX500_SOC_DB5500) + help + This module is used for read and write data between APE and + Access side in u5500 platform. diff --git a/drivers/misc/modem_audio/Makefile b/drivers/misc/modem_audio/Makefile new file mode 100644 index 00000000000..a5c1740ea48 --- /dev/null +++ b/drivers/misc/modem_audio/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_MODEM_AUDIO_DRIVER) += mad.o + diff --git a/drivers/misc/modem_audio/mad.c b/drivers/misc/modem_audio/mad.c new file mode 100644 index 00000000000..e9284b66dab --- /dev/null +++ b/drivers/misc/modem_audio/mad.c @@ -0,0 +1,486 @@ +/* + * Copyright (C) ST-Ericsson AB 2011 + * + * Modem Audio Driver + * + * Author:Rahul Venkatram for ST-Ericsson + * Haridhar KALVALA for ST-Ericsson + * Amaresh Mulage for ST-Ericsson. + * + * License terms:GNU General Public License (GPLv2)version 2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("Modem Audio Driver"); +MODULE_LICENSE("GPLv2"); + +/** + * ----------------------------------------------------- + * | | | | + * | Data[0] |Data[1] |Data[2] |===>Data word 32 bits + * ----------------------------------------------------- + * | MESSAGE |Data | Index | + * | TYPE |length | number |===>READ/WRITE message + * ----------------------------------------------------- + * ----------------------------------------------------- + * | MESSAGE | DSP SHM addr | max_no_of_buffers |===> READ + * | TYPE | to write data | ||buffersize |WRITE SETUP message + * ----------------------------------------------------- + */ + + +#define MAD_NAME "mad" +/* Bit mask */ +#define MASK_UPPER_WORD 0xFFFF + +/* channel values for each direction */ +#define CHANNEL_NUM_RX 0x500 +#define CHANNEL_NUM_TX 0x900 + +/* + * Maximum number of datawords which can be sent + * in the mailbox each word is 32 bits + */ +#define MAX_NR_OF_DATAWORDS 3 +#define MAX_NUM_RX_BUFF 4 +#define NR_OF_DATAWORDS_REQD_FOR_ACK 1 + +/** + * Message types, must be identical in DSP Side + * VCS_MBOX_MSG_WRITE_IF_SETUP : DSP -> ARM + * VCS_MBOX_MSG_WRITE_IF_SETUP_ACK : ARM -> DSP + * VCS_MBOX_MSG_READ_IF_SETUP : DSP -> ARM + * VCS_MBOX_MSG_READ_IF_SETUP_ACK : ARM -> DSP + * VCS_MBOX_MSG_IF_ENC_DATA : ARM -> DSP + * VCS_MBOX_MSG_IF_DEC_DATA : DSP -> ARM + */ +#define VCS_MBOX_MSG_WRITE_IF_SETUP 0x200 +#define VCS_MBOX_MSG_WRITE_IF_SETUP_ACK 0x201 +#define VCS_MBOX_MSG_READ_IF_SETUP 0x400 +#define VCS_MBOX_MSG_READ_IF_SETUP_ACK 0x401 +#define VCS_MBOX_MSG_IF_ENC_DATA 0x80 +#define VCS_MBOX_MSG_IF_DEC_DATA 0x100 + +/** + * struct mad_data - This structure holds the state of the Modem Audio Driver. + * + * @dsp_shm_write_ptr : Ptr to the first TX buffer in DSP + * @dsp_shm_read_ptr : Ptr to the first RX buffer in DSP + * @max_tx_buffs : No. of DSP buffers available to write + * @max_rx_buffs : No. of DSP buffers available to read + * @write_offset : Size of each buffer in the DSP + * @read_offset : Size of each buffer in the DSP + * @rx_buff : Buffer for incoming data + * @tx_buff : Buffer for outgoing data + * @tx_buffer_num : Buffer counter for writing to DSP + * @rx_buffer_num : Buffer counter for reading to DSP + * @rx_buffer_read : Buffer counter for reading from userspace + * @data_written : RX data message arrival indicator + * @read_setup_msg : flag for opening read data + * @readq : read queue of data message + * @lock : lock for r/w message queue + */ +struct mad_data { + void __iomem *dsp_shm_write_ptr; + void __iomem *dsp_shm_read_ptr; + int max_tx_buffs; + int max_rx_buffs; + int write_offset; + int read_offset; + u32 *rx_buff; + u32 *tx_buff; + int tx_buffer_num; + int rx_buffer_num; + int rx_buffer_read; + u32 data_written; + bool read_setup_msg; + bool open_check; + wait_queue_head_t readq; + spinlock_t lock; +}; + +static struct mad_data *mad; + +/** + * mad_send_cb - This function is default callback for send. + * @data -Pointer to the data buffer + * @len -Data buffer length + * @arg -Private data pointer associated with test + */ +static void mad_send_cb(u32 *data, u32 len, void *arg) +{ + pr_debug("%s", __func__); +} + +/** + * mad_receive_cb - This callback function is for receiving data from mailbox + * @data -Pointer to the data buffer + * @len -length of the Mailbox + * @arg -Private data pointer associated with test + */ +static void mad_receive_cb(u32 *data, u32 length, void *priv) +{ + struct mad_data *mad = priv; + struct mbox_channel_msg msg; + u32 ack_to_dsp; + unsigned long flags; + + /* setup message for write address */ + if (*data == VCS_MBOX_MSG_WRITE_IF_SETUP) { + + ack_to_dsp = VCS_MBOX_MSG_WRITE_IF_SETUP_ACK; + + /* if setup message comes again.unmap */ + if (mad->dsp_shm_write_ptr != NULL) { + iounmap(mad->dsp_shm_write_ptr); + mad->dsp_shm_write_ptr = NULL; + mad->write_offset = 0; + mad->max_tx_buffs = 0; + } + + /* convert offset to uint size */ + mad->write_offset = (data[2] & MASK_UPPER_WORD); + mad->max_tx_buffs = (data[2] >> 16); + + mad->dsp_shm_write_ptr = ioremap(data[1], + mad->max_tx_buffs * mad->write_offset); + if (mad->dsp_shm_write_ptr == NULL) + pr_err("%s :Error write address ioremap failed %X\n", + __func__ ,(u32) mad->dsp_shm_write_ptr); + + /* Initialize all buffer numbers */ + mad->tx_buffer_num = 0; + + /* Send ACK to the DSP */ + msg.channel = CHANNEL_NUM_TX; + msg.data = &ack_to_dsp; + msg.length = NR_OF_DATAWORDS_REQD_FOR_ACK; + msg.cb = mad_send_cb; + msg.priv = mad; + + if (mbox_channel_send(&msg)) + pr_err("%s: cannot send data\n", __func__); + + } /* setup message for reading SHM */ + else if (*data == VCS_MBOX_MSG_READ_IF_SETUP) { + + ack_to_dsp = VCS_MBOX_MSG_READ_IF_SETUP_ACK; + + /* if setup message comes again.unmap */ + if (mad->dsp_shm_read_ptr != NULL) { + iounmap(mad->dsp_shm_read_ptr); + mad->dsp_shm_read_ptr = NULL; + mad->read_offset = 0; + mad->max_rx_buffs = 0; + } + + /*convert offset to uint size*/ + mad->read_offset = (data[2] & MASK_UPPER_WORD); + mad->max_rx_buffs = data[2] >> 16; + + mad->dsp_shm_read_ptr = ioremap(data[1], + mad->max_rx_buffs * mad->read_offset); + + /* Initialize all buffer numbers and flags */ + mad->rx_buffer_num = 0; + mad->rx_buffer_read = 0; + mad->data_written = 0; + + /* Send ACK to the DSP */ + msg.channel = CHANNEL_NUM_TX; + msg.data = &ack_to_dsp; + msg.length = NR_OF_DATAWORDS_REQD_FOR_ACK; + msg.cb = mad_send_cb; + msg.priv = mad; + + if (mbox_channel_send(&msg)) + pr_err("%s: cannot send data\n", __func__); + + /* allow read */ + spin_lock_irqsave(&mad->lock, flags); + mad->read_setup_msg = true; + spin_unlock_irqrestore(&mad->lock, flags); + /* blocked in select() */ + wake_up_interruptible(&mad->readq); + + } else if (*data == VCS_MBOX_MSG_IF_DEC_DATA) { + /* + * Check if you have valid message with proper length in message + * otherwise Dont care + */ + if ((data[1] <= 0) || (mad->rx_buff == NULL)) { + if (mad->rx_buff == NULL) + pr_notice("%s :MAD closed", __func__); + else + pr_notice("%s : Zero size message", __func__); + } else { + mad->rx_buff[mad->rx_buffer_num] = data[1]; + mad->rx_buffer_num++; + + /* store the offset */ + mad->rx_buff[mad->rx_buffer_num] = data[2]; + + if (mad->rx_buffer_num < ((MAX_NUM_RX_BUFF * 2)-1)) + mad->rx_buffer_num++; + else + mad->rx_buffer_num = 0; + + spin_lock_irqsave(&mad->lock, flags); + mad->data_written++; + + if (mad->data_written > MAX_NUM_RX_BUFF) { + pr_notice("%s : Read msg overflow = %u\n", + mad->data_written, __func__); + /* + * Donot exceed MAX_NUM_RX_BUFF size of buffer + * TO DO overflow control + */ + mad->data_written = MAX_NUM_RX_BUFF ; + } + spin_unlock_irqrestore(&mad->lock, flags); + wake_up_interruptible(&mad->readq); + } + } else { + /* received Invalid message */ + pr_err("%s : Invalid Mesasge sent by the DSP\n ", __func__); + } +} + +static int mad_read(struct file *filp, char __user *buff, size_t count, + loff_t *offp) +{ + unsigned long flags; + unsigned int size = 0; + void __iomem *shm_ptr = NULL; + + pr_debug("%s", __func__); + + if (!(mad->data_written > 0)) { + if (wait_event_interruptible(mad->readq, + ((mad->data_written > 0) && + (mad->dsp_shm_read_ptr != NULL)))) + return -ERESTARTSYS; + } + + if (mad->dsp_shm_read_ptr == NULL) { + pr_err("%s :No Read shared memory pointer yet", __func__); + return -EINVAL ; + } + + if (mad->rx_buff[mad->rx_buffer_read] > count) { + /* + * Size of message greater than buffer , this shouldnt happen + * It shouldnt come here : we ensured that message size + * smaller that buffer length + */ + pr_err("%s : Incorrect length", __func__); + return -EFAULT; + } + size = mad->rx_buff[mad->rx_buffer_read]; + mad->rx_buff[mad->rx_buffer_read] = 0; + mad->rx_buffer_read++; + shm_ptr = (u8 *)(mad->dsp_shm_read_ptr + + (mad->rx_buff[mad->rx_buffer_read] * mad->read_offset)); + if (copy_to_user(buff, shm_ptr, size) < 0) { + pr_err("%s :Error in copy to user", __func__); + return -EFAULT; + } + + if (mad->rx_buffer_read < ((MAX_NUM_RX_BUFF*2)-1)) + mad->rx_buffer_read++; + else + mad->rx_buffer_read = 0; + + spin_lock_irqsave(&mad->lock, flags); + mad->data_written--; + if (mad->data_written < 0) { + /* Means wrong read*/ + mad->data_written = 0; + pr_err("%s :Read More than Received", __func__); + } + spin_unlock_irqrestore(&mad->lock, flags); + return size; +} + +static int mad_write(struct file *filp, const char __user *buff, size_t count, + loff_t *offp) +{ + int retval = 0; + void __iomem *dsp_write_address; + struct mbox_channel_msg msg; + + pr_debug("%s", __func__); + + /* check for valid write pointer else skip writing*/ + if (mad->dsp_shm_write_ptr == NULL) { + pr_err("%s : Illegal memory access\n", __func__); + return -EFAULT; + } + + dsp_write_address = (mad->dsp_shm_write_ptr + + (mad->tx_buffer_num * mad->write_offset)); + + if (copy_from_user(dsp_write_address, buff, count)) { + pr_err("%s: Cannot access memory\n", __func__); + return -EFAULT; + } + + mad->tx_buff[0] = VCS_MBOX_MSG_IF_ENC_DATA; + mad->tx_buff[1] = count; + mad->tx_buff[2] = mad->tx_buffer_num; + + if (mad->tx_buffer_num < (mad->max_tx_buffs-1)) + mad->tx_buffer_num++; + else + mad->tx_buffer_num = 0; + + msg.channel = CHANNEL_NUM_TX; + msg.data = mad->tx_buff; + msg.length = MAX_NR_OF_DATAWORDS; + msg.cb = mad_send_cb; + msg.priv = mad; + + retval = mbox_channel_send(&msg); + if (retval) { + pr_err("%s: cannot send data\n", __func__); + return retval; + } + return count; +} + +static unsigned int mad_select(struct file *filp, poll_table *wait) +{ + unsigned int mask = 0; + unsigned long flags; + + pr_debug("%s", __func__); + + poll_wait(filp, &mad->readq, wait); + spin_lock_irqsave(&mad->lock, flags); + + if ((true == mad->read_setup_msg) && (mad->data_written > 0)) + mask |= POLLIN | POLLRDNORM; /* allow readable */ + spin_unlock_irqrestore(&mad->lock, flags); + + return mask; +} + +static int mad_open(struct inode *ino, struct file *filp) +{ + int err = 0; + + pr_debug("%s", __func__); + + if (mad->open_check == true) { + pr_err("%s :MAD is already opened", __func__); + return -EFAULT; + } + + mad->rx_buff = kzalloc((MAX_NUM_RX_BUFF*2 * + sizeof(mad->rx_buff)), GFP_KERNEL); + + if (mad->rx_buff == NULL) { + pr_err("%s: Cannot allocate RX memory\n", __func__); + err = -ENOMEM; + goto error; + } + + mad->tx_buff = kzalloc(MAX_NR_OF_DATAWORDS, GFP_KERNEL); + if (mad->tx_buff == NULL) { + pr_err("%s: Cannot allocate TX memory\n", __func__); + err = -ENOMEM; + goto error; + } + + /* Init spinlock for critical section access*/ + spin_lock_init(&mad->lock); + init_waitqueue_head(&(mad->readq)); + + err = mbox_channel_register(CHANNEL_NUM_RX, mad_receive_cb, mad); + if (err) { + pr_err("%s: Cannot register channel\n", __func__); + err = -EFAULT; + goto error; + } + mad->open_check = true; + + return 0; +error: + kfree(mad->rx_buff); + kfree(mad->tx_buff); + return err; +} + +static int mad_close(struct inode *ino, struct file *filp) +{ + pr_debug("%s", __func__); + + if (mad->dsp_shm_write_ptr != NULL) { + iounmap(mad->dsp_shm_write_ptr); + mad->dsp_shm_write_ptr = NULL; + } + + if (mad->dsp_shm_read_ptr != NULL) { + iounmap(mad->dsp_shm_read_ptr); + mad->dsp_shm_read_ptr = NULL; + } + + kfree(mad->rx_buff); + kfree(mad->tx_buff); + + mad->open_check = false; + + return 0; +} + +static const struct file_operations mad_fops = { + .release = mad_close, + .open = mad_open, + .read = mad_read, + .write = mad_write, + .poll = mad_select, + .owner = THIS_MODULE, +}; + +static struct miscdevice mad_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = MAD_NAME, + .fops = &mad_fops +}; + +static int __init mad_init(void) +{ + pr_debug("%s", __func__); + + mad = kzalloc(sizeof(*mad), GFP_KERNEL); + if (mad == NULL) { + pr_err("%s :MAD Memory Allocation Failed", __func__); + return -ENOMEM; + } + + return misc_register(&mad_dev); +} +module_init(mad_init); + +static void __exit mad_exit(void) +{ + pr_debug("%s", __func__); + + kfree(mad); + + misc_deregister(&mad_dev); +} -- cgit v1.2.3 From caed96618070a32dd8b7540106e223e24126b4b8 Mon Sep 17 00:00:00 2001 From: Haridhar Kalvala Date: Tue, 6 Sep 2011 18:07:20 +0530 Subject: drivers: misc: pr* log prints replaced with dev* pr* debug log type used in the code is replaced with dev* log type. ST-Ericsson Linux next: Not tested, ER 350625 ST-Ericsson ID: 350625 ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-10006 Change-Id: I2e8141ad0bd476ccfbaf86560cb1f754548cacc0 Author: Haridhar Kalvala Signed-off-by: Haridhar Kalvala Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27447 Reviewed-by: QATOOLS Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR Reviewed-by: Amaresh MULAGE Tested-by: Amaresh MULAGE --- drivers/misc/modem_audio/mad.c | 102 +++++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 44 deletions(-) diff --git a/drivers/misc/modem_audio/mad.c b/drivers/misc/modem_audio/mad.c index e9284b66dab..6332d84659a 100644 --- a/drivers/misc/modem_audio/mad.c +++ b/drivers/misc/modem_audio/mad.c @@ -115,6 +115,31 @@ struct mad_data { static struct mad_data *mad; +static void mad_receive_cb(u32 *data, u32 length, void *priv); +static int mad_read(struct file *filp, char __user *buff, size_t count, + loff_t *offp); +static int mad_write(struct file *filp, const char __user *buff, size_t count, + loff_t *offp); +static unsigned int mad_select(struct file *filp, poll_table *wait); +static void mad_send_cb(u32 *data, u32 len, void *arg); +static int mad_open(struct inode *ino, struct file *filp); +static int mad_close(struct inode *ino, struct file *filp); + +static const struct file_operations mad_fops = { + .release = mad_close, + .open = mad_open, + .read = mad_read, + .write = mad_write, + .poll = mad_select, + .owner = THIS_MODULE, +}; + +static struct miscdevice mad_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = MAD_NAME, + .fops = &mad_fops +}; + /** * mad_send_cb - This function is default callback for send. * @data -Pointer to the data buffer @@ -123,7 +148,7 @@ static struct mad_data *mad; */ static void mad_send_cb(u32 *data, u32 len, void *arg) { - pr_debug("%s", __func__); + dev_dbg(mad_dev.this_device, "%s", __func__); } /** @@ -159,8 +184,7 @@ static void mad_receive_cb(u32 *data, u32 length, void *priv) mad->dsp_shm_write_ptr = ioremap(data[1], mad->max_tx_buffs * mad->write_offset); if (mad->dsp_shm_write_ptr == NULL) - pr_err("%s :Error write address ioremap failed %X\n", - __func__ ,(u32) mad->dsp_shm_write_ptr); + dev_err(mad_dev.this_device, "incrt write address"); /* Initialize all buffer numbers */ mad->tx_buffer_num = 0; @@ -173,7 +197,8 @@ static void mad_receive_cb(u32 *data, u32 length, void *priv) msg.priv = mad; if (mbox_channel_send(&msg)) - pr_err("%s: cannot send data\n", __func__); + dev_err(mad_dev.this_device, "%s: can't send data\n", + __func__); } /* setup message for reading SHM */ else if (*data == VCS_MBOX_MSG_READ_IF_SETUP) { @@ -208,7 +233,8 @@ static void mad_receive_cb(u32 *data, u32 length, void *priv) msg.priv = mad; if (mbox_channel_send(&msg)) - pr_err("%s: cannot send data\n", __func__); + dev_err(mad_dev.this_device, "%s: can't send data\n", + __func__); /* allow read */ spin_lock_irqsave(&mad->lock, flags); @@ -224,9 +250,11 @@ static void mad_receive_cb(u32 *data, u32 length, void *priv) */ if ((data[1] <= 0) || (mad->rx_buff == NULL)) { if (mad->rx_buff == NULL) - pr_notice("%s :MAD closed", __func__); + dev_warn(mad_dev.this_device, "%s :MAD closed", + __func__); else - pr_notice("%s : Zero size message", __func__); + dev_warn(mad_dev.this_device, "%s :0-len msg", + __func__); } else { mad->rx_buff[mad->rx_buffer_num] = data[1]; mad->rx_buffer_num++; @@ -243,8 +271,9 @@ static void mad_receive_cb(u32 *data, u32 length, void *priv) mad->data_written++; if (mad->data_written > MAX_NUM_RX_BUFF) { - pr_notice("%s : Read msg overflow = %u\n", - mad->data_written, __func__); + dev_warn(mad_dev.this_device, + "%s :Read msg overflow = %u\n", + __func__ , mad->data_written); /* * Donot exceed MAX_NUM_RX_BUFF size of buffer * TO DO overflow control @@ -256,7 +285,7 @@ static void mad_receive_cb(u32 *data, u32 length, void *priv) } } else { /* received Invalid message */ - pr_err("%s : Invalid Mesasge sent by the DSP\n ", __func__); + dev_err(mad_dev.this_device, "%s : Invalid Msg", __func__); } } @@ -267,7 +296,7 @@ static int mad_read(struct file *filp, char __user *buff, size_t count, unsigned int size = 0; void __iomem *shm_ptr = NULL; - pr_debug("%s", __func__); + dev_dbg(mad_dev.this_device, "%s", __func__); if (!(mad->data_written > 0)) { if (wait_event_interruptible(mad->readq, @@ -277,7 +306,7 @@ static int mad_read(struct file *filp, char __user *buff, size_t count, } if (mad->dsp_shm_read_ptr == NULL) { - pr_err("%s :No Read shared memory pointer yet", __func__); + dev_err(mad_dev.this_device, "%s :pointer err", __func__); return -EINVAL ; } @@ -287,7 +316,7 @@ static int mad_read(struct file *filp, char __user *buff, size_t count, * It shouldnt come here : we ensured that message size * smaller that buffer length */ - pr_err("%s : Incorrect length", __func__); + dev_err(mad_dev.this_device, "%s : Incrct length", __func__); return -EFAULT; } size = mad->rx_buff[mad->rx_buffer_read]; @@ -296,7 +325,7 @@ static int mad_read(struct file *filp, char __user *buff, size_t count, shm_ptr = (u8 *)(mad->dsp_shm_read_ptr + (mad->rx_buff[mad->rx_buffer_read] * mad->read_offset)); if (copy_to_user(buff, shm_ptr, size) < 0) { - pr_err("%s :Error in copy to user", __func__); + dev_err(mad_dev.this_device, "%s :copy to user", __func__); return -EFAULT; } @@ -310,7 +339,7 @@ static int mad_read(struct file *filp, char __user *buff, size_t count, if (mad->data_written < 0) { /* Means wrong read*/ mad->data_written = 0; - pr_err("%s :Read More than Received", __func__); + dev_err(mad_dev.this_device, "%s :data Rcev err", __func__); } spin_unlock_irqrestore(&mad->lock, flags); return size; @@ -323,11 +352,11 @@ static int mad_write(struct file *filp, const char __user *buff, size_t count, void __iomem *dsp_write_address; struct mbox_channel_msg msg; - pr_debug("%s", __func__); + dev_dbg(mad_dev.this_device, "%s", __func__); /* check for valid write pointer else skip writing*/ if (mad->dsp_shm_write_ptr == NULL) { - pr_err("%s : Illegal memory access\n", __func__); + dev_err(mad_dev.this_device, "%s :Illegal memory", __func__); return -EFAULT; } @@ -335,7 +364,7 @@ static int mad_write(struct file *filp, const char __user *buff, size_t count, (mad->tx_buffer_num * mad->write_offset)); if (copy_from_user(dsp_write_address, buff, count)) { - pr_err("%s: Cannot access memory\n", __func__); + dev_err(mad_dev.this_device, "%s:copy_from_user\n", __func__); return -EFAULT; } @@ -356,7 +385,7 @@ static int mad_write(struct file *filp, const char __user *buff, size_t count, retval = mbox_channel_send(&msg); if (retval) { - pr_err("%s: cannot send data\n", __func__); + dev_err(mad_dev.this_device, "%s:can't send data", __func__); return retval; } return count; @@ -367,7 +396,7 @@ static unsigned int mad_select(struct file *filp, poll_table *wait) unsigned int mask = 0; unsigned long flags; - pr_debug("%s", __func__); + dev_dbg(mad_dev.this_device, "%s", __func__); poll_wait(filp, &mad->readq, wait); spin_lock_irqsave(&mad->lock, flags); @@ -383,10 +412,10 @@ static int mad_open(struct inode *ino, struct file *filp) { int err = 0; - pr_debug("%s", __func__); + dev_dbg(mad_dev.this_device, "%s", __func__); if (mad->open_check == true) { - pr_err("%s :MAD is already opened", __func__); + dev_err(mad_dev.this_device, "%s :Already opened", __func__); return -EFAULT; } @@ -394,14 +423,14 @@ static int mad_open(struct inode *ino, struct file *filp) sizeof(mad->rx_buff)), GFP_KERNEL); if (mad->rx_buff == NULL) { - pr_err("%s: Cannot allocate RX memory\n", __func__); + dev_err(mad_dev.this_device, "%s:RX memory\n", __func__); err = -ENOMEM; goto error; } mad->tx_buff = kzalloc(MAX_NR_OF_DATAWORDS, GFP_KERNEL); if (mad->tx_buff == NULL) { - pr_err("%s: Cannot allocate TX memory\n", __func__); + dev_err(mad_dev.this_device, "%s:TX memory\n", __func__); err = -ENOMEM; goto error; } @@ -412,7 +441,7 @@ static int mad_open(struct inode *ino, struct file *filp) err = mbox_channel_register(CHANNEL_NUM_RX, mad_receive_cb, mad); if (err) { - pr_err("%s: Cannot register channel\n", __func__); + dev_err(mad_dev.this_device, "%s: register err", __func__); err = -EFAULT; goto error; } @@ -427,7 +456,7 @@ error: static int mad_close(struct inode *ino, struct file *filp) { - pr_debug("%s", __func__); + dev_dbg(mad_dev.this_device, "%s", __func__); if (mad->dsp_shm_write_ptr != NULL) { iounmap(mad->dsp_shm_write_ptr); @@ -447,28 +476,13 @@ static int mad_close(struct inode *ino, struct file *filp) return 0; } -static const struct file_operations mad_fops = { - .release = mad_close, - .open = mad_open, - .read = mad_read, - .write = mad_write, - .poll = mad_select, - .owner = THIS_MODULE, -}; - -static struct miscdevice mad_dev = { - .minor = MISC_DYNAMIC_MINOR, - .name = MAD_NAME, - .fops = &mad_fops -}; - static int __init mad_init(void) { - pr_debug("%s", __func__); + dev_dbg(mad_dev.this_device, "%s", __func__); mad = kzalloc(sizeof(*mad), GFP_KERNEL); if (mad == NULL) { - pr_err("%s :MAD Memory Allocation Failed", __func__); + dev_err(mad_dev.this_device, "%s :MAD failed", __func__); return -ENOMEM; } @@ -478,7 +492,7 @@ module_init(mad_init); static void __exit mad_exit(void) { - pr_debug("%s", __func__); + dev_dbg(mad_dev.this_device, "%s", __func__); kfree(mad); -- cgit v1.2.3 From 4d817e06bb1d285d877a8c600cb543df81a40c02 Mon Sep 17 00:00:00 2001 From: ing07300 Date: Tue, 6 Sep 2011 18:09:13 +0530 Subject: drivers: misc: Shared Address checking on re-open. Shared memory address check on re-open of driver.And reset of data written count on close of driver. ST-Ericsson Linux next: Not tested, ER 350889 ST-Ericsson ID: 350889 ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-10006 Change-Id: I1cc1c41e4bab2799c2110aa575506bce97d0e273 Author: Haridhar Kalvala Signed-off-by: ing07300 Signed-off-by: Haridhar Kalvala Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27388 Reviewed-by: QATOOLS Reviewed-by: QATEST Tested-by: Ganesh V Reviewed-by: Ganesh V Reviewed-by: Srinidhi KASAGAR --- drivers/misc/modem_audio/mad.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/misc/modem_audio/mad.c b/drivers/misc/modem_audio/mad.c index 6332d84659a..62bef3c75fd 100644 --- a/drivers/misc/modem_audio/mad.c +++ b/drivers/misc/modem_audio/mad.c @@ -248,7 +248,8 @@ static void mad_receive_cb(u32 *data, u32 length, void *priv) * Check if you have valid message with proper length in message * otherwise Dont care */ - if ((data[1] <= 0) || (mad->rx_buff == NULL)) { + if ((data[1] <= 0) || (mad->rx_buff == NULL) + || (mad->dsp_shm_read_ptr == NULL)) { if (mad->rx_buff == NULL) dev_warn(mad_dev.this_device, "%s :MAD closed", __func__); @@ -470,7 +471,7 @@ static int mad_close(struct inode *ino, struct file *filp) kfree(mad->rx_buff); kfree(mad->tx_buff); - + mad->data_written = 0; mad->open_check = false; return 0; -- cgit v1.2.3 From b1e3ca8b1cfe7947cd7bb9cc2138050e5c1fcb56 Mon Sep 17 00:00:00 2001 From: Marcus Danielsson Date: Mon, 5 Sep 2011 14:35:29 +0200 Subject: ux500: changed the end variable in modem trace. Fixed an off by one error in modem tracer. ST-Ericsson ID: 356944 ST-Ericsson Linux next: N/A ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I82ee4d37a677e54e88c3de2a75e099be1c7a77e9 Signed-off-by: Marcus Danielsson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30638 Reviewed-by: Daniel WILLERUD Reviewed-by: Jonas ABERG --- drivers/misc/db8500-modem-trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/db8500-modem-trace.c b/drivers/misc/db8500-modem-trace.c index 0d739fb4694..b757b742121 100644 --- a/drivers/misc/db8500-modem-trace.c +++ b/drivers/misc/db8500-modem-trace.c @@ -203,7 +203,7 @@ static int trace_probe(struct platform_device *pdev) trace_priv->misc_dev.name = DEVICE_NAME; trace_priv->misc_dev.fops = &trace_fops; trace_priv->area = (void __iomem *)ioremap_nocache(mem->start, - mem->end - mem->start); + resource_size(mem)); if (!trace_priv->area) { rv = -ENOMEM; goto outfree; -- cgit v1.2.3 From 104c93875301883ff8e39d54e560515f87780a85 Mon Sep 17 00:00:00 2001 From: Bibek Basu Date: Thu, 15 Sep 2011 09:36:16 +0530 Subject: u5500 : modem irq update irq request flag should also be IRQF_NO_SUSPEND Signed-off-by: Bibek Basu Change-Id: I6db0770c7cfd62ee0feab03553a315781177def8 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/31000 Reviewed-by: Arun MURTHY --- arch/arm/mach-ux500/modem-irq-db5500.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-ux500/modem-irq-db5500.c b/arch/arm/mach-ux500/modem-irq-db5500.c index 52bca4eb8b6..7c2947af984 100644 --- a/arch/arm/mach-ux500/modem-irq-db5500.c +++ b/arch/arm/mach-ux500/modem-irq-db5500.c @@ -133,7 +133,7 @@ static int modem_irq_init(void) err = request_threaded_irq(IRQ_DB5500_MODEM, NULL, modem_cpu_irq_handler, - IRQF_ONESHOT | IRQF_ONESHOT, + IRQF_NO_SUSPEND | IRQF_ONESHOT, "modem_irq", mi); if (err) pr_err("modem_irq: Could not register IRQ %d\n", -- cgit v1.2.3 From 06cf5b68297be444b4deda42384b921a6cf24a87 Mon Sep 17 00:00:00 2001 From: "Rajanikanth H.V" Date: Thu, 15 Sep 2011 15:36:50 +0530 Subject: mbox: fix mbox crash added the missing mutex lock Change-Id: Ieab65c85408b783f029e04155be32fdc2299fa7e Signed-off-by: Rajanikanth H.V --- drivers/misc/mbox_channels-db5500.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/misc/mbox_channels-db5500.c b/drivers/misc/mbox_channels-db5500.c index 6bd4afa5fe2..a0b72d4d97a 100644 --- a/drivers/misc/mbox_channels-db5500.c +++ b/drivers/misc/mbox_channels-db5500.c @@ -282,7 +282,6 @@ static int send_pdu(struct channel_status *chan_status, int command, struct mbox *mbox; u32 header = 0; int ret = 0; - /* SEND PDU is not supported */ if (command == MBOX_SEND) { dev_err(&channels.pdev->dev, "SEND command not implemented\n"); @@ -387,8 +386,10 @@ void mbox_handle_open_msg(struct work_struct *work) /* Change channel state to OPEN */ tx_chan->state = MBOX_OPEN; /* If pending list not empty, start sending data */ + mutex_lock(&tx_chan->lock); if (!list_empty(&tx_chan->tx.pending)) send_pdu(tx_chan, MBOX_CAST, tx_chan->channel); + mutex_unlock(&tx_chan->lock); } void mbox_handle_cast_msg(struct work_struct *work) @@ -528,8 +529,6 @@ static void mbox_cb(u32 mbox_msg, void *priv) struct channel_status *rx_chan = NULL; bool is_Payload = 0; - dev_dbg(&channels.pdev->dev, "%s type %d\t, mbox_msg %x\n", - __func__, type, mbox_msg); /* Get RX channels list for given mbox unit */ rx_list = get_rx_list(mbox_id); if (rx_list == NULL) { @@ -607,7 +606,6 @@ int mbox_channel_register(u16 channel, mbox_channel_cb_t *cb, void *priv) int res = 0; struct mbox_unit_status *mbox_unit; - dev_dbg(&channels.pdev->dev, " %s channel = %d\n", __func__, channel); /* Check for callback fcn */ if (cb == NULL) { dev_err(&channels.pdev->dev, @@ -983,7 +981,6 @@ static void mboxtest_receive_cb(u32 *data, u32 len, void *arg) printk(KERN_INFO "receive_cb.. data.= 0x%X, len = %d\n", *data, len); - for (i = 0; i < len; i++) *(mboxtest->rx_pointer++) = *(data++); -- cgit v1.2.3 From d25d3ae51f46400d791753f99c2178c602abd270 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Thu, 8 Sep 2011 16:56:48 +0530 Subject: mbox: optimize tx path and fix bug in rx path During tx, in the work function check for if any messages are pending to be transmitted, if so, re-transmit. If in rx path work function is queued twice then the work is executed only once so the other message will be lost. Hence check if work has been queued and if so read the next message. ST-Ericsson Linux next: NA ST-Ericsson ID: 357703 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I7f3a18947e36cae30da8ca391d10ed5d3ac0eb03 Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/31742 Reviewed-by: Bibek BASU Tested-by: Bibek BASU --- drivers/misc/mbox.c | 6 ++++++ drivers/misc/mbox_channels-db5500.c | 36 ++++++++++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/drivers/misc/mbox.c b/drivers/misc/mbox.c index b2810718822..5f5a457b277 100644 --- a/drivers/misc/mbox.c +++ b/drivers/misc/mbox.c @@ -348,6 +348,7 @@ static irqreturn_t mbox_irq(int irq, void *arg) if (nbr_occup == 0) goto exit; +redo: if (mbox->cb == NULL) { dev_dbg(&(mbox->pdev->dev), "No receive callback registered, " "leaving %d incoming messages in fifo!\n", nbr_occup); @@ -363,6 +364,11 @@ static irqreturn_t mbox_irq(int irq, void *arg) mbox_value); mbox->cb(mbox_value, mbox->client_data); + nbr_occup = readl(mbox->virtbase_local + MBOX_FIFO_STATUS) & 0x7; + + if (nbr_occup > 0) + goto redo; + exit: dev_dbg(&(mbox->pdev->dev), "Exit mbox IRQ. ri = %d, wi = %d\n", mbox->read_index, mbox->write_index); diff --git a/drivers/misc/mbox_channels-db5500.c b/drivers/misc/mbox_channels-db5500.c index a0b72d4d97a..43a4d57caaf 100644 --- a/drivers/misc/mbox_channels-db5500.c +++ b/drivers/misc/mbox_channels-db5500.c @@ -121,7 +121,7 @@ struct rx_pending_elem { void *priv; }; -struct rx_pending_elem rx_pending[NUM_DSP_BUFFER + 1]; +struct rx_pending_elem rx_pending[NUM_DSP_BUFFER]; /* This structure holds list of pending elements for mbox tx channel */ struct tx_channel { @@ -154,6 +154,7 @@ struct rx_channel { * @lock: holds lock for channel */ struct channel_status { + atomic_t rcv_counter; struct list_head list; u16 channel; int state; @@ -362,6 +363,9 @@ void mbox_handle_receive_msg(struct work_struct *work) struct channel_status, receive_msg); + if (!atomic_read(&rx_chan->rcv_counter)) + return; +rcv_msg: /* Call client's callback and reset state */ if (rx_chan->cb) { static int rx_pending_count; @@ -375,6 +379,8 @@ void mbox_handle_receive_msg(struct work_struct *work) dev_err(&channels.pdev->dev, "%s no callback provided\n", __func__); } + if (atomic_dec_and_test(&rx_chan->rcv_counter) > 0) + goto rcv_msg; } @@ -413,12 +419,14 @@ static bool handle_receive_msg(u32 mbox_msg, struct channel_status *rx_chan) if (rx_chan) { /* Store received data in RX channel buffer */ rx_chan->rx.buffer[rx_chan->rx.index++] = mbox_msg; + /* Check if it's last data of PDU */ if (rx_chan->rx.index == rx_chan->rx.length) { for (i = 0; i < MAILBOX_NR_OF_DATAWORDS; i++) { rx_pending[rx_pending_count].buffer[i] = rx_chan->rx.buffer[i]; } + rx_pending[rx_pending_count].length = rx_chan->rx.length; rx_pending[rx_pending_count].priv = rx_chan->priv; @@ -429,10 +437,12 @@ static bool handle_receive_msg(u32 mbox_msg, struct channel_status *rx_chan) rx_pending_count++; if (rx_pending_count == NUM_DSP_BUFFER) rx_pending_count = 0; + atomic_inc(&rx_chan->rcv_counter); queue_work(rx_chan->receive_wq, &rx_chan->receive_msg); } dev_dbg(&channels.pdev->dev, "%s OK\n", __func__); + return true; } return false; @@ -529,6 +539,9 @@ static void mbox_cb(u32 mbox_msg, void *priv) struct channel_status *rx_chan = NULL; bool is_Payload = 0; + dev_dbg(&channels.pdev->dev, "%s type %d\t, mbox_msg %x\n", + __func__, type, mbox_msg); + /* Get RX channels list for given mbox unit */ rx_list = get_rx_list(mbox_id); if (rx_list == NULL) { @@ -606,6 +619,7 @@ int mbox_channel_register(u16 channel, mbox_channel_cb_t *cb, void *priv) int res = 0; struct mbox_unit_status *mbox_unit; + dev_dbg(&channels.pdev->dev, " %s channel = %d\n", __func__, channel); /* Check for callback fcn */ if (cb == NULL) { dev_err(&channels.pdev->dev, @@ -655,6 +669,7 @@ int mbox_channel_register(u16 channel, mbox_channel_cb_t *cb, void *priv) goto exit; } + atomic_set(&rx_chan->rcv_counter, 0); /* Fill out newly allocated element and add it to rx list */ rx_chan->channel = channel; rx_chan->cb = cb; @@ -780,22 +795,26 @@ int mbox_channel_send(struct mbox_channel_msg *msg) struct channel_status *tx_chan = NULL; struct pending_elem *pending; struct mbox_unit_status *mbox_unit; + int res = 0; if (msg->length > MAILBOX_NR_OF_DATAWORDS || msg->length == 0) { dev_err(&channels.pdev->dev, "data length incorrect\n"); - return -EINVAL; + res = -EINVAL; + goto exit; } if (!check_channel(msg->channel, MBOX_TX)) { dev_err(&channels.pdev->dev, "wrong channel number %d\n", msg->channel); - return -EINVAL; + res = -EINVAL; + goto exit; } tx_list = get_tx_list(get_mbox_id(msg->channel)); if (tx_list == NULL) { dev_err(&channels.pdev->dev, "given mbox id is not valid\n"); - return -EINVAL; + res = -EINVAL; + goto exit; } mbox_unit = container_of(tx_list, struct mbox_unit_status, tx_chans); @@ -814,7 +833,8 @@ int mbox_channel_send(struct mbox_channel_msg *msg) if (pending == NULL) { dev_err(&channels.pdev->dev, "couldn't allocate memory for pending\n"); - return -ENOMEM; + res = -ENOMEM; + goto exit; } pending->data = msg->data; pending->length = msg->length; @@ -836,7 +856,8 @@ int mbox_channel_send(struct mbox_channel_msg *msg) dev_err(&channels.pdev->dev, "couldn't allocate memory for \ tx_chan\n"); - return -ENOMEM; + res = -ENOMEM; + goto exit; } tx_chan->channel = msg->channel; tx_chan->cb = msg->cb; @@ -856,6 +877,9 @@ int mbox_channel_send(struct mbox_channel_msg *msg) mutex_unlock(&tx_chan->lock); } return 0; + +exit: + return res; } EXPORT_SYMBOL(mbox_channel_send); -- cgit v1.2.3 From 24b793d3f2cd4392a7359c8b02130fecaf48101c Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Tue, 30 Aug 2011 11:52:02 +0200 Subject: modem: shrm: Remove a warning ST-Ericsson ID: - ST-Ericsson Linux next: Not tested ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ib02e686a4eff7e23da9a5454ffbc60a97a1d9d48 Signed-off-by: Jonas Aaberg Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29720 Reviewed-by: QABUILD Reviewed-by: QATEST Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/31931 Reviewed-by: Arun MURTHY Tested-by: Arun MURTHY --- drivers/modem/shrm/shrm_fifo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/modem/shrm/shrm_fifo.c b/drivers/modem/shrm/shrm_fifo.c index 9e6564aa6bc..1f0f9202e51 100644 --- a/drivers/modem/shrm/shrm_fifo.c +++ b/drivers/modem/shrm/shrm_fifo.c @@ -519,7 +519,7 @@ u8 read_one_l2msg_audio(struct shrm_dev *shrm, fifo->availablesize); dev_info(shrm->dev, "end_fifo= %x\n", fifo->end_addr_fifo); - dev_info(shrm->dev, "Received msgtype is &d\n", msgtype); + dev_info(shrm->dev, "Received msgtype is %d\n", msgtype); /* Fatal ERROR - should never happens */ dev_crit(shrm->dev, "Fatal ERROR - should never happen\n"); BUG(); -- cgit v1.2.3 From f68767246dc472f9906a6ec6ae090ea4d48d774f Mon Sep 17 00:00:00 2001 From: Johan Ydstrom Date: Tue, 6 Sep 2011 10:30:25 +0200 Subject: misc: shrm: Move IRQ disable to IRQ handler As soon as receving CP reset interrupt, all IRQ should be disabled and CP boot state should be changed. ST-Ericsson ID: 359707 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I1326bd18ef7a2f7694d82b280eecdd053d1857b1 Signed-off-by: Johan Ydstrom Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30684 Reviewed-by: Daniel WILLERUD Reviewed-by: Arun MURTHY Reviewed-by: Jonas ABERG Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/31932 Tested-by: Arun MURTHY --- drivers/modem/shrm/shrm_protocol.c | 49 +++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index 990591faee1..883c46b4f8f 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -480,33 +480,6 @@ 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); /* @@ -582,6 +555,7 @@ DECLARE_TASKLET(shrm_sw_reset_callback, shrm_modem_reset_callback, static irqreturn_t shrm_prcmu_irq_handler(int irq, void *data) { struct shrm_dev *shrm = data; + unsigned long flags; switch (irq) { case IRQ_PRCMU_CA_WAKE: @@ -597,6 +571,27 @@ static irqreturn_t shrm_prcmu_irq_handler(int irq, void *data) queue_work(shrm->shm_ca_wake_wq, &shrm->shm_ca_sleep_req); break; case IRQ_PRCMU_MODEM_SW_RESET_REQ: + /* 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); + + disable_irq_nosync(shrm->ac_read_notif_0_irq); + disable_irq_nosync(shrm->ac_read_notif_1_irq); + disable_irq_nosync(shrm->ca_msg_pending_notif_0_irq); + disable_irq_nosync(shrm->ca_msg_pending_notif_1_irq); + disable_irq_nosync(IRQ_PRCMU_CA_WAKE); + disable_irq_nosync(IRQ_PRCMU_CA_SLEEP); + tasklet_schedule(&shrm_sw_reset_callback); break; default: -- cgit v1.2.3 From 6c5289cb78a930f165929765067b3a610fa01830 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Mon, 17 Oct 2011 12:05:11 +0200 Subject: ARM: shrm: Remove powersave Since we don't know really the status of powersave related feature on this development track, let's remove everything, except: - prcmu driver: New location, which is the same as linux-next - cpufreq: new location, same as linux-next - regulator: keep as is for the moment, needed for the system to work. - clocks: same as for regulator. - pm-runtime: keep it as is. Later patches will re-add powersave, based upon what exists on SI u8500-android-2.3_v3.15 MTU from plat-nomadik is now used instead of own copy in mach. Signed-off-by: Jonas Aaberg Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32048 --- drivers/modem/shrm/shrm_protocol.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index 883c46b4f8f..3b7abf7da61 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #define L2_HEADER_ISI 0x0 @@ -449,9 +448,6 @@ void shm_ca_sleep_req_work(struct work_struct *work) 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); } @@ -559,9 +555,6 @@ static irqreturn_t shrm_prcmu_irq_handler(int irq, void *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); -- cgit v1.2.3 From b6fe998c08e3301a7f9b88f941ba868dc2e3fe5f Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Mon, 17 Oct 2011 13:08:24 +0200 Subject: ARM: ux500: modem: Replace mach prcmu driver with mainlined version The prcmu driver now exists in drivers/mfd Signed-off-by: Jonas Aaberg Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32057 --- drivers/modem/modem_u8500.c | 2 +- drivers/modem/shrm/shrm_protocol.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/modem/modem_u8500.c b/drivers/modem/modem_u8500.c index 20b5fe78ef7..2f5288d07bf 100644 --- a/drivers/modem/modem_u8500.c +++ b/drivers/modem/modem_u8500.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include static void u8500_modem_request(struct modem_dev *mdev) { diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index 3b7abf7da61..137b70e9cbf 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -16,8 +16,7 @@ #include #include #include -#include -#include +#include #include #define L2_HEADER_ISI 0x0 @@ -31,6 +30,8 @@ #define L2_HEADER_CIQ 0xC3 #define MAX_PAYLOAD 1024 +#define PRCM_HOSTACCESS_REQ (_PRCMU_BASE + 0x334) + static u8 boot_state = BOOT_INIT; static u8 recieve_common_msg[8*1024]; static u8 recieve_audio_msg[8*1024]; -- cgit v1.2.3 From d6d4065e6c6be7406db4c889a7bb53905ae23c4d Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Tue, 20 Sep 2011 12:03:08 +0200 Subject: drivers: modem: shrm: Block suspend when needed Change-Id: Ic86e900e38d61e1c9aa9d4a54daf1d610f0f2484 Signed-off-by: Jonas Aaberg Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32079 --- drivers/modem/shrm/shrm_protocol.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index 137b70e9cbf..50443e4803a 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -18,6 +18,7 @@ #include #include #include +#include #define L2_HEADER_ISI 0x0 #define L2_HEADER_RPC 0x1 @@ -449,6 +450,7 @@ void shm_ca_sleep_req_work(struct work_struct *work) hrtimer_start(&timer, ktime_set(0, 10*NSEC_PER_MSEC), HRTIMER_MODE_REL); + suspend_unblock_sleep(); atomic_dec(&ac_sleep_disable_count); } @@ -556,6 +558,7 @@ static irqreturn_t shrm_prcmu_irq_handler(int irq, void *data) switch (irq) { case IRQ_PRCMU_CA_WAKE: + suspend_block_sleep(); if (shrm->msr_flag) atomic_set(&ac_sleep_disable_count, 0); atomic_inc(&ac_sleep_disable_count); -- cgit v1.2.3 From 603e9ba27bf9abeb9b5417f4e096a253e47362ff Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Thu, 29 Sep 2011 13:11:25 +0530 Subject: u5500-mbox: Add platform driver remove Also correct the improper usage of platform data. ST-Ericsson Linux next: NA ST-Ericsson ID: 341807 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I9468996195c3652e94fc908f90ce85c66a5db519 Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32490 Reviewed-by: Srinidhi KASAGAR --- drivers/misc/mbox.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/drivers/misc/mbox.c b/drivers/misc/mbox.c index 5f5a457b277..73f34069d84 100644 --- a/drivers/misc/mbox.c +++ b/drivers/misc/mbox.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -130,7 +131,8 @@ static ssize_t mbox_write_fifo(struct device *dev, char *token; char *val; - struct mbox *mbox = (struct mbox *) dev->platform_data; + struct platform_device *pdev = to_platform_device(dev); + struct mbox *mbox = platform_get_drvdata(pdev); strncpy((char *) &int_buf, buf, sizeof(int_buf)); token = (char *) &int_buf; @@ -158,7 +160,8 @@ static ssize_t mbox_read_fifo(struct device *dev, char *buf) { int mbox_value; - struct mbox *mbox = (struct mbox *) dev->platform_data; + struct platform_device *pdev = to_platform_device(dev); + struct mbox *mbox = platform_get_drvdata(pdev); if ((readl(mbox->virtbase_local + MBOX_FIFO_STATUS) & 0x7) <= 0) return sprintf(buf, "Mailbox is empty\n"); @@ -515,24 +518,18 @@ EXPORT_SYMBOL(mbox_setup); int __init mbox_probe(struct platform_device *pdev) { - struct mbox local_mbox; struct mbox *mbox; int res = 0; dev_dbg(&(pdev->dev), "Probing mailbox (pdev = 0x%X)...\n", (u32) pdev); - memset(&local_mbox, 0x0, sizeof(struct mbox)); - - /* Associate our mbox data with the platform device */ - res = platform_device_add_data(pdev, - (void *) &local_mbox, - sizeof(struct mbox)); - if (res != 0) { - dev_err(&(pdev->dev), - "Unable to allocate driver platform data!\n"); - goto exit; + mbox = kzalloc(sizeof(struct mbox), GFP_KERNEL); + if (mbox == NULL) { + dev_err(&pdev->dev, + "Could not allocate memory for struct mbox\n"); + return -ENOMEM; } - mbox = (struct mbox *) pdev->dev.platform_data; + mbox->pdev = pdev; mbox->write_index = 0; mbox->read_index = 0; @@ -543,13 +540,23 @@ int __init mbox_probe(struct platform_device *pdev) sprintf(mbox->name, "%s", MBOX_NAME); spin_lock_init(&mbox->lock); + platform_set_drvdata(pdev, mbox); dev_info(&(pdev->dev), "Mailbox driver loaded\n"); -exit: return res; } +static int __exit mbox_remove(struct platform_device *pdev) +{ + struct mbox *mbox = platform_get_drvdata(pdev); + + list_del(&mbox->list); + kfree(mbox); + return 0; +} + static struct platform_driver mbox_driver = { + .remove = __exit_p(mbox_remove), .driver = { .name = MBOX_NAME, .owner = THIS_MODULE, -- cgit v1.2.3 From cc63a79965890121c217828fec54be7fec83d662 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Fri, 30 Sep 2011 14:21:55 +0530 Subject: ux500-mbox: Proper shutdown of mailbox Implement clean shutdown of mailbox, which includes, unmapping the io mapped memory, free the requested interrupt, reset all state machine and remove from list. A function has been added to reset the mailbox which will be used in case of MSR. ST-Ericsson Linux next: NA ST-Ericsson ID: 341807 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I1435345d155717a5276a7a5d74da84d3eeef7f61 Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32680 Reviewed-by: Srinidhi KASAGAR --- arch/arm/mach-ux500/include/mach/mbox-db5500.h | 5 ++ drivers/misc/mbox.c | 79 +++++++++++++++++++++++--- 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-ux500/include/mach/mbox-db5500.h b/arch/arm/mach-ux500/include/mach/mbox-db5500.h index 7f9da4d2fbd..24af854befe 100644 --- a/arch/arm/mach-ux500/include/mach/mbox-db5500.h +++ b/arch/arm/mach-ux500/include/mach/mbox-db5500.h @@ -40,6 +40,7 @@ typedef void mbox_recv_cb_t (u32 mbox_msg, void *priv); * @lock: Spinlock to protect this mailbox instance. * @write_index: Index in internal buffer to write to. * @read_index: Index in internal buffer to read from. + * @irq: mailbox interrupt. * @allocated: Indicates whether this particular mailbox * id has been allocated by someone. */ @@ -57,7 +58,11 @@ struct mbox { spinlock_t lock; u8 write_index; u8 read_index; + int irq; bool allocated; +#if defined(CONFIG_DEBUG_FS) + struct dentry *dentry; +#endif }; /** diff --git a/drivers/misc/mbox.c b/drivers/misc/mbox.c index 73f34069d84..9bd5db9b23c 100644 --- a/drivers/misc/mbox.c +++ b/drivers/misc/mbox.c @@ -380,11 +380,35 @@ exit: return IRQ_HANDLED; } +static void mbox_shutdown(struct mbox *mbox) +{ +#if defined(CONFIG_DEBUG_FS) + debugfs_remove(mbox->dentry); + device_remove_file(&mbox->pdev->dev, &dev_attr_fifo); +#endif + writel(MBOX_DISABLE_IRQ, mbox->virtbase_local + MBOX_FIFO_THRES_OCCUP); + writel(MBOX_DISABLE_IRQ, mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); + free_irq(mbox->irq, NULL); + mbox->client_blocked = 0; + iounmap(mbox->virtbase_local); + iounmap(mbox->virtbase_peer); + mbox->cb = NULL; + mbox->client_data = NULL; + mbox->allocated = false; +} + +void mbox_reset_state(struct mbox *mbox) +{ + list_for_each_entry(mbox, &mboxs, list) { + mbox_shutdown(mbox); + } +} + + /* Setup is executed once for each mbox pair */ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv) { struct resource *resource; - int irq; int res; struct mbox *mbox; @@ -463,21 +487,21 @@ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv) mbox->client_blocked = 0; /* Get IRQ for mailbox and allocate it */ - irq = platform_get_irq_byname(mbox->pdev, "mbox_irq"); - if (irq < 0) { + mbox->irq = platform_get_irq_byname(mbox->pdev, "mbox_irq"); + if (mbox->irq < 0) { dev_err(&(mbox->pdev->dev), "Unable to retrieve mbox irq resource\n"); mbox = NULL; goto exit; } - dev_dbg(&(mbox->pdev->dev), "Allocating irq %d...\n", irq); - res = request_threaded_irq(irq, NULL, mbox_irq, + dev_dbg(&(mbox->pdev->dev), "Allocating irq %d...\n", mbox->irq); + res = request_threaded_irq(mbox->irq, NULL, mbox_irq, IRQF_NO_SUSPEND | IRQF_ONESHOT, mbox->name, (void *) mbox); if (res < 0) { dev_err(&(mbox->pdev->dev), - "Unable to allocate mbox irq %d\n", irq); + "Unable to allocate mbox irq %d\n", mbox->irq); mbox = NULL; goto exit; } @@ -503,7 +527,7 @@ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv) dev_warn(&(mbox->pdev->dev), "Unable to create mbox sysfs entry"); - (void) debugfs_create_file("mbox", S_IFREG | S_IRUGO, NULL, + mbox->dentry = debugfs_create_file("mbox", S_IFREG | S_IRUGO, NULL, NULL, &mbox_operations); #endif @@ -550,16 +574,57 @@ static int __exit mbox_remove(struct platform_device *pdev) { struct mbox *mbox = platform_get_drvdata(pdev); + mbox_shutdown(mbox); list_del(&mbox->list); kfree(mbox); return 0; } +#ifdef CONFIG_PM +int mbox_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mbox *mbox = platform_get_drvdata(pdev); + + /* + * Nothing to be done for now, once APE-Modem power management is + * in place communication will have to be stopped. + */ + + list_for_each_entry(mbox, &mboxs, list) { + if (mbox->client_blocked) + return -EBUSY; + } + return 0; +} + +int mbox_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mbox *mbox = platform_get_drvdata(pdev); + + /* + * Nothing to be done for now, once APE-Modem power management is + * in place communication will have to be resumed. + */ + + return 0; +} + +static const struct dev_pm_ops mbox_dev_pm_ops = { + .suspend_noirq = mbox_suspend, + .resume_noirq = mbox_resume, +}; +#endif + static struct platform_driver mbox_driver = { .remove = __exit_p(mbox_remove), .driver = { .name = MBOX_NAME, .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &mbox_dev_pm_ops, +#endif }, }; -- cgit v1.2.3 From 1f89a49680e3a9d341bd2497af4a5f3cd21d6e69 Mon Sep 17 00:00:00 2001 From: Bengt Jonsson Date: Tue, 4 Oct 2011 11:40:10 +0200 Subject: regulators: ab8500: Add support of low voltage battery Low voltage batteries have a wider voltage range with lower operating voltages. Some consumers in the platform may not work with the lower voltages and therefore need an extra regulator to boost the voltage in this case. This driver adds support for checking the consumers that need higher voltage (Vaux1, 2 and 3 regulators, 3 V SIM) and control the external buck/boost regulator accordingly. Note that to utilize the low voltage battery support, the battery voltage thresholds must be changed. This applies for the low battery voltage threshold of the battery manager and the OTP setting for the AB8500 BattOk levels. ST-Ericsson ID: 282517, 363432 ST-Ericsson Linux next: - ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ife07a622ec9748c027dbbd78b01e4ee7e92629ec Signed-off-by: Bengt Jonsson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33616 Reviewed-by: QABUILD --- drivers/misc/sim_detect.c | 144 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 2 deletions(-) diff --git a/drivers/misc/sim_detect.c b/drivers/misc/sim_detect.c index c267fe95ee3..213645ca46d 100644 --- a/drivers/misc/sim_detect.c +++ b/drivers/misc/sim_detect.c @@ -2,29 +2,130 @@ * Copyright (C) ST-Ericsson SA 2011 * * Author: BIBEK BASU + * * 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 /* time in millisec */ #define TIMER_DELAY 10 + struct sim_detect{ struct work_struct timer_expired; struct device *dev; struct modem *modem; struct hrtimer timer; + struct mutex lock; + int voltage; + struct regulator *vinvsim_regulator; + bool regulator_enabled; +}; + +static ssize_t show_voltage(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sim_detect *data = dev_get_drvdata(dev); + int ret, len; + + ret = mutex_lock_interruptible(&data->lock); + if (ret < 0) + return ret; + + len = sprintf(buf, "%i\n", data->voltage); + + mutex_unlock(&data->lock); + + return len; +} + +static ssize_t write_voltage(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sim_detect *sim_detect = dev_get_drvdata(dev); + long val; + int ret; + + /* check input */ + if (strict_strtol(buf, 0, &val) != 0) { + dev_err(dev, "Invalid voltage class configured.\n"); + return count; + } + + switch (val) { + case -1: + case 0: + case 1800000: + case 3000000: + break; + default: + dev_err(dev, "Invalid voltage class configured.\n"); + return count; + } + + /* lock */ + ret = mutex_lock_interruptible(&sim_detect->lock); + if (ret < 0) + return ret; + + /* update state */ + sim_detect->voltage = val; + + /* call regulator */ + switch (sim_detect->voltage) { + case 0: + /* SIM voltage is unknown, turn on regulator for 3 V SIM */ + case 3000000: + /* Vinvsim supply is used only for 3 V SIM */ + if (!sim_detect->regulator_enabled) { + ret = regulator_enable(sim_detect->vinvsim_regulator); + if (ret) { + dev_err(dev, "Failed to enable regulator.\n"); + goto out_unlock; + } + sim_detect->regulator_enabled = true; + } + break; + case 1800000: + case -1: + /* Vbatvsim is used otherwise */ + if (sim_detect->regulator_enabled) { + regulator_disable(sim_detect->vinvsim_regulator); + sim_detect->regulator_enabled = false; + } + } + +out_unlock: + /* unlock and return */ + mutex_unlock(&sim_detect->lock); + + return count; +} + +static DEVICE_ATTR(voltage, 0666, show_voltage, write_voltage); + +static struct attribute *sim_attributes[] = { + &dev_attr_voltage.attr, + NULL +}; + +static const struct attribute_group sim_attr_group = { + .attrs = sim_attributes, }; static void inform_modem_release(struct work_struct *work) @@ -87,6 +188,7 @@ static const struct dev_pm_ops sim_detect_dev_pm_ops = { }; #endif + static int __devinit sim_detect_probe(struct platform_device *pdev) { struct sim_detect_platform_data *plat = dev_get_platdata(&pdev->dev); @@ -98,25 +200,61 @@ static int __devinit sim_detect_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to allocate memory\n"); return -ENOMEM; } + + /* initialize data */ + mutex_init(&sim_detect->lock); + sim_detect->voltage = 0; + sim_detect->dev = &pdev->dev; INIT_WORK(&sim_detect->timer_expired, inform_modem_release); hrtimer_init(&sim_detect->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); sim_detect->timer.function = timer_callback; + sim_detect->modem = modem_get(sim_detect->dev, "u8500-shrm-modem"); if (IS_ERR(sim_detect->modem)) { ret = PTR_ERR(sim_detect->modem); dev_err(sim_detect->dev, "Could not retrieve the modem\n"); goto out_free; } + + /* set drvdata */ platform_set_drvdata(pdev, sim_detect); + + /* request irq */ ret = request_threaded_irq(plat->irq_num, NULL, sim_activity_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "sim activity", sim_detect); if (ret < 0) + goto out_put_modem; + + /* get regulator */ + sim_detect->regulator_enabled = false; + sim_detect->vinvsim_regulator = regulator_get(sim_detect->dev, + "vinvsim"); + if (IS_ERR(sim_detect->vinvsim_regulator)) { + dev_err(&pdev->dev, + "Failed to get regulator. (dev_name %s).\n", + dev_name(sim_detect->dev)); + ret = PTR_ERR(sim_detect->vinvsim_regulator); goto out_free_irq; + } + + /* register sysfs entry */ + ret = sysfs_create_group(&pdev->dev.kobj, &sim_attr_group); + if (ret != 0) { + dev_err(&pdev->dev, + "Failed to create attribute group: %d\n", ret); + goto out_free_regulator; + } + return 0; + +out_free_regulator: + regulator_put(sim_detect->vinvsim_regulator); out_free_irq: + free_irq(plat->irq_num, sim_detect); +out_put_modem: modem_put(sim_detect->modem); platform_set_drvdata(pdev, NULL); out_free: @@ -128,6 +266,8 @@ static int __devexit sim_detect_remove(struct platform_device *pdev) { struct sim_detect *sim_detect = platform_get_drvdata(pdev); + sysfs_remove_group(&pdev->dev.kobj, &sim_attr_group); + regulator_put(sim_detect->vinvsim_regulator); modem_put(sim_detect->modem); platform_set_drvdata(pdev, NULL); kfree(sim_detect); @@ -136,7 +276,7 @@ static int __devexit sim_detect_remove(struct platform_device *pdev) static struct platform_driver sim_detect_driver = { .driver = { - .name = "sim_detect", + .name = "sim-detect", .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &sim_detect_dev_pm_ops, @@ -160,5 +300,5 @@ module_exit(sim_detect_exit); MODULE_AUTHOR("BIBEK BASU "); MODULE_DESCRIPTION("Detects SIM Hot Swap and wakes modem"); -MODULE_ALIAS("SIM DETECT INTERRUPT driver"); +MODULE_ALIAS("platform:sim-detect"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 34d303fe6838a2dc710c99a2d5f07f06e8dedf03 Mon Sep 17 00:00:00 2001 From: Bibek Basu Date: Fri, 23 Sep 2011 16:14:40 +0530 Subject: u5500 : check for modem_itp in mloader collect the modem type info from bootargs and expose over sysfs ST-Ericsson ID: 337490 ST-Ericsson FOSS-OUT ID: Trivial ST-Ericsson Linux next: NA Change-Id: Ia705e168568a0be08409af792a04091d4e96a50a Signed-off-by: Bibek Basu Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32480 Reviewed-by: Rabin VINCENT --- arch/arm/mach-ux500/mloader-db5500.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-ux500/mloader-db5500.c b/arch/arm/mach-ux500/mloader-db5500.c index 7798a1646d4..bc3a57af28b 100644 --- a/arch/arm/mach-ux500/mloader-db5500.c +++ b/arch/arm/mach-ux500/mloader-db5500.c @@ -20,13 +20,17 @@ static ssize_t db5500_mloader_sysfs_finalize(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); +static ssize_t db5500_mloader_sysfs_itpmode(struct device *dev, + struct device_attribute *attr, + char *buf); static DEVICE_ATTR(addr, S_IRUSR|S_IRGRP, db5500_mloader_sysfs_addr, NULL); static DEVICE_ATTR(finalize, S_IWUSR, NULL, db5500_mloader_sysfs_finalize); +static DEVICE_ATTR(is_itpmode, S_IRUSR|S_IRGRP, db5500_mloader_sysfs_itpmode, NULL); static unsigned int db5500_bootargs_memmap_modem_start; static unsigned int db5500_bootargs_memmap_modem_total_size; - +static unsigned int db5500_mloader_itpmode; static unsigned int db5500_mloader_shm_total_size; module_param_named(shm_total_size, db5500_mloader_shm_total_size, uint, 0600); MODULE_PARM_DESC(shm_total_size, "Total Size of SHM shared memory"); @@ -51,10 +55,23 @@ static int __init db5500_bootargs_shm_total_size(char *str) } early_param("mloader.shm_total_size", db5500_bootargs_shm_total_size); +static int __init db5500_bootargs_itpmode(char *p) +{ + int ret; + int count = 3; + if (!memcmp(p, "itp", count)) + db5500_mloader_itpmode = true; + else + db5500_mloader_itpmode = false; + return 1; +} +early_param("modem_boot_type", db5500_bootargs_itpmode); + static int __exit db5500_mloader_remove(struct platform_device *pdev) { sysfs_remove_file(&pdev->dev.kobj, &dev_attr_addr.attr); sysfs_remove_file(&pdev->dev.kobj, &dev_attr_finalize.attr); + sysfs_remove_file(&pdev->dev.kobj, &dev_attr_is_itpmode.attr); return 0; } @@ -96,6 +113,14 @@ static ssize_t db5500_mloader_sysfs_addr(struct device *dev, db5500_mloader_shm_total_size); } +static ssize_t db5500_mloader_sysfs_itpmode(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "0x%x\n", + db5500_mloader_itpmode); +} + static ssize_t db5500_mloader_sysfs_finalize(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -138,7 +163,12 @@ static int __init db5500_mloader_probe(struct platform_device *pdev) sysfs_remove_file(&pdev->dev.kobj, &dev_attr_addr.attr); return ret; } - + ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_is_itpmode.attr); + if (ret) { + sysfs_remove_file(&pdev->dev.kobj, &dev_attr_finalize.attr); + sysfs_remove_file(&pdev->dev.kobj, &dev_attr_addr.attr); + return ret; + } return 0; } -- cgit v1.2.3 From 74d2fc32aa4b51a735764414e04eaaef56746a1a Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Mon, 10 Oct 2011 14:55:06 +0530 Subject: u8500-shrm: Initiate MSR in case of serious bug During APE-Modem communication, for some reasson if software or hardware fails, instead of calling kernel function BUG() and halting the system making it no more useful until reboot, initiate a MSR. ST-Ericsson Linux next: NA ST-Ericsson ID: 366150 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I59e93b338c3242b506be7775487be065421022b8 Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33416 Reviewed-by: Bibek BASU Reviewed-by: QABUILD Reviewed-by: Magnus TEMPLING Reviewed-by: Rickard EVERTSSON Reviewed-by: Srinidhi KASAGAR --- drivers/modem/shrm/shrm_fifo.c | 14 +++++++++++--- drivers/modem/shrm/shrm_protocol.c | 29 ++++++++++++++++++++++------- include/linux/modem/shrm/shrm_driver.h | 2 ++ 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/drivers/modem/shrm/shrm_fifo.c b/drivers/modem/shrm/shrm_fifo.c index 1f0f9202e51..57da56f4905 100644 --- a/drivers/modem/shrm/shrm_fifo.c +++ b/drivers/modem/shrm/shrm_fifo.c @@ -11,6 +11,7 @@ #include #include #include +#include #define L1_BOOT_INFO_REQ 1 #define L1_BOOT_INFO_RESP 2 @@ -109,7 +110,10 @@ u8 read_boot_info_req(struct shrm_dev *shrm, if (msgtype != L1_BOOT_INFO_REQ) { dev_err(shrm->dev, "Read_Boot_Info_Req Fatal ERROR\n"); dev_err(shrm->dev, "Received msgtype is %d\n", msgtype); - BUG(); + dev_info(shrm->dev, "Initiating a modem reset\n"); + queue_work(shrm->shm_ac_wake_wq, + &shrm->shm_mod_reset_req); + return 0; } *config = (header >> CONFIG_OFFSET) & MASK_0_15_BIT; *version = header & MASK_0_15_BIT; @@ -416,7 +420,9 @@ u8 read_one_l2msg_common(struct shrm_dev *shrm, fifo->end_addr_fifo); /* Fatal ERROR - should never happens */ dev_crit(shrm->dev, "Fatal ERROR - should never happen\n"); - BUG(); + dev_info(shrm->dev, "Initiating a modem reset\n"); + queue_work(shrm->shm_ac_wake_wq, + &shrm->shm_mod_reset_req); } if (fifo->reader_local_rptr == (fifo->end_addr_fifo-1)) { l2_header = (*((u32 *)fifo->fifo_virtual_addr)); @@ -522,7 +528,9 @@ u8 read_one_l2msg_audio(struct shrm_dev *shrm, dev_info(shrm->dev, "Received msgtype is %d\n", msgtype); /* Fatal ERROR - should never happens */ dev_crit(shrm->dev, "Fatal ERROR - should never happen\n"); - BUG(); + dev_info(shrm->dev, "Initiating a modem reset\n"); + queue_work(shrm->shm_ac_wake_wq, + &shrm->shm_mod_reset_req); } if (fifo->reader_local_rptr == (fifo->end_addr_fifo-1)) { l2_header = (*((u32 *)fifo->fifo_virtual_addr)); diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index 50443e4803a..e34a88676be 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -70,6 +70,11 @@ enum shrm_nl { SHRM_NL_STATUS_MOD_OFFLINE, }; +void shm_mod_reset_req_work(struct work_struct *work) +{ + prcmu_modem_reset(); +} + static void shm_ac_sleep_req_work(struct work_struct *work) { mutex_lock(&ac_state_mutex); @@ -264,7 +269,7 @@ void shm_ca_msgpending_0_tasklet(unsigned long tasklet_data) if (!read_boot_info_req(shrm, &config, &version)) { dev_err(shrm->dev, "Unable to read boot state\n"); - BUG(); + return; } /* SendReadNotification */ ca_msg_read_notification_0(shrm); @@ -468,8 +473,11 @@ void shm_ca_wake_req_work(struct work_struct *work) mutex_unlock(&ac_state_mutex); /* send ca_wake_ack_interrupt to CMU */ - if (!get_host_accessport_val()) - BUG(); + if (!get_host_accessport_val()) { + dev_crit(shrm->dev, "get_host_accessport failed\n"); + dev_info(shrm->dev, "Initiating a modem reset\n"); + prcmu_modem_reset(); + } writel((1<intr_base + GOP_SET_REGISTER_BASE); } @@ -611,8 +619,11 @@ static void send_ac_msg_pend_notify_0_work(struct work_struct *work) modem_request(shrm->modem); mutex_unlock(&ac_state_mutex); - if (!get_host_accessport_val()) - BUG(); + if (!get_host_accessport_val()) { + dev_crit(shrm->dev, "get_host_accessport failed\n"); + dev_info(shrm->dev, "Initiating a modem reset\n"); + prcmu_modem_reset(); + } /* Trigger AcMsgPendingNotification to CMU */ writel((1<modem); mutex_unlock(&ac_state_mutex); - if (!get_host_accessport_val()) - BUG(); + if (!get_host_accessport_val()) { + dev_crit(shrm->dev, "get_host_accessport failed\n"); + dev_info(shrm->dev, "Initiating a modem reset\n"); + prcmu_modem_reset(); + } /* Trigger AcMsgPendingNotification to CMU */ writel((1<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); + INIT_WORK(&shrm->shm_mod_reset_req, shm_mod_reset_req_work); /* set tasklet data */ shm_ca_0_tasklet.data = (unsigned long)shrm; diff --git a/include/linux/modem/shrm/shrm_driver.h b/include/linux/modem/shrm/shrm_driver.h index e7b87005565..8fa215571eb 100644 --- a/include/linux/modem/shrm/shrm_driver.h +++ b/include/linux/modem/shrm/shrm_driver.h @@ -77,6 +77,7 @@ * @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 + * @shm_mod_reset_req: work to send a reset request to modem */ struct shrm_dev { u8 ca_wake_irq; @@ -127,6 +128,7 @@ struct shrm_dev { struct work_struct shm_ca_wake_req; struct work_struct shm_ca_sleep_req; struct work_struct shm_ac_sleep_req; + struct work_struct shm_mod_reset_req; }; /** -- cgit v1.2.3 From df2d23c69df125ec12ca3cf2babc5acfd4159a85 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 17 Nov 2011 15:45:34 +0100 Subject: staging: Add AB5500 SIM interface Driver Compilation Signed-off-by: Philippe Langlais --- drivers/staging/Kconfig | 10 ++++++++++ drivers/staging/Makefile | 1 + 2 files changed, 11 insertions(+) diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 97d412d9145..eeac488e3be 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -24,6 +24,16 @@ menuconfig STAGING if STAGING +config AB5500_SIM + bool "ST-Ericsson AB5500 SIM Interface driver" + depends on AB5500_CORE + help + SIM Interface driver provides interface to configure + various parameters of AB5550 SIM Level Shifter.Support provided are: + Configure Pull up on sim lines + Configure Operation Mode + Notify Sim Insert/Extract Interrupt + source "drivers/staging/serial/Kconfig" source "drivers/staging/et131x/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index ffe7d44374e..c6f1f5e35fd 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -57,3 +57,4 @@ obj-$(CONFIG_ANDROID) += android/ obj-$(CONFIG_PHONE) += telephony/ obj-$(CONFIG_RAMSTER) += ramster/ obj-$(CONFIG_USB_WPAN_HCD) += ozwpan/ +obj-$(CONFIG_AB5500_SIM) += ab5500_sim/ -- cgit v1.2.3 From 8da7b2539b8a1cc9b03f1854ac12298064494c89 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 18 Nov 2011 09:45:25 +0100 Subject: mach-ux500: For topic branches isolation, define Modem Loaders config flags here Signed-off-by: Philippe Langlais --- arch/arm/mach-ux500/Kconfig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig index ef7099eea0f..983da1319bd 100644 --- a/arch/arm/mach-ux500/Kconfig +++ b/arch/arm/mach-ux500/Kconfig @@ -88,4 +88,18 @@ config U5500_MBOX help Add support for U5500 mailbox communication with modem side +config DB8500_MLOADER + bool "Modem firmware upload/download support" + depends on UX500_SOC_DB8500 + select DBX500_MLOADER + help + Adds Modem firmware upload/download support to DB8500. + +config U5500_MLOADER + bool "mLoader, mem config from kernel boot args exported to sysfs" + depends on UX500_SOC_DB5500 + help + Link between boot args and user space program that loads the modem ELF. + This is used to expose the modem parameters using sysfs interface. + endif -- cgit v1.2.3 From 28c170e4d8a32b3d16c2ed52ec54aca39c112f92 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 18 Nov 2011 12:12:41 +0100 Subject: misc: Compile modem sound specific misc drivers Signed-off-by: Philippe Langlais --- drivers/misc/Kconfig | 1 + drivers/misc/Makefile | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index c7795096d43..6342fb85ee5 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -506,4 +506,5 @@ source "drivers/misc/ti-st/Kconfig" source "drivers/misc/lis3lv02d/Kconfig" source "drivers/misc/carma/Kconfig" source "drivers/misc/altera-stapl/Kconfig" +source "drivers/misc/modem_audio/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 3e1d80106f0..4fc062fc073 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -49,3 +49,4 @@ obj-y += carma/ obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ obj-$(CONFIG_MAX8997_MUIC) += max8997-muic.o +obj-y += modem_audio/ -- cgit v1.2.3 From df7cc1f99a678689fffb1bfa783e4132f02d1989 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Mon, 21 Nov 2011 17:11:53 +0100 Subject: misc: Compile modem specific misc drivers Signed-off-by: Philippe Langlais --- drivers/misc/Kconfig | 30 ++++++++++++++++++++++++++++++ drivers/misc/Makefile | 4 ++++ 2 files changed, 34 insertions(+) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 6342fb85ee5..496df238eb5 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -451,6 +451,20 @@ config ARM_CHARLCD line and the Linux version on the second line, but that's still useful. +config STE_TRACE_MODEM + tristate "DB8500 trace Modem" + depends on ARCH_U8500 + default n + help + Select this option to enable modem tracing by APE + +config DBX500_MLOADER + tristate "Modem firmware loader for db8500" + default n + depends on UX500_SOC_DB8500 || UX500_SOC_DB5500 + help + Provides a user interface to load modem firmware on dbx500 SOCs + config BMP085 tristate "BMP085 digital pressure sensor" depends on I2C && SYSFS @@ -481,6 +495,22 @@ config PCH_PHUB To compile this driver as a module, choose M here: the module will be called pch_phub. +config U5500_MBOX + bool "Mailbox support" + depends on (UX500_SOC_DB5500 && U5500_MODEM_IRQ) + default y + help + Add support for U5500 mailbox communication with modem side + +config U8500_SIM_DETECT + bool "Sim hot swap detection support" + depends on (MODEM && UX500_SOC_DB8500) + default n + help + Add support for sim hot swap detection support in U8500.Driver + basically wakes up the modem if its sleeping when sim hot plug + in/out has happened. + config USB_SWITCH_FSA9480 tristate "FSA9480 USB Switch" depends on I2C diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 4fc062fc073..7219df1e890 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -46,6 +46,10 @@ obj-y += ti-st/ obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o obj-y += lis3lv02d/ obj-y += carma/ +obj-$(CONFIG_STE_TRACE_MODEM) += db8500-modem-trace.o +obj-$(CONFIG_DBX500_MLOADER) += dbx500-mloader.o +obj-$(CONFIG_U5500_MBOX) += mbox.o mbox_channels-db5500.o +obj-$(CONFIG_U8500_SIM_DETECT) += sim_detect.o obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ obj-$(CONFIG_MAX8997_MUIC) += max8997-muic.o -- cgit v1.2.3 From b661789ed01243585915f78162fc1b7bfab990af Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 2 Dec 2011 14:31:13 +0100 Subject: modem: Include module.h after 3.2 update Signed-off-by: Philippe Langlais --- drivers/modem/modem_access.c | 1 + drivers/modem/modem_u8500.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/modem/modem_access.c b/drivers/modem/modem_access.c index 7337029c251..6e5c3a74f74 100644 --- a/drivers/modem/modem_access.c +++ b/drivers/modem/modem_access.c @@ -10,6 +10,7 @@ * Also, exposes APIs for gettng/releasing the access and even * query the access status, and the modem usage status. */ +#include #include #include #include diff --git a/drivers/modem/modem_u8500.c b/drivers/modem/modem_u8500.c index 2f5288d07bf..39951995e8e 100644 --- a/drivers/modem/modem_u8500.c +++ b/drivers/modem/modem_u8500.c @@ -8,6 +8,7 @@ * on U8500 which uses Shared Memroy as IPC between Application * Processor and Modem processor. */ +#include #include #include #include -- cgit v1.2.3 From c4ba3258812ab3cf0e34d89671e4a8d9c6cf3836 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Fri, 14 Oct 2011 14:51:36 +0530 Subject: U8500 shrm: Create RT workqueue for shm_audio/ac_wake_req/ca_wake_req Increase priority of workqueues to realtime in order to keep correct timing for audio messages send to modem. This patch was submitted on 2.6.35 by "cedric.izoard@stericsson.com". Now it has been ported to kernel-3.0.0 ST-Ericsson ID : ER361756 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I62e8244496cb9bc64b29b0013745d30bffb26f6b Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/34050 Reviewed-by: Srinidhi KASAGAR --- drivers/modem/shrm/shrm_protocol.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index e34a88676be..78dc4be0754 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -714,20 +714,22 @@ int shrm_protocol_init(struct shrm_dev *shrm, 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"); + shrm->shm_audio_ch_wr_wq = alloc_workqueue("shm_audio_channel_irq", + WQ_UNBOUND | WQ_MEM_RECLAIM, 1); 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"); + shrm->shm_ac_wake_wq = alloc_workqueue("shm_ac_wake_req", + WQ_UNBOUND | WQ_MEM_RECLAIM, 1); 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"); + shrm->shm_ca_wake_wq = alloc_workqueue("shm_ca_wake_req", + WQ_UNBOUND | WQ_MEM_RECLAIM, 1); if (!shrm->shm_ca_wake_wq) { dev_err(shrm->dev, "failed to create work queue\n"); err = -ENOMEM; -- cgit v1.2.3 From dccad4536e9ec23bf11bce684b460263a86fa16a Mon Sep 17 00:00:00 2001 From: Haridhar Kalvala Date: Mon, 31 Oct 2011 16:08:33 +0530 Subject: drivers: misc: Deregistering of rx- channel added. Mad driver updated with de-registering of RX channel with the mailbox.Moved unmap of shared memory out of mad_close function. ST-Ericsson Linux next: - ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I50351e594d746f5c2891a86351407a5bf8f934ab Signed-off-by: Haridhar Kalvala Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/35913 Reviewed-by: Srinidhi KASAGAR --- .../mach-ux500/include/mach/mbox_channels-db5500.h | 8 +++++++ drivers/misc/modem_audio/mad.c | 27 +++++++++++++--------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h b/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h index 0b2fc604873..c29dfff0ff7 100644 --- a/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h +++ b/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h @@ -68,5 +68,13 @@ int mbox_channel_send(struct mbox_channel_msg *msg); */ int mbox_channel_revoke_messages(u16 channel); +/** + * mbox_channel_deregister - de-register given mailbox channel. + * @channel: Mailbox channel number. + * + * Returns 0 on success or a negative error code on error. + */ +int mbox_channel_deregister(u16 channel); + #endif /*INC_STE_MBOX_H*/ diff --git a/drivers/misc/modem_audio/mad.c b/drivers/misc/modem_audio/mad.c index 62bef3c75fd..88bbcaa3c9c 100644 --- a/drivers/misc/modem_audio/mad.c +++ b/drivers/misc/modem_audio/mad.c @@ -459,19 +459,15 @@ static int mad_close(struct inode *ino, struct file *filp) { dev_dbg(mad_dev.this_device, "%s", __func__); - if (mad->dsp_shm_write_ptr != NULL) { - iounmap(mad->dsp_shm_write_ptr); - mad->dsp_shm_write_ptr = NULL; - } - - if (mad->dsp_shm_read_ptr != NULL) { - iounmap(mad->dsp_shm_read_ptr); - mad->dsp_shm_read_ptr = NULL; + if (mbox_channel_deregister(CHANNEL_NUM_RX)) { + dev_err(mad_dev.this_device, "%s:deregister err", __func__); + return -EFAULT; } - kfree(mad->rx_buff); kfree(mad->tx_buff); mad->data_written = 0; + mad->rx_buffer_num = 0; + mad->rx_buffer_read = 0; mad->open_check = false; return 0; @@ -495,7 +491,16 @@ static void __exit mad_exit(void) { dev_dbg(mad_dev.this_device, "%s", __func__); - kfree(mad); + if (mad->dsp_shm_write_ptr != NULL) { + iounmap(mad->dsp_shm_write_ptr); + mad->dsp_shm_write_ptr = NULL; + } + + if (mad->dsp_shm_read_ptr != NULL) { + iounmap(mad->dsp_shm_read_ptr); + mad->dsp_shm_read_ptr = NULL; + } - misc_deregister(&mad_dev); + kfree(mad); + misc_deregister(&mad_dev); } -- cgit v1.2.3 From 9248d80ae25edb2b61110a3d47690a0388d78c51 Mon Sep 17 00:00:00 2001 From: Bibek Basu Date: Wed, 2 Nov 2011 12:31:11 +0530 Subject: u5500 : mbox: wrong handling of atomic_dec_and_test atomic_dec_and_test wrongly used. ST-Ericsson Linux next: NA ST-Ericsson ID: 367390 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I37ffaa0afd6df3472469842925030e8a04a42a6b Signed-off-by: Bibek Basu Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36026 Reviewed-by: Srinidhi KASAGAR --- drivers/misc/mbox_channels-db5500.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/mbox_channels-db5500.c b/drivers/misc/mbox_channels-db5500.c index 43a4d57caaf..b3baae12d4b 100644 --- a/drivers/misc/mbox_channels-db5500.c +++ b/drivers/misc/mbox_channels-db5500.c @@ -379,7 +379,7 @@ rcv_msg: dev_err(&channels.pdev->dev, "%s no callback provided\n", __func__); } - if (atomic_dec_and_test(&rx_chan->rcv_counter) > 0) + if (!atomic_dec_and_test(&rx_chan->rcv_counter)) goto rcv_msg; } -- cgit v1.2.3 From b2d7e3663953da7a80a705ec1f04c7019a4b017c Mon Sep 17 00:00:00 2001 From: Bibek Basu Date: Wed, 9 Nov 2011 08:43:54 +0530 Subject: u5500 : Logical mailbox : add support for 16 DSP buffers Number of DSP read buffers has ben increased to 16 to avoid message loss when system waking up. Updated check for message pending with atomic_dec_return API which is more proper in this case. ST-Ericsson Linux next: NA ST-Ericsson ID: 367390 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I99ef44ead99a2b9eb856c01e025423a990b0ba98 Signed-off-by: Bibek Basu Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36973 Reviewed-by: Srinidhi KASAGAR --- arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h | 3 +++ drivers/misc/mbox_channels-db5500.c | 5 +---- drivers/misc/modem_audio/mad.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h b/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h index c29dfff0ff7..549a9c2c8cb 100644 --- a/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h +++ b/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h @@ -11,6 +11,9 @@ /* Maximum number of datawords which can be send in one PDU */ #define MAILBOX_NR_OF_DATAWORDS 3 +/* Number of buffers */ +#define NUM_DSP_BUFFER 16 + /** * mbox_channel_cb_t - Definition of the mailbox channel callback. * @data: Pointer to the data. diff --git a/drivers/misc/mbox_channels-db5500.c b/drivers/misc/mbox_channels-db5500.c index b3baae12d4b..919be308ed4 100644 --- a/drivers/misc/mbox_channels-db5500.c +++ b/drivers/misc/mbox_channels-db5500.c @@ -51,9 +51,6 @@ /* Returns sequence number from mbox message header */ #define GET_SEQ_NUMBER(mbox_msg) (((mbox_msg) >> 24) -/* Number of buffers */ -#define NUM_DSP_BUFFER 4 - enum mbox_msg{ MBOX_CLOSE, MBOX_OPEN, @@ -379,7 +376,7 @@ rcv_msg: dev_err(&channels.pdev->dev, "%s no callback provided\n", __func__); } - if (!atomic_dec_and_test(&rx_chan->rcv_counter)) + if (atomic_dec_return(&rx_chan->rcv_counter) > 0) goto rcv_msg; } diff --git a/drivers/misc/modem_audio/mad.c b/drivers/misc/modem_audio/mad.c index 88bbcaa3c9c..d31d78ba3f2 100644 --- a/drivers/misc/modem_audio/mad.c +++ b/drivers/misc/modem_audio/mad.c @@ -55,8 +55,8 @@ MODULE_LICENSE("GPLv2"); * Maximum number of datawords which can be sent * in the mailbox each word is 32 bits */ -#define MAX_NR_OF_DATAWORDS 3 -#define MAX_NUM_RX_BUFF 4 +#define MAX_NR_OF_DATAWORDS MAILBOX_NR_OF_DATAWORDS +#define MAX_NUM_RX_BUFF NUM_DSP_BUFFER #define NR_OF_DATAWORDS_REQD_FOR_ACK 1 /** -- cgit v1.2.3 From 42ed8e2dcd853a1d53e4a30c6fb1dba4e2cb7e7a Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 12 Jan 2012 13:11:07 +0100 Subject: modem: ux500: Coding style fixes Fix the most obvious violations of the kernel coding style Signed-off-by: Jonas Aaberg --- arch/arm/mach-ux500/include/mach/isa_ioctl.h | 9 ++++----- arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h | 1 - arch/arm/mach-ux500/include/mach/sim_detect.h | 1 - arch/arm/mach-ux500/mloader-db8500.c | 1 - 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-ux500/include/mach/isa_ioctl.h b/arch/arm/mach-ux500/include/mach/isa_ioctl.h index b05726f8c3c..b1f3ba159da 100644 --- a/arch/arm/mach-ux500/include/mach/isa_ioctl.h +++ b/arch/arm/mach-ux500/include/mach/isa_ioctl.h @@ -5,10 +5,10 @@ /* Free Software Foundation; either version 2.1 of the License, or */ /* (at your option) any later version. */ /* */ -/* This program is distributed in the hope that it will be useful, but */ -/* WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ -/* See the GNU General Public License for more details. */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ +/* See the GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program. If not, see . */ @@ -48,4 +48,3 @@ DLP IOCTLs for Userland _IOWR(DLP_IOCTL_MAGIC_NUMBER, 3, struct t_dlp_message *) #endif /*__MODEM_IPC_INCLUDED*/ - diff --git a/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h b/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h index 549a9c2c8cb..20940e11875 100644 --- a/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h +++ b/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h @@ -80,4 +80,3 @@ int mbox_channel_revoke_messages(u16 channel); int mbox_channel_deregister(u16 channel); #endif /*INC_STE_MBOX_H*/ - diff --git a/arch/arm/mach-ux500/include/mach/sim_detect.h b/arch/arm/mach-ux500/include/mach/sim_detect.h index 8d6e81f1e8a..4dae656b7e0 100644 --- a/arch/arm/mach-ux500/include/mach/sim_detect.h +++ b/arch/arm/mach-ux500/include/mach/sim_detect.h @@ -13,4 +13,3 @@ struct sim_detect_platform_data { }; #endif - diff --git a/arch/arm/mach-ux500/mloader-db8500.c b/arch/arm/mach-ux500/mloader-db8500.c index 6171a9db82f..b13652f55cb 100644 --- a/arch/arm/mach-ux500/mloader-db8500.c +++ b/arch/arm/mach-ux500/mloader-db8500.c @@ -79,4 +79,3 @@ static int __init early_modem_trace(char *p) return 0; } early_param("mem_mtrace", early_modem_trace); - -- cgit v1.2.3 From 3c5a53db080203d69be59b756106f4a573ad775c Mon Sep 17 00:00:00 2001 From: Thierry STRUDEL Date: Thu, 27 Oct 2011 18:47:58 +0200 Subject: shrm: use kthread_worker instead of workqueue Workqueue updates no longer guarantee fixed workqueue to worker kthread association, so giving RT priority to the irq worker won't work. Use kthread_worker which guarantees specific kthread association instead. This also makes setting the priority cleaner. ST-Ericsson ID: 361756 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Id54eafae1391ef8b09e93a4815efa1b674d3d90d Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36062 Reviewed-by: Srinidhi KASAGAR Reviewed-by: QABUILD Tested-by: Christoffer LONNERFORS Reviewed-by: Arun MURTHY --- drivers/modem/shrm/shrm_fifo.c | 6 +- drivers/modem/shrm/shrm_protocol.c | 160 +++++++++++++++++++-------------- include/linux/modem/shrm/shrm_driver.h | 45 ++++++---- 3 files changed, 124 insertions(+), 87 deletions(-) diff --git a/drivers/modem/shrm/shrm_fifo.c b/drivers/modem/shrm/shrm_fifo.c index 57da56f4905..1804c1be69e 100644 --- a/drivers/modem/shrm/shrm_fifo.c +++ b/drivers/modem/shrm/shrm_fifo.c @@ -111,7 +111,7 @@ u8 read_boot_info_req(struct shrm_dev *shrm, dev_err(shrm->dev, "Read_Boot_Info_Req Fatal ERROR\n"); dev_err(shrm->dev, "Received msgtype is %d\n", msgtype); dev_info(shrm->dev, "Initiating a modem reset\n"); - queue_work(shrm->shm_ac_wake_wq, + queue_kthread_work(&shrm->shm_ac_wake_kw, &shrm->shm_mod_reset_req); return 0; } @@ -421,7 +421,7 @@ u8 read_one_l2msg_common(struct shrm_dev *shrm, /* Fatal ERROR - should never happens */ dev_crit(shrm->dev, "Fatal ERROR - should never happen\n"); dev_info(shrm->dev, "Initiating a modem reset\n"); - queue_work(shrm->shm_ac_wake_wq, + queue_kthread_work(&shrm->shm_ac_wake_kw, &shrm->shm_mod_reset_req); } if (fifo->reader_local_rptr == (fifo->end_addr_fifo-1)) { @@ -529,7 +529,7 @@ u8 read_one_l2msg_audio(struct shrm_dev *shrm, /* Fatal ERROR - should never happens */ dev_crit(shrm->dev, "Fatal ERROR - should never happen\n"); dev_info(shrm->dev, "Initiating a modem reset\n"); - queue_work(shrm->shm_ac_wake_wq, + queue_kthread_work(&shrm->shm_ac_wake_kw, &shrm->shm_mod_reset_req); } if (fifo->reader_local_rptr == (fifo->end_addr_fifo-1)) { diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index 78dc4be0754..c13f05d50e8 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include @@ -70,12 +70,12 @@ enum shrm_nl { SHRM_NL_STATUS_MOD_OFFLINE, }; -void shm_mod_reset_req_work(struct work_struct *work) +void shm_mod_reset_req_work(struct kthread_work *work) { prcmu_modem_reset(); } -static void shm_ac_sleep_req_work(struct work_struct *work) +static void shm_ac_sleep_req_work(struct kthread_work *work) { mutex_lock(&ac_state_mutex); if (atomic_read(&ac_sleep_disable_count) == 0) @@ -83,7 +83,7 @@ static void shm_ac_sleep_req_work(struct work_struct *work) mutex_unlock(&ac_state_mutex); } -static void shm_ac_wake_req_work(struct work_struct *work) +static void shm_ac_wake_req_work(struct kthread_work *work) { mutex_lock(&ac_state_mutex); modem_request(shm_dev->modem); @@ -119,7 +119,7 @@ static enum hrtimer_restart callback(struct hrtimer *timer) shrm_common_tx_state = SHRM_SLEEP_STATE; shrm_audio_tx_state = SHRM_SLEEP_STATE; - queue_work(shm_dev->shm_ac_sleep_wq, + queue_kthread_work(&shm_dev->shm_ac_sleep_kw, &shm_dev->shm_ac_sleep_req); } @@ -284,7 +284,7 @@ void shm_ca_msgpending_0_tasklet(unsigned long tasklet_data) 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, + queue_kthread_work(&shrm->shm_common_ch_wr_kw, &shrm->send_ac_msg_pend_notify_0); } else { ca_msg_read_notification_0(shrm); @@ -388,7 +388,7 @@ void shm_ac_read_notif_0_tasklet(unsigned long tasklet_data) } 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, + queue_kthread_work(&shrm->shm_common_ch_wr_kw, &shrm->send_ac_msg_pend_notify_0); } else { shrm_common_tx_state = SHRM_IDLE; @@ -430,7 +430,7 @@ void shm_ac_read_notif_1_tasklet(unsigned long tasklet_data) } if (writer_local_rptr != writer_local_wptr) { shrm_audio_tx_state = SHRM_PTR_FREE; - queue_work(shrm->shm_audio_ch_wr_wq, + queue_kthread_work(&shrm->shm_audio_ch_wr_kw, &shrm->send_ac_msg_pend_notify_1); } else { shrm_audio_tx_state = SHRM_IDLE; @@ -443,7 +443,7 @@ void shm_ac_read_notif_1_tasklet(unsigned long tasklet_data) dev_dbg(shrm->dev, "%s OUT\n", __func__); } -void shm_ca_sleep_req_work(struct work_struct *work) +void shm_ca_sleep_req_work(struct kthread_work *work) { dev_dbg(shm_dev->dev, "%s:IRQ_PRCMU_CA_SLEEP\n", __func__); @@ -459,7 +459,7 @@ void shm_ca_sleep_req_work(struct work_struct *work) atomic_dec(&ac_sleep_disable_count); } -void shm_ca_wake_req_work(struct work_struct *work) +void shm_ca_wake_req_work(struct kthread_work *work) { struct shrm_dev *shrm = container_of(work, struct shrm_dev, shm_ca_wake_req); @@ -496,7 +496,7 @@ static int shrm_modem_reset_sequence(void) atomic_set(&ac_sleep_disable_count, 0); /* workaround for MSR */ - queue_work(shm_dev->shm_ac_wake_wq, + queue_kthread_work(&shm_dev->shm_ac_wake_kw, &shm_dev->shm_ac_wake_req); /* stop network queue */ @@ -570,10 +570,10 @@ static irqreturn_t shrm_prcmu_irq_handler(int irq, void *data) 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); + queue_kthread_work(&shrm->shm_ca_wake_kw, &shrm->shm_ca_wake_req); break; case IRQ_PRCMU_CA_SLEEP: - queue_work(shrm->shm_ca_wake_wq, &shrm->shm_ca_sleep_req); + queue_kthread_work(&shrm->shm_ca_wake_kw, &shrm->shm_ca_sleep_req); break; case IRQ_PRCMU_MODEM_SW_RESET_REQ: /* update the boot_state */ @@ -606,7 +606,7 @@ static irqreturn_t shrm_prcmu_irq_handler(int irq, void *data) return IRQ_HANDLED; } -static void send_ac_msg_pend_notify_0_work(struct work_struct *work) +static void send_ac_msg_pend_notify_0_work(struct kthread_work *work) { struct shrm_dev *shrm = container_of(work, struct shrm_dev, send_ac_msg_pend_notify_0); @@ -635,7 +635,7 @@ static void send_ac_msg_pend_notify_0_work(struct work_struct *work) dev_dbg(shrm->dev, "%s OUT\n", __func__); } -static void send_ac_msg_pend_notify_1_work(struct work_struct *work) +static void send_ac_msg_pend_notify_1_work(struct kthread_work *work) { struct shrm_dev *shrm = container_of(work, struct shrm_dev, send_ac_msg_pend_notify_1); @@ -697,6 +697,7 @@ int shrm_protocol_init(struct shrm_dev *shrm, received_msg_handler audio_rx_handler) { int err; + struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; shm_dev = shrm; boot_state = BOOT_INIT; @@ -708,49 +709,70 @@ int shrm_protocol_init(struct shrm_dev *shrm, 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"); + init_kthread_worker(&shrm->shm_common_ch_wr_kw); + shrm->shm_common_ch_wr_kw_task = kthread_run(kthread_worker_fn, + &shrm->shm_common_ch_wr_kw, + "shm_common_channel_irq"); + if (IS_ERR(shrm->shm_common_ch_wr_kw_task)) { + dev_err(shrm->dev, "failed to create work task\n"); return -ENOMEM; } - shrm->shm_audio_ch_wr_wq = alloc_workqueue("shm_audio_channel_irq", - WQ_UNBOUND | WQ_MEM_RECLAIM, 1); - if (!shrm->shm_audio_ch_wr_wq) { - dev_err(shrm->dev, "failed to create work queue\n"); + + init_kthread_worker(&shrm->shm_audio_ch_wr_kw); + shrm->shm_audio_ch_wr_kw_task = kthread_run(kthread_worker_fn, + &shrm->shm_audio_ch_wr_kw, + "shm_audio_channel_irq"); + if (IS_ERR(shrm->shm_audio_ch_wr_kw_task)) { + dev_err(shrm->dev, "failed to create work task\n"); err = -ENOMEM; - goto free_wq1; + goto free_kw1; } - shrm->shm_ac_wake_wq = alloc_workqueue("shm_ac_wake_req", - WQ_UNBOUND | WQ_MEM_RECLAIM, 1); - if (!shrm->shm_ac_wake_wq) { - dev_err(shrm->dev, "failed to create work queue\n"); + /* must use the FIFO scheduler as it is realtime sensitive */ + sched_setscheduler(shrm->shm_audio_ch_wr_kw_task, SCHED_FIFO, ¶m); + + init_kthread_worker(&shrm->shm_ac_wake_kw); + shrm->shm_ac_wake_kw_task = kthread_run(kthread_worker_fn, + &shrm->shm_ac_wake_kw, + "shm_ac_wake_req"); + if (IS_ERR(shrm->shm_ac_wake_kw_task)) { + dev_err(shrm->dev, "failed to create work task\n"); err = -ENOMEM; - goto free_wq2; + goto free_kw2; } - shrm->shm_ca_wake_wq = alloc_workqueue("shm_ca_wake_req", - WQ_UNBOUND | WQ_MEM_RECLAIM, 1); - if (!shrm->shm_ca_wake_wq) { - dev_err(shrm->dev, "failed to create work queue\n"); + /* must use the FIFO scheduler as it is realtime sensitive */ + sched_setscheduler(shrm->shm_ac_wake_kw_task, SCHED_FIFO, ¶m); + + init_kthread_worker(&shrm->shm_ca_wake_kw); + shrm->shm_ca_wake_kw_task = kthread_run(kthread_worker_fn, + &shrm->shm_ca_wake_kw, + "shm_ca_wake_req"); + if (IS_ERR(shrm->shm_ca_wake_kw_task)) { + dev_err(shrm->dev, "failed to create work task\n"); err = -ENOMEM; - goto free_wq3; + goto free_kw3; } - 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"); + /* must use the FIFO scheduler as it is realtime sensitive */ + sched_setscheduler(shrm->shm_ca_wake_kw_task, SCHED_FIFO, ¶m); + + init_kthread_worker(&shrm->shm_ac_sleep_kw); + shrm->shm_ac_sleep_kw_task = kthread_run(kthread_worker_fn, + &shrm->shm_ac_sleep_kw, + "shm_ac_sleep_req"); + if (IS_ERR(shrm->shm_ac_sleep_kw_task)) { + dev_err(shrm->dev, "failed to create work task\n"); err = -ENOMEM; - goto free_wq4; + goto free_kw4; } - 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); - INIT_WORK(&shrm->shm_mod_reset_req, shm_mod_reset_req_work); + + init_kthread_work(&shrm->send_ac_msg_pend_notify_0, + send_ac_msg_pend_notify_0_work); + init_kthread_work(&shrm->send_ac_msg_pend_notify_1, + send_ac_msg_pend_notify_1_work); + init_kthread_work(&shrm->shm_ca_wake_req, shm_ca_wake_req_work); + init_kthread_work(&shrm->shm_ca_sleep_req, shm_ca_sleep_req_work); + init_kthread_work(&shrm->shm_ac_sleep_req, shm_ac_sleep_req_work); + init_kthread_work(&shrm->shm_ac_wake_req, shm_ac_wake_req_work); + init_kthread_work(&shrm->shm_mod_reset_req, shm_mod_reset_req_work); /* set tasklet data */ shm_ca_0_tasklet.data = (unsigned long)shrm; @@ -760,7 +782,7 @@ int shrm_protocol_init(struct shrm_dev *shrm, IRQF_NO_SUSPEND, "ca-sleep", shrm); if (err < 0) { dev_err(shm_dev->dev, "Failed alloc IRQ_PRCMU_CA_SLEEP.\n"); - goto free_wq5; + goto free_kw5; } err = request_irq(IRQ_PRCMU_CA_WAKE, shrm_prcmu_irq_handler, @@ -798,16 +820,16 @@ 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); +free_kw5: + kthread_stop(shrm->shm_ac_sleep_kw_task); +free_kw4: + kthread_stop(shrm->shm_ca_wake_kw_task); +free_kw3: + kthread_stop(shrm->shm_ac_wake_kw_task); +free_kw2: + kthread_stop(shrm->shm_audio_ch_wr_kw_task); +free_kw1: + kthread_stop(shrm->shm_common_ch_wr_kw_task); return err; } @@ -816,12 +838,16 @@ 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); + flush_kthread_worker(&shrm->shm_common_ch_wr_kw); + flush_kthread_worker(&shrm->shm_audio_ch_wr_kw); + flush_kthread_worker(&shrm->shm_ac_wake_kw); + flush_kthread_worker(&shrm->shm_ca_wake_kw); + flush_kthread_worker(&shrm->shm_ac_sleep_kw); + kthread_stop(shrm->shm_common_ch_wr_kw_task); + kthread_stop(shrm->shm_audio_ch_wr_kw_task); + kthread_stop(shrm->shm_ac_wake_kw_task); + kthread_stop(shrm->shm_ca_wake_kw_task); + kthread_stop(shrm->shm_ac_sleep_kw_task); modem_put(shrm->modem); } @@ -1038,10 +1064,10 @@ int shm_write_msg(struct shrm_dev *shrm, u8 l2_header, /* Send Message Pending Noitication to CMT */ if (channel == 0) - queue_work(shrm->shm_common_ch_wr_wq, + queue_kthread_work(&shrm->shm_common_ch_wr_kw, &shrm->send_ac_msg_pend_notify_0); else - queue_work(shrm->shm_audio_ch_wr_wq, + queue_kthread_work(&shrm->shm_audio_ch_wr_kw, &shrm->send_ac_msg_pend_notify_1); } diff --git a/include/linux/modem/shrm/shrm_driver.h b/include/linux/modem/shrm/shrm_driver.h index 8fa215571eb..689ce207df5 100644 --- a/include/linux/modem/shrm/shrm_driver.h +++ b/include/linux/modem/shrm/shrm_driver.h @@ -23,6 +23,7 @@ #include #include #include +#include #define ISA_DEVICES 7 @@ -64,11 +65,16 @@ * @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 + * @shm_common_ch_wr_wk: kthread worker for writing to common channel + * @shm_common_ch_wr_wk_task: task for writing to common channel + * @shm_audio_ch_wr_wk: kthread worker for writing to audio channel + * @shm_audio_ch_wr_wk_task: task for writing to audio channel + * @shm_ac_wake_wk: kthread worker for receiving ape-cmt wake requests + * @shm_ac_wake_wk_task: task for receiving ape-cmt wake requests + * @shm_ca_wake_wk: kthread worker for receiving cmt-ape wake requests + * @shm_ca_wake_wk_task: task for receiving cmt-ape wake requests + * @shm_ac_sleep_wk: kthread worker for recieving ape-cmt sleep requests + * @shm_ac_sleep_wk_task: task 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 @@ -117,18 +123,23 @@ struct shrm_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 work_struct shm_mod_reset_req; + struct kthread_worker shm_common_ch_wr_kw; + struct task_struct *shm_common_ch_wr_kw_task; + struct kthread_worker shm_audio_ch_wr_kw; + struct task_struct *shm_audio_ch_wr_kw_task; + struct kthread_worker shm_ac_wake_kw; + struct task_struct *shm_ac_wake_kw_task; + struct kthread_worker shm_ca_wake_kw; + struct task_struct *shm_ca_wake_kw_task; + struct kthread_worker shm_ac_sleep_kw; + struct task_struct *shm_ac_sleep_kw_task; + struct kthread_work send_ac_msg_pend_notify_0; + struct kthread_work send_ac_msg_pend_notify_1; + struct kthread_work shm_ac_wake_req; + struct kthread_work shm_ca_wake_req; + struct kthread_work shm_ca_sleep_req; + struct kthread_work shm_ac_sleep_req; + struct kthread_work shm_mod_reset_req; }; /** -- cgit v1.2.3 From c5eb0e12bed431f77f06a59d1d4d92e9bfe8421f Mon Sep 17 00:00:00 2001 From: Chris Blair Date: Wed, 2 Nov 2011 13:09:42 +0000 Subject: modem: Release mutex when modem is not found Releases the mutex when the requested modem is not found. ST-Ericsson ID: 369397 ST-Ericsson FOSS-OUT ID: Trivial ST-Ericsson Linux next: NA Change-Id: Iee00b1dd24251afcff31ce2e03f07832e26ac725 Signed-off-by: Chris Blair Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36315 Reviewed-by: Andrew LYNN Reviewed-by: QABUILD Reviewed-by: Jonas ABERG --- drivers/modem/modem_access.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/modem/modem_access.c b/drivers/modem/modem_access.c index 6e5c3a74f74..2bd32957ae2 100644 --- a/drivers/modem/modem_access.c +++ b/drivers/modem/modem_access.c @@ -194,7 +194,7 @@ static struct modem *_modem_get(struct device *dev, const char *id, goto found; } - return ERR_PTR(-ENODEV); + goto out; found: if (!try_module_get(mdev_ptr->owner)) -- cgit v1.2.3 From 64b767f38d9f08d78444b2d5c8cf47198aab132a Mon Sep 17 00:00:00 2001 From: Chris Blair Date: Fri, 4 Nov 2011 08:20:33 +0000 Subject: modem: Add M6718 modem access driver Adds support for the STE M6718 modem to the modem access framework. ST-Ericsson ID: 369397 ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-12224 ST-Ericsson Linux next: NA Change-Id: Iefbd8db9a7ab77c21fb09cbb3699a7d34ee555e1 Signed-off-by: Chris Blair Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36316 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Andrew LYNN Reviewed-by: Jonas ABERG --- drivers/modem/Kconfig | 9 +++++ drivers/modem/Makefile | 2 +- drivers/modem/modem_m6718.c | 95 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 drivers/modem/modem_m6718.c diff --git a/drivers/modem/Kconfig b/drivers/modem/Kconfig index 95d4307775b..9ecbe00fd61 100644 --- a/drivers/modem/Kconfig +++ b/drivers/modem/Kconfig @@ -20,3 +20,12 @@ config MODEM_U8500 If unsure, say N. source "drivers/modem/shrm/Kconfig" + +config MODEM_M6718 + tristate "Modem Access driver for STE M6718 modem" + depends on MODEM + default n + help + Add support for the modem access driver for the M6718 modem. + + If unsure, say N. diff --git a/drivers/modem/Makefile b/drivers/modem/Makefile index e1b3f376cce..c0df71e65a1 100644 --- a/drivers/modem/Makefile +++ b/drivers/modem/Makefile @@ -1,4 +1,4 @@ obj-$(CONFIG_MODEM) := modem_access.o obj-$(CONFIG_MODEM_U8500) += modem_u8500.o obj-$(CONFIG_U8500_SHRM) += shrm/ - +obj-$(CONFIG_MODEM_M6718) += modem_m6718.o diff --git a/drivers/modem/modem_m6718.c b/drivers/modem/modem_m6718.c new file mode 100644 index 00000000000..5e457c16003 --- /dev/null +++ b/drivers/modem/modem_m6718.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * License Terms: GNU General Public License v2 + * Author: Chris Blair + * based on modem_u8500.c + * + * Platform driver implementing access mechanisms to the M6718 modem. + */ +#include +#include +#include + +static void modem_m6718_request(struct modem_dev *mdev) +{ + /* nothing to do - modem will wake when data is sent */ +} + +static void modem_m6718_release(struct modem_dev *mdev) +{ + /* nothing to do - modem does not need to be requested/released */ +} + +static int modem_m6718_is_requested(struct modem_dev *mdev) +{ + return 0; +} + +static struct modem_ops modem_m6718_ops = { + .request = modem_m6718_request, + .release = modem_m6718_release, + .is_requested = modem_m6718_is_requested, +}; + +static struct modem_desc modem_m6718_desc = { + .name = "m6718", + .id = 0, + .ops = &modem_m6718_ops, + .owner = THIS_MODULE, +}; + +static int __devinit modem_m6718_probe(struct platform_device *pdev) +{ + struct modem_dev *mdev; + int err; + + mdev = modem_register(&modem_m6718_desc, &pdev->dev, + NULL); + if (IS_ERR(mdev)) { + err = PTR_ERR(mdev); + dev_err(&pdev->dev, "failed to register %s: err %i\n", + modem_m6718_desc.name, err); + } + + return 0; +} + +static int __devexit modem_m6718_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver modem_m6718_driver = { + .driver = { + .name = "modem-m6718", + .owner = THIS_MODULE, + }, + .probe = modem_m6718_probe, + .remove = __devexit_p(modem_m6718_remove), +}; + +static int __init modem_m6718_init(void) +{ + int ret; + + ret = platform_driver_register(&modem_m6718_driver); + if (ret < 0) { + printk(KERN_ERR "modem_m6718: platform driver reg failed\n"); + return ret; + } + + return 0; +} + +static void __exit modem_m6718_exit(void) +{ + platform_driver_unregister(&modem_m6718_driver); +} + +module_init(modem_m6718_init); +module_exit(modem_m6718_exit); + +MODULE_AUTHOR("Chris Blair "); +MODULE_DESCRIPTION("M6718 modem access driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 205a30eb9c473d8fdf2c4a3d484d2ab70ebd3790 Mon Sep 17 00:00:00 2001 From: Chris Blair Date: Mon, 7 Nov 2011 14:34:38 +0000 Subject: modem: Add M6718 modem SPI IPC driver framework Adds support for the M6718 modem IPC SPI driver. This patch adds the spi driver under which the protocol will be added. ST-Ericsson ID: 369397 ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-12224 ST-Ericsson Linux next: NA Change-Id: Ifa60efa9cb6a9d30b02b88b563624a293bc0930c Signed-off-by: Chris Blair Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36433 Reviewed-by: Jonas ABERG --- drivers/modem/Kconfig | 2 + drivers/modem/Makefile | 1 + drivers/modem/m6718_spi/Kconfig | 83 ++++++++++++++++ drivers/modem/m6718_spi/Makefile | 10 ++ drivers/modem/m6718_spi/modem_driver.c | 134 ++++++++++++++++++++++++++ include/linux/modem/m6718_spi/modem_driver.h | 135 +++++++++++++++++++++++++++ 6 files changed, 365 insertions(+) create mode 100644 drivers/modem/m6718_spi/Kconfig create mode 100644 drivers/modem/m6718_spi/Makefile create mode 100644 drivers/modem/m6718_spi/modem_driver.c create mode 100644 include/linux/modem/m6718_spi/modem_driver.h diff --git a/drivers/modem/Kconfig b/drivers/modem/Kconfig index 9ecbe00fd61..323ef88af18 100644 --- a/drivers/modem/Kconfig +++ b/drivers/modem/Kconfig @@ -29,3 +29,5 @@ config MODEM_M6718 Add support for the modem access driver for the M6718 modem. If unsure, say N. + +source "drivers/modem/m6718_spi/Kconfig" diff --git a/drivers/modem/Makefile b/drivers/modem/Makefile index c0df71e65a1..a7c1df50074 100644 --- a/drivers/modem/Makefile +++ b/drivers/modem/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_MODEM) := modem_access.o obj-$(CONFIG_MODEM_U8500) += modem_u8500.o obj-$(CONFIG_U8500_SHRM) += shrm/ obj-$(CONFIG_MODEM_M6718) += modem_m6718.o +obj-$(CONFIG_MODEM_M6718_SPI) += m6718_spi/ diff --git a/drivers/modem/m6718_spi/Kconfig b/drivers/modem/m6718_spi/Kconfig new file mode 100644 index 00000000000..f945d24a094 --- /dev/null +++ b/drivers/modem/m6718_spi/Kconfig @@ -0,0 +1,83 @@ +# +# M6718 modem SPI IPC driver kernel configuration +# +config MODEM_M6718_SPI + tristate "M6718 modem IPC SPI driver" + depends on MODEM_M6718 + default y + ---help--- + If you say Y here, you will enable the M6718 modem IPC SPI driver. + + If unsure, say Y. + +config MODEM_M6718_SPI_DEBUG + boolean "Modem driver debug" + depends on MODEM_M6718_SPI + default N + ---help--- + If you say Y here, you will enable full debug trace from the M6718 + modem driver. This should not be enabled by default. + + If unsure, say N. + +config MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE + boolean "M6718 modem state driver integration" + depends on MODEM_M6718_SPI + default y + ---help--- + Enables integration of the IPC driver with the modem state driver. + This allows the IPC driver to be notified of changes in modem state + (on, off, reset) and allows the IPC driver to cause modem state + changes if needed. + + By default this should be enabled. + +config MODEM_M6718_SPI_ENABLE_FEATURE_FRAME_DUMP + boolean "IPC SPI L1 frame dump" + depends on MODEM_M6718_SPI + default n + ---help--- + If you say Y here, you will enable dumping of the raw TX and RX frames + by the IPC driver L1. + + If unsure, say N. + +config MODEM_M6718_SPI_ENABLE_FEATURE_LOOPBACK + boolean "Modem IPC loopback support" + depends on MODEM_M6718_SPI + default y + ---help--- + If you say Y here, you will enable the IPC loopback channels/devices. + + If unsure, say Y. + + +config MODEM_M6718_SPI_ENABLE_FEATURE_VERIFY_FRAMES + boolean "Verify loopback frames" + depends on MODEM_M6718_SPI + default n + ---help--- + This will enabling checking of loopback frames to verify that the data + received is identical to the data sent. + + If unsure, say N. + +config MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT + boolean "Modem IPC throughput measurement" + depends on MODEM_M6718_SPI + default n + ---help--- + If you say Y here, you will enable the IPC link throughput + measurement and reporting. + + If unsure, say N. + +config MODEM_M6718_SPI_SET_THROUGHPUT_FREQUENCY + int "Sample rate for throughput measurements (seconds)" + default "5" + depends on MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT + help + The sample frequency for taking IPC SPI link throughput measurements. + Increasing the rate (reducing the time) will increase the accuracy of + the measurements, but will also increase the impact on link and system + performance. diff --git a/drivers/modem/m6718_spi/Makefile b/drivers/modem/m6718_spi/Makefile new file mode 100644 index 00000000000..96bf481e986 --- /dev/null +++ b/drivers/modem/m6718_spi/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for M6718 SPI driver +# +ifeq ($(CONFIG_MODEM_M6718_SPI_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif + +m6718_modem_spi-objs := modem_driver.o + +obj-$(CONFIG_MODEM_M6718_SPI) += m6718_modem_spi.o diff --git a/drivers/modem/m6718_spi/modem_driver.c b/drivers/modem/m6718_spi/modem_driver.c new file mode 100644 index 00000000000..7d0efa6e395 --- /dev/null +++ b/drivers/modem/m6718_spi/modem_driver.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Chris Blair for ST-Ericsson + * based on modem_shrm_driver.c + * + * License terms: GNU General Public License (GPL) version 2 + * + * SPI driver implementing the M6718 inter-processor communication protocol. + */ +#include +#include +#include +#include +#include + +static struct modem_spi_dev modem_driver_data = { + .dev = NULL, + .ndev = NULL, + .modem = NULL, + .isa_context = NULL, + .netdev_flag_up = 0 +}; + +/** + * modem_m6718_spi_receive() - Receive a frame from L1 physical layer + * @sdev: pointer to spi device structure + * @channel: L2 mux channel id + * @len: frame data length + * @data: pointer to frame data + * + * This function is called from the driver L1 physical transport layer. It + * copied the frame data to the receive queue for the channel on which the data + * was received. + * + * 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) +{ + return -ENODEV; +} +EXPORT_SYMBOL_GPL(modem_m6718_spi_receive); + +static int spi_probe(struct spi_device *sdev) +{ + int result = 0; + + spi_set_drvdata(sdev, &modem_driver_data); + + /* + * Since we can have multiple spi links for the same modem, only + * initialise the modem data and char/net interfaces once. + */ + if (modem_driver_data.dev == NULL) { + modem_driver_data.dev = &sdev->dev; + modem_driver_data.modem = + modem_get(modem_driver_data.dev, "m6718"); + if (modem_driver_data.modem == NULL) { + dev_err(&sdev->dev, + "failed to retrieve modem description\n"); + result = -ENODEV; + } + } + return result; +} + +static int __exit spi_remove(struct spi_device *sdev) +{ + return 0; +} + +#ifdef CONFIG_PM +/** + * spi_suspend() - This routine puts the IPC driver in to suspend state. + * @sdev: pointer to spi device structure. + * @mesg: pm operation + * + * This routine checks the current ongoing communication with modem + * and prevents suspend if modem communication is on-going. + */ +static int spi_suspend(struct spi_device *sdev, pm_message_t mesg) +{ + dev_dbg(&sdev->dev, "suspend called\n"); + return 0; +} + +/** + * spi_resume() - This routine resumes the IPC driver from suspend state. + * @sdev: pointer to spi device structure + */ +static int spi_resume(struct spi_device *sdev) +{ + dev_dbg(&sdev->dev, "resume called\n"); + return 0; +} +#endif /* CONFIG_PM */ + +static struct spi_driver spi_driver = { + .driver = { + .name = "spimodem", + .bus = &spi_bus_type, + .owner = THIS_MODULE + }, + .probe = spi_probe, + .remove = __exit_p(spi_remove), +#ifdef CONFIG_PM + .suspend = spi_suspend, + .resume = spi_resume, +#endif +}; + +static int __init m6718_spi_driver_init(void) +{ + pr_info("M6718 modem driver initialising\n"); + return spi_register_driver(&spi_driver); +} +module_init(m6718_spi_driver_init); + +static void __exit m6718_spi_driver_exit(void) +{ + pr_debug("M6718 modem SPI IPC driver exit\n"); + spi_unregister_driver(&spi_driver); +} +module_exit(m6718_spi_driver_exit); + +MODULE_AUTHOR("Chris Blair "); +MODULE_DESCRIPTION("M6718 modem IPC SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/modem/m6718_spi/modem_driver.h b/include/linux/modem/m6718_spi/modem_driver.h new file mode 100644 index 00000000000..8c5209c228c --- /dev/null +++ b/include/linux/modem/m6718_spi/modem_driver.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Chris Blair for ST-Ericsson + * based on shrm_driver.h + * + * License terms: GNU General Public License (GPL) version 2 + * + * Modem IPC driver interface header. + */ +#ifndef _MODEM_DRIVER_H_ +#define _MODEM_DRIVER_H_ + +#include +#include +#include +#include + + +/* driver L2 mux channels */ +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_LOOPBACK +#define MODEM_M6718_SPI_MAX_CHANNELS (9) +#else +#define MODEM_M6718_SPI_MAX_CHANNELS (3) +#endif + +#define MODEM_M6718_SPI_CHN_ISI (0) +/*#define MODEM_M6718_SPI_CHN_RPC (1) not supported */ +#define MODEM_M6718_SPI_CHN_AUDIO (2) +/*#define MODEM_M6718_SPI_CHN_SECURITY (3) not supported */ +/* (4) not supported */ +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_LOOPBACK +#define MODEM_M6718_SPI_CHN_MASTER_LOOPBACK0 (5) +#define MODEM_M6718_SPI_CHN_SLAVE_LOOPBACK0 (6) +#define MODEM_M6718_SPI_CHN_MASTER_LOOPBACK1 (7) +#define MODEM_M6718_SPI_CHN_SLAVE_LOOPBACK1 (8) +#endif + +/** + * 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 + * @channel: L2 mux channel served by this queue + * @fifo_base: pointer to the respective fifo base + * @size: size of the data to be read + * @free: free space in the queue + * @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 read pointer is valid + * @wq_readable: wait queue head + * @msg_list: message list + * @modem_spi_dev: pointer to modem device information structure + */ +struct message_queue { + u8 channel; + u8 *fifo_base; + u32 size; + u32 free; + 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 modem_spi_dev *modem_spi_dev; +}; + +/** + * struct isa_device_context - modem char interface device information + * @dl_queue: structre to store the queue related info + * @device_id: channel id (ISI, AUDIO, RPC, ...) + * @addr: device address + */ +struct isa_device_context { + struct message_queue dl_queue; + u8 device_id; + void *addr; +}; + +/** + * struct isa_driver_context - modem char interface driver information + * @is_open: flag to check the usage of queue + * @isadev: pointer to struct t_isadev_context + * @common_tx_lock: spinlock for protecting common channel + * @audio_tx_mutex: mutex for protecting audio channel + * @cdev: character device structre + * @modem_class: pointer to the class structure + */ +struct isa_driver_context { + atomic_t is_open[MODEM_M6718_SPI_MAX_CHANNELS]; + struct isa_device_context *isadev; + spinlock_t common_tx_lock; + struct mutex audio_tx_mutex; + struct cdev cdev; + struct class *modem_class; +}; + +/** + * struct modem_spi_dev - modem device information + * @dev pointer to device + * @ndev pointer to net_device interface + * @modem pointer to registered modem structure + * @isa_context pointer to char device interface + * @netdev_flag_up: flag to indicate up/down of network device + * @msr_flag: flag to indicate modem-silent-reset is in progress + */ +struct modem_spi_dev { + struct device *dev; + struct net_device *ndev; + struct modem *modem; + struct isa_driver_context *isa_context; + int netdev_flag_up; + bool msr_flag; +}; + +/* function exported for L1 to call with received frames */ +int modem_m6718_spi_receive(struct spi_device *sdev, u8 channel, + u32 len, void *data); + +#endif /* _MODEM_DRIVER_H_ */ -- cgit v1.2.3 From 726fba97127de03f8b4ca6f7ec29e66b1d1b76e3 Mon Sep 17 00:00:00 2001 From: Chris Blair Date: Fri, 4 Nov 2011 09:29:42 +0000 Subject: modem: Port M6718 modem state driver Adds the modem state driver to the M6718 modem IPC interface. ST-Ericsson ID: 369397 ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-12224 ST-Ericsson Linux next: NA Change-Id: I5566277b950aa0be954f4350bdc1ed38c7afd7d8 Signed-off-by: Chris Blair Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36434 Reviewed-by: Derek MORTON Reviewed-by: QATOOLS Reviewed-by: Andrew LYNN Reviewed-by: QABUILD Reviewed-by: Jonas ABERG --- drivers/modem/m6718_spi/Makefile | 4 + drivers/modem/m6718_spi/modem_state.c | 1300 +++++++++++++++++++++++++++ include/linux/modem/m6718_spi/modem_state.h | 36 + 3 files changed, 1340 insertions(+) create mode 100644 drivers/modem/m6718_spi/modem_state.c create mode 100644 include/linux/modem/m6718_spi/modem_state.h diff --git a/drivers/modem/m6718_spi/Makefile b/drivers/modem/m6718_spi/Makefile index 96bf481e986..492995bc753 100644 --- a/drivers/modem/m6718_spi/Makefile +++ b/drivers/modem/m6718_spi/Makefile @@ -7,4 +7,8 @@ endif m6718_modem_spi-objs := modem_driver.o +ifeq ($(CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE),y) +m6718_modem_spi-objs += modem_state.o +endif + obj-$(CONFIG_MODEM_M6718_SPI) += m6718_modem_spi.o diff --git a/drivers/modem/m6718_spi/modem_state.c b/drivers/modem/m6718_spi/modem_state.c new file mode 100644 index 00000000000..266f75a7a71 --- /dev/null +++ b/drivers/modem/m6718_spi/modem_state.c @@ -0,0 +1,1300 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Derek Morton + * + * License terms: GNU General Public License (GPL) version 2 + * + * Power state driver for M6718 MODEM + */ + +/* define DEBUG to enable debug logging */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * To enable this driver add a struct platform_device in the board + * configuration file (e.g. board-*.c) with name="modemstate" + * optionally specify dev.initname="m6718" to define the driver + * name as it will appear in the file system. + * e.g. + * static struct platform_device modem_state_device = + * { + * .name = "modemstate", + * .dev = + * { + * .init_name = "m6718" // Name that will appear in FS + * }, + * .num_resources = ARRAY_SIZE(modem_state_resources), + * .resource = modem_state_resources + * }; + * + * This driver uses gpio pins which should be specified as resources * + * e.g. + * static struct resource modem_state_resources[] = ....... + * Output pins are specified as IORESOURCE_IO + * Currently supported Output pins are: + * onkey_pin + * reset_pin + * vbat_pin + * Input pins are specified as IORESOURCE_IRQ + * Currently supported input pins are: + * rsthc_pin + * rstext_pin + * crash_pin + * Currently only the start value is used as the gpio pin number but + * end should also be specified as the gpio pin number in case gpio ranges + * are used in the future. + * e.g. if gpio 161 is used as the onkey pin + * { + * .start = 161, + * .end = 161, + * .name = "onkey_pin", + * .flags = IORESOURCE_IO, + * }, + */ + +struct modem_state_dev { + int onkey_pin; + int rsthc_pin; + int rstext_pin; + int crash_pin; + int reset_pin; + int vbat_pin; + int power_state; + int irq_state; + int busy; + struct timer_list onkey_timer; + struct timer_list reset_timer; + struct timer_list onkey_debounce_timer; + struct timer_list vbat_off_timer; + struct timer_list busy_timer; + spinlock_t lock; + struct device *dev; + struct workqueue_struct *workqueue; + struct work_struct wq_rsthc; + struct work_struct wq_rstext; + struct work_struct wq_crash; +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfsdir; + struct dentry *debugfs_debug; +#endif +}; + +struct callback_list { + struct list_head node; + int (*callback) (unsigned long); + unsigned long data; +}; +LIST_HEAD(callback_list); + +static char *modem_state_str[] = { + "off", + "reset", + "crash", + "on", + /* + * Add new states before error and update enum modem_states + * in modem_state.h + */ + "error" +}; + +static struct modem_state_dev *modem_state; + +static void set_on_config(struct modem_state_dev *msdev) +{ + if (msdev->crash_pin) + nmk_config_pin(PIN_CFG(msdev->crash_pin, GPIO) | + PIN_INPUT_PULLDOWN, false); + if (msdev->rstext_pin) + nmk_config_pin(PIN_CFG(msdev->rstext_pin, GPIO) | + PIN_INPUT_PULLDOWN, false); + if (msdev->rsthc_pin) + nmk_config_pin(PIN_CFG(msdev->rsthc_pin, GPIO) | + PIN_INPUT_PULLDOWN, false); + if (msdev->reset_pin) + nmk_config_pin(PIN_CFG(msdev->reset_pin, GPIO) | + PIN_OUTPUT_HIGH, false); +} + +static void set_off_config(struct modem_state_dev *msdev) +{ + if (msdev->crash_pin) + nmk_config_pin(PIN_CFG(msdev->crash_pin, GPIO) | + PIN_INPUT_PULLDOWN, false); + if (msdev->rstext_pin) + nmk_config_pin(PIN_CFG(msdev->rstext_pin, GPIO) | + PIN_OUTPUT_LOW, false); + if (msdev->rsthc_pin) + nmk_config_pin(PIN_CFG(msdev->rsthc_pin, GPIO) | PIN_OUTPUT_LOW, + false); + if (msdev->reset_pin) + nmk_config_pin(PIN_CFG(msdev->reset_pin, GPIO) | + PIN_OUTPUT_HIGH, false); +} + +static void enable_irq_all(struct modem_state_dev *msdev) +{ + if (msdev->rsthc_pin) { + enable_irq(GPIO_TO_IRQ(msdev->rsthc_pin)); + if ((0 > enable_irq_wake(GPIO_TO_IRQ(msdev->rsthc_pin)))) + dev_err(msdev->dev, + "Request for wake on pin %d failed\n", + msdev->rsthc_pin); + } + if (msdev->rstext_pin) { + enable_irq(GPIO_TO_IRQ(msdev->rstext_pin)); + if ((0 > enable_irq_wake(GPIO_TO_IRQ(msdev->rstext_pin)))) + dev_err(msdev->dev, + "Request for wake on pin %d failed\n", + msdev->rstext_pin); + } + if (msdev->crash_pin) { + enable_irq(GPIO_TO_IRQ(msdev->crash_pin)); + if ((0 > enable_irq_wake(GPIO_TO_IRQ(msdev->crash_pin)))) + dev_err(msdev->dev, + "Request for wake on pin %d failed\n", + msdev->crash_pin); + } +} + +static void disable_irq_all(struct modem_state_dev *msdev) +{ + if (msdev->rsthc_pin) { + disable_irq_wake(GPIO_TO_IRQ(msdev->rsthc_pin)); + disable_irq(GPIO_TO_IRQ(msdev->rsthc_pin)); + } + if (msdev->rstext_pin) { + disable_irq_wake(GPIO_TO_IRQ(msdev->rstext_pin)); + disable_irq(GPIO_TO_IRQ(msdev->rstext_pin)); + } + if (msdev->crash_pin) { + disable_irq_wake(GPIO_TO_IRQ(msdev->crash_pin)); + disable_irq(GPIO_TO_IRQ(msdev->crash_pin)); + } +} + +/* + * These functions which access GPIO must only be called + * with spinlock enabled. + */ + +/* + * Toggle ONKEY pin high then low to turn modem on or off. Modem expects + * ONKEY line to be pulled low then high. GPIO needs to be driven high then + * low as logic is inverted through a transistor. + */ +static void toggle_modem_power(struct modem_state_dev *msdev) +{ + dev_info(msdev->dev, "Modem power toggle\n"); + msdev->busy = 1; + gpio_set_value(msdev->onkey_pin, 1); + msdev->onkey_timer.data = (unsigned long)msdev; + /* Timeout of at least 1 second */ + mod_timer(&msdev->onkey_timer, jiffies + (1 * HZ) + 1); +} + +/* Modem is forced into reset when its reset line is pulled low */ +/* Drive GPIO low then high to reset modem */ +static void modem_reset(struct modem_state_dev *msdev) +{ + dev_info(msdev->dev, "Modem reset\n"); + msdev->busy = 1; + gpio_set_value(msdev->reset_pin, 0); + msdev->reset_timer.data = (unsigned long)msdev; + /* Wait a couple of Jiffies */ + mod_timer(&msdev->reset_timer, jiffies + 2); +} + +static void modem_vbat_set_value(struct modem_state_dev *msdev, int vbat_val) +{ + switch (vbat_val) { + case 0: + msdev->power_state = 0; + dev_info(msdev->dev, "Modem vbat off\n"); + gpio_set_value(msdev->vbat_pin, vbat_val); + if (1 == msdev->irq_state) { + msdev->irq_state = 0; + disable_irq_all(msdev); + set_off_config(msdev); + } + break; + case 1: + dev_info(msdev->dev, "Modem vbat on\n"); + if (0 == msdev->irq_state) { + msdev->irq_state = 1; + set_on_config(msdev); + enable_irq_all(msdev); + } + gpio_set_value(msdev->vbat_pin, vbat_val); + break; + default: + return; + break; + } +} + +static void modem_power_on(struct modem_state_dev *msdev) +{ + int rsthc = gpio_get_value(msdev->rsthc_pin); + msdev->power_state = 1; + del_timer(&msdev->vbat_off_timer); + if (rsthc == 0) { + modem_vbat_set_value(msdev, 1); + toggle_modem_power(msdev); + } +} + +static void modem_power_off(struct modem_state_dev *msdev) +{ + int rsthc = gpio_get_value(msdev->rsthc_pin); + + msdev->power_state = 0; + if (rsthc == 1) { + toggle_modem_power(msdev); + /* Cut power to modem after 10 seconds */ + msdev->vbat_off_timer.data = (unsigned long)msdev; + mod_timer(&msdev->vbat_off_timer, jiffies + (10 * HZ)); + } +} +/* End of functions requiring spinlock */ + +static void call_callbacks(void) +{ + struct callback_list *item; + + list_for_each_entry(item, &callback_list, node) + item->callback(item->data); +} + +static int get_modem_state(struct modem_state_dev *msdev) +{ + int state; + unsigned long flags; + + spin_lock_irqsave(&msdev->lock, flags); + if (0 == gpio_get_value(msdev->rsthc_pin)) + state = MODEM_STATE_OFF; + else if (0 == gpio_get_value(msdev->rstext_pin)) + state = MODEM_STATE_RESET; + else if (1 == gpio_get_value(msdev->crash_pin)) + state = MODEM_STATE_CRASH; + else + state = MODEM_STATE_ON; + spin_unlock_irqrestore(&msdev->lock, flags); + + return state; +} + +/* modempower read handler */ +static ssize_t modem_state_power_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int rsthc; + int power_state; + unsigned long flags; + struct modem_state_dev *msdev = + platform_get_drvdata(to_platform_device(dev)); + + spin_lock_irqsave(&msdev->lock, flags); + rsthc = gpio_get_value(msdev->rsthc_pin); + power_state = msdev->power_state; + spin_unlock_irqrestore(&msdev->lock, flags); + + return sprintf(buf, "state=%d, expected=%d\n", rsthc, power_state); +} + +/* + * modempower write handler + * Write '0' to /sys/devices/platform/modemstate/modempower to turn modem off + * Write '1' to /sys/devices/platform/modemstate/modempower to turn modem on + */ +static ssize_t modem_state_power_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long flags; + int ret = count; + struct modem_state_dev *msdev = + platform_get_drvdata(to_platform_device(dev)); + + spin_lock_irqsave(&msdev->lock, flags); + if (msdev->busy) { + ret = -EAGAIN; + } else if (count > 0) { + switch (buf[0]) { + case '0': + modem_power_off(msdev); + break; + case '1': + modem_power_on(msdev); + break; + default: + break; + } + } + spin_unlock_irqrestore(&msdev->lock, flags); + return ret; +} + +/* reset read handler */ +static ssize_t modem_state_reset_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int rstext; + struct modem_state_dev *msdev = + platform_get_drvdata(to_platform_device(dev)); + + /* No need for spinlocks here as there is only 1 value */ + rstext = gpio_get_value(msdev->rstext_pin); + + return sprintf(buf, "state=%d\n", rstext); +} + +/* reset write handler */ +/* Write '1' to /sys/devices/platform/modemstate/reset to reset modem */ +static ssize_t modem_state_reset_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long flags; + int ret = count; + struct modem_state_dev *msdev = + platform_get_drvdata(to_platform_device(dev)); + + spin_lock_irqsave(&msdev->lock, flags); + if (msdev->busy) { + ret = -EAGAIN; + } else if (count > 0) { + if (buf[0] == '1') + modem_reset(msdev); + } + spin_unlock_irqrestore(&msdev->lock, flags); + + return ret; +} + +/* crash read handler */ +static ssize_t modem_state_crash_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int crash; + struct modem_state_dev *msdev = + platform_get_drvdata(to_platform_device(dev)); + + /* No need for spinlocks here as there is only 1 value */ + crash = gpio_get_value(msdev->crash_pin); + + return sprintf(buf, "state=%d\n", crash); +} + +/* state read handler */ +static ssize_t modem_state_state_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int state; + struct modem_state_dev *msdev = + platform_get_drvdata(to_platform_device(dev)); + + state = get_modem_state(msdev); + if (state > MODEM_STATE_END_MARKER) + state = MODEM_STATE_END_MARKER; + + return sprintf(buf, "%s\n", modem_state_str[state]); +} + +#ifdef CONFIG_DEBUG_FS +static int modem_state_debug_get(struct seq_file *s, void *data) +{ + int onkey; + int rsthc; + int rstext; + int reset; + int crash; + int vbat; + unsigned long flags; + struct modem_state_dev *msdev = s->private; + + spin_lock_irqsave(&msdev->lock, flags); + onkey = gpio_get_value(msdev->onkey_pin); + rsthc = gpio_get_value(msdev->rsthc_pin); + rstext = gpio_get_value(msdev->rstext_pin); + reset = gpio_get_value(msdev->reset_pin); + crash = gpio_get_value(msdev->crash_pin); + vbat = gpio_get_value(msdev->vbat_pin); + spin_unlock_irqrestore(&msdev->lock, flags); + + seq_printf(s, "onkey=%d, rsthc=%d, rstext=%d, " + "reset=%d, crash=%d, vbat=%d\n", + onkey, rsthc, rstext, reset, crash, vbat); + return 0; +} + +/* + * debug write handler + * Write o['0'|'1'] to /sys/devices/platform/modemstate/debug to set + * onkey line low or high. + * Write r['0'|'1'] to /sys/devices/platform/modemstate/debug to set + * reset line low or high. + * Write v['0'|'1'] to /sys/devices/platform/modemstate/debug to set + * vbat line low or high. + */ +static ssize_t modem_state_debug_set(struct file *file, + const char __user *user_buf, + size_t count, + loff_t *ppos) +{ + unsigned long flags; + int bufsize; + char buf[128]; + + bufsize = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, bufsize)) + return -EFAULT; + buf[bufsize] = 0; + + spin_lock_irqsave(&modem_state->lock, flags); + if (modem_state->busy) { + return -EAGAIN; + } else if (count > 1) { + switch (buf[1]) { + case '0': /* fallthrough */ + case '1': + switch (buf[0]) { + case 'o': + gpio_set_value(modem_state->onkey_pin, + buf[1] - '0'); + break; + case 'r': + gpio_set_value(modem_state->reset_pin, + buf[1] - '0'); + break; + case 'v': + gpio_set_value(modem_state->vbat_pin, + buf[1] - '0'); + break; + default: + break; + } + break; + default: + break; + } + } + spin_unlock_irqrestore(&modem_state->lock, flags); + + return bufsize; +} + +static int modem_state_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, modem_state_debug_get, inode->i_private); +} +#endif /* CONFIG_DEBUG_FS */ + +static DEVICE_ATTR(modempower, S_IRUSR | S_IWUSR, + modem_state_power_get, modem_state_power_set); +static DEVICE_ATTR(reset, S_IRUSR | S_IWUSR, + modem_state_reset_get, modem_state_reset_set); +static DEVICE_ATTR(crash, S_IRUSR, modem_state_crash_get, NULL); +static DEVICE_ATTR(state, S_IRUSR, modem_state_state_get, NULL); + +static struct attribute *modemstate_attributes[] = { + &dev_attr_modempower.attr, + &dev_attr_reset.attr, + &dev_attr_crash.attr, + &dev_attr_state.attr, + NULL +}; + +static struct attribute_group modemstate_attr_group = { + .attrs = modemstate_attributes, + .name = "modemstate" +}; + +#ifdef CONFIG_DEBUG_FS +static const struct file_operations debugfs_debug_fops = { + .open = modem_state_debug_open, + .read = seq_read, + .write = modem_state_debug_set, + .llseek = seq_lseek, + .release = single_release +}; +#endif + +static void sysfs_notify_rsthc(struct modem_state_dev *msdev) +{ + sysfs_notify(&msdev->dev->kobj, NULL, dev_attr_modempower.attr.name); + sysfs_notify(&msdev->dev->kobj, NULL, dev_attr_state.attr.name); +} + +static void sysfs_notify_rstext(struct modem_state_dev *msdev) +{ + sysfs_notify(&msdev->dev->kobj, NULL, dev_attr_reset.attr.name); + sysfs_notify(&msdev->dev->kobj, NULL, dev_attr_state.attr.name); +} + +static void sysfs_notify_crash(struct modem_state_dev *msdev) +{ + sysfs_notify(&msdev->dev->kobj, NULL, dev_attr_crash.attr.name); + sysfs_notify(&msdev->dev->kobj, NULL, dev_attr_state.attr.name); +} + +static void wq_rsthc(struct work_struct *work) +{ + unsigned long flags; + int rsthc; + struct modem_state_dev *msdev = + container_of(work, struct modem_state_dev, wq_rsthc); + + spin_lock_irqsave(&msdev->lock, flags); + rsthc = gpio_get_value(msdev->rsthc_pin); + dev_dbg(msdev->dev, "RSTHC interrupt detected, rsthc=%d\n", rsthc); + if (msdev->power_state == rsthc) { + if (!rsthc) { + /* Modem has turned off, and we were expecting it to. + turn vbat to the modem off now */ + del_timer(&msdev->vbat_off_timer); + modem_vbat_set_value(msdev, 0); + } + } else { + dev_dbg(msdev->dev, + "Modem power state is %d, expected %d\n", rsthc, + msdev->power_state); + dev_dbg(msdev->dev, + "Attempting to change modem power state " + "in 2 seconds\n"); + + msdev->onkey_debounce_timer.data = (unsigned long)msdev; + /* Wait > 2048ms due to debounce timer */ + mod_timer(&msdev->onkey_debounce_timer, + jiffies + ((2050 * HZ) / 1000)); + } + spin_unlock_irqrestore(&msdev->lock, flags); + + call_callbacks(); + sysfs_notify_rsthc(msdev); +} + +static void wq_rstext(struct work_struct *work) +{ + struct modem_state_dev *msdev = + container_of(work, struct modem_state_dev, wq_rstext); + + dev_dbg(msdev->dev, "RSTEXT interrupt detected, rstext=%d\n", + gpio_get_value(msdev->rstext_pin)); + + call_callbacks(); + sysfs_notify_rstext(msdev); +} + +static void wq_crash(struct work_struct *work) +{ + struct modem_state_dev *msdev = + container_of(work, struct modem_state_dev, wq_rstext); + + dev_dbg(msdev->dev, "modem crash interrupt detected. crash=%d\n", + gpio_get_value(msdev->crash_pin)); + + call_callbacks(); + sysfs_notify_crash(msdev); +} + +/* Populate device structure used by the driver */ +static int modem_state_dev_init(struct platform_device *pdev, + struct modem_state_dev *msdev) +{ + int err = 0; + struct resource *r; + + r = platform_get_resource_byname(pdev, IORESOURCE_IO, "onkey_pin"); + if (r == NULL) { + err = -ENXIO; + dev_err(&pdev->dev, + "Could not get GPIO number for onkey pin\n"); + goto err_resource; + } + msdev->onkey_pin = r->start; + + r = platform_get_resource_byname(pdev, IORESOURCE_IO, "reset_pin"); + if (r == NULL) { + err = -ENXIO; + dev_err(&pdev->dev, + "Could not get GPIO number for reset pin\n"); + goto err_resource; + } + msdev->reset_pin = r->start; + + r = platform_get_resource_byname(pdev, IORESOURCE_IO, "vbat_pin"); + if (r == NULL) { + err = -ENXIO; + dev_err(&pdev->dev, "Could not get GPIO number for vbat pin\n"); + goto err_resource; + } + msdev->vbat_pin = r->start; + + msdev->rsthc_pin = platform_get_irq_byname(pdev, "rsthc_pin"); + if (msdev->rsthc_pin < 0) { + err = msdev->rsthc_pin; + dev_err(&pdev->dev, + "Could not get GPIO number for rsthc pin\n"); + goto err_resource; + } + + msdev->rstext_pin = platform_get_irq_byname(pdev, "rstext_pin"); + if (msdev->rstext_pin < 0) { + err = msdev->rstext_pin; + dev_err(&pdev->dev, + "Could not get GPIO number for retext pin\n"); + goto err_resource; + } + + msdev->crash_pin = platform_get_irq_byname(pdev, "crash_pin"); + if (msdev->crash_pin < 0) { + err = msdev->crash_pin; + dev_err(&pdev->dev, + "Could not get GPIO number for crash pin\n"); + goto err_resource; + } +err_resource: + return err; +} + +/* IRQ handlers */ + +/* Handlers for rsthc (modem power off indication) IRQ */ +static irqreturn_t rsthc_irq(int irq, void *dev) +{ + struct modem_state_dev *msdev = (struct modem_state_dev *)dev; + + /* check it's our interrupt */ + if (irq != GPIO_TO_IRQ(msdev->rsthc_pin)) { + dev_err(msdev->dev, "Spurious RSTHC irq\n"); + return IRQ_NONE; + } + + queue_work(msdev->workqueue, &msdev->wq_rsthc); + return IRQ_HANDLED; +} + +/* Handlers for rstext (modem reset indication) IRQ */ +static irqreturn_t rstext_irq(int irq, void *dev) +{ + struct modem_state_dev *msdev = (struct modem_state_dev *)dev; + + /* check it's our interrupt */ + if (irq != GPIO_TO_IRQ(msdev->rstext_pin)) { + dev_err(msdev->dev, "Spurious RSTEXT irq\n"); + return IRQ_NONE; + } + + queue_work(msdev->workqueue, &msdev->wq_rstext); + return IRQ_HANDLED; +} + +/* Handlers for modem crash indication IRQ */ +static irqreturn_t crash_irq(int irq, void *dev) +{ + struct modem_state_dev *msdev = (struct modem_state_dev *)dev; + + /* check it's our interrupt */ + if (irq != GPIO_TO_IRQ(msdev->crash_pin)) { + dev_err(msdev->dev, "Spurious modem crash irq\n"); + return IRQ_NONE; + } + + queue_work(msdev->workqueue, &msdev->wq_crash); + return IRQ_HANDLED; +} + +static int request_irq_pin(int pin, irq_handler_t handler, unsigned long flags, + struct modem_state_dev *msdev) +{ + int err = 0; + if (pin) { + err = request_irq(GPIO_TO_IRQ(pin), handler, flags, + dev_name(msdev->dev), msdev); + if (err == 0) { + err = enable_irq_wake(GPIO_TO_IRQ(pin)); + if (err < 0) { + dev_err(msdev->dev, + "Request for wake on pin %d failed\n", + pin); + free_irq(GPIO_TO_IRQ(pin), NULL); + } + } else { + dev_err(msdev->dev, + "Request for irq on pin %d failed\n", pin); + } + } + return err; +} + +static void free_irq_pin(int pin) +{ + disable_irq_wake(GPIO_TO_IRQ(pin)); + free_irq(GPIO_TO_IRQ(pin), NULL); +} + +static int request_irq_all(struct modem_state_dev *msdev) +{ + int err; + + err = request_irq_pin(msdev->rsthc_pin, rsthc_irq, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_NO_SUSPEND, msdev); + if (err < 0) + goto err_rsthc_irq_req; + + err = request_irq_pin(msdev->rstext_pin, rstext_irq, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_NO_SUSPEND, msdev); + if (err < 0) + goto err_rstext_irq_req; + + err = request_irq_pin(msdev->crash_pin, crash_irq, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_NO_SUSPEND, msdev); + if (err < 0) + goto err_crash_irq_req; + + return 0; + +err_crash_irq_req: + free_irq_pin(msdev->rstext_pin); +err_rstext_irq_req: + free_irq_pin(msdev->rsthc_pin); +err_rsthc_irq_req: + return err; +} + +/* Configure GPIO used by the driver */ +static int modem_state_gpio_init(struct platform_device *pdev, + struct modem_state_dev *msdev) +{ + int err = 0; + + /* Reserve gpio pins */ + if (msdev->onkey_pin != 0) { + err = gpio_request(msdev->onkey_pin, dev_name(msdev->dev)); + if (err < 0) { + dev_err(&pdev->dev, "Request for onkey pin failed\n"); + goto err_onkey_req; + } + } + if (msdev->reset_pin != 0) { + err = gpio_request(msdev->reset_pin, dev_name(msdev->dev)); + if (err < 0) { + dev_err(&pdev->dev, "Request for reset pin failed\n"); + goto err_reset_req; + } + } + if (msdev->rsthc_pin != 0) { + err = gpio_request(msdev->rsthc_pin, dev_name(msdev->dev)); + if (err < 0) { + dev_err(&pdev->dev, "Request for rsthc pin failed\n"); + goto err_rsthc_req; + } + } + if (msdev->rstext_pin != 0) { + err = gpio_request(msdev->rstext_pin, dev_name(msdev->dev)); + if (err < 0) { + dev_err(&pdev->dev, "Request for rstext pin failed\n"); + goto err_rstext_req; + } + } + if (msdev->crash_pin != 0) { + err = gpio_request(msdev->crash_pin, dev_name(msdev->dev)); + if (err < 0) { + dev_err(&pdev->dev, "Request for crash pin failed\n"); + goto err_crash_req; + } + } + if (msdev->vbat_pin != 0) { + err = gpio_request(msdev->vbat_pin, dev_name(msdev->dev)); + if (err < 0) { + dev_err(&pdev->dev, "Request for vbat pin failed\n"); + goto err_vbat_req; + } + } + + /* Set initial pin config */ + set_on_config(msdev); + if (msdev->onkey_pin) + nmk_config_pin(PIN_CFG(msdev->onkey_pin, GPIO) | + PIN_OUTPUT_LOW, false); + if (msdev->vbat_pin) + nmk_config_pin(PIN_CFG(msdev->vbat_pin, GPIO) | PIN_OUTPUT_HIGH, + false); + + /* Configure IRQs for GPIO pins */ + err = request_irq_all(msdev); + if (err < 0) { + dev_err(&pdev->dev, "Request for irqs failed, err = %d\n", err); + goto err_irq_req; + } + msdev->irq_state = 1; + + /* Save current modem state */ + msdev->power_state = gpio_get_value(msdev->rsthc_pin); + + return 0; + +err_irq_req: + gpio_free(msdev->vbat_pin); +err_vbat_req: + gpio_free(msdev->crash_pin); +err_crash_req: + gpio_free(msdev->rstext_pin); +err_rstext_req: + gpio_free(msdev->rsthc_pin); +err_rsthc_req: + gpio_free(msdev->reset_pin); +err_reset_req: + gpio_free(msdev->onkey_pin); +err_onkey_req: + return err; +} + +/* Timer handlers */ + +static void modem_power_timeout(unsigned long data) +{ + unsigned long flags; + struct modem_state_dev *msdev = (struct modem_state_dev *)data; + + spin_lock_irqsave(&msdev->lock, flags); + if (msdev->busy) + msdev->busy = 0; + else + dev_err(msdev->dev, + "onkey timer expired and busy flag not set\n"); + + gpio_set_value(msdev->onkey_pin, 0); + spin_unlock_irqrestore(&msdev->lock, flags); +} + +static void modem_reset_timeout(unsigned long data) +{ + unsigned long flags; + struct modem_state_dev *msdev = (struct modem_state_dev *)data; + + spin_lock_irqsave(&modem_state->lock, flags); + if (msdev->busy) + msdev->busy = 0; + else + dev_err(msdev->dev, + "reset timer expired and busy flag not set\n"); + + gpio_set_value(msdev->reset_pin, 1); + spin_unlock_irqrestore(&modem_state->lock, flags); +} + +static void modem_onkey_debounce_timeout(unsigned long data) +{ + unsigned long flags; + struct modem_state_dev *msdev = (struct modem_state_dev *)data; + + spin_lock_irqsave(&msdev->lock, flags); + if (msdev->busy) { + dev_info(msdev->dev, + "Delayed onkey change aborted. " + "Another action in progress\n"); + } else { + if (gpio_get_value(msdev->rsthc_pin) != msdev->power_state) { + if (0 == msdev->power_state) + modem_power_off(msdev); + else + modem_power_on(msdev); + } + } + spin_unlock_irqrestore(&msdev->lock, flags); +} + +static void modem_vbat_off_timeout(unsigned long data) +{ + struct modem_state_dev *msdev = (struct modem_state_dev *)data; + unsigned long flags; + spin_lock_irqsave(&msdev->lock, flags); + if (0 == msdev->power_state) + modem_vbat_set_value(msdev, 0); + spin_unlock_irqrestore(&msdev->lock, flags); +} + +static void modem_busy_on_timeout(unsigned long data) +{ + unsigned long flags; + struct modem_state_dev *msdev = (struct modem_state_dev *)data; + + spin_lock_irqsave(&msdev->lock, flags); + if (msdev->busy) { + mod_timer(&msdev->busy_timer, jiffies + 1); + } else { + msdev->busy_timer.function = NULL; + modem_power_on(msdev); + } + spin_unlock_irqrestore(&msdev->lock, flags); +} + +static void modem_busy_off_timeout(unsigned long data) +{ + unsigned long flags; + struct modem_state_dev *msdev = (struct modem_state_dev *)data; + + spin_lock_irqsave(&msdev->lock, flags); + if (msdev->busy) { + mod_timer(&msdev->busy_timer, jiffies + 1); + } else { + msdev->busy_timer.function = NULL; + modem_power_off(msdev); + } + spin_unlock_irqrestore(&msdev->lock, flags); +} + +static void modem_busy_reset_timeout(unsigned long data) +{ + unsigned long flags; + struct modem_state_dev *msdev = (struct modem_state_dev *)data; + + spin_lock_irqsave(&msdev->lock, flags); + if (msdev->busy) { + mod_timer(&msdev->busy_timer, jiffies + 1); + } else { + msdev->busy_timer.function = NULL; + modem_reset(msdev); + } + spin_unlock_irqrestore(&msdev->lock, flags); +} + +#ifdef DEBUG +static int callback_test(unsigned long data) +{ + struct modem_state_dev *msdev = (struct modem_state_dev *)data; + dev_info(msdev->dev, "Test callback. Modem state is %s\n", + modem_state_to_str(modem_state_get_state())); + return 0; +} +#endif + +/* Exported functions */ + +void modem_state_power_on(void) +{ + unsigned long flags; + spin_lock_irqsave(&modem_state->lock, flags); + if (modem_state->busy) { + /* + * Ignore on request if turning off is queued, + * cancel any queued reset request + */ + if (modem_busy_reset_timeout == + modem_state->busy_timer.function) { + del_timer_sync(&modem_state->busy_timer); + modem_state->busy_timer.function = NULL; + } + if (NULL == modem_state->busy_timer.function) { + modem_state->busy_timer.function = + modem_busy_on_timeout; + modem_state->busy_timer.data = + (unsigned long)modem_state; + mod_timer(&modem_state->busy_timer, jiffies + 1); + } + } else { + modem_power_on(modem_state); + } + spin_unlock_irqrestore(&modem_state->lock, flags); +} + +void modem_state_power_off(void) +{ + unsigned long flags; + spin_lock_irqsave(&modem_state->lock, flags); + if (modem_state->busy) { + /* + * Prioritize off request if others are queued. + * Must turn modem off if system is shutting down + */ + if (NULL != modem_state->busy_timer.function) + del_timer_sync(&modem_state->busy_timer); + + modem_state->busy_timer.function = modem_busy_off_timeout; + modem_state->busy_timer.data = (unsigned long)modem_state; + mod_timer(&modem_state->busy_timer, jiffies + 1); + } else { + modem_power_off(modem_state); + } + spin_unlock_irqrestore(&modem_state->lock, flags); +} + +void modem_state_force_reset(void) +{ + unsigned long flags; + spin_lock_irqsave(&modem_state->lock, flags); + if (modem_state->busy) { + /* Ignore reset request if turning on or off is queued */ + if (NULL == modem_state->busy_timer.function) { + modem_state->busy_timer.function = + modem_busy_reset_timeout; + modem_state->busy_timer.data = + (unsigned long)modem_state; + mod_timer(&modem_state->busy_timer, jiffies + 1); + } + } else { + modem_reset(modem_state); + } + spin_unlock_irqrestore(&modem_state->lock, flags); +} + +int modem_state_get_state(void) +{ + return get_modem_state(modem_state); +} + +char *modem_state_to_str(int state) +{ + if (state > MODEM_STATE_END_MARKER) + state = MODEM_STATE_END_MARKER; + + return modem_state_str[state]; +} + +int modem_state_register_callback(int (*callback) (unsigned long), + unsigned long data) +{ + struct callback_list *item; + unsigned long flags; + + if (NULL == modem_state) + return -EAGAIN; + + if (NULL == callback) + return -EINVAL; + + item = kzalloc(sizeof(*item), GFP_KERNEL); + if (NULL == item) { + dev_err(modem_state->dev, + "Could not allocate memory for struct callback_list\n"); + return -ENOMEM; + } + item->callback = callback; + item->data = data; + + spin_lock_irqsave(&modem_state->lock, flags); + list_add_tail(&item->node, &callback_list); + spin_unlock_irqrestore(&modem_state->lock, flags); + + return 0; +} + +int modem_state_remove_callback(int (*callback) (unsigned long)) +{ + struct callback_list *iterator; + struct callback_list *item; + unsigned long flags; + int ret = -ENXIO; + + if (NULL == callback) + return -EINVAL; + + spin_lock_irqsave(&modem_state->lock, flags); + list_for_each_entry_safe(iterator, item, &callback_list, node) { + if (callback == item->callback) { + list_del(&item->node); + kfree(item); + ret = 0; + break; + } + } + spin_unlock_irqrestore(&modem_state->lock, flags); + + return ret; +} + +#ifdef CONFIG_PM +int modem_state_suspend(struct device *dev) +{ + struct modem_state_dev *msdev = + platform_get_drvdata(to_platform_device(dev)); + + if (msdev->busy) { + dev_info(dev, "Driver is busy\n"); + return -EBUSY; + } else { + return 0; + } +} + +int modem_state_resume(struct device *dev) +{ + return 0; +} +#endif + +static int __devinit modem_state_probe(struct platform_device *pdev) +{ + int err = 0; + + dev_info(&pdev->dev, "Starting probe\n"); + + modem_state = kzalloc(sizeof(struct modem_state_dev), GFP_KERNEL); + if (NULL == modem_state) { + dev_err(&pdev->dev, + "Could not allocate memory for modem_state_dev\n"); + return -ENOMEM; + } + modem_state->dev = &pdev->dev; + + spin_lock_init(&modem_state->lock); + + INIT_WORK(&modem_state->wq_rsthc, wq_rsthc); + INIT_WORK(&modem_state->wq_rstext, wq_rstext); + INIT_WORK(&modem_state->wq_crash, wq_crash); + modem_state->workqueue = + create_singlethread_workqueue(dev_name(&pdev->dev)); + if (modem_state->workqueue == NULL) { + dev_err(&pdev->dev, "Failed to create workqueue\n"); + goto err_queue; + } + + err = modem_state_dev_init(pdev, modem_state); + if (err != 0) { + dev_err(&pdev->dev, "Could not initialize device structure\n"); + goto err_dev; + } + + init_timer(&modem_state->onkey_timer); + init_timer(&modem_state->reset_timer); + init_timer(&modem_state->onkey_debounce_timer); + init_timer(&modem_state->vbat_off_timer); + init_timer(&modem_state->busy_timer); + modem_state->onkey_timer.function = modem_power_timeout; + modem_state->reset_timer.function = modem_reset_timeout; + modem_state->onkey_debounce_timer.function = + modem_onkey_debounce_timeout; + modem_state->vbat_off_timer.function = modem_vbat_off_timeout; + modem_state->busy_timer.function = NULL; + + platform_set_drvdata(pdev, modem_state); + + err = modem_state_gpio_init(pdev, modem_state); + if (err != 0) { + dev_err(&pdev->dev, "Could not initialize GPIO\n"); + goto err_gpio; + } + + if (sysfs_create_group(&pdev->dev.kobj, &modemstate_attr_group) < 0) { + dev_err(&pdev->dev, "failed to create sysfs nodes\n"); + goto err_sysfs; + } + +#ifdef CONFIG_DEBUG_FS + modem_state->debugfsdir = debugfs_create_dir("modemstate", NULL); + modem_state->debugfs_debug = debugfs_create_file("debug", + S_IRUGO | S_IWUGO, + modem_state->debugfsdir, + modem_state, + &debugfs_debug_fops); +#endif + +#ifdef DEBUG + modem_state_register_callback(callback_test, + (unsigned long)modem_state); +#endif + return 0; + +err_sysfs: +err_gpio: +err_dev: + destroy_workqueue(modem_state->workqueue); +err_queue: + kfree(modem_state); + return err; +} + +static int __devexit modem_state_remove(struct platform_device *pdev) +{ + struct modem_state_dev *msdev = platform_get_drvdata(pdev); + + sysfs_remove_group(&pdev->dev.kobj, &modemstate_attr_group); + destroy_workqueue(msdev->workqueue); + kfree(msdev); + return 0; +} + +static void modem_state_shutdown(struct platform_device *pdev) +{ + /* + * Trigger software shutdown of the modem and then wait until + * modem-off state is detected. If the modem does not power off + * when requested power will be removed and we will detect the + * modem-off state that way. + */ + modem_state_power_off(); + if (MODEM_STATE_OFF != modem_state_get_state()) + dev_alert(&pdev->dev, "Waiting for modem to power down\n"); + while (MODEM_STATE_OFF != modem_state_get_state()) + cond_resched(); +} + +#ifdef CONFIG_PM +static const struct dev_pm_ops modem_state_dev_pm_ops = { + .suspend_noirq = modem_state_suspend, + .resume_noirq = modem_state_resume, +}; +#endif + +static struct platform_driver modem_state_driver = { + .probe = modem_state_probe, + .remove = __devexit_p(modem_state_remove), + .shutdown = modem_state_shutdown, + .driver = { + .name = "modemstate", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &modem_state_dev_pm_ops, +#endif + }, +}; + +static int __init modem_state_init(void) +{ +#ifdef DEBUG + printk(KERN_ALERT "Modem state driver init\n"); +#endif + return platform_driver_probe(&modem_state_driver, modem_state_probe); +} + +static void __exit modem_state_exit(void) +{ + platform_driver_unregister(&modem_state_driver); +} + +module_init(modem_state_init); +module_exit(modem_state_exit); + +MODULE_AUTHOR("Derek Morton"); +MODULE_DESCRIPTION("M6718 modem power state driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/modem/m6718_spi/modem_state.h b/include/linux/modem/m6718_spi/modem_state.h new file mode 100644 index 00000000000..a2f1d9fbe3e --- /dev/null +++ b/include/linux/modem/m6718_spi/modem_state.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Derek Morton + * + * License terms: GNU General Public License (GPL) version 2 + * + * Power state driver for M6718 MODEM + */ +#ifndef MODEM_STATE_H +#define MODEM_STATE_H + +enum modem_states { + MODEM_STATE_OFF, + MODEM_STATE_RESET, + MODEM_STATE_CRASH, + MODEM_STATE_ON, + /* + * Add new states before end marker and update modem_state_str[] + * in modem_state.c + */ + MODEM_STATE_END_MARKER +}; + +void modem_state_power_on(void); +void modem_state_power_off(void); +void modem_state_force_reset(void); +int modem_state_get_state(void); +char *modem_state_to_str(int state); + +/* Callbacks will be running in tasklet context */ +int modem_state_register_callback(int (*callback) (unsigned long), + unsigned long data); +int modem_state_remove_callback(int (*callback) (unsigned long)); + +#endif -- cgit v1.2.3 From 014c417fd3ca9f29f7a35edc7128f057a8b35dff Mon Sep 17 00:00:00 2001 From: Bibek Basu Date: Mon, 21 Nov 2011 17:10:17 +0530 Subject: u5500 :logical mailbox: support for 32 dsp buffer add support for 32 dsp buffer ST-Ericsson Linux next: NA ST-Ericsson ID: 367390 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Id031ef5879906230b2dac8b3723bdef58da9bd4a Signed-off-by: Bibek Basu Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/39093 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Rabin VINCENT --- arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h b/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h index 20940e11875..69616c4cdec 100644 --- a/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h +++ b/arch/arm/mach-ux500/include/mach/mbox_channels-db5500.h @@ -12,7 +12,7 @@ #define MAILBOX_NR_OF_DATAWORDS 3 /* Number of buffers */ -#define NUM_DSP_BUFFER 16 +#define NUM_DSP_BUFFER 32 /** * mbox_channel_cb_t - Definition of the mailbox channel callback. -- cgit v1.2.3 From a9e977b5fb41043e24e4080a80acbd1ec3f01a02 Mon Sep 17 00:00:00 2001 From: Bibek Basu Date: Fri, 14 Oct 2011 12:45:50 +0530 Subject: u5500 : modem crashdump detection driver Detects modem crashdump ready and informs userspace ST-Ericsson Linux next: NA ST-Ericsson ID: 354626 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I204eeb48b7353509b07b9e3c6ec08bc7026985fa Signed-off-by: Bibek Basu Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/34170 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Sandeep TRIPATHY Tested-by: Sandeep TRIPATHY Reviewed-by: Srinidhi KASAGAR --- drivers/modem/Kconfig | 13 +++- drivers/modem/Makefile | 1 + drivers/modem/mcdd.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 drivers/modem/mcdd.c diff --git a/drivers/modem/Kconfig b/drivers/modem/Kconfig index 323ef88af18..be8476ed0f9 100644 --- a/drivers/modem/Kconfig +++ b/drivers/modem/Kconfig @@ -1,6 +1,6 @@ config MODEM bool "Modem Access Framework" - default n + default y help Add support for Modem Access Framework. It allows different platform specific drivers to register modem access mechanisms @@ -8,6 +8,17 @@ config MODEM If unsure, say N. +config MODEM_U5500_MCDD + tristate "Modem crash dump detection driver for STE U5500 platform" + depends on (UX500_SOC_DB5500 && U5500_MODEM_IRQ && MODEM) + default y + help + Add support for Modem crash detection + driver for STE U5500 platform. + And inform userspace. + + If unsure, say N. + config MODEM_U8500 bool "Modem Access driver for STE U8500 platform" depends on MODEM diff --git a/drivers/modem/Makefile b/drivers/modem/Makefile index a7c1df50074..82921988f27 100644 --- a/drivers/modem/Makefile +++ b/drivers/modem/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_MODEM_U8500) += modem_u8500.o obj-$(CONFIG_U8500_SHRM) += shrm/ obj-$(CONFIG_MODEM_M6718) += modem_m6718.o obj-$(CONFIG_MODEM_M6718_SPI) += m6718_spi/ +obj-$(CONFIG_MODEM_U5500_MCDD) += mcdd.o diff --git a/drivers/modem/mcdd.c b/drivers/modem/mcdd.c new file mode 100644 index 00000000000..d291944e810 --- /dev/null +++ b/drivers/modem/mcdd.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Modem Crash Detection Driver + * + * Author:Bibek Basu for ST-Ericsson + * + * License terms:GNU General Public License (GPLv2)version 2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MCDD_INTERRUPT_CLEAR (1 << 13) +#define MODEM_CRASH_EVT 1 + +struct mcdd_data { + bool modem_event; + u32 event_type; + wait_queue_head_t readq; + spinlock_t lock; + void __iomem *remap_intcon; + struct device *dev; + struct miscdevice misc_dev; +}; + +static struct mcdd_data *mcdd; + +static irqreturn_t mcdd_interrupt_cb(int irq, void *dev) +{ + writel(MCDD_INTERRUPT_CLEAR, (u32 *)mcdd->remap_intcon); + spin_lock(&mcdd->lock); + mcdd->modem_event = true; + mcdd->event_type = MODEM_CRASH_EVT; + spin_unlock(&mcdd->lock); + wake_up_interruptible(&mcdd->readq); + return IRQ_HANDLED; +} + +static unsigned int mcdd_select(struct file *filp, poll_table *wait) +{ + unsigned int mask = 0; + unsigned long flags; + + poll_wait(filp, &mcdd->readq, wait); + spin_lock_irqsave(&mcdd->lock, flags); + + if (mcdd->modem_event == true) { + mask |= POLLPRI; + mcdd->modem_event = false; + } + spin_unlock_irqrestore(&mcdd->lock, flags); + + return mask; +} + +static int mcdd_open(struct inode *ino, struct file *filp) +{ + /* Do nothing */ + return 0; +} + +ssize_t mcdd_read(struct file *filp, char __user *buff, size_t size, loff_t *t) +{ + if (copy_to_user(buff, &mcdd->event_type, size)) + return -EFAULT; + return 0; +}; + +static const struct file_operations mcdd_fops = { + .open = mcdd_open, + .poll = mcdd_select, + .read = mcdd_read, + .owner = THIS_MODULE, +}; + +static int __devinit u5500_mcdd_probe(struct platform_device *pdev) +{ + struct resource *resource; + int ret = 0; + int irq; + + mcdd = kzalloc(sizeof(*mcdd), GFP_KERNEL); + if (!mcdd) { + dev_err(&pdev->dev, "Memory Allocation Failed"); + return -ENOMEM; + } + mcdd->dev = &pdev->dev; + mcdd->misc_dev.minor = MISC_DYNAMIC_MINOR; + mcdd->misc_dev.name = "mcdd"; + mcdd->misc_dev.fops = &mcdd_fops; + spin_lock_init(&mcdd->lock); + init_waitqueue_head(&(mcdd->readq)); + + /* Get addr for mcdd crash interrupt reset register and ioremap it */ + resource = platform_get_resource_byname(pdev, + IORESOURCE_MEM, + "mcdd_intreset_addr"); + if (resource == NULL) { + dev_err(&pdev->dev, + "Unable to retrieve mcdd_intreset_addr resource\n"); + goto exit_free; + } + mcdd->remap_intcon = ioremap(resource->start, resource_size(resource)); + if (!mcdd->remap_intcon) { + dev_err(&pdev->dev, "Unable to ioremap intcon mbox1\n"); + ret = -EINVAL; + goto exit_free; + } + + /* Get IRQ for mcdd mbox interrupt and allocate it */ + irq = platform_get_irq_byname(pdev, "mcdd_mbox_irq"); + if (irq < 0) { + dev_err(&pdev->dev, + "Unable to retrieve mcdd mbox irq resource\n"); + goto exit_unmap; + } + + ret = request_threaded_irq(irq, NULL, + mcdd_interrupt_cb, IRQF_NO_SUSPEND | IRQF_ONESHOT, + "mcdd", &mcdd); + if (ret < 0) { + dev_err(&pdev->dev, + "Could not allocate irq %d,error %d\n", + irq, ret); + goto exit_unmap; + } + + ret = misc_register(&mcdd->misc_dev); + if (ret) { + dev_err(&pdev->dev, "can't misc-register\n"); + goto exit_unmap; + } + dev_info(&pdev->dev, "mcdd driver registration done\n"); + return 0; + +exit_unmap: + iounmap(mcdd->remap_intcon); +exit_free: + kfree(mcdd); + return ret; +} + +static int u5500_mcdd_remove(struct platform_device *pdev) +{ + int ret = 0; + + if (mcdd) { + iounmap(mcdd->remap_intcon); + ret = misc_deregister(&mcdd->misc_dev); + kfree(mcdd); + } + return ret; +} + +static struct platform_driver u5500_mcdd_driver = { + .driver = { + .name = "u5500-mcdd-modem", + .owner = THIS_MODULE, + }, + .probe = u5500_mcdd_probe, + .remove = __devexit_p(u5500_mcdd_remove), +}; + +static int __init mcdd_init(void) +{ + return platform_driver_register(&u5500_mcdd_driver); +} +module_init(mcdd_init); + +static void __exit mcdd_exit(void) +{ + platform_driver_unregister(&u5500_mcdd_driver); +} +module_exit(mcdd_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("BIBEK BASU "); +MODULE_DESCRIPTION("Modem Dump Detection Driver"); +MODULE_ALIAS("mcdd driver"); -- cgit v1.2.3 From 66494ce129180419c8a7921e41ce570070b1ec16 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Mon, 10 Oct 2011 11:42:00 +0530 Subject: u5500-mbox: APE - Modem power management protocol Implement a protocol for APE - Modem communication which helps in achieving system power management. ST-Ericsson Linux next: NA ST-Ericsson ID: 350108 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Id42b0d50d118d5d3215a60f3123c365db20cfc6b Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36183 Reviewed-by: Bibek BASU Reviewed-by: QATOOLS Reviewed-by: Rabin VINCENT --- drivers/misc/mbox.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 138 insertions(+), 3 deletions(-) diff --git a/drivers/misc/mbox.c b/drivers/misc/mbox.c index 9bd5db9b23c..6e22653eed9 100644 --- a/drivers/misc/mbox.c +++ b/drivers/misc/mbox.c @@ -39,7 +39,12 @@ #include #include #include +#include +#include +#include +#include #include +#include #define MBOX_NAME "mbox" @@ -54,8 +59,54 @@ #define MBOX_ENABLE_IRQ 0x0 #define MBOX_LATCH 1 +struct mbox_device_info { + struct workqueue_struct *mbox_modem_rel_wq; + struct work_struct mbox_modem_rel; + struct completion mod_req_ack_work; + atomic_t ape_state; + atomic_t mod_req; +}; + /* Global list of all mailboxes */ +struct hrtimer ape_timer; +struct hrtimer modem_timer; +static DEFINE_MUTEX(modem_state_mutex); static struct list_head mboxs = LIST_HEAD_INIT(mboxs); +static struct mbox_device_info *mb; + +static enum hrtimer_restart mbox_ape_callback(struct hrtimer *hrtimer) +{ + queue_work(mb->mbox_modem_rel_wq, &mb->mbox_modem_rel); + + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart mbox_mod_callback(struct hrtimer *hrtimer) +{ + atomic_set(&mb->ape_state, 0); + return HRTIMER_NORESTART; +} + +static void mbox_modem_rel_work(struct work_struct *work) +{ + mutex_lock(&modem_state_mutex); + prcmu_modem_rel(); + atomic_set(&mb->mod_req, 0); + mutex_unlock(&modem_state_mutex); +} + +static void mbox_modem_req() +{ + mutex_lock(&modem_state_mutex); + if (!db5500_prcmu_is_modem_requested()) { + prcmu_modem_req(); + if (!wait_for_completion_timeout(&mb->mod_req_ack_work, + msecs_to_jiffies(8000))) + printk(KERN_ERR "mbox:modem_req_ack timedout(8sec)\n"); + } + atomic_set(&mb->mod_req, 1); + mutex_unlock(&modem_state_mutex); +} static struct mbox *get_mbox_with_id(u8 id) { @@ -72,14 +123,17 @@ int mbox_send(struct mbox *mbox, u32 mbox_msg, bool block) int res = 0; unsigned long flag; - spin_lock_irqsave(&mbox->lock, flag); - dev_dbg(&(mbox->pdev->dev), "About to buffer 0x%X to mailbox 0x%X." " ri = %d, wi = %d\n", mbox_msg, (u32)mbox, mbox->read_index, mbox->write_index); + /* Request for modem */ + if (!db5500_prcmu_is_modem_requested()) + mbox_modem_req(); + + spin_lock_irqsave(&mbox->lock, flag); /* Check if write buffer is full */ while (((mbox->write_index + 1) % MBOX_BUF_SIZE) == mbox->read_index) { if (!block) { @@ -346,6 +400,10 @@ static irqreturn_t mbox_irq(int irq, void *arg) } } + /* Start timer and on timer expiry call modem_rel */ + hrtimer_start(&ape_timer, ktime_set(0, 10*NSEC_PER_MSEC), + HRTIMER_MODE_REL); + /* Check if we have any incoming messages */ nbr_occup = readl(mbox->virtbase_local + MBOX_FIFO_STATUS) & 0x7; if (nbr_occup == 0) @@ -357,6 +415,7 @@ redo: "leaving %d incoming messages in fifo!\n", nbr_occup); goto exit; } + atomic_set(&mb->ape_state, 1); /* Read and acknowledge the message */ mbox_value = readl(mbox->virtbase_local + MBOX_FIFO_DATA); @@ -372,6 +431,9 @@ redo: if (nbr_occup > 0) goto redo; + /* Start a timer and timer expiry will be the criteria for sleep */ + hrtimer_start(&modem_timer, ktime_set(0, 100*MSEC_PER_SEC), + HRTIMER_MODE_REL); exit: dev_dbg(&(mbox->pdev->dev), "Exit mbox IRQ. ri = %d, wi = %d\n", mbox->read_index, mbox->write_index); @@ -539,6 +601,17 @@ exit: } EXPORT_SYMBOL(mbox_setup); +static irqreturn_t mbox_prcmu_mod_req_ack_handler(int irq, void *data) +{ + complete(&mb->mod_req_ack_work); + return IRQ_HANDLED; +} + +static irqreturn_t mbox_prcmu_ape_req_handler(int irq, void *data) +{ + prcmu_ape_ack(); + return IRQ_HANDLED; +} int __init mbox_probe(struct platform_device *pdev) { @@ -553,7 +626,6 @@ int __init mbox_probe(struct platform_device *pdev) return -ENOMEM; } - mbox->pdev = pdev; mbox->write_index = 0; mbox->read_index = 0; @@ -574,6 +646,8 @@ static int __exit mbox_remove(struct platform_device *pdev) { struct mbox *mbox = platform_get_drvdata(pdev); + hrtimer_cancel(&ape_timer); + hrtimer_cancel(&modem_timer); mbox_shutdown(mbox); list_del(&mbox->list); kfree(mbox); @@ -595,6 +669,11 @@ int mbox_suspend(struct device *dev) if (mbox->client_blocked) return -EBUSY; } + dev_dbg(dev, "APE_STATE = %d\n", atomic_read(&mb->ape_state)); + dev_dbg(dev, "MODEM_STATE = %d\n", db5500_prcmu_is_modem_requested()); + if (atomic_read(&mb->ape_state) || db5500_prcmu_is_modem_requested() || + atomic_read(&mb->mod_req)) + return -EBUSY; return 0; } @@ -630,14 +709,70 @@ static struct platform_driver mbox_driver = { static int __init mbox_init(void) { + struct mbox_device_info *mb_di; + int err; + + mb_di = kzalloc(sizeof(struct mbox_device_info), GFP_KERNEL); + if (mb_di == NULL) { + printk(KERN_ERR + "mbox:Could not allocate memory for struct mbox_device_info\n"); + return -ENOMEM; + } + + mb_di->mbox_modem_rel_wq = create_singlethread_workqueue( + "mbox_modem_rel"); + if (!mb_di->mbox_modem_rel_wq) { + printk(KERN_ERR "mbox:failed to create work queue\n"); + err = -ENOMEM; + goto free_mem; + } + + INIT_WORK(&mb_di->mbox_modem_rel, mbox_modem_rel_work); + + hrtimer_init(&ape_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ape_timer.function = mbox_ape_callback; + hrtimer_init(&modem_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + modem_timer.function = mbox_mod_callback; + + atomic_set(&mb_di->ape_state, 0); + atomic_set(&mb_di->mod_req, 0); + + err = request_irq(IRQ_DB5500_PRCMU_APE_REQ, mbox_prcmu_ape_req_handler, + IRQF_NO_SUSPEND, "ape_req", NULL); + if (err < 0) { + printk(KERN_ERR "mbox:Failed alloc IRQ_DB5500_PRCMU_APE_REQ.\n"); + goto free_wq1; + } + + err = request_irq(IRQ_DB5500_PRCMU_AC_WAKE_ACK, + mbox_prcmu_mod_req_ack_handler, + IRQF_NO_SUSPEND, "mod_req_ack", NULL); + if (err < 0) { + printk(KERN_ERR "mbox:Failed alloc IRQ_PRCMU_CA_SLEEP.\n"); + goto free_irq; + } + + init_completion(&mb_di->mod_req_ack_work); + mb = mb_di; return platform_driver_probe(&mbox_driver, mbox_probe); +free_irq: + free_irq(IRQ_DB5500_PRCMU_APE_REQ, NULL); +free_wq1: + destroy_workqueue(mb_di->mbox_modem_rel_wq); +free_mem: + kfree(mb_di); + return err; } module_init(mbox_init); void __exit mbox_exit(void) { + free_irq(IRQ_DB5500_PRCMU_APE_REQ, NULL); + free_irq(IRQ_DB5500_PRCMU_AC_WAKE_ACK, NULL); + destroy_workqueue(mb->mbox_modem_rel_wq); platform_driver_unregister(&mbox_driver); + kfree(mb); } module_exit(mbox_exit); -- cgit v1.2.3 From 0ac258cfc8e78e8c1eeeaa182e0307d74b70543e Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Wed, 23 Nov 2011 16:05:33 +0530 Subject: u8500-shrm: Stop network queue on modem reset On shrm receiving modem reset interrupt, it will disable interrupts and schedule a tasklet for initiating MSR if enabled. The tasklet function includes stopping the network queue. But as soon as ISR is served, if phonet starts ending message, shrm will return with a -ve value. Hence phonet retries sending the same message again. This will lead to a dedlock and finally ends up with a watchdog timeout. Hence stop the network queue on receiving modem reset interrupt in the interrupt handler. ST-Ericsson Linux next: NA ST-Ericsson ID: 375164 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Id608024590cea26e15f04afa7786e245825da2ed Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/39434 Reviewed-by: Bibek BASU Tested-by: Bibek BASU Reviewed-by: Rabin VINCENT --- drivers/modem/shrm/shrm_protocol.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index c13f05d50e8..19644ca8742 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -499,9 +499,6 @@ static int shrm_modem_reset_sequence(void) queue_kthread_work(&shm_dev->shm_ac_wake_kw, &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); @@ -597,6 +594,9 @@ static irqreturn_t shrm_prcmu_irq_handler(int irq, void *data) disable_irq_nosync(IRQ_PRCMU_CA_WAKE); disable_irq_nosync(IRQ_PRCMU_CA_SLEEP); + /* stop network queue */ + shrm_stop_netdev(shm_dev->ndev); + tasklet_schedule(&shrm_sw_reset_callback); break; default: -- cgit v1.2.3 From 21b8f8e3134a838a71c90d467c4f72d7148651e7 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Thu, 24 Nov 2011 11:39:41 +0530 Subject: u8500-shrm: check for modem status while writing to GOP While writing to the modem GOP registers, check if modem is in reset else writing to GOP registers while modem being in reset will lead to system dreeze. ST-Ericsson Linux next: NA ST-Ericsson ID: 375164 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ic4534c1b9c9eee09eb09fad298adddc6bfcb7c3f Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/39433 Reviewed-by: QABUILD Reviewed-by: Srinidhi KASAGAR --- drivers/modem/shrm/shrm_protocol.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index 19644ca8742..82fed247969 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -450,6 +450,12 @@ void shm_ca_sleep_req_work(struct kthread_work *work) shrm_common_rx_state = SHRM_IDLE; shrm_audio_rx_state = SHRM_IDLE; + if (check_modem_in_reset()) { + dev_err(shm_dev->dev, "%s:Modem state reset or unknown\n", + __func__); + return; + } + writel((1<intr_base + GOP_SET_REGISTER_BASE); @@ -478,6 +484,13 @@ void shm_ca_wake_req_work(struct kthread_work *work) dev_info(shrm->dev, "Initiating a modem reset\n"); prcmu_modem_reset(); } + + if (check_modem_in_reset()) { + dev_err(shrm->dev, "%s:Modem state reset or unknown\n", + __func__); + return; + } + writel((1<intr_base + GOP_SET_REGISTER_BASE); } @@ -625,6 +638,12 @@ static void send_ac_msg_pend_notify_0_work(struct kthread_work *work) prcmu_modem_reset(); } + if (check_modem_in_reset()) { + dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", + __func__); + return; + } + /* Trigger AcMsgPendingNotification to CMU */ writel((1<intr_base + GOP_SET_REGISTER_BASE); @@ -655,6 +674,12 @@ static void send_ac_msg_pend_notify_1_work(struct kthread_work *work) prcmu_modem_reset(); } + if (check_modem_in_reset()) { + dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", + __func__); + return; + } + /* Trigger AcMsgPendingNotification to CMU */ writel((1<intr_base + GOP_SET_REGISTER_BASE); -- cgit v1.2.3 From 56972bcf391f80737671ff89badd69fd9101f2c5 Mon Sep 17 00:00:00 2001 From: Chris Blair Date: Fri, 4 Nov 2011 11:03:27 +0000 Subject: modem: Add M6718 IPC SPI driver protocol framework Adds the M6718 modem IPC SPI driver procotol framework and types. Placeholders remain where future parts of the protocol will be called. ST-Ericsson ID: 369397 ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-12224 ST-Ericsson Linux next: NA Change-Id: Id4cd5a7fa730c06838c9dd5b1a1cc0045135fdd4 Signed-off-by: Chris Blair Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36466 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Jonas ABERG --- drivers/modem/m6718_spi/Makefile | 2 +- drivers/modem/m6718_spi/modem_driver.c | 26 ++- drivers/modem/m6718_spi/modem_private.h | 103 ++++++++++ drivers/modem/m6718_spi/modem_protocol.h | 22 ++ drivers/modem/m6718_spi/modem_state.c | 2 +- drivers/modem/m6718_spi/modem_state.h | 36 ++++ drivers/modem/m6718_spi/modem_statemachine.h | 74 +++++++ drivers/modem/m6718_spi/protocol.c | 287 +++++++++++++++++++++++++++ include/linux/modem/m6718_spi/modem_driver.h | 32 ++- include/linux/modem/m6718_spi/modem_state.h | 36 ---- 10 files changed, 579 insertions(+), 41 deletions(-) create mode 100644 drivers/modem/m6718_spi/modem_private.h create mode 100644 drivers/modem/m6718_spi/modem_protocol.h create mode 100644 drivers/modem/m6718_spi/modem_state.h create mode 100644 drivers/modem/m6718_spi/modem_statemachine.h create mode 100644 drivers/modem/m6718_spi/protocol.c delete mode 100644 include/linux/modem/m6718_spi/modem_state.h diff --git a/drivers/modem/m6718_spi/Makefile b/drivers/modem/m6718_spi/Makefile index 492995bc753..66bad09c0dd 100644 --- a/drivers/modem/m6718_spi/Makefile +++ b/drivers/modem/m6718_spi/Makefile @@ -5,7 +5,7 @@ ifeq ($(CONFIG_MODEM_M6718_SPI_DEBUG),y) EXTRA_CFLAGS += -DDEBUG endif -m6718_modem_spi-objs := modem_driver.o +m6718_modem_spi-objs := modem_driver.o protocol.o ifeq ($(CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE),y) m6718_modem_spi-objs += modem_state.o diff --git a/drivers/modem/m6718_spi/modem_driver.c b/drivers/modem/m6718_spi/modem_driver.c index 7d0efa6e395..13d4a3e28b2 100644 --- a/drivers/modem/m6718_spi/modem_driver.c +++ b/drivers/modem/m6718_spi/modem_driver.c @@ -13,6 +13,7 @@ #include #include #include +#include "modem_protocol.h" static struct modem_spi_dev modem_driver_data = { .dev = NULL, @@ -53,6 +54,13 @@ static int spi_probe(struct spi_device *sdev) spi_set_drvdata(sdev, &modem_driver_data); + if (modem_protocol_probe(sdev) != 0) { + dev_err(&sdev->dev, + "failed to initialise link protocol\n"); + result = -ENODEV; + goto rollback; + } + /* * Since we can have multiple spi links for the same modem, only * initialise the modem data and char/net interfaces once. @@ -65,13 +73,20 @@ static int spi_probe(struct spi_device *sdev) dev_err(&sdev->dev, "failed to retrieve modem description\n"); result = -ENODEV; + goto rollback_protocol_init; } } return result; + +rollback_protocol_init: + modem_protocol_exit(); +rollback: + return result; } static int __exit spi_remove(struct spi_device *sdev) { + modem_protocol_exit(); return 0; } @@ -86,8 +101,14 @@ static int __exit spi_remove(struct spi_device *sdev) */ static int spi_suspend(struct spi_device *sdev, pm_message_t mesg) { - dev_dbg(&sdev->dev, "suspend called\n"); - return 0; + bool busy; + + busy = modem_protocol_is_busy(sdev); + dev_dbg(&sdev->dev, "suspend called, protocol busy:%d\n", busy); + if (!busy) + return 0; + else + return -EBUSY; } /** @@ -118,6 +139,7 @@ static struct spi_driver spi_driver = { static int __init m6718_spi_driver_init(void) { pr_info("M6718 modem driver initialising\n"); + modem_protocol_init(); return spi_register_driver(&spi_driver); } module_init(m6718_spi_driver_init); diff --git a/drivers/modem/m6718_spi/modem_private.h b/drivers/modem/m6718_spi/modem_private.h new file mode 100644 index 00000000000..383699f1f3e --- /dev/null +++ b/drivers/modem/m6718_spi/modem_private.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Chris Blair for ST-Ericsson + * based on shrm_driver.h + * + * License terms: GNU General Public License (GPL) version 2 + * + * Modem IPC driver protocol interface header: + * private data + */ +#ifndef _MODEM_PRIVATE_H_ +#define _MODEM_PRIVATE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "modem_protocol.h" +#include "modem_statemachine.h" + +#define IPC_DRIVER_VERSION (0x03) /* APE protocol version */ +#define IPC_DRIVER_MODEM_MIN_VER (0x03) /* version required from modem */ + +#define IPC_NBR_SUPPORTED_SPI_LINKS (2) +#define IPC_LINK_COMMON (0) +#define IPC_LINK_AUDIO (1) + +#define IPC_TX_QUEUE_MAX_SIZE (1024*1024) + +#define IPC_L1_HDR_SIZE (4) +#define IPC_L2_HDR_SIZE (4) + +/* tx queue item (frame) */ +struct ipc_tx_queue { + struct list_head node; + int actual_len; + int len; + void *data; + int counter; +}; + +/* context structure for an spi link */ +struct ipc_link_context { + struct modem_m6718_spi_link_platform_data *link; + struct spi_device *sdev; + atomic_t gpio_configured; + atomic_t state_int; + spinlock_t sm_lock; + spinlock_t tx_q_update_lock; + atomic_t tx_q_count; + int tx_q_free; + struct list_head tx_q; + int tx_frame_counter; + const struct ipc_sm_state *state; + u32 cmd; + struct ipc_tx_queue *frame; + struct spi_message spi_message; + struct spi_transfer spi_transfer; + struct timer_list comms_timer; + struct timer_list slave_stable_timer; +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_VERIFY_FRAMES + struct ipc_tx_queue *last_frame; +#endif +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT + u32 tx_bytes; + u32 rx_bytes; + unsigned long idl_measured_at; + unsigned long idl_idle_enter; + unsigned long idl_idle_total; +#endif +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfsfile; + u8 lastevent; + u8 lastignored; + enum ipc_sm_state_id lastignored_in; + bool lastignored_inthis; + int tx_q_min; + unsigned long statesince; +#endif +}; + +/* context structure for the spi driver */ +struct ipc_l1_context { + bool init_done; + atomic_t boot_sync_done; + struct ipc_link_context device_context[IPC_NBR_SUPPORTED_SPI_LINKS]; +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT + struct timer_list tp_timer; +#endif +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfsdir; + struct dentry *debugfs_silentreset; + bool msr_disable; +#endif +}; + +#endif /* _MODEM_PRIVATE_H_ */ diff --git a/drivers/modem/m6718_spi/modem_protocol.h b/drivers/modem/m6718_spi/modem_protocol.h new file mode 100644 index 00000000000..69511945cb5 --- /dev/null +++ b/drivers/modem/m6718_spi/modem_protocol.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Chris Blair for ST-Ericsson + * based on shrm_driver.h + * + * License terms: GNU General Public License (GPL) version 2 + * + * Modem IPC driver protocol interface header. + */ +#ifndef _MODEM_PROTOCOL_H_ +#define _MODEM_PROTOCOL_H_ + +#include + +void modem_protocol_init(void); +int modem_protocol_probe(struct spi_device *sdev); +void modem_protocol_exit(void); +bool modem_protocol_is_busy(struct spi_device *sdev); +bool modem_protocol_channel_is_open(u8 channel); + +#endif /* _MODEM_PROTOCOL_H_ */ diff --git a/drivers/modem/m6718_spi/modem_state.c b/drivers/modem/m6718_spi/modem_state.c index 266f75a7a71..47376934bcb 100644 --- a/drivers/modem/m6718_spi/modem_state.c +++ b/drivers/modem/m6718_spi/modem_state.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -30,6 +29,7 @@ #include #include #include +#include "modem_state.h" /* * To enable this driver add a struct platform_device in the board diff --git a/drivers/modem/m6718_spi/modem_state.h b/drivers/modem/m6718_spi/modem_state.h new file mode 100644 index 00000000000..a2f1d9fbe3e --- /dev/null +++ b/drivers/modem/m6718_spi/modem_state.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Derek Morton + * + * License terms: GNU General Public License (GPL) version 2 + * + * Power state driver for M6718 MODEM + */ +#ifndef MODEM_STATE_H +#define MODEM_STATE_H + +enum modem_states { + MODEM_STATE_OFF, + MODEM_STATE_RESET, + MODEM_STATE_CRASH, + MODEM_STATE_ON, + /* + * Add new states before end marker and update modem_state_str[] + * in modem_state.c + */ + MODEM_STATE_END_MARKER +}; + +void modem_state_power_on(void); +void modem_state_power_off(void); +void modem_state_force_reset(void); +int modem_state_get_state(void); +char *modem_state_to_str(int state); + +/* Callbacks will be running in tasklet context */ +int modem_state_register_callback(int (*callback) (unsigned long), + unsigned long data); +int modem_state_remove_callback(int (*callback) (unsigned long)); + +#endif diff --git a/drivers/modem/m6718_spi/modem_statemachine.h b/drivers/modem/m6718_spi/modem_statemachine.h new file mode 100644 index 00000000000..bc737dbb7e8 --- /dev/null +++ b/drivers/modem/m6718_spi/modem_statemachine.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Chris Blair for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * Modem IPC driver protocol interface header: + * statemachine functionality. + */ +#ifndef _MODEM_STATEMACHINE_H_ +#define _MODEM_STATEMACHINE_H_ + +#include + +/* valid states for the driver state machine */ +enum ipc_sm_state_id { + /* common link and shared states below */ + IPC_SM_INIT, + IPC_SM_HALT, + IPC_SM_RESET, + IPC_SM_WAIT_SLAVE_STABLE, + IPC_SM_WAIT_HANDSHAKE_INACTIVE, + IPC_SM_SLW_TX_BOOTREQ, + IPC_SM_ACT_TX_BOOTREQ, + IPC_SM_SLW_RX_BOOTRESP, + IPC_SM_ACT_RX_BOOTRESP, + IPC_SM_IDL, + IPC_SM_SLW_TX_WR_CMD, + IPC_SM_ACT_TX_WR_CMD, + IPC_SM_SLW_TX_WR_DAT, + IPC_SM_ACT_TX_WR_DAT, + IPC_SM_SLW_TX_RD_CMD, + IPC_SM_ACT_TX_RD_CMD, + IPC_SM_SLW_RX_WR_CMD, + IPC_SM_ACT_RX_WR_CMD, + IPC_SM_ACT_RX_WR_DAT, + /* audio link states below */ + IPC_SM_INIT_AUD, + IPC_SM_HALT_AUD, + IPC_SM_RESET_AUD, + IPC_SM_IDL_AUD, + IPC_SM_SLW_TX_WR_DAT_AUD, + IPC_SM_ACT_TX_WR_DAT_AUD, + IPC_SM_SLW_RX_WR_DAT_AUD, + IPC_SM_ACT_RX_WR_DAT_AUD, + IPC_SM_STATE_ID_NBR +}; + +/* state machine trigger causes events */ +#define IPC_SM_RUN_NONE (0x00) +#define IPC_SM_RUN_SLAVE_IRQ (0x01) +#define IPC_SM_RUN_TFR_COMPLETE (0x02) +#define IPC_SM_RUN_TX_REQ (0x04) +#define IPC_SM_RUN_INIT (0x08) +#define IPC_SM_RUN_ABORT (0x10) +#define IPC_SM_RUN_COMMS_TMO (0x20) +#define IPC_SM_RUN_STABLE_TMO (0x40) +#define IPC_SM_RUN_RESET (0x80) + +struct ipc_link_context; /* forward declaration */ + +typedef u8 (*ipc_sm_enter_func)(u8 event, struct ipc_link_context *context); +typedef const struct ipc_sm_state *(*ipc_sm_exit_func)(u8 event, + struct ipc_link_context *context); + +struct ipc_sm_state { + enum ipc_sm_state_id id; + ipc_sm_enter_func enter; + ipc_sm_exit_func exit; + u8 events; +}; + +#endif /* _MODEM_STATEMACHINE_H_ */ diff --git a/drivers/modem/m6718_spi/protocol.c b/drivers/modem/m6718_spi/protocol.c new file mode 100644 index 00000000000..5f03167d024 --- /dev/null +++ b/drivers/modem/m6718_spi/protocol.c @@ -0,0 +1,287 @@ +/* + * Copyright (C) ST-Ericsson SA 2010,2011 + * + * Author: Chris Blair for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * U9500 <-> M6718 IPC protocol implementation using SPI. + */ +#include +#include "modem_protocol.h" +#include "modem_private.h" + +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE +#include +#include "modem_state.h" + +#define MODEM_STATE_REGISTER_TMO_MS (500) +#endif + +#ifdef WORKAROUND_DUPLICATED_IRQ +#include +#endif + +struct l2mux_channel { + u8 open:1; + u8 link:7; +}; + +/* valid open L2 mux channels */ +static const struct l2mux_channel channels[255] = { + [MODEM_M6718_SPI_CHN_ISI] = { + .open = true, + .link = IPC_LINK_COMMON + }, + [MODEM_M6718_SPI_CHN_AUDIO] = { + .open = true, + .link = IPC_LINK_AUDIO + }, + [MODEM_M6718_SPI_CHN_MASTER_LOOPBACK0] = { + .open = true, + .link = IPC_LINK_COMMON + }, + [MODEM_M6718_SPI_CHN_SLAVE_LOOPBACK0] = { + .open = true, + .link = IPC_LINK_COMMON + }, + [MODEM_M6718_SPI_CHN_MASTER_LOOPBACK1] = { + .open = true, + .link = IPC_LINK_AUDIO + }, + [MODEM_M6718_SPI_CHN_SLAVE_LOOPBACK1] = { + .open = true, + .link = IPC_LINK_AUDIO + } +}; + +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE +static void modem_state_reg_wq(struct work_struct *work); +static DECLARE_DELAYED_WORK(modem_state_reg_work, modem_state_reg_wq); +#endif + +/* the spi driver context */ +struct ipc_l1_context l1_context = { +#ifdef CONFIG_DEBUG_FS + .msr_disable = false, +#endif + .init_done = false +}; + +bool modem_protocol_channel_is_open(u8 channel) +{ + return channels[channel].open; +} + +void modem_comms_timeout(unsigned long data) +{ + /* TODO: statemachine will be kicked here */ +} + +void slave_stable_timeout(unsigned long data) +{ + /* TODO: statemachine will be kicked here */ +} + +/** + * modem_protocol_init() - initialise the IPC protocol + * + * Initialises the IPC protocol in preparation for use. After this is called + * the protocol is ready to be probed for each link to be supported. + */ +void modem_protocol_init(void) +{ + pr_info("M6718 IPC protocol initialising version %02x\n", + IPC_DRIVER_VERSION); + + atomic_set(&l1_context.boot_sync_done, 0); + l1_context.init_done = true; +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE + schedule_delayed_work(&modem_state_reg_work, 0); +#endif +} + +/** + * modem_m6718_spi_send() - send a frame using the IPC protocol + * @modem_spi_dev: pointer to modem driver information structure + * @channel: L2 channel to send on + * @len: length of data to send + * @data: pointer to buffer containing data + * + * Check that the requested channel is supported and open, queue a frame + * containing the data on the appropriate link and ensure the state machine + * is running to start the transfer. + */ +int modem_m6718_spi_send(struct modem_spi_dev *modem_spi_dev, u8 channel, + u32 len, void *data) +{ + struct ipc_link_context *context; + + if (!channels[channel].open) { + dev_err(modem_spi_dev->dev, + "error: invalid channel (%d), discarding frame\n", + channel); + return -EINVAL; + } + + context = &l1_context.device_context[channels[channel].link]; + if (context->state == NULL || context->state->id == IPC_SM_HALT) { + static unsigned long linkfail_warn_time; + if (printk_timed_ratelimit(&linkfail_warn_time, 60 * 1000)) + dev_err(modem_spi_dev->dev, + "error: link %d for ch %d is not available, " + "discarding frames\n", + channels[channel].link, channel); + return -ENODEV; + } + + /* TODO: statemachine will be kicked here */ + return 0; +} +EXPORT_SYMBOL_GPL(modem_m6718_spi_send); + +/** + * modem_m6718_spi_is_boot_done() - check if boot handshake with modem is done + */ +bool modem_m6718_spi_is_boot_done(void) +{ + return atomic_read(&l1_context.boot_sync_done); +} +EXPORT_SYMBOL_GPL(modem_m6718_spi_is_boot_done); + +/** + * modem_protocol_is_busy() - check if the protocol is currently active + * @sdev: pointer to spi_device for link to check + * + * Checks each of the IPC links to see if they are inactive: this means they + * can be in either IDLE or INIT states. If any of the links are not idle then + * true is returned to indicate that the protocol is busy. + */ +bool modem_protocol_is_busy(struct spi_device *sdev) +{ + int i; + + for (i = 0; i < IPC_NBR_SUPPORTED_SPI_LINKS; i++) + switch (l1_context.device_context[i].state->id) { + case IPC_SM_IDL: + case IPC_SM_IDL_AUD: + case IPC_SM_INIT: + case IPC_SM_INIT_AUD: + case IPC_SM_WAIT_SLAVE_STABLE: + /* not busy; continue checking */ + break; + default: + dev_info(&sdev->dev, "link %d is busy\n", i); + return true; + } + return false; +} + +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE +static int modem_state_callback(unsigned long unused) +{ + int modem_state = modem_state_get_state(); + + pr_info("M6718 IPC protocol modemstate reports modem is %s\n", + modem_state_to_str(modem_state)); + + switch (modem_state) { + case MODEM_STATE_ON: + /* TODO: statemachine will be kicked here */ + break; + case MODEM_STATE_OFF: + case MODEM_STATE_RESET: + case MODEM_STATE_CRASH: + /* TODO: statemachine will be kicked here */ + break; + default: + break; + } + return 0; +} + +static void modem_state_reg_wq(struct work_struct *work) +{ + if (modem_state_register_callback(modem_state_callback, 0) == -EAGAIN) { + pr_info("M6718 IPC protocol failed to register with " + "modemstate, will retry\n"); + schedule_delayed_work(&modem_state_reg_work, + (MODEM_STATE_REGISTER_TMO_MS * HZ) / 1000); + } else { + pr_info("M6718 IPC protocol registered with modemstate\n"); + } +} +#endif + +int modem_protocol_probe(struct spi_device *sdev) +{ + struct modem_m6718_spi_link_platform_data *link = + sdev->dev.platform_data; + struct ipc_link_context *context; + int link_id; + + if (link == NULL) { + /* platform data missing in board config? */ + dev_err(&sdev->dev, "error: no platform data for link!\n"); + return -ENODEV; + } + + link_id = link->id; + context = &l1_context.device_context[link_id]; + + if (link_id >= IPC_NBR_SUPPORTED_SPI_LINKS) { + dev_err(&sdev->dev, + "link %d error: too many links! (max %d)\n", + link->id, IPC_NBR_SUPPORTED_SPI_LINKS); + return -ENODEV; + } + + dev_info(&sdev->dev, + "link %d: registering SPI link bus:%d cs:%d\n", + link->id, sdev->master->bus_num, sdev->chip_select); + + /* update spi device with correct word size for our device */ + sdev->bits_per_word = 16; + spi_setup(sdev); + + /* init link context */ + context->link = link; + context->sdev = sdev; + atomic_set(&context->gpio_configured, 0); + spin_lock_init(&context->sm_lock); + init_timer(&context->comms_timer); + context->comms_timer.function = modem_comms_timeout; + context->comms_timer.data = (unsigned long)context; + init_timer(&context->slave_stable_timer); + context->slave_stable_timer.function = slave_stable_timeout; + context->slave_stable_timer.data = (unsigned long)context; + +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_VERIFY_FRAMES + context->last_frame = NULL; +#endif + + /* + * For link0 (the handshake link) we force a state transition now so + * that it prepares for boot sync. + */ + /* TODO: statemachine will be kicked here */ + + /* + * unlikely but possible: for links other than 0, check if handshake is + * already complete by the time this link is probed - if so we force a + * state transition since the one issued by the handshake exit actions + * will have been ignored. + */ + if (link->id > 0 && atomic_read(&l1_context.boot_sync_done)) { + dev_dbg(&sdev->dev, + "link %d: boot sync is done, kicking state machine\n", + link->id); + /* TODO: statemachine will be kicked here */ + } + return 0; +} + +void modem_protocol_exit(void) +{ + pr_info("M6718 IPC protocol exit\n"); +} diff --git a/include/linux/modem/m6718_spi/modem_driver.h b/include/linux/modem/m6718_spi/modem_driver.h index 8c5209c228c..f3aae4a7116 100644 --- a/include/linux/modem/m6718_spi/modem_driver.h +++ b/include/linux/modem/m6718_spi/modem_driver.h @@ -128,8 +128,38 @@ struct modem_spi_dev { bool msr_flag; }; -/* function exported for L1 to call with received frames */ +/** + * struct modem_m6718_spi_link_gpio - gpio configuration for an IPC link + * @ss_pin: pins to use for slave-select + * @ss_active: active level for slave-select pin + * @int_pin: pin to use for slave-int (ready) + * @int_active: active level for slave-int + */ +struct modem_m6718_spi_link_gpio { + int ss_pin; + int ss_active; + int int_pin; + int int_active; +}; + +/** + * struct modem_m6718_spi_link_platform_data - IPC link data + * @id: link id + * @gpio: link gpio configuration + * @name: link name (to appear in debugfs) + */ +struct modem_m6718_spi_link_platform_data { + int id; + struct modem_m6718_spi_link_gpio gpio; +#ifdef CONFIG_DEBUG_FS + const char *name; +#endif +}; + int modem_m6718_spi_receive(struct spi_device *sdev, u8 channel, u32 len, void *data); +int modem_m6718_spi_send(struct modem_spi_dev *modem_spi_dev, u8 channel, + u32 len, void *data); +bool modem_m6718_spi_is_boot_done(void); #endif /* _MODEM_DRIVER_H_ */ diff --git a/include/linux/modem/m6718_spi/modem_state.h b/include/linux/modem/m6718_spi/modem_state.h deleted file mode 100644 index a2f1d9fbe3e..00000000000 --- a/include/linux/modem/m6718_spi/modem_state.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2011 - * - * Author: Derek Morton - * - * License terms: GNU General Public License (GPL) version 2 - * - * Power state driver for M6718 MODEM - */ -#ifndef MODEM_STATE_H -#define MODEM_STATE_H - -enum modem_states { - MODEM_STATE_OFF, - MODEM_STATE_RESET, - MODEM_STATE_CRASH, - MODEM_STATE_ON, - /* - * Add new states before end marker and update modem_state_str[] - * in modem_state.c - */ - MODEM_STATE_END_MARKER -}; - -void modem_state_power_on(void); -void modem_state_power_off(void); -void modem_state_force_reset(void); -int modem_state_get_state(void); -char *modem_state_to_str(int state); - -/* Callbacks will be running in tasklet context */ -int modem_state_register_callback(int (*callback) (unsigned long), - unsigned long data); -int modem_state_remove_callback(int (*callback) (unsigned long)); - -#endif -- cgit v1.2.3 From fcdbaf8bcdd4200b1be748fa0130f2d34a34201d Mon Sep 17 00:00:00 2001 From: Chris Blair Date: Sat, 19 Nov 2011 07:54:35 +0000 Subject: modem: Add M6718 IPC SPI driver utilities Adds utility functions to be used by the IPC protocol. ST-Ericsson ID: 369397 ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-12224 ST-Ericsson Linux next: NA Change-Id: I27a1329b980f69671c7ae7f2c975ade80d6aa409 Signed-off-by: Chris Blair Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36479 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Jonas ABERG --- drivers/modem/m6718_spi/Makefile | 2 +- drivers/modem/m6718_spi/modem_util.h | 53 +++++++ drivers/modem/m6718_spi/protocol.c | 50 +++++++ drivers/modem/m6718_spi/util.c | 268 +++++++++++++++++++++++++++++++++++ 4 files changed, 372 insertions(+), 1 deletion(-) create mode 100644 drivers/modem/m6718_spi/modem_util.h create mode 100644 drivers/modem/m6718_spi/util.c diff --git a/drivers/modem/m6718_spi/Makefile b/drivers/modem/m6718_spi/Makefile index 66bad09c0dd..ea14319ad7d 100644 --- a/drivers/modem/m6718_spi/Makefile +++ b/drivers/modem/m6718_spi/Makefile @@ -5,7 +5,7 @@ ifeq ($(CONFIG_MODEM_M6718_SPI_DEBUG),y) EXTRA_CFLAGS += -DDEBUG endif -m6718_modem_spi-objs := modem_driver.o protocol.o +m6718_modem_spi-objs := modem_driver.o protocol.o util.o ifeq ($(CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE),y) m6718_modem_spi-objs += modem_state.o diff --git a/drivers/modem/m6718_spi/modem_util.h b/drivers/modem/m6718_spi/modem_util.h new file mode 100644 index 00000000000..3219900a3c8 --- /dev/null +++ b/drivers/modem/m6718_spi/modem_util.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Chris Blair for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * Modem IPC driver protocol interface header: + * utility functionality. + */ +#ifndef _MODEM_UTIL_H_ +#define _MODEM_UTIL_H_ + +#include +#include "modem_private.h" + +bool ipc_util_channel_is_loopback(u8 channel); + +u32 ipc_util_make_l2_header(u8 channel, u32 len); +u8 ipc_util_get_l2_channel(u32 hdr); +u32 ipc_util_get_l2_length(u32 hdr); +u32 ipc_util_make_l1_header(u8 cmd, u8 counter, u32 len); +u8 ipc_util_get_l1_cmd(u32 hdr); +u8 ipc_util_get_l1_counter(u32 hdr); +u32 ipc_util_get_l1_length(u32 hdr); +u8 ipc_util_get_l1_bootresp_ver(u32 bootresp); + +int ipc_util_ss_level_active(struct ipc_link_context *context); +int ipc_util_ss_level_inactive(struct ipc_link_context *context); +int ipc_util_int_level_active(struct ipc_link_context *context); +int ipc_util_int_level_inactive(struct ipc_link_context *context); + +void ipc_util_deactivate_ss(struct ipc_link_context *context); +void ipc_util_activate_ss(struct ipc_link_context *context); +void ipc_util_activate_ss_with_tmo(struct ipc_link_context *context); + +bool ipc_util_int_is_active(struct ipc_link_context *context); + +bool ipc_util_link_is_idle(struct ipc_link_context *context); + +void ipc_util_start_slave_stable_timer(struct ipc_link_context *context); + +void ipc_util_spi_message_prepare(struct ipc_link_context *link_context, + void *tx_buf, void *rx_buf, int len); +void ipc_util_spi_message_init(struct ipc_link_context *link_context, + void (*complete)(void *)); + +bool ipc_util_link_gpio_request(struct ipc_link_context *context, + irqreturn_t (*irqhnd)(int, void *)); +bool ipc_util_link_gpio_config(struct ipc_link_context *context); +bool ipc_util_link_gpio_unconfig(struct ipc_link_context *context); + +#endif /* _MODEM_UTIL_H_ */ diff --git a/drivers/modem/m6718_spi/protocol.c b/drivers/modem/m6718_spi/protocol.c index 5f03167d024..6879d58798b 100644 --- a/drivers/modem/m6718_spi/protocol.c +++ b/drivers/modem/m6718_spi/protocol.c @@ -10,6 +10,7 @@ #include #include "modem_protocol.h" #include "modem_private.h" +#include "modem_util.h" #ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE #include @@ -177,16 +178,53 @@ bool modem_protocol_is_busy(struct spi_device *sdev) return false; } +static void spi_tfr_complete(void *context) +{ + /* TODO: statemachine will be kicked here */ +} + +static irqreturn_t slave_ready_irq(int irq, void *dev) +{ + struct ipc_link_context *context = (struct ipc_link_context *)dev; + struct modem_m6718_spi_link_platform_data *link = context->link; + struct spi_device *sdev = context->sdev; + + if (irq != GPIO_TO_IRQ(link->gpio.int_pin)) { + dev_err(&sdev->dev, + "link %d error: spurious slave irq!", link->id); + return IRQ_NONE; + } + +#ifdef WORKAROUND_DUPLICATED_IRQ + if (link->id != IPC_LINK_AUDIO && pl022_tfr_in_progress(sdev)) { + dev_warn(&sdev->dev, + "link %d warning: slave irq while transfer " + "is active! discarding event\n", link->id); + return IRQ_HANDLED; + } +#endif + /* TODO: statemachine will be kicked here */ + return IRQ_HANDLED; +} + #ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE static int modem_state_callback(unsigned long unused) { int modem_state = modem_state_get_state(); + struct ipc_link_context *contexts = l1_context.device_context; + u8 i; pr_info("M6718 IPC protocol modemstate reports modem is %s\n", modem_state_to_str(modem_state)); switch (modem_state) { case MODEM_STATE_ON: + /* + * Modem is on, ensure each link is configured and trigger + * a state change on link0 to begin handshake. + */ + for (i = 0; i < IPC_NBR_SUPPORTED_SPI_LINKS; i++) + ipc_util_link_gpio_config(&contexts[i]); /* TODO: statemachine will be kicked here */ break; case MODEM_STATE_OFF: @@ -248,7 +286,10 @@ int modem_protocol_probe(struct spi_device *sdev) context->link = link; context->sdev = sdev; atomic_set(&context->gpio_configured, 0); + atomic_set(&context->state_int, + ipc_util_int_level_inactive(context)); spin_lock_init(&context->sm_lock); + ipc_util_spi_message_init(context, spi_tfr_complete); init_timer(&context->comms_timer); context->comms_timer.function = modem_comms_timeout; context->comms_timer.data = (unsigned long)context; @@ -256,6 +297,11 @@ int modem_protocol_probe(struct spi_device *sdev) context->slave_stable_timer.function = slave_stable_timeout; context->slave_stable_timer.data = (unsigned long)context; + if (!ipc_util_link_gpio_request(context, slave_ready_irq)) + return -ENODEV; + if (!ipc_util_link_gpio_config(context)) + return -ENODEV; + #ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_VERIFY_FRAMES context->last_frame = NULL; #endif @@ -283,5 +329,9 @@ int modem_protocol_probe(struct spi_device *sdev) void modem_protocol_exit(void) { + int i; + pr_info("M6718 IPC protocol exit\n"); + for (i = 0; i < IPC_NBR_SUPPORTED_SPI_LINKS; i++) + ipc_util_link_gpio_unconfig(&l1_context.device_context[i]); } diff --git a/drivers/modem/m6718_spi/util.c b/drivers/modem/m6718_spi/util.c new file mode 100644 index 00000000000..43862baff99 --- /dev/null +++ b/drivers/modem/m6718_spi/util.c @@ -0,0 +1,268 @@ +/* + * Copyright (C) ST-Ericsson SA 2010,2011 + * + * Author: Chris Blair for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * U9500 <-> M6718 IPC protocol implementation using SPI: + * utility functions. + */ +#include +#include +#include "modem_util.h" + +#define MODEM_COMMS_TMO_MS (5000) /* 0 == no timeout */ +#define SLAVE_STABLE_TMO_MS (1000) + +#define DRIVER_NAME "ipcspi" /* name used when reserving gpio pins */ + + +bool ipc_util_channel_is_loopback(u8 channel) +{ + return channel == MODEM_M6718_SPI_CHN_MASTER_LOOPBACK0 || + channel == MODEM_M6718_SPI_CHN_MASTER_LOOPBACK1; +} + +u32 ipc_util_make_l2_header(u8 channel, u32 len) +{ + return ((channel & 0xf) << 28) | (len & 0x000fffff); +} + +u8 ipc_util_get_l2_channel(u32 hdr) +{ + return hdr >> 28; +} + +u32 ipc_util_get_l2_length(u32 hdr) +{ + return hdr & 0x000fffff; +} + +u32 ipc_util_make_l1_header(u8 cmd, u8 counter, u32 len) +{ + return (cmd << 28) | + ((counter & 0x000000ff) << 20) | + (len & 0x000fffff); +} + +u8 ipc_util_get_l1_cmd(u32 hdr) +{ + return hdr >> 28; +} + +u8 ipc_util_get_l1_counter(u32 hdr) +{ + return (hdr >> 20) & 0x000000ff; +} + +u32 ipc_util_get_l1_length(u32 hdr) +{ + return hdr & 0x000fffff; +} + +u8 ipc_util_get_l1_bootresp_ver(u32 bootresp) +{ + return bootresp & 0x000000ff; +} + +int ipc_util_ss_level_active(struct ipc_link_context *context) +{ + return context->link->gpio.ss_active == 0 ? 0 : 1; +} + +int ipc_util_ss_level_inactive(struct ipc_link_context *context) +{ + return !ipc_util_ss_level_active(context); +} + +int ipc_util_int_level_active(struct ipc_link_context *context) +{ + return context->link->gpio.int_active == 0 ? 0 : 1; +} + +int ipc_util_int_level_inactive(struct ipc_link_context *context) +{ + return !ipc_util_int_level_active(context); +} + +void ipc_util_deactivate_ss(struct ipc_link_context *context) +{ + gpio_set_value(context->link->gpio.ss_pin, + ipc_util_ss_level_inactive(context)); + + dev_dbg(&context->sdev->dev, + "link %d: deactivated SS\n", context->link->id); +} + +void ipc_util_activate_ss(struct ipc_link_context *context) +{ + gpio_set_value(context->link->gpio.ss_pin, + ipc_util_ss_level_active(context)); + + dev_dbg(&context->sdev->dev, + "link %d: activated SS\n", context->link->id); +} + +void ipc_util_activate_ss_with_tmo(struct ipc_link_context *context) +{ + gpio_set_value(context->link->gpio.ss_pin, + ipc_util_ss_level_active(context)); + +#if MODEM_COMMS_TMO_MS == 0 + dev_dbg(&context->sdev->dev, + "link %d: activated SS (timeout is disabled)\n", + context->link->id); +#else + context->comms_timer.expires = jiffies + + ((MODEM_COMMS_TMO_MS * HZ) / 1000); + add_timer(&context->comms_timer); + + dev_dbg(&context->sdev->dev, + "link %d: activated SS with timeout\n", context->link->id); +#endif +} + +bool ipc_util_int_is_active(struct ipc_link_context *context) +{ + return gpio_get_value(context->link->gpio.int_pin) == + ipc_util_int_level_active(context); +} + +bool ipc_util_link_is_idle(struct ipc_link_context *context) +{ + if (context->state == NULL) + return false; + + switch (context->state->id) { + case IPC_SM_IDL: + case IPC_SM_IDL_AUD: + return true; + default: + return false; + } +} + +void ipc_util_start_slave_stable_timer(struct ipc_link_context *context) +{ + context->slave_stable_timer.expires = + jiffies + ((SLAVE_STABLE_TMO_MS * HZ) / 1000); + add_timer(&context->slave_stable_timer); +} + +void ipc_util_spi_message_prepare(struct ipc_link_context *link_context, + void *tx_buf, void *rx_buf, int len) +{ + struct spi_transfer *tfr = &link_context->spi_transfer; + struct spi_message *msg = &link_context->spi_message; + + tfr->tx_buf = tx_buf; + tfr->rx_buf = rx_buf; + tfr->len = len; + msg->context = link_context; +} + +void ipc_util_spi_message_init(struct ipc_link_context *link_context, + void (*complete)(void *)) +{ + struct spi_message *msg = &link_context->spi_message; + struct spi_transfer *tfr = &link_context->spi_transfer; + + tfr->bits_per_word = 16; + + /* common init of transfer - use default from board device */ + tfr->cs_change = 0; + tfr->speed_hz = 0; + tfr->delay_usecs = 0; + + /* common init of message */ + spi_message_init(msg); + msg->spi = link_context->sdev; + msg->complete = complete; + spi_message_add_tail(tfr, msg); +} + +bool ipc_util_link_gpio_request(struct ipc_link_context *context, + irqreturn_t (*irqhnd)(int, void*)) +{ + struct spi_device *sdev = context->sdev; + struct modem_m6718_spi_link_platform_data *link = context->link; + unsigned long irqflags; + + if (gpio_request(link->gpio.ss_pin, DRIVER_NAME) < 0) { + dev_err(&sdev->dev, + "link %d error: failed to get gpio %d for SS pin\n", + link->id, + link->gpio.ss_pin); + return false; + } + if (gpio_request(link->gpio.int_pin, DRIVER_NAME) < 0) { + dev_err(&sdev->dev, + "link %d error: failed to get gpio %d for INT pin\n", + link->id, + link->gpio.int_pin); + return false; + } + + if (ipc_util_int_level_active(context) == 1) + irqflags = IRQF_TRIGGER_RISING; + else + irqflags = IRQF_TRIGGER_FALLING; + + if (request_irq(GPIO_TO_IRQ(link->gpio.int_pin), + irqhnd, + irqflags, + DRIVER_NAME, + context) < 0) { + dev_err(&sdev->dev, + "link %d error: could not get irq %d\n", + link->id, GPIO_TO_IRQ(link->gpio.int_pin)); + return false; + } + return true; +} + +bool ipc_util_link_gpio_config(struct ipc_link_context *context) +{ + struct spi_device *sdev = context->sdev; + struct modem_m6718_spi_link_platform_data *link = context->link; + + if (atomic_read(&context->gpio_configured) == 1) + return true; + + dev_dbg(&sdev->dev, "link %d: configuring GPIO\n", link->id); + + ipc_util_deactivate_ss(context); + gpio_direction_input(link->gpio.int_pin); + if (enable_irq_wake(GPIO_TO_IRQ(link->gpio.int_pin)) < 0) { + dev_err(&sdev->dev, + "link %d error: failed to enable wake on INT\n", + link->id); + return false; + } + + atomic_set(&context->state_int, gpio_get_value(link->gpio.int_pin)); + atomic_set(&context->gpio_configured, 1); + return true; +} + +bool ipc_util_link_gpio_unconfig(struct ipc_link_context *context) +{ + struct spi_device *sdev = context->sdev; + struct modem_m6718_spi_link_platform_data *link = context->link; + + if (atomic_read(&context->gpio_configured) == 0) + return true; + + dev_dbg(&sdev->dev, "link %d: un-configuring GPIO\n", link->id); + + /* SS: output anyway, just make sure it is low */ + gpio_set_value(link->gpio.ss_pin, 0); + + /* INT: disable system-wake, reconfigure as output-low */ + disable_irq_wake(GPIO_TO_IRQ(link->gpio.int_pin)); + gpio_direction_output(link->gpio.int_pin, 0); + atomic_set(&context->gpio_configured, 0); + return true; +} + -- cgit v1.2.3 From 49d1710b8557e3c1cd0e243a3574eabe5cc49d5d Mon Sep 17 00:00:00 2001 From: Chris Blair Date: Sat, 19 Nov 2011 08:04:34 +0000 Subject: modem: Add M6718 IPC SPI driver queue functions Adds the IPC driver TX queue related functionality ST-Ericsson ID: 369397 ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-12224 ST-Ericsson Linux next: NA Change-Id: I09982aea887877b7793e17edb250d2c9ea9b2996 Signed-off-by: Chris Blair Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36484 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Jonas ABERG --- drivers/modem/m6718_spi/Makefile | 2 +- drivers/modem/m6718_spi/modem_queue.h | 24 +++++ drivers/modem/m6718_spi/protocol.c | 17 +++- drivers/modem/m6718_spi/queue.c | 183 ++++++++++++++++++++++++++++++++++ 4 files changed, 224 insertions(+), 2 deletions(-) create mode 100644 drivers/modem/m6718_spi/modem_queue.h create mode 100644 drivers/modem/m6718_spi/queue.c diff --git a/drivers/modem/m6718_spi/Makefile b/drivers/modem/m6718_spi/Makefile index ea14319ad7d..60e8adbbb81 100644 --- a/drivers/modem/m6718_spi/Makefile +++ b/drivers/modem/m6718_spi/Makefile @@ -5,7 +5,7 @@ ifeq ($(CONFIG_MODEM_M6718_SPI_DEBUG),y) EXTRA_CFLAGS += -DDEBUG endif -m6718_modem_spi-objs := modem_driver.o protocol.o util.o +m6718_modem_spi-objs := modem_driver.o protocol.o util.o queue.o ifeq ($(CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE),y) m6718_modem_spi-objs += modem_state.o diff --git a/drivers/modem/m6718_spi/modem_queue.h b/drivers/modem/m6718_spi/modem_queue.h new file mode 100644 index 00000000000..62604129945 --- /dev/null +++ b/drivers/modem/m6718_spi/modem_queue.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Chris Blair for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * Modem IPC driver protocol interface header: + * queue functionality. + */ +#ifndef _MODEM_QUEUE_H_ +#define _MODEM_QUEUE_H_ + +void ipc_queue_init(struct ipc_link_context *context); +void ipc_queue_delete_frame(struct ipc_tx_queue *frame); +struct ipc_tx_queue *ipc_queue_new_frame(struct ipc_link_context *link_context, + u32 l2_length); +bool ipc_queue_is_empty(struct ipc_link_context *context); +int ipc_queue_push_frame(struct ipc_link_context *link_context, u8 l2_header, + u32 l2_length, void *l2_data); +struct ipc_tx_queue *ipc_queue_get_frame(struct ipc_link_context *context); +void ipc_queue_reset(struct ipc_link_context *context); + +#endif /* _MODEM_QUEUE_H_ */ diff --git a/drivers/modem/m6718_spi/protocol.c b/drivers/modem/m6718_spi/protocol.c index 6879d58798b..046157dcd5e 100644 --- a/drivers/modem/m6718_spi/protocol.c +++ b/drivers/modem/m6718_spi/protocol.c @@ -11,6 +11,7 @@ #include "modem_protocol.h" #include "modem_private.h" #include "modem_util.h" +#include "modem_queue.h" #ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE #include @@ -116,6 +117,7 @@ void modem_protocol_init(void) int modem_m6718_spi_send(struct modem_spi_dev *modem_spi_dev, u8 channel, u32 len, void *data) { + int err; struct ipc_link_context *context; if (!channels[channel].open) { @@ -136,7 +138,18 @@ int modem_m6718_spi_send(struct modem_spi_dev *modem_spi_dev, u8 channel, return -ENODEV; } - /* TODO: statemachine will be kicked here */ + err = ipc_queue_push_frame(context, channel, len, data); + if (err < 0) + return err; + + if (ipc_util_link_is_idle(context)) { + dev_dbg(modem_spi_dev->dev, + "link %d is idle, kicking\n", channels[channel].link); + /* TODO: statemachine will be kicked here */ + } else { + dev_dbg(modem_spi_dev->dev, + "link %d is already running\n", channels[channel].link); + } return 0; } EXPORT_SYMBOL_GPL(modem_m6718_spi_send); @@ -306,6 +319,8 @@ int modem_protocol_probe(struct spi_device *sdev) context->last_frame = NULL; #endif + ipc_queue_init(context); + /* * For link0 (the handshake link) we force a state transition now so * that it prepares for boot sync. diff --git a/drivers/modem/m6718_spi/queue.c b/drivers/modem/m6718_spi/queue.c new file mode 100644 index 00000000000..911d538ee82 --- /dev/null +++ b/drivers/modem/m6718_spi/queue.c @@ -0,0 +1,183 @@ +/* + * Copyright (C) ST-Ericsson SA 2010,2011 + * + * Author: Chris Blair for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * U9500 <-> M6718 IPC protocol implementation using SPI: + * TX queue functionality. + */ +#include +#include "modem_util.h" + +#define FRAME_LENGTH_ALIGN (4) +#define MAX_FRAME_COUNTER (256) + +/* fixed L1 frame size for audio link: 4 byte L2 header + 664 byte L2 payload */ +#define FRAME_SIZE_AUDIO (668) + +void ipc_queue_init(struct ipc_link_context *context) +{ + spin_lock_init(&context->tx_q_update_lock); + atomic_set(&context->tx_q_count, 0); + context->tx_q_free = IPC_TX_QUEUE_MAX_SIZE; + INIT_LIST_HEAD(&context->tx_q); + context->tx_frame_counter = 0; +} + +void ipc_queue_delete_frame(struct ipc_tx_queue *frame) +{ + kfree(frame); +} + +struct ipc_tx_queue *ipc_queue_new_frame(struct ipc_link_context *link_context, + u32 l2_length) +{ + struct ipc_tx_queue *frame; + u32 padded_len = l2_length; + + /* audio link frames are always a fixed size */ + if (link_context->link->id == IPC_LINK_AUDIO) { + if (l2_length > FRAME_SIZE_AUDIO) { + dev_err(&link_context->sdev->dev, + "link %d error: invalid frame size %d " + "requested, max is %d\n", + link_context->link->id, + l2_length, + FRAME_SIZE_AUDIO); + return NULL; + } + padded_len = FRAME_SIZE_AUDIO; + } else { + /* frame length padded to alignment boundary */ + if (padded_len % FRAME_LENGTH_ALIGN) + padded_len += (FRAME_LENGTH_ALIGN - + (padded_len % FRAME_LENGTH_ALIGN)); + } + + dev_dbg(&link_context->sdev->dev, + "link %d: new frame: length %d, padded to %d\n", + link_context->link->id, l2_length, padded_len); + + frame = kzalloc(sizeof(*frame) + padded_len, GFP_ATOMIC); + if (frame == NULL) { + dev_err(&link_context->sdev->dev, + "link %d error: failed to allocate frame\n", + link_context->link->id); + return NULL; + } + + frame->actual_len = l2_length; + frame->len = padded_len; + frame->data = frame + 1; + return frame; +} + +bool ipc_queue_is_empty(struct ipc_link_context *context) +{ + unsigned long flags; + bool empty; + + spin_lock_irqsave(&context->tx_q_update_lock, flags); + empty = list_empty(&context->tx_q); + spin_unlock_irqrestore(&context->tx_q_update_lock, flags); + + return empty; +} + +int ipc_queue_push_frame(struct ipc_link_context *context, u8 channel, + u32 length, void *data) +{ + u32 l2_hdr; + unsigned long flags; + struct ipc_tx_queue *frame; + int *tx_frame_counter = &context->tx_frame_counter; + int qcount; + + /* + * Max queue size is only approximate so we allow it to go a few bytes + * over the limit + */ + if (context->tx_q_free < length) { + dev_dbg(&context->sdev->dev, + "link %d: tx queue full, wanted %d free %d\n", + context->link->id, + length, + context->tx_q_free); + return -EAGAIN; + } + + frame = ipc_queue_new_frame(context, length + IPC_L2_HDR_SIZE); + if (frame == NULL) + return -ENOMEM; + + /* create l2 header and copy to pdu buffer */ + l2_hdr = ipc_util_make_l2_header(channel, length); + *(u32 *)frame->data = l2_hdr; + + /* copy the l2 sdu into the pdu buffer after the header */ + memcpy(frame->data + IPC_L2_HDR_SIZE, data, length); + + spin_lock_irqsave(&context->tx_q_update_lock, flags); + frame->counter = *tx_frame_counter; + *tx_frame_counter = (*tx_frame_counter + 1) % MAX_FRAME_COUNTER; + list_add_tail(&frame->node, &context->tx_q); + qcount = atomic_add_return(1, &context->tx_q_count); + /* tx_q_free could go negative here */ + context->tx_q_free -= frame->len; +#ifdef CONFIG_DEBUG_FS + context->tx_q_min = min(context->tx_q_free, context->tx_q_min); +#endif + spin_unlock_irqrestore(&context->tx_q_update_lock, flags); + + dev_dbg(&context->sdev->dev, + "link %d: push tx frame %d: %08x (ch %d len %d), " + "new count %d, new free %d\n", + context->link->id, + frame->counter, + l2_hdr, + ipc_util_get_l2_channel(l2_hdr), + ipc_util_get_l2_length(l2_hdr), + qcount, + context->tx_q_free); + return 0; +} + +struct ipc_tx_queue *ipc_queue_get_frame(struct ipc_link_context *context) +{ + unsigned long flags; + struct ipc_tx_queue *frame; + int qcount; + + spin_lock_irqsave(&context->tx_q_update_lock, flags); + frame = list_first_entry(&context->tx_q, struct ipc_tx_queue, node); + list_del(&frame->node); + qcount = atomic_sub_return(1, &context->tx_q_count); + context->tx_q_free += frame->len; + spin_unlock_irqrestore(&context->tx_q_update_lock, flags); + + dev_dbg(&context->sdev->dev, + "link %d: get tx frame %d, new count %d, " + "new free %d\n", + context->link->id, frame->counter, qcount, context->tx_q_free); + return frame; +} + +void ipc_queue_reset(struct ipc_link_context *context) +{ + unsigned long flags; + struct ipc_tx_queue *frame; + int qcount; + + spin_lock_irqsave(&context->tx_q_update_lock, flags); + qcount = atomic_read(&context->tx_q_count); + while (qcount != 0) { + frame = list_first_entry(&context->tx_q, + struct ipc_tx_queue, node); + list_del(&frame->node); + ipc_queue_delete_frame(frame); + qcount = atomic_sub_return(1, &context->tx_q_count); + } + spin_unlock_irqrestore(&context->tx_q_update_lock, flags); +} -- cgit v1.2.3 From 193d466619010b2bbc39ecc210b75310ce39a75c Mon Sep 17 00:00:00 2001 From: Chris Blair Date: Sat, 19 Nov 2011 08:12:40 +0000 Subject: modem: Add M6718 IPC SPI driver debug functions Adds the debug related functionality for the modem driver. ST-Ericsson ID: 369397 ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-12224 ST-Ericsson Linux next: NA Change-Id: I1b1144dd01b574410a86d34169e01621f56271cb Signed-off-by: Chris Blair Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36486 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Jonas ABERG --- drivers/modem/m6718_spi/Makefile | 2 +- drivers/modem/m6718_spi/debug.c | 490 ++++++++++++++++++++++++++++++++ drivers/modem/m6718_spi/modem_debug.h | 36 +++ drivers/modem/m6718_spi/modem_private.h | 2 + drivers/modem/m6718_spi/protocol.c | 6 + 5 files changed, 535 insertions(+), 1 deletion(-) create mode 100644 drivers/modem/m6718_spi/debug.c create mode 100644 drivers/modem/m6718_spi/modem_debug.h diff --git a/drivers/modem/m6718_spi/Makefile b/drivers/modem/m6718_spi/Makefile index 60e8adbbb81..b3898c62662 100644 --- a/drivers/modem/m6718_spi/Makefile +++ b/drivers/modem/m6718_spi/Makefile @@ -5,7 +5,7 @@ ifeq ($(CONFIG_MODEM_M6718_SPI_DEBUG),y) EXTRA_CFLAGS += -DDEBUG endif -m6718_modem_spi-objs := modem_driver.o protocol.o util.o queue.o +m6718_modem_spi-objs := modem_driver.o protocol.o util.o queue.o debug.o ifeq ($(CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE),y) m6718_modem_spi-objs += modem_state.o diff --git a/drivers/modem/m6718_spi/debug.c b/drivers/modem/m6718_spi/debug.c new file mode 100644 index 00000000000..522a37163c1 --- /dev/null +++ b/drivers/modem/m6718_spi/debug.c @@ -0,0 +1,490 @@ +/* + * Copyright (C) ST-Ericsson SA 2010,2011 + * + * Author: Chris Blair for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * U9500 <-> M6718 IPC protocol implementation using SPI: + * debug functionality. + */ +#include +#include +#include "modem_debug.h" +#include "modem_private.h" +#include "modem_util.h" +#include "modem_queue.h" + +/* name of each state - must match enum ipc_sm_state_id */ +static const char * const sm_state_id_str[] = { + "IPC_INIT", + "IPC_HALT", + "IPC_RESET", + "IPC_WAIT_SLAVE_STABLE", + "IPC_WAIT_HANDSHAKE_INACTIVE", + "IPC_SLW_TX_BOOTREQ", + "IPC_ACT_TX_BOOTREQ", + "IPC_SLW_RX_BOOTRESP", + "IPC_ACT_RX_BOOTRESP", + "IPC_IDL", + "IPC_SLW_TX_WR_CMD", + "IPC_ACT_TX_WR_CMD", + "IPC_SLW_TX_WR_DAT", + "IPC_ACT_TX_WR_DAT", + "IPC_SLW_TX_RD_CMD", + "IPC_ACT_TX_RD_CMD", + "IPC_SLW_RX_WR_CMD", + "IPC_ACT_RX_WR_CMD", + "IPC_ACT_RX_WR_DAT", + "IPC_INIT_AUD", + "IPC_HALT_AUD", + "IPC_RESET_AUD", + "IPC_IDL_AUD", + "IPC_SLW_TX_WR_DAT_AUD", + "IPC_ACT_TX_WR_DAT_AUD", + "IPC_SLW_RX_WR_DAT_AUD", + "IPC_ACT_RX_WR_DAT_AUD", +}; + +/* name of each state machine run cause */ +static const char * const sm_run_cause_str[] = { + [IPC_SM_RUN_NONE] = "IPC_SM_RUN_NONE", + [IPC_SM_RUN_SLAVE_IRQ] = "IPC_SM_RUN_SLAVE_IRQ", + [IPC_SM_RUN_TFR_COMPLETE] = "IPC_SM_RUN_TFR_COMPLETE", + [IPC_SM_RUN_TX_REQ] = "IPC_SM_RUN_TX_REQ", + [IPC_SM_RUN_INIT] = "IPC_SM_RUN_INIT", + [IPC_SM_RUN_ABORT] = "IPC_SM_RUN_ABORT", + [IPC_SM_RUN_COMMS_TMO] = "IPC_SM_RUN_COMMS_TMO", + [IPC_SM_RUN_STABLE_TMO] = "IPC_SM_RUN_STABLE_TMO", + [IPC_SM_RUN_RESET] = "IPC_SM_RUN_RESET" +}; + + +#if defined DUMP_SPI_TFRS || \ + defined CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_FRAME_DUMP +static const char *format_buf(const void *buffer, int len) +{ + static char dumpbuf[6000]; + char *wr = dumpbuf; + const char *rd = buffer; + int maxlen = min(len, (int)(sizeof(dumpbuf) / 3)); + int i; + + for (i = 0 ; i < maxlen ; i++) { + sprintf(wr, "%02x ", rd[i]); + wr += 3; + } + return dumpbuf; +} +#endif + +void ipc_dbg_dump_frame(struct device *dev, int linkid, + struct ipc_tx_queue *frame, bool tx) +{ +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_FRAME_DUMP + if (frame->actual_len == 0) + return; + + /* + * Use printk(KERN_DEBUG... directly to ensure these are printed even + * when DEBUG is not defined for this device - we want to be able to + * dump the frames independently from the debug logging. + */ + printk(KERN_DEBUG "IPC link%d %s %3d %4d bytes:%s\n", + linkid, (tx ? "TX" : "RX"), frame->counter, frame->len, + format_buf(frame->data, frame->len)); +#endif +} + +void ipc_dbg_dump_spi_tfr(struct ipc_link_context *context) +{ +#ifdef DUMP_SPI_TFRS + struct spi_transfer *tfr = &context->spi_transfer; + struct spi_message *msg = &context->spi_message; + + if (tfr->tx_buf != NULL) + dev_info(&context->sdev->dev, "link%d TX %4d bytes:%s\n", + context->link->id, msg->actual_length, + format_buf(tfr->tx_buf, msg->actual_length)); + + if (tfr->rx_buf != NULL) + dev_info(&context->sdev->dev, "link%d RX %4d bytes:%s\n", + context->link->id, msg->actual_length, + format_buf(tfr->rx_buf, msg->actual_length)); +#endif +} + +const char *ipc_dbg_state_id(const struct ipc_sm_state *state) +{ + if (state == NULL) + return "(unknown)"; + else + return sm_state_id_str[state->id]; +} + +const char *ipc_dbg_event(u8 event) +{ + return sm_run_cause_str[event]; +} + +char *ipc_dbg_link_state_str(struct ipc_link_context *context) +{ + char *statestr; + int ss_pin; + int int_pin; + int min_free_pc; + + if (context == NULL) + return NULL; + + statestr = kmalloc(500, GFP_ATOMIC); + if (statestr == NULL) + return NULL; + + ss_pin = gpio_get_value(context->link->gpio.ss_pin); + int_pin = gpio_get_value(context->link->gpio.int_pin); + min_free_pc = context->tx_q_min > 0 ? + (context->tx_q_min * 100) / IPC_TX_QUEUE_MAX_SIZE : + 0; + + sprintf(statestr, + "state=%s (for %lus)\n" + "ss=%s(%d)\n" + "int=%s(%d)\n" + "lastevent=%s\n" + "lastignored=%s in %s (ignoredinthis=%d)\n" + "tx_q_min=%d(%d%%)\n" + "tx_q_count=%d\n" + "lastcmd=0x%08x (type %d count %d len %d)\n", + sm_state_id_str[context->state->id], + (jiffies - context->statesince) / HZ, + ss_pin == ipc_util_ss_level_active(context) ? + "ACTIVE" : "INACTIVE", + ss_pin, + int_pin == ipc_util_int_level_active(context) ? + "ACTIVE" : "INACTIVE", + int_pin, + sm_run_cause_str[context->lastevent], + sm_run_cause_str[context->lastignored], + sm_state_id_str[context->lastignored_in], + context->lastignored_inthis, + context->tx_q_min, + min_free_pc, + atomic_read(&context->tx_q_count), + context->cmd, + ipc_util_get_l1_cmd(context->cmd), + ipc_util_get_l1_counter(context->cmd), + ipc_util_get_l1_length(context->cmd)); + return statestr; +} + +void ipc_dbg_verify_rx_frame(struct ipc_link_context *context) +{ +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_VERIFY_FRAMES + int i; + u8 *last; + u8 *curr; + bool good = true; + + if (context->last_frame == NULL) + return; + + if (context->last_frame->actual_len != context->frame->actual_len) { + dev_err(&context->sdev->dev, + "link %d error: loopback frame length error, " + "TX %d RX %d\n", + context->link->id, + context->last_frame->actual_len, + context->frame->actual_len); + good = false; + goto out; + } + + last = (u8 *)context->last_frame->data; + curr = (u8 *)context->frame->data; + + /* skip any padding bytes */ + for (i = 0; i < context->last_frame->actual_len; i++) { + if (last[i] != curr[i]) { + dev_err(&context->sdev->dev, + "link %d bad byte %05d: " + "TX %02x RX %02x\n", + context->link->id, + i, + last[i], + curr[i]); + good = false; + } + } + +out: + if (!good) + dev_info(&context->sdev->dev, + "link %d error: loopback frame verification failed!\n", + context->link->id); + + ipc_queue_delete_frame(context->last_frame); + context->last_frame = NULL; +#endif +} + +#ifdef CONFIG_DEBUG_FS +static int debugfs_linkstate_open(struct inode *inode, struct file *file); +static int debugfs_linkstate_show(struct seq_file *s, void *data); + +static int debugfs_msr_open(struct inode *inode, struct file *file); +static int debugfs_msr_show(struct seq_file *s, void *data); +static ssize_t debugfs_msr_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos); + +static const struct file_operations debugfs_fops = { + .open = debugfs_linkstate_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release +}; + +static const struct file_operations debugfs_msr_fops = { + .open = debugfs_msr_open, + .read = seq_read, + .write = debugfs_msr_write, + .llseek = seq_lseek, + .release = single_release +}; + +static int debugfs_linkstate_open(struct inode *inode, struct file *file) +{ + return single_open(file, debugfs_linkstate_show, inode->i_private); +} + +static int debugfs_linkstate_show(struct seq_file *s, void *data) +{ + struct ipc_link_context *context = s->private; + char *statestr; + + if (context == NULL) { + seq_printf(s, "invalid context\n"); + return 0; + } + + statestr = ipc_dbg_link_state_str(context); + if (statestr == NULL) { + seq_printf(s, "unable to get link state string\n"); + return 0; + } + + seq_printf(s, "%s:\n%s", context->link->name, statestr); + kfree(statestr); + return 0; +} + +static int debugfs_msr_open(struct inode *inode, struct file *file) +{ + return single_open(file, debugfs_msr_show, inode->i_private); +} + +static int debugfs_msr_show(struct seq_file *s, void *data) +{ + struct ipc_l1_context *context = s->private; + + if (context == NULL) { + seq_printf(s, "invalid context\n"); + return 0; + } + + seq_printf(s, "msr %s\n", + context->msr_disable ? "disabled" : "enabled"); + return 0; +} + +static ssize_t debugfs_msr_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + char buf[128]; + int buf_size; + + /* get user space string and assure termination */ + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + buf[buf_size] = 0; + + if (buf[0] == '0' || buf[0] == 'd') { + pr_info("disabling msr\n"); + l1_context.msr_disable = true; + } else if (buf[0] == '1' || buf[0] == 'e') { + pr_info("enabling msr\n"); + l1_context.msr_disable = false; + } else { + pr_info("unknown request\n"); + } + + return buf_size; +} +#endif /* CONFIG_DEBUG_FS */ + +void ipc_dbg_debugfs_init(void) +{ +#ifdef CONFIG_DEBUG_FS + /* create debugfs directory entry for ipc in debugfs root */ + l1_context.debugfsdir = debugfs_create_dir("modemipc", NULL); + l1_context.debugfs_silentreset = + debugfs_create_file("msrenable", S_IRUSR | S_IWUSR, + l1_context.debugfsdir, &l1_context, &debugfs_msr_fops); + if (l1_context.debugfs_silentreset == NULL) + pr_err("failed to create debugfs MSR control file\n"); +#endif +} + +void ipc_dbg_debugfs_link_init(struct ipc_link_context *context) +{ +#ifdef CONFIG_DEBUG_FS + context->debugfsfile = NULL; + context->lastevent = IPC_SM_RUN_NONE; + context->lastignored = IPC_SM_RUN_NONE; + context->lastignored_in = IPC_SM_IDL; + context->lastignored_inthis = false; + context->tx_q_min = IPC_TX_QUEUE_MAX_SIZE; + context->statesince = 0; + + if (l1_context.debugfsdir != NULL) { + context->debugfsfile = + debugfs_create_file(context->link->name, S_IRUGO, + l1_context.debugfsdir, context, &debugfs_fops); + if (context->debugfsfile == NULL) + dev_err(&context->sdev->dev, + "link %d: failed to create debugfs file %s\n", + context->link->id, + context->link->name); + } +#endif +} + +void ipc_dbg_ignoring_event(struct ipc_link_context *context, u8 event) +{ +#ifdef CONFIG_DEBUG_FS + context->lastignored = event; + context->lastignored_in = context->state->id; + context->lastignored_inthis = true; +#endif +} + +void ipc_dbg_handling_event(struct ipc_link_context *context, u8 event) +{ +#ifdef CONFIG_DEBUG_FS + context->lastevent = event; + context->lastignored_inthis = false; +#endif +} + +void ipc_dbg_entering_state(struct ipc_link_context *context) +{ +#ifdef CONFIG_DEBUG_FS + context->statesince = jiffies; +#endif +} + +void ipc_dbg_enter_idle(struct ipc_link_context *context) +{ +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT + context->idl_idle_enter = jiffies; +#endif +} + +void ipc_dbg_exit_idle(struct ipc_link_context *context) +{ +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT + context->idl_idle_total += jiffies - context->idl_idle_enter; +#endif +} + +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT +static int measure_usage(struct ipc_link_context *context) +{ + unsigned long now = jiffies; + unsigned long idle; + unsigned long total; + + if (ipc_util_link_is_idle(context)) + ipc_dbg_exit_idle(context); + + idle = context->idl_idle_total; + total = now - context->idl_measured_at; + + context->idl_measured_at = now; + context->idl_idle_total = 0; + if (ipc_util_link_is_idle(context)) + context->idl_idle_enter = now; + + return 100 - ((idle * 100) / total); +} +#endif + +void ipc_dbg_measure_throughput(unsigned long unused) +{ +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT + u32 tx_bps_0, tx_bps_1; + u32 rx_bps_0, rx_bps_1; + int pc0, pc1; + + tx_bps_0 = tx_bps_1 = 0; + rx_bps_0 = rx_bps_1 = 0; + + /* link0 */ + tx_bps_0 = (l1_context.device_context[0].tx_bytes * 8) / + CONFIG_MODEM_M6718_SPI_SET_THROUGHPUT_FREQUENCY; + rx_bps_0 = (l1_context.device_context[0].rx_bytes * 8) / + CONFIG_MODEM_M6718_SPI_SET_THROUGHPUT_FREQUENCY; + l1_context.device_context[0].tx_bytes = 0; + l1_context.device_context[0].rx_bytes = 0; + pc0 = measure_usage(&l1_context.device_context[0]); +#if IPC_NBR_SUPPORTED_SPI_LINKS > 0 + /* link1 */ + tx_bps_1 = (l1_context.device_context[1].tx_bytes * 8) / + CONFIG_MODEM_M6718_SPI_SET_THROUGHPUT_FREQUENCY; + rx_bps_1 = (l1_context.device_context[1].rx_bytes * 8) / + CONFIG_MODEM_M6718_SPI_SET_THROUGHPUT_FREQUENCY; + l1_context.device_context[1].tx_bytes = 0; + l1_context.device_context[1].rx_bytes = 0; + pc1 = measure_usage(&l1_context.device_context[1]); +#endif + + pr_info("IPC THROUGHPUT (bit/s): " + "link0 TX:%8d RX:%8d %3d%% " + "link1 TX:%8d RX:%8d %3d%%\n", + tx_bps_0, rx_bps_0, pc0, + tx_bps_1, rx_bps_1, pc1); + + /* restart the measurement timer */ + l1_context.tp_timer.expires = jiffies + + (CONFIG_MODEM_M6718_SPI_SET_THROUGHPUT_FREQUENCY * HZ); + add_timer(&l1_context.tp_timer); +#endif +} + +void ipc_dbg_throughput_init(void) +{ +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT + pr_info("M6718 IPC throughput measurement interval: %d\n", + CONFIG_MODEM_M6718_SPI_SET_THROUGHPUT_FREQUENCY); + /* init the throughput measurement timer */ + init_timer(&l1_context.tp_timer); + l1_context.tp_timer.function = ipc_dbg_measure_throughput; + l1_context.tp_timer.data = 0; +#endif +} + +void ipc_dbg_throughput_link_init(struct ipc_link_context *context) +{ +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT + context->tx_bytes = 0; + context->rx_bytes = 0; + context->idl_measured_at = jiffies; + context->idl_idle_enter = 0; + context->idl_idle_total = 0; +#endif +} + diff --git a/drivers/modem/m6718_spi/modem_debug.h b/drivers/modem/m6718_spi/modem_debug.h new file mode 100644 index 00000000000..9a2fa39acb4 --- /dev/null +++ b/drivers/modem/m6718_spi/modem_debug.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Chris Blair for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * Modem IPC driver protocol interface header: + * debug functionality. + */ +#ifndef _MODEM_DEBUG_H_ +#define _MODEM_DEBUG_H_ + +#include "modem_private.h" + +void ipc_dbg_dump_frame(struct device *dev, int linkid, + struct ipc_tx_queue *frame, bool tx); +void ipc_dbg_dump_spi_tfr(struct ipc_link_context *context); +const char *ipc_dbg_state_id(const struct ipc_sm_state *state); +const char *ipc_dbg_event(u8 event); +char *ipc_dbg_link_state_str(struct ipc_link_context *context); +void ipc_dbg_verify_rx_frame(struct ipc_link_context *context); + +void ipc_dbg_debugfs_init(void); +void ipc_dbg_debugfs_link_init(struct ipc_link_context *context); + +void ipc_dbg_ignoring_event(struct ipc_link_context *context, u8 event); +void ipc_dbg_handling_event(struct ipc_link_context *context, u8 event); +void ipc_dbg_entering_state(struct ipc_link_context *context); +void ipc_dbg_enter_idle(struct ipc_link_context *context); +void ipc_dbg_exit_idle(struct ipc_link_context *context); +void ipc_dbg_measure_throughput(unsigned long unused); +void ipc_dbg_throughput_init(void); +void ipc_dbg_throughput_link_init(struct ipc_link_context *context); + +#endif /* _MODEM_DEBUG_H_ */ diff --git a/drivers/modem/m6718_spi/modem_private.h b/drivers/modem/m6718_spi/modem_private.h index 383699f1f3e..929cb62d672 100644 --- a/drivers/modem/m6718_spi/modem_private.h +++ b/drivers/modem/m6718_spi/modem_private.h @@ -100,4 +100,6 @@ struct ipc_l1_context { #endif }; +extern struct ipc_l1_context l1_context; + #endif /* _MODEM_PRIVATE_H_ */ diff --git a/drivers/modem/m6718_spi/protocol.c b/drivers/modem/m6718_spi/protocol.c index 046157dcd5e..a8e3ce42389 100644 --- a/drivers/modem/m6718_spi/protocol.c +++ b/drivers/modem/m6718_spi/protocol.c @@ -12,6 +12,7 @@ #include "modem_private.h" #include "modem_util.h" #include "modem_queue.h" +#include "modem_debug.h" #ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE #include @@ -97,7 +98,10 @@ void modem_protocol_init(void) IPC_DRIVER_VERSION); atomic_set(&l1_context.boot_sync_done, 0); + ipc_dbg_debugfs_init(); + ipc_dbg_throughput_init(); l1_context.init_done = true; + ipc_dbg_measure_throughput(0); #ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE schedule_delayed_work(&modem_state_reg_work, 0); #endif @@ -320,6 +324,8 @@ int modem_protocol_probe(struct spi_device *sdev) #endif ipc_queue_init(context); + ipc_dbg_debugfs_link_init(context); + ipc_dbg_throughput_link_init(context); /* * For link0 (the handshake link) we force a state transition now so -- cgit v1.2.3 From 744cc630af5e610167cbe690c0b1124aaced1fc6 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Fri, 25 Nov 2011 12:49:12 +0530 Subject: u5500-mbox: API to reset mailbox state API that can be used by modem control driver to reset the mailbox state on receiving the modem reset interrupt. This APE will be called in case of modem silen reboot. ST-Ericsson Linux next: NA ST-Ericsson ID: 341807 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Idc7e4eeeaa410185d34c1be9abcb59f4dd0a60af Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/39815 Reviewed-by: Bibek BASU Reviewed-by: QABUILD Reviewed-by: Srinidhi KASAGAR --- arch/arm/mach-ux500/include/mach/mbox-db5500.h | 2 +- drivers/misc/mbox.c | 125 ++++++++++++++++++++++--- 2 files changed, 115 insertions(+), 12 deletions(-) diff --git a/arch/arm/mach-ux500/include/mach/mbox-db5500.h b/arch/arm/mach-ux500/include/mach/mbox-db5500.h index 24af854befe..2da180b8df6 100644 --- a/arch/arm/mach-ux500/include/mach/mbox-db5500.h +++ b/arch/arm/mach-ux500/include/mach/mbox-db5500.h @@ -89,5 +89,5 @@ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv); * specify "block" in order to block until send is possible). */ int mbox_send(struct mbox *mbox, u32 mbox_msg, bool block); - +void mbox_state_reset(void); #endif /*INC_STE_MBOX_H*/ diff --git a/drivers/misc/mbox.c b/drivers/misc/mbox.c index 6e22653eed9..acea6962478 100644 --- a/drivers/misc/mbox.c +++ b/drivers/misc/mbox.c @@ -60,11 +60,13 @@ #define MBOX_LATCH 1 struct mbox_device_info { + struct mbox *mbox; struct workqueue_struct *mbox_modem_rel_wq; struct work_struct mbox_modem_rel; struct completion mod_req_ack_work; atomic_t ape_state; atomic_t mod_req; + atomic_t mod_reset; }; /* Global list of all mailboxes */ @@ -95,7 +97,7 @@ static void mbox_modem_rel_work(struct work_struct *work) mutex_unlock(&modem_state_mutex); } -static void mbox_modem_req() +static void mbox_modem_req(void) { mutex_lock(&modem_state_mutex); if (!db5500_prcmu_is_modem_requested()) { @@ -123,6 +125,11 @@ int mbox_send(struct mbox *mbox, u32 mbox_msg, bool block) int res = 0; unsigned long flag; + if (atomic_read(&mb->mod_reset)) { + dev_err(&mbox->pdev->dev, + "mbox_send called after modem reset\n"); + return -EINVAL; + } dev_dbg(&(mbox->pdev->dev), "About to buffer 0x%X to mailbox 0x%X." " ri = %d, wi = %d\n", @@ -160,6 +167,12 @@ int mbox_send(struct mbox *mbox, u32 mbox_msg, bool block) * Indicate that we want an IRQ as soon as there is a slot * in the FIFO */ + if (atomic_read(&mb->mod_reset)) { + dev_err(&mbox->pdev->dev, + "modem is in reset state, cannot proceed\n"); + res -EINVAL; + goto exit; + } writel(MBOX_ENABLE_IRQ, mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); exit: @@ -217,6 +230,10 @@ static ssize_t mbox_read_fifo(struct device *dev, struct platform_device *pdev = to_platform_device(dev); struct mbox *mbox = platform_get_drvdata(pdev); + if (atomic_read(&mb->mod_reset)) { + dev_err(&mbox->pdev->dev, "modem crashed, returning\n"); + return 0; + } if ((readl(mbox->virtbase_local + MBOX_FIFO_STATUS) & 0x7) <= 0) return sprintf(buf, "Mailbox is empty\n"); @@ -251,6 +268,11 @@ static int mbox_show(struct seq_file *s, void *data) continue; } + if (atomic_read(&mb->mod_reset)) { + dev_err(&m->pdev->dev, "modem crashed, returning\n"); + spin_unlock(&m->lock); + return 0; + } seq_printf(s, "===========================\n" " MAILBOX %d\n" @@ -330,6 +352,10 @@ static irqreturn_t mbox_irq(int irq, void *arg) int nbr_free; struct mbox *mbox = (struct mbox *) arg; + if (atomic_read(&mb->mod_reset)) { + dev_err(&mbox->pdev->dev, "modem in reset state\n"); + return IRQ_HANDLED; + } spin_lock(&mbox->lock); dev_dbg(&(mbox->pdev->dev), @@ -353,6 +379,11 @@ static irqreturn_t mbox_irq(int irq, void *arg) while ((nbr_free > 0) && (mbox->read_index != mbox->write_index)) { + if (atomic_read(&mb->mod_reset)) { + dev_err(&mbox->pdev->dev, + "modem in reset state\n"); + goto exit; + } /* Write the message and latch it into the FIFO */ writel(mbox->buffer[mbox->read_index], (mbox->virtbase_peer + MBOX_FIFO_DATA)); @@ -368,6 +399,10 @@ static irqreturn_t mbox_irq(int irq, void *arg) (mbox->read_index + 1) % MBOX_BUF_SIZE; } + if (atomic_read(&mb->mod_reset)) { + dev_err(&mbox->pdev->dev, "modem in reset state\n"); + goto exit; + } /* * Check if we still want IRQ:s when there is free * space to send @@ -404,6 +439,10 @@ static irqreturn_t mbox_irq(int irq, void *arg) hrtimer_start(&ape_timer, ktime_set(0, 10*NSEC_PER_MSEC), HRTIMER_MODE_REL); + if (atomic_read(&mb->mod_reset)) { + dev_err(&mbox->pdev->dev, "modem in reset state\n"); + goto exit; + } /* Check if we have any incoming messages */ nbr_occup = readl(mbox->virtbase_local + MBOX_FIFO_STATUS) & 0x7; if (nbr_occup == 0) @@ -417,6 +456,10 @@ redo: } atomic_set(&mb->ape_state, 1); + if (atomic_read(&mb->mod_reset)) { + dev_err(&mbox->pdev->dev, "modem in reset state\n"); + goto exit; + } /* Read and acknowledge the message */ mbox_value = readl(mbox->virtbase_local + MBOX_FIFO_DATA); writel(MBOX_LATCH, (mbox->virtbase_local + MBOX_FIFO_REMOVE)); @@ -444,13 +487,20 @@ exit: static void mbox_shutdown(struct mbox *mbox) { + if (!mbox->allocated) + return; #if defined(CONFIG_DEBUG_FS) debugfs_remove(mbox->dentry); device_remove_file(&mbox->pdev->dev, &dev_attr_fifo); #endif - writel(MBOX_DISABLE_IRQ, mbox->virtbase_local + MBOX_FIFO_THRES_OCCUP); - writel(MBOX_DISABLE_IRQ, mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); - free_irq(mbox->irq, NULL); + /* TODO: Need to check if we can write after modem reset */ + if (!atomic_read(&mb->mod_reset)) { + writel(MBOX_DISABLE_IRQ, mbox->virtbase_local + + MBOX_FIFO_THRES_OCCUP); + writel(MBOX_DISABLE_IRQ, mbox->virtbase_peer + + MBOX_FIFO_THRES_FREE); + } + free_irq(mbox->irq, (void *)mbox); mbox->client_blocked = 0; iounmap(mbox->virtbase_local); iounmap(mbox->virtbase_peer); @@ -459,11 +509,39 @@ static void mbox_shutdown(struct mbox *mbox) mbox->allocated = false; } -void mbox_reset_state(struct mbox *mbox) +/** mbox_state_reset - Reset the mailbox state machine + * + * This function is called on receiving modem reset interrupt. Reset all + * the mailbox state machine, disable irq, cancel timers, shutdown the + * mailboxs and re-enable irq's. + */ +void mbox_state_reset(void) { + struct mbox *mbox = mb->mbox; + + /* Common for all mailbox */ + atomic_set(&mb->mod_reset, 1); + + /* Disable IRQ */ + disable_irq_nosync(IRQ_DB5500_PRCMU_APE_REQ); + disable_irq_nosync(IRQ_DB5500_PRCMU_AC_WAKE_ACK); + + /* Cancel sleep_req timers */ + hrtimer_cancel(&modem_timer); + hrtimer_cancel(&ape_timer); + + /* specific to each mailbox */ list_for_each_entry(mbox, &mboxs, list) { mbox_shutdown(mbox); } + + /* Reset mailbox state machine */ + atomic_set(&mb->mod_req, 0); + atomic_set(&mb->ape_state, 0); + + /* Enable irq */ + enable_irq(IRQ_DB5500_PRCMU_APE_REQ); + enable_irq(IRQ_DB5500_PRCMU_AC_WAKE_ACK); } @@ -474,6 +552,13 @@ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv) int res; struct mbox *mbox; + /* + * set mod_reset flag to '0', clients calling this APE should make sure + * that modem is rebooted after MSR. Mailbox doesnt have any means of + * knowing the boot status of modem. + */ + atomic_set(&mb->mod_reset, 0); + mbox = get_mbox_with_id(mbox_id); if (mbox == NULL) { dev_err(&(mbox->pdev->dev), "Incorrect mailbox id: %d!\n", @@ -507,7 +592,7 @@ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv) dev_err(&(mbox->pdev->dev), "Unable to retrieve mbox peer resource\n"); mbox = NULL; - goto exit; + goto free_mbox; } dev_dbg(&(mbox->pdev->dev), "Resource name: %s start: 0x%X, end: 0x%X\n", @@ -516,7 +601,7 @@ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv) if (!mbox->virtbase_peer) { dev_err(&(mbox->pdev->dev), "Unable to ioremap peer mbox\n"); mbox = NULL; - goto exit; + goto free_mbox; } dev_dbg(&(mbox->pdev->dev), "ioremapped peer physical: (0x%X-0x%X) to virtual: 0x%X\n", @@ -530,7 +615,7 @@ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv) dev_err(&(mbox->pdev->dev), "Unable to retrieve mbox local resource\n"); mbox = NULL; - goto exit; + goto free_map; } dev_dbg(&(mbox->pdev->dev), "Resource name: %s start: 0x%X, end: 0x%X\n", @@ -539,7 +624,7 @@ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv) if (!mbox->virtbase_local) { dev_err(&(mbox->pdev->dev), "Unable to ioremap local mbox\n"); mbox = NULL; - goto exit; + goto free_map; } dev_dbg(&(mbox->pdev->dev), "ioremapped local physical: (0x%X-0x%X) to virtual: 0x%X\n", @@ -554,7 +639,7 @@ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv) dev_err(&(mbox->pdev->dev), "Unable to retrieve mbox irq resource\n"); mbox = NULL; - goto exit; + goto free_map1; } dev_dbg(&(mbox->pdev->dev), "Allocating irq %d...\n", mbox->irq); @@ -568,6 +653,13 @@ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv) goto exit; } + /* check if modem has reset */ + if (atomic_read(&mb->mod_reset)) { + dev_err(&mbox->pdev->dev, + "modem is in reset state, cannot proceed\n"); + mbox = NULL; + goto free_irq; + } /* Set up mailbox to not launch IRQ on free space in mailbox */ writel(MBOX_DISABLE_IRQ, mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); @@ -592,10 +684,19 @@ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv) mbox->dentry = debugfs_create_file("mbox", S_IFREG | S_IRUGO, NULL, NULL, &mbox_operations); #endif - dev_info(&(mbox->pdev->dev), "Mailbox driver with index %d initiated!\n", mbox_id); + return mbox; +free_irq: + free_irq(mbox->irq, (void *)mbox); +free_map1: + iounmap(mbox->virtbase_local); +free_map: + iounmap(mbox->virtbase_peer); +free_mbox: + mbox->client_data = NULL; + mbox->cb = NULL; exit: return mbox; } @@ -637,6 +738,7 @@ int __init mbox_probe(struct platform_device *pdev) spin_lock_init(&mbox->lock); platform_set_drvdata(pdev, mbox); + mb->mbox = mbox; dev_info(&(pdev->dev), "Mailbox driver loaded\n"); return res; @@ -736,6 +838,7 @@ static int __init mbox_init(void) atomic_set(&mb_di->ape_state, 0); atomic_set(&mb_di->mod_req, 0); + atomic_set(&mb_di->mod_reset, 0); err = request_irq(IRQ_DB5500_PRCMU_APE_REQ, mbox_prcmu_ape_req_handler, IRQF_NO_SUSPEND, "ape_req", NULL); -- cgit v1.2.3 From 74125ebc91076fb683dcdd31670bddcb48515868 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Fri, 2 Dec 2011 15:22:25 +0530 Subject: u5500-mbox: reduce the hostaccess port ack timeout Reduce the timeout to 2sec for now as modem doesnt go to sleep. ST-Ericsson Linux next: NA ST-Ericsson ID: 400364 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Icd9fed7b22d23d000aecf7643b0664fcd4ed2905 Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/40785 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Bibek BASU Tested-by: Bibek BASU Reviewed-by: Srinidhi KASAGAR --- drivers/misc/mbox.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/misc/mbox.c b/drivers/misc/mbox.c index acea6962478..5a292e0053b 100644 --- a/drivers/misc/mbox.c +++ b/drivers/misc/mbox.c @@ -102,9 +102,10 @@ static void mbox_modem_req(void) mutex_lock(&modem_state_mutex); if (!db5500_prcmu_is_modem_requested()) { prcmu_modem_req(); + /* TODO: optimize this timeout */ if (!wait_for_completion_timeout(&mb->mod_req_ack_work, - msecs_to_jiffies(8000))) - printk(KERN_ERR "mbox:modem_req_ack timedout(8sec)\n"); + msecs_to_jiffies(2000))) + printk(KERN_ERR "mbox:modem_req_ack timedout(2sec)\n"); } atomic_set(&mb->mod_req, 1); mutex_unlock(&modem_state_mutex); @@ -170,7 +171,7 @@ int mbox_send(struct mbox *mbox, u32 mbox_msg, bool block) if (atomic_read(&mb->mod_reset)) { dev_err(&mbox->pdev->dev, "modem is in reset state, cannot proceed\n"); - res -EINVAL; + res = -EINVAL; goto exit; } writel(MBOX_ENABLE_IRQ, mbox->virtbase_peer + MBOX_FIFO_THRES_FREE); -- cgit v1.2.3 From 25c60d8f42d8a66bca043af98a8069846ccba6a5 Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Thu, 24 Nov 2011 11:06:18 +0100 Subject: misc: shrm: use prcmu_read Use prcmu_read instead of global prcmu address variable. ST-Ericsson Linux next: - ST-Ericsson ID: 370799 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I55db2ab4d2fdc647e03791b88c1669bef62d07f5 Signed-off-by: Jonas Aaberg Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/39654 --- drivers/modem/shrm/shrm_protocol.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index 82fed247969..7f168ae278f 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -31,7 +31,7 @@ #define L2_HEADER_CIQ 0xC3 #define MAX_PAYLOAD 1024 -#define PRCM_HOSTACCESS_REQ (_PRCMU_BASE + 0x334) +#define PRCM_HOSTACCESS_REQ 0x334 static u8 boot_state = BOOT_INIT; static u8 recieve_common_msg[8*1024]; @@ -94,7 +94,7 @@ static u32 get_host_accessport_val(void) { u32 prcm_hostaccess; - prcm_hostaccess = readl(PRCM_HOSTACCESS_REQ); + prcm_hostaccess = prcmu_read(PRCM_HOSTACCESS_REQ); wmb(); prcm_hostaccess = prcm_hostaccess & 0x01; -- cgit v1.2.3 From c5648a9b856b3c59d7229a50304e0e976735a4ad Mon Sep 17 00:00:00 2001 From: Chris Blair Date: Sat, 19 Nov 2011 08:29:32 +0000 Subject: modem: Add M6718 IPC SPI driver char interface Adds the char 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: Ia0a5e8a8e95f28ee4a812795db91dc872b39f560 Signed-off-by: Chris Blair Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36491 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Jonas ABERG --- drivers/char/Makefile | 1 + drivers/char/m6718_modem_char.c | 722 +++++++++++++++++++++++++++++ drivers/modem/m6718_spi/modem_driver.c | 73 ++- include/linux/modem/m6718_spi/modem_char.h | 26 ++ 4 files changed, 816 insertions(+), 6 deletions(-) create mode 100644 drivers/char/m6718_modem_char.c create mode 100644 include/linux/modem/m6718_spi/modem_char.h diff --git a/drivers/char/Makefile b/drivers/char/Makefile index bd155f091c4..a73e2c197cd 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_TELCLOCK) += tlclk.o ifdef CONFIG_PHONET obj-$(CONFIG_U8500_SHRM) += shrm_char.o +obj-$(CONFIG_MODEM_M6718_SPI) += m6718_modem_char.o endif obj-$(CONFIG_MWAVE) += mwave/ diff --git a/drivers/char/m6718_modem_char.c b/drivers/char/m6718_modem_char.c new file mode 100644 index 00000000000..34bfe98aa57 --- /dev/null +++ b/drivers/char/m6718_modem_char.c @@ -0,0 +1,722 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Chris Blair for ST-Ericsson + * based on shrm_char.c + * + * License terms: GNU General Public License (GPL) version 2 + * + * M6718 modem char device interface. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NAME "IPC_ISA" + +#define MAX_PDU_SIZE (2000) /* largest frame we need to send */ +#define MAX_RX_FIFO_ENTRIES (10) +#define SIZE_OF_RX_FIFO (MAX_PDU_SIZE * MAX_RX_FIFO_ENTRIES) +#define SIZE_OF_TX_COPY_BUFFER (MAX_PDU_SIZE) /* only need 1 at a time */ + +static u8 message_fifo[MODEM_M6718_SPI_MAX_CHANNELS][SIZE_OF_RX_FIFO]; + +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_LOOPBACK +static u8 wr_mlb_msg[SIZE_OF_TX_COPY_BUFFER]; +#endif +static u8 wr_audio_msg[SIZE_OF_TX_COPY_BUFFER]; + +struct map_device { + u8 l2_header; + u8 idx; + char *name; +}; + +static struct map_device map_dev[] = { + {MODEM_M6718_SPI_CHN_ISI, 0, "isi"}, + {MODEM_M6718_SPI_CHN_AUDIO, 1, "modemaudio"}, +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_LOOPBACK + {MODEM_M6718_SPI_CHN_MASTER_LOOPBACK0, 2, "master_loopback0"}, + {MODEM_M6718_SPI_CHN_SLAVE_LOOPBACK0, 3, "slave_loopback0"}, + {MODEM_M6718_SPI_CHN_MASTER_LOOPBACK1, 4, "master_loopback1"}, + {MODEM_M6718_SPI_CHN_SLAVE_LOOPBACK1, 5, "slave_loopback1"}, +#endif +}; + +/* + * major - variable exported as module_param to specify major node number + */ +static int major; +module_param(major, int, 0); +MODULE_PARM_DESC(major, "Major device number"); + +/* global fops mutex */ +static DEFINE_MUTEX(isa_lock); + +/** + * modem_get_cdev_index() - return the index mapped to l2 header + * @l2_header: L2 header + * + * struct map_device maps the index(count) with the device L2 header. + * This function returns the index for the provided L2 header in case + * of success else -ve value. + */ +int modem_get_cdev_index(u8 l2_header) +{ + u8 cnt; + for (cnt = 0; cnt < ARRAY_SIZE(map_dev); cnt++) { + if (map_dev[cnt].l2_header == l2_header) + return map_dev[cnt].idx; + } + return -EINVAL; +} +EXPORT_SYMBOL_GPL(modem_get_cdev_index); + +/** + * modem_get_cdev_l2header() - return l2_header mapped to the index + * @idx: index + * + * struct map_device maps the index(count) with the device L2 header. + * This function returns the L2 header for the given index in case + * of success else -ve value. + */ +int modem_get_cdev_l2header(u8 idx) +{ + u8 cnt; + for (cnt = 0; cnt < ARRAY_SIZE(map_dev); cnt++) { + if (map_dev[cnt].idx == idx) + return map_dev[cnt].l2_header; + } + return -EINVAL; +} +EXPORT_SYMBOL_GPL(modem_get_cdev_l2header); + +/** + * modem_isa_reset() - reset device interfaces + * @modem_spi_dev: pointer to modem driver information structure + * + * Emptys the queue for each L2 mux channel. + */ +void modem_isa_reset(struct modem_spi_dev *modem_spi_dev) +{ + struct isa_device_context *isadev; + struct isa_driver_context *isa_context; + struct queue_element *cur_msg = NULL; + struct list_head *cur_msg_ptr = NULL; + struct list_head *msg_ptr; + struct message_queue *q; + int devidx; + + dev_info(modem_spi_dev->dev, "resetting char device queues\n"); + + isa_context = modem_spi_dev->isa_context; + for (devidx = 0; devidx < ARRAY_SIZE(map_dev); devidx++) { + isadev = &isa_context->isadev[devidx]; + 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, + struct queue_element, entry); + list_del(cur_msg_ptr); + kfree(cur_msg); + } + + /* reset the msg queue pointers */ + q->size = SIZE_OF_RX_FIFO; + q->readptr = 0; + q->writeptr = 0; + q->no = 0; + + /* wake up the blocking read/select */ + atomic_set(&q->q_rp, 1); + wake_up_interruptible(&q->wq_readable); + spin_unlock_bh(&q->update_lock); + } +} +EXPORT_SYMBOL_GPL(modem_isa_reset); + +static void create_queue(struct message_queue *q, u8 channel, + struct modem_spi_dev *modem_spi_dev) +{ + q->channel = channel; + q->fifo_base = (u8 *)&message_fifo[channel]; + q->size = SIZE_OF_RX_FIFO; + q->free = q->size; + q->readptr = 0; + q->writeptr = 0; + q->no = 0; + spin_lock_init(&q->update_lock); + atomic_set(&q->q_rp, 0); + init_waitqueue_head(&q->wq_readable); + INIT_LIST_HEAD(&q->msg_list); + q->modem_spi_dev = modem_spi_dev; +} + +static void delete_queue(struct message_queue *q) +{ + q->size = 0; + q->readptr = 0; + q->writeptr = 0; +} + +/** + * modem_isa_queue_msg() - Add a message to a queue queue + * @q: message queue + * @size: size in bytes + * + * This function tries to allocate size bytes in FIFO q. + * It returns negative number when no memory can be allocated + * currently. + */ +int modem_isa_queue_msg(struct message_queue *q, u32 size) +{ + struct queue_element *new_msg = NULL; + struct modem_spi_dev *modem_spi_dev = q->modem_spi_dev; + + new_msg = kmalloc(sizeof(struct queue_element), GFP_ATOMIC); + if (new_msg == NULL) { + dev_err(modem_spi_dev->dev, + "failed to allocate memory for queue item\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(modem_spi_dev->dev, "rx q++ ch %d %d (%d)\n", + q->channel, size, q->free); + dev_err(modem_spi_dev->dev, + "ch%d buffer overflow, frame discarded\n", + q->channel); + return -ENOMEM; + } + } else { + if ((q->writeptr + size) >= q->readptr) { + dev_err(modem_spi_dev->dev, "rx q++ ch %d %d (%d)\n", + q->channel, size, q->free); + dev_err(modem_spi_dev->dev, + "ch%d buffer overflow, frame discarded\n", + q->channel); + return -ENOMEM; + } + } + q->free -= size; + 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); + } + return 0; +} +EXPORT_SYMBOL_GPL(modem_isa_queue_msg); + +/** + * modem_isa_unqueue_msg() - remove a message from the msg queue + * @q: message queue + * + * Deletes a message from the message list associated with message + * queue q and also updates read ptr. If the message list is empty + * then a flag 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 modem_isa_unqueue_msg(struct message_queue *q) +{ + struct queue_element *old_msg = NULL; + struct list_head *msg_ptr = NULL; + struct list_head *old_msg_ptr = NULL; + + list_for_each_safe(old_msg_ptr, msg_ptr, &q->msg_list) { + old_msg = list_entry(old_msg_ptr, struct queue_element, entry); + if (old_msg == NULL) + return -EFAULT; + list_del(old_msg_ptr); + q->readptr = (q->readptr + old_msg->size) % q->size; + q->free += old_msg->size; + kfree(old_msg); + break; + } + if (list_empty(&q->msg_list)) + atomic_set(&q->q_rp, 0); + return 0; +} +EXPORT_SYMBOL_GPL(modem_isa_unqueue_msg); + +/** + * modem_isa_msg_size() - retrieve the size of the most recent message + * @q: message queue + */ +int modem_isa_msg_size(struct message_queue *q) +{ + struct queue_element *new_msg = NULL; + struct list_head *msg_list; + unsigned long flags; + int size = 0; + + spin_lock_irqsave(&q->update_lock, flags); + list_for_each(msg_list, &q->msg_list) { + new_msg = list_entry(msg_list, struct queue_element, entry); + if (new_msg == NULL) { + spin_unlock_irqrestore(&q->update_lock, flags); + return -EFAULT; + } + size = new_msg->size; + break; + } + spin_unlock_irqrestore(&q->update_lock, flags); + return size; +} +EXPORT_SYMBOL_GPL(modem_isa_msg_size); + +static u32 isa_select(struct file *filp, struct poll_table_struct *wait) +{ + struct isa_device_context *isadev = filp->private_data; + struct modem_spi_dev *modem_spi_dev = isadev->dl_queue.modem_spi_dev; + struct message_queue *q; + u32 m = iminor(filp->f_path.dentry->d_inode); + u8 idx = modem_get_cdev_index(m); + + if (modem_spi_dev->msr_flag) + return -ENODEV; + if (isadev->device_id != idx) + return -1; + + q = &isadev->dl_queue; + poll_wait(filp, &q->wq_readable, wait); + if (atomic_read(&q->q_rp) == 1) + return POLLIN | POLLRDNORM; + return 0; +} + +static ssize_t isa_read(struct file *filp, char __user *buf, + size_t len, loff_t *ppos) +{ + u32 size = 0; + int ret; + char *psrc; + struct isa_device_context *isadev = + (struct isa_device_context *)filp->private_data; + struct message_queue *q = &isadev->dl_queue; + struct modem_spi_dev *modem_spi_dev = q->modem_spi_dev; + u32 msgsize; + unsigned long flags; + + if (len <= 0) + return -EFAULT; + + if (modem_spi_dev->msr_flag) { + atomic_set(&q->q_rp, 0); + return -ENODEV; + } + + spin_lock_irqsave(&q->update_lock, flags); + if (list_empty(&q->msg_list)) { + spin_unlock_irqrestore(&q->update_lock, flags); + dev_dbg(modem_spi_dev->dev, "waiting for data on device %d\n", + isadev->device_id); + if (wait_event_interruptible(q->wq_readable, + atomic_read(&q->q_rp) == 1)) + return -ERESTARTSYS; + } else { + spin_unlock_irqrestore(&q->update_lock, flags); + } + + if (modem_spi_dev->msr_flag) { + atomic_set(&q->q_rp, 0); + return -ENODEV; + } + + msgsize = modem_isa_msg_size(q); + if (len < msgsize) + return -EINVAL; + + if ((q->readptr + msgsize) >= q->size) { + 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)) + 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))) + return -EFAULT; + } else { + if (copy_to_user(buf, (u8 *)(q->fifo_base + q->readptr), + msgsize)) + return -EFAULT; + } + + spin_lock_irqsave(&q->update_lock, flags); + ret = modem_isa_unqueue_msg(q); + if (ret < 0) + msgsize = ret; + spin_unlock_irqrestore(&q->update_lock, flags); + return msgsize; +} + +static ssize_t isa_write(struct file *filp, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct isa_device_context *isadev = filp->private_data; + struct message_queue *q = &isadev->dl_queue; + struct modem_spi_dev *modem_spi_dev = q->modem_spi_dev; + struct isa_driver_context *isa_context = modem_spi_dev->isa_context; + void *addr = 0; + int err; + int l2_header; + int ret = 0; + unsigned long flags; + + if (len <= 0 || buf == NULL) + return -EFAULT; + + if (len > SIZE_OF_TX_COPY_BUFFER) { + dev_err(modem_spi_dev->dev, + "invalid message size %d! max is %d bytes\n", + len, SIZE_OF_TX_COPY_BUFFER); + return -EFAULT; + } + + l2_header = modem_get_cdev_l2header(isadev->device_id); + if (l2_header < 0) { + dev_err(modem_spi_dev->dev, "invalid L2 channel!\n"); + return l2_header; + } + + switch (l2_header) { + case MODEM_M6718_SPI_CHN_AUDIO: + addr = (void *)wr_audio_msg; + break; + case MODEM_M6718_SPI_CHN_MASTER_LOOPBACK0: + case MODEM_M6718_SPI_CHN_SLAVE_LOOPBACK0: + case MODEM_M6718_SPI_CHN_MASTER_LOOPBACK1: + case MODEM_M6718_SPI_CHN_SLAVE_LOOPBACK1: + addr = (void *)wr_mlb_msg; + break; + default: + dev_dbg(modem_spi_dev->dev, "invalid device!\n"); + return -EFAULT; + } + + if (copy_from_user(addr, buf, len)) + return -EFAULT; + + /* + * Special handling for audio channel: + * uses a mutext instead of a spinlock + */ + if (l2_header == MODEM_M6718_SPI_CHN_AUDIO || + l2_header == MODEM_M6718_SPI_CHN_MASTER_LOOPBACK1) { + mutex_lock(&isa_context->audio_tx_mutex); + err = modem_m6718_spi_send(modem_spi_dev, l2_header, len, addr); + if (!err) + ret = len; + else + ret = err; + mutex_unlock(&modem_spi_dev->isa_context->audio_tx_mutex); + } else { + spin_lock_irqsave(&isa_context->common_tx_lock, flags); + err = modem_m6718_spi_send(modem_spi_dev, l2_header, len, addr); + if (!err) + ret = len; + else + ret = err; + spin_unlock_irqrestore(&isa_context->common_tx_lock, flags); + } + return ret; +} + +static long isa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + return -EINVAL; +} + +static int isa_mmap(struct file *filp, struct vm_area_struct *vma) +{ + return -EINVAL; +} + +static int isa_close(struct inode *inode, struct file *filp) +{ + struct isa_device_context *isadev = filp->private_data; + struct modem_spi_dev *modem_spi_dev = isadev->dl_queue.modem_spi_dev; + struct isa_driver_context *isa_context = modem_spi_dev->isa_context; + u8 m; + int idx; + + mutex_lock(&isa_lock); + m = iminor(filp->f_path.dentry->d_inode); + idx = modem_get_cdev_index(m); + if (idx < 0) { + dev_err(modem_spi_dev->dev, "invalid L2 channel!\n"); + return idx; + } + + if (atomic_dec_and_test(&isa_context->is_open[idx])) { + atomic_inc(&isa_context->is_open[idx]); + dev_err(modem_spi_dev->dev, "device is not open yet!\n"); + mutex_unlock(&isa_lock); + return -ENODEV; + } + atomic_set(&isa_context->is_open[idx], 1); + + switch (m) { + case MODEM_M6718_SPI_CHN_AUDIO: + dev_dbg(modem_spi_dev->dev, "close channel AUDIO\n"); + break; +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_LOOPBACK + case MODEM_M6718_SPI_CHN_MASTER_LOOPBACK0: + dev_dbg(modem_spi_dev->dev, "close channel MASTER_LOOPBACK0\n"); + break; + case MODEM_M6718_SPI_CHN_SLAVE_LOOPBACK0: + dev_dbg(modem_spi_dev->dev, "close channel SLAVE_LOOPBACK0\n"); + break; + case MODEM_M6718_SPI_CHN_MASTER_LOOPBACK1: + dev_dbg(modem_spi_dev->dev, "close channel MASTER_LOOPBACK1\n"); + break; + case MODEM_M6718_SPI_CHN_SLAVE_LOOPBACK1: + dev_dbg(modem_spi_dev->dev, "close channel SLAVE_LOOPBACK1\n"); + break; +#endif + default: + dev_dbg(modem_spi_dev->dev, "invalid device\n"); + mutex_unlock(&isa_lock); + return -ENODEV; + } + mutex_unlock(&isa_lock); + return 0; +} + +static int isa_open(struct inode *inode, struct file *filp) +{ + int err = 0; + u8 m; + int idx; + struct isa_device_context *isadev; + struct isa_driver_context *isa_context = + container_of(inode->i_cdev, struct isa_driver_context, cdev); + struct modem_spi_dev *modem_spi_dev = + isa_context->isadev->dl_queue.modem_spi_dev; + + if (!modem_m6718_spi_is_boot_done()) { + dev_dbg(modem_spi_dev->dev, + "failed to open device, boot is not complete\n"); + err = -EBUSY; + goto out; + } + + mutex_lock(&isa_lock); + m = iminor(inode); + idx = modem_get_cdev_index(m); + if (idx < 0) { + dev_err(modem_spi_dev->dev, "invalid device\n"); + err = -ENODEV; + goto cleanup; + } + + if (!atomic_dec_and_test(&isa_context->is_open[idx])) { + atomic_inc(&isa_context->is_open[idx]); + dev_err(modem_spi_dev->dev, "device is already open\n"); + err = -EBUSY; + goto cleanup; + } + + isadev = &isa_context->isadev[idx]; + if (filp != NULL) + filp->private_data = isadev; + + switch (m) { + case MODEM_M6718_SPI_CHN_AUDIO: + dev_dbg(modem_spi_dev->dev, "open channel AUDIO\n"); + break; +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_LOOPBACK + case MODEM_M6718_SPI_CHN_MASTER_LOOPBACK0: + dev_dbg(modem_spi_dev->dev, "open channel MASTER_LOOPBACK0\n"); + break; + case MODEM_M6718_SPI_CHN_SLAVE_LOOPBACK0: + dev_dbg(modem_spi_dev->dev, "open channel SLAVE_LOOPBACK0\n"); + break; + case MODEM_M6718_SPI_CHN_MASTER_LOOPBACK1: + dev_dbg(modem_spi_dev->dev, "open channel MASTER_LOOPBACK1\n"); + break; + case MODEM_M6718_SPI_CHN_SLAVE_LOOPBACK1: + dev_dbg(modem_spi_dev->dev, "open channel SLAVE_LOOPBACK1\n"); + break; +#endif + } + +cleanup: + mutex_unlock(&isa_lock); +out: + return err; +} + +const struct file_operations isa_fops = { + .owner = THIS_MODULE, + .open = isa_open, + .release = isa_close, + .unlocked_ioctl = isa_ioctl, + .mmap = isa_mmap, + .read = isa_read, + .write = isa_write, + .poll = isa_select, +}; + +/** + * modem_isa_init() - initialise the modem char device interfaces + * @modem_spi_dev: pointer to the modem driver information structure + * + * This function registers registers as a char device driver and creates the + * char device nodes supported by the modem. + */ +int modem_isa_init(struct modem_spi_dev *modem_spi_dev) +{ + dev_t dev_id; + int retval; + int devidx; + struct isa_device_context *isadev; + struct isa_driver_context *isa_context; + + dev_dbg(modem_spi_dev->dev, "registering char device interfaces\n"); + + isa_context = kzalloc(sizeof(struct isa_driver_context), GFP_KERNEL); + if (isa_context == NULL) { + dev_err(modem_spi_dev->dev, "failed to allocate context\n"); + retval = -ENOMEM; + goto rollback; + } + + modem_spi_dev->isa_context = isa_context; + if (major) { + /* major node specified at module load */ + dev_id = MKDEV(major, 0); + retval = register_chrdev_region(dev_id, + MODEM_M6718_SPI_MAX_CHANNELS, NAME); + } else { + retval = alloc_chrdev_region(&dev_id, 0, + MODEM_M6718_SPI_MAX_CHANNELS, NAME); + major = MAJOR(dev_id); + } + + dev_dbg(modem_spi_dev->dev, "device major is %d\n", major); + + cdev_init(&isa_context->cdev, &isa_fops); + isa_context->cdev.owner = THIS_MODULE; + retval = cdev_add(&isa_context->cdev, dev_id, + MODEM_M6718_SPI_MAX_CHANNELS); + if (retval) { + dev_err(modem_spi_dev->dev, "failed to add char device\n"); + goto rollback_register; + } + + isa_context->modem_class = class_create(THIS_MODULE, NAME); + if (IS_ERR(isa_context->modem_class)) { + dev_err(modem_spi_dev->dev, "failed to create modem class\n"); + retval = PTR_ERR(isa_context->modem_class); + goto rollback_add_dev; + } + isa_context->isadev = kzalloc(sizeof(struct isa_device_context) * + MODEM_M6718_SPI_MAX_CHANNELS, GFP_KERNEL); + if (isa_context->isadev == NULL) { + dev_err(modem_spi_dev->dev, + "failed to allocate device context\n"); + goto rollback_create_dev; + } + + for (devidx = 0; devidx < ARRAY_SIZE(map_dev); devidx++) { + atomic_set(&isa_context->is_open[devidx], 1); + device_create(isa_context->modem_class, + NULL, + MKDEV(MAJOR(dev_id), map_dev[devidx].l2_header), + NULL, + map_dev[devidx].name); + + isadev = &isa_context->isadev[devidx]; + isadev->device_id = devidx; + create_queue(&isadev->dl_queue, + isadev->device_id, modem_spi_dev); + + dev_dbg(modem_spi_dev->dev, "created device %d (%s) (%d.%d)\n", + devidx, map_dev[devidx].name, major, + map_dev[devidx].l2_header); + } + + mutex_init(&isa_context->audio_tx_mutex); + spin_lock_init(&isa_context->common_tx_lock); + + dev_dbg(modem_spi_dev->dev, "registered modem char devices\n"); + return 0; + +rollback_create_dev: + for (devidx = 0; devidx < ARRAY_SIZE(map_dev); devidx++) { + device_destroy(isa_context->modem_class, + MKDEV(MAJOR(dev_id), map_dev[devidx].l2_header)); + } + class_destroy(isa_context->modem_class); +rollback_add_dev: + cdev_del(&isa_context->cdev); +rollback_register: + unregister_chrdev_region(dev_id, MODEM_M6718_SPI_MAX_CHANNELS); + kfree(isa_context); + modem_spi_dev->isa_context = NULL; +rollback: + return retval; +} +EXPORT_SYMBOL_GPL(modem_isa_init); + +/** + * modem_isa_exit() - remove the char device interfaces and clean up + * @modem_spi_dev: pointer to the modem driver information structure + */ +void modem_isa_exit(struct modem_spi_dev *modem_spi_dev) +{ + int devidx; + struct isa_device_context *isadev; + struct isa_driver_context *isa_context = modem_spi_dev->isa_context; + dev_t dev_id = MKDEV(major, 0); + + if (!modem_spi_dev || !modem_spi_dev->isa_context) + return; + + for (devidx = 0; devidx < ARRAY_SIZE(map_dev); devidx++) + device_destroy(isa_context->modem_class, + MKDEV(MAJOR(dev_id), + map_dev[devidx].l2_header)); + for (devidx = 0; devidx < MODEM_M6718_SPI_MAX_CHANNELS; devidx++) { + isadev = &isa_context->isadev[devidx]; + delete_queue(&isadev->dl_queue); + kfree(isadev); + } + class_destroy(isa_context->modem_class); + cdev_del(&isa_context->cdev); + unregister_chrdev_region(dev_id, MODEM_M6718_SPI_MAX_CHANNELS); + kfree(isa_context); + modem_spi_dev->isa_context = NULL; + dev_dbg(modem_spi_dev->dev, "removed modem char devices\n"); +} +EXPORT_SYMBOL_GPL(modem_isa_exit); + +MODULE_AUTHOR("Chris Blair "); +MODULE_DESCRIPTION("M6718 modem IPC char device interface"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/modem/m6718_spi/modem_driver.c b/drivers/modem/m6718_spi/modem_driver.c index 13d4a3e28b2..d85e6943c23 100644 --- a/drivers/modem/m6718_spi/modem_driver.c +++ b/drivers/modem/m6718_spi/modem_driver.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "modem_protocol.h" static struct modem_spi_dev modem_driver_data = { @@ -31,20 +32,70 @@ static struct modem_spi_dev modem_driver_data = { * @data: pointer to frame data * * This function is called from the driver L1 physical transport layer. It - * copied the frame data to the receive queue for the channel on which the data + * copies the frame data to the receive queue for the channel on which the data * was received. * * 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) { - return -ENODEV; + u32 size = 0; + int ret = 0; + int idx; + u8 *psrc; + u32 writeptr; + struct message_queue *q; + struct isa_device_context *isadev; + + dev_dbg(&sdev->dev, "L2 received frame from L1: channel %d len %d\n", + channel, len); + +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_LOOPBACK + if (channel == MODEM_M6718_SPI_CHN_SLAVE_LOOPBACK0 || + channel == MODEM_M6718_SPI_CHN_SLAVE_LOOPBACK1) { + /* data received on slave loopback channel - loop it back */ + modem_m6718_spi_send(&modem_driver_data, channel, len, data); + return 0; + } +#endif + + /* find the isa device index for this L2 channel */ + idx = modem_get_cdev_index(channel); + if (idx < 0) { + dev_err(&sdev->dev, "failed to get isa device index\n"); + return idx; + } + isadev = &modem_driver_data.isa_context->isadev[idx]; + q = &isadev->dl_queue; + + spin_lock(&q->update_lock); + + /* verify message can be contained in buffer */ + writeptr = q->writeptr; + ret = modem_isa_queue_msg(q, len); + if (ret >= 0) { + /* memcopy RX data */ + if ((writeptr + len) >= q->size) { + psrc = (u8 *)data; + size = q->size - writeptr; + /* copy first part of msg */ + memcpy((q->fifo_base + writeptr), psrc, size); + psrc += size; + /* copy second part of msg at the top of fifo */ + memcpy(q->fifo_base, psrc, (len - size)); + } else { + memcpy((q->fifo_base + writeptr), data, len); + } + } + spin_unlock(&q->update_lock); + + if (ret < 0) { + dev_err(&sdev->dev, "failed to queue frame!"); + return ret; + } + return ret; } EXPORT_SYMBOL_GPL(modem_m6718_spi_receive); @@ -75,9 +126,18 @@ static int spi_probe(struct spi_device *sdev) result = -ENODEV; goto rollback_protocol_init; } + + result = modem_isa_init(&modem_driver_data); + if (result < 0) { + dev_err(&sdev->dev, + "failed to initialise char interface\n"); + goto rollback_modem_get; + } } return result; +rollback_modem_get: + modem_put(modem_driver_data.modem); rollback_protocol_init: modem_protocol_exit(); rollback: @@ -87,6 +147,7 @@ rollback: static int __exit spi_remove(struct spi_device *sdev) { modem_protocol_exit(); + modem_isa_exit(&modem_driver_data); return 0; } diff --git a/include/linux/modem/m6718_spi/modem_char.h b/include/linux/modem/m6718_spi/modem_char.h new file mode 100644 index 00000000000..04d82eaa03c --- /dev/null +++ b/include/linux/modem/m6718_spi/modem_char.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Chris Blair for ST-Ericsson + * based on shrm_driver.h + * + * License terms: GNU General Public License (GPL) version 2 + * + * Modem IPC driver char interface header. + */ +#ifndef _MODEM_CHAR_H_ +#define _MODEM_CHAR_H_ + +#include + +int modem_isa_init(struct modem_spi_dev *modem_spi_dev); +void modem_isa_exit(struct modem_spi_dev *modem_spi_dev); + +int modem_isa_queue_msg(struct message_queue *q, u32 size); +int modem_isa_msg_size(struct message_queue *q); +int modem_isa_unqueue_msg(struct message_queue *q); +void modem_isa_reset(struct modem_spi_dev *modem_spi_dev); +int modem_get_cdev_index(u8 l2_header); +int modem_get_cdev_l2header(u8 idx); + +#endif /* _MODEM_CHAR_H_ */ -- cgit v1.2.3 From d15cba295cd8b01d7f44a62e636f3e95a51ec497 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Mon, 5 Dec 2011 17:00:02 +0530 Subject: u8500-shrm: DocBook: Remove warnings in shrm driver ST-Ericsson Linux next: NA ST-Ericsson ID: 354484 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I9b086aae5dc697c1b0fccc0913b170c3ef32ea97 Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/41014 Reviewed-by: QABUILD Reviewed-by: Bibek BASU Reviewed-by: Srinidhi KASAGAR --- include/linux/modem/shrm/shrm_driver.h | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/include/linux/modem/shrm/shrm_driver.h b/include/linux/modem/shrm/shrm_driver.h index 689ce207df5..081dfd111e4 100644 --- a/include/linux/modem/shrm/shrm_driver.h +++ b/include/linux/modem/shrm/shrm_driver.h @@ -64,17 +64,18 @@ * @ca_audio_shared_rptr: cmt-ape audio channel read pointer * @dev: pointer to the driver device * @ndev: pointer to the network device structure + * @modem: poiner to struct modem * @isa_context: pointer to t_isa_driver_sontext dtructure - * @shm_common_ch_wr_wk: kthread worker for writing to common channel - * @shm_common_ch_wr_wk_task: task for writing to common channel - * @shm_audio_ch_wr_wk: kthread worker for writing to audio channel - * @shm_audio_ch_wr_wk_task: task for writing to audio channel - * @shm_ac_wake_wk: kthread worker for receiving ape-cmt wake requests - * @shm_ac_wake_wk_task: task for receiving ape-cmt wake requests - * @shm_ca_wake_wk: kthread worker for receiving cmt-ape wake requests - * @shm_ca_wake_wk_task: task for receiving cmt-ape wake requests - * @shm_ac_sleep_wk: kthread worker for recieving ape-cmt sleep requests - * @shm_ac_sleep_wk_task: task for recieving ape-cmt sleep requests + * @shm_common_ch_wr_kw: kthread worker for writing to common channel + * @shm_common_ch_wr_kw_task: task for writing to common channel + * @shm_audio_ch_wr_kw: kthread worker for writing to audio channel + * @shm_audio_ch_wr_kw_task: task for writing to audio channel + * @shm_ac_wake_kw: kthread worker for receiving ape-cmt wake requests + * @shm_ac_wake_kw_task: task for receiving ape-cmt wake requests + * @shm_ca_wake_kw: kthread worker for receiving cmt-ape wake requests + * @shm_ca_wake_kw_task: task for receiving cmt-ape wake requests + * @shm_ac_sleep_kw: kthread worker for recieving ape-cmt sleep requests + * @shm_ac_sleep_kw_task: task 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 -- cgit v1.2.3 From 7452ef587924d61fd9eb957945e6832c37905faa Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 13 Jan 2012 15:52:19 +0100 Subject: misc: sim_detect: Change sysfs attributes permission change write permissions for syfs attributes to user only, give only read permissions for group and others. Signed-off-by: Naga Radhesh --- drivers/misc/sim_detect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/sim_detect.c b/drivers/misc/sim_detect.c index 213645ca46d..6e6446d0fc5 100644 --- a/drivers/misc/sim_detect.c +++ b/drivers/misc/sim_detect.c @@ -117,7 +117,7 @@ out_unlock: return count; } -static DEVICE_ATTR(voltage, 0666, show_voltage, write_voltage); +static DEVICE_ATTR(voltage, S_IWUSR | S_IRUGO, show_voltage, write_voltage); static struct attribute *sim_attributes[] = { &dev_attr_voltage.attr, -- cgit v1.2.3 From e6676501bbf6fc77ba3185025a09f3cace501f70 Mon Sep 17 00:00:00 2001 From: Mark Godfrey Date: Fri, 4 Nov 2011 09:39:47 +0000 Subject: u8500: shrm: Add support for RTC Calib messages A new shared-mem IPC logical channel (mapID=200) allows APE to send and receive messages to the modem in support of the calibration of the RealTimeClock. The modem can measure the 32KHz clock drift (against network clock) and report the drift to the APE. This new logical channel supports this reporting and can be accessed via a new /dev/rtc_calibration device. ST-Ericsson ID: 362204 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ic0ed097c1bbd8b3a6816642840c258e598b79715 Signed-off-by: Mark Godfrey Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36440 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Jonas ABERG --- drivers/char/shrm_char.c | 21 ++++++++++++++++++--- drivers/modem/shrm/shrm_protocol.c | 4 +++- include/linux/modem/shrm/shrm_driver.h | 2 +- include/linux/modem/shrm/shrm_private.h | 1 + 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/drivers/char/shrm_char.c b/drivers/char/shrm_char.c index fa44b469b98..e8f350e5da8 100644 --- a/drivers/char/shrm_char.c +++ b/drivers/char/shrm_char.c @@ -25,8 +25,8 @@ #define NAME "IPC_ISA" -/* L2 header for ciq device is 0xc3 and hence 0xc3+1 = 196*/ -#define MAX_L2_HEADERS 196 +/* L2 header for rtc_calibration device is 0xC8 and hence 0xC8 + 1 = 201 */ +#define MAX_L2_HEADERS 201 #define SIZE_OF_FIFO (512*1024) @@ -35,6 +35,7 @@ static u8 message_fifo[ISA_DEVICES][SIZE_OF_FIFO]; static u8 wr_rpc_msg[10*1024]; static u8 wr_sec_msg[10*1024]; static u8 wr_audio_msg[10*1024]; +static u8 wr_rtc_cal_msg[100]; struct map_device { u8 l2_header; @@ -50,6 +51,7 @@ static struct map_device map_dev[] = { {COMMON_LOOPBACK_MESSAGING, 4, "common_loopback"}, {AUDIO_LOOPBACK_MESSAGING, 5, "audio_loopback"}, {CIQ_MESSAGING, 6, "ciq"}, + {RTC_CAL_MESSAGING, 7, "rtc_calibration"}, }; /* @@ -488,6 +490,10 @@ ssize_t isa_write(struct file *filp, const char __user *buf, dev_dbg(shrm->dev, "CIQ\n"); addr = isadev->addr; break; + case RTC_CAL_MESSAGING: + dev_dbg(shrm->dev, "isa_write(): RTC Calibration\n"); + addr = (void *)wr_rtc_cal_msg; + break; default: dev_dbg(shrm->dev, "Wrong device\n"); return -EFAULT; @@ -612,6 +618,7 @@ static int isa_close(struct inode *inode, struct file *filp) idx = shrm_get_cdev_index(m); if (idx < 0) { dev_err(shrm->dev, "failed to get index\n"); + mutex_unlock(&isa_lock); return idx; } dev_dbg(shrm->dev, "isa_close %d", m); @@ -646,6 +653,9 @@ static int isa_close(struct inode *inode, struct file *filp) kfree(isadev->addr); dev_info(shrm->dev, "Close CIQ_MESSAGING Device\n"); break; + case RTC_CAL_MESSAGING: + dev_info(shrm->dev, "Close RTC_CAL_MESSAGING Device\n"); + break; default: dev_info(shrm->dev, "No such device present\n"); mutex_unlock(&isa_lock); @@ -693,7 +703,8 @@ static int isa_open(struct inode *inode, struct file *filp) (m != COMMON_LOOPBACK_MESSAGING) && (m != AUDIO_MESSAGING) && (m != SECURITY_MESSAGING) && - (m != CIQ_MESSAGING)) { + (m != CIQ_MESSAGING) && + (m != RTC_CAL_MESSAGING)) { dev_err(shrm->dev, "No such device present\n"); mutex_unlock(&isa_lock); return -ENODEV; @@ -701,6 +712,7 @@ static int isa_open(struct inode *inode, struct file *filp) idx = shrm_get_cdev_index(m); if (idx < 0) { dev_err(shrm->dev, "failed to get index\n"); + mutex_unlock(&isa_lock); return idx; } if (!atomic_dec_and_test(&isa_context->is_open[idx])) { @@ -747,6 +759,9 @@ static int isa_open(struct inode *inode, struct file *filp) } dev_info(shrm->dev, "Open CIQ_MESSAGING Device\n"); break; + case RTC_CAL_MESSAGING: + dev_info(shrm->dev, "Open RTC_CAL_MESSAGING Device\n"); + break; }; mutex_unlock(&isa_lock); diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index 7f168ae278f..6cc6e347188 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -29,6 +29,7 @@ #define L2_HEADER_AUDIO_SIMPLE_LOOPBACK 0x80 #define L2_HEADER_AUDIO_ADVANCED_LOOPBACK 0x81 #define L2_HEADER_CIQ 0xC3 +#define L2_HEADER_RTC_CALIBRATION 0xC8 #define MAX_PAYLOAD 1024 #define PRCM_HOSTACCESS_REQ 0x334 @@ -1056,7 +1057,8 @@ int shm_write_msg(struct shrm_dev *shrm, u8 l2_header, (l2_header == L2_HEADER_SECURITY) || (l2_header == L2_HEADER_COMMON_SIMPLE_LOOPBACK) || (l2_header == L2_HEADER_COMMON_ADVANCED_LOOPBACK) || - (l2_header == L2_HEADER_CIQ)) { + (l2_header == L2_HEADER_CIQ) || + (l2_header == L2_HEADER_RTC_CALIBRATION)) { channel = 0; if (shrm_common_tx_state == SHRM_SLEEP_STATE) shrm_common_tx_state = SHRM_PTR_FREE; diff --git a/include/linux/modem/shrm/shrm_driver.h b/include/linux/modem/shrm/shrm_driver.h index 081dfd111e4..96b5c594d34 100644 --- a/include/linux/modem/shrm/shrm_driver.h +++ b/include/linux/modem/shrm/shrm_driver.h @@ -25,7 +25,7 @@ #include #include -#define ISA_DEVICES 7 +#define ISA_DEVICES 8 #define BOOT_INIT (0) #define BOOT_INFO_SYNC (1) diff --git a/include/linux/modem/shrm/shrm_private.h b/include/linux/modem/shrm/shrm_private.h index 888a7c200fd..23caabf5a06 100644 --- a/include/linux/modem/shrm/shrm_private.h +++ b/include/linux/modem/shrm/shrm_private.h @@ -47,6 +47,7 @@ #define COMMON_LOOPBACK_MESSAGING (0xC0) #define AUDIO_LOOPBACK_MESSAGING (0x80) #define CIQ_MESSAGING (0xC3) +#define RTC_CAL_MESSAGING (0xC8) #define COMMON_CHANNEL 0 #define AUDIO_CHANNEL 1 -- cgit v1.2.3 From 5128200053711fa60d5448c837bc13b02a932f4b Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Mon, 19 Dec 2011 09:41:46 +0530 Subject: u5500-mbox: remove ape_req ape_req interrupt is not to be used as mailbox interrupt serves as a wakeup interrupt to ARM. ST-Ericsson Linux next: NA ST-Ericsson ID: 402718 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I069bfb6b93fdb02b6ce106bfccba23820d6284b8 Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/43026 Reviewed-by: QATOOLS Reviewed-by: Bibek BASU Reviewed-by: Rabin VINCENT --- drivers/misc/mbox.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/drivers/misc/mbox.c b/drivers/misc/mbox.c index 5a292e0053b..4199f9b1247 100644 --- a/drivers/misc/mbox.c +++ b/drivers/misc/mbox.c @@ -524,7 +524,6 @@ void mbox_state_reset(void) atomic_set(&mb->mod_reset, 1); /* Disable IRQ */ - disable_irq_nosync(IRQ_DB5500_PRCMU_APE_REQ); disable_irq_nosync(IRQ_DB5500_PRCMU_AC_WAKE_ACK); /* Cancel sleep_req timers */ @@ -541,7 +540,6 @@ void mbox_state_reset(void) atomic_set(&mb->ape_state, 0); /* Enable irq */ - enable_irq(IRQ_DB5500_PRCMU_APE_REQ); enable_irq(IRQ_DB5500_PRCMU_AC_WAKE_ACK); } @@ -709,12 +707,6 @@ static irqreturn_t mbox_prcmu_mod_req_ack_handler(int irq, void *data) return IRQ_HANDLED; } -static irqreturn_t mbox_prcmu_ape_req_handler(int irq, void *data) -{ - prcmu_ape_ack(); - return IRQ_HANDLED; -} - int __init mbox_probe(struct platform_device *pdev) { struct mbox *mbox; @@ -841,13 +833,6 @@ static int __init mbox_init(void) atomic_set(&mb_di->mod_req, 0); atomic_set(&mb_di->mod_reset, 0); - err = request_irq(IRQ_DB5500_PRCMU_APE_REQ, mbox_prcmu_ape_req_handler, - IRQF_NO_SUSPEND, "ape_req", NULL); - if (err < 0) { - printk(KERN_ERR "mbox:Failed alloc IRQ_DB5500_PRCMU_APE_REQ.\n"); - goto free_wq1; - } - err = request_irq(IRQ_DB5500_PRCMU_AC_WAKE_ACK, mbox_prcmu_mod_req_ack_handler, IRQF_NO_SUSPEND, "mod_req_ack", NULL); @@ -860,8 +845,6 @@ static int __init mbox_init(void) mb = mb_di; return platform_driver_probe(&mbox_driver, mbox_probe); free_irq: - free_irq(IRQ_DB5500_PRCMU_APE_REQ, NULL); -free_wq1: destroy_workqueue(mb_di->mbox_modem_rel_wq); free_mem: kfree(mb_di); @@ -872,7 +855,6 @@ module_init(mbox_init); void __exit mbox_exit(void) { - free_irq(IRQ_DB5500_PRCMU_APE_REQ, NULL); free_irq(IRQ_DB5500_PRCMU_AC_WAKE_ACK, NULL); destroy_workqueue(mb->mbox_modem_rel_wq); platform_driver_unregister(&mbox_driver); -- 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 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 3407b005f7f85fd3f4060ad211e8ab6e10a7a729 Mon Sep 17 00:00:00 2001 From: Chris Blair Date: Sat, 19 Nov 2011 08:51:15 +0000 Subject: modem: Add M6718 IPC SPI driver netlink support Adds netlink support to the modem driver. Userspace can send requests for status on the link and they can subscribe for broadcasrs on the link. ST-Ericsson ID: 369397 ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-12224 ST-Ericsson Linux next: NA Change-Id: I9159080c7337498368da759ca2b20106dd41381c Signed-off-by: Chris Blair Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36499 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Jonas ABERG --- drivers/modem/m6718_spi/Makefile | 3 +- drivers/modem/m6718_spi/modem_netlink.h | 20 ++++ drivers/modem/m6718_spi/netlink.c | 182 ++++++++++++++++++++++++++++++++ drivers/modem/m6718_spi/protocol.c | 2 + 4 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 drivers/modem/m6718_spi/modem_netlink.h create mode 100644 drivers/modem/m6718_spi/netlink.c diff --git a/drivers/modem/m6718_spi/Makefile b/drivers/modem/m6718_spi/Makefile index b3898c62662..5cb265b00ac 100644 --- a/drivers/modem/m6718_spi/Makefile +++ b/drivers/modem/m6718_spi/Makefile @@ -5,7 +5,8 @@ ifeq ($(CONFIG_MODEM_M6718_SPI_DEBUG),y) EXTRA_CFLAGS += -DDEBUG endif -m6718_modem_spi-objs := modem_driver.o protocol.o util.o queue.o debug.o +m6718_modem_spi-objs := modem_driver.o protocol.o util.o queue.o debug.o \ + netlink.o ifeq ($(CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE),y) m6718_modem_spi-objs += modem_state.o diff --git a/drivers/modem/m6718_spi/modem_netlink.h b/drivers/modem/m6718_spi/modem_netlink.h new file mode 100644 index 00000000000..19e123d9b12 --- /dev/null +++ b/drivers/modem/m6718_spi/modem_netlink.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Chris Blair for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * Modem IPC driver protocol interface header: + * netlink related functionality. + */ +#ifndef _MODEM_NETLINK_H_ +#define _MODEM_NETLINK_H_ + +#include "modem_protocol.h" + +bool ipc_create_netlink_socket(struct ipc_link_context *context); +void ipc_broadcast_modem_online(struct ipc_link_context *context); +void ipc_broadcast_modem_reset(struct ipc_link_context *context); + +#endif /* _MODEM_NETLINK_H_ */ diff --git a/drivers/modem/m6718_spi/netlink.c b/drivers/modem/m6718_spi/netlink.c new file mode 100644 index 00000000000..253b19162b1 --- /dev/null +++ b/drivers/modem/m6718_spi/netlink.c @@ -0,0 +1,182 @@ +/* + * Copyright (C) ST-Ericsson SA 2010,2011 + * + * Author: Chris Blair for ST-Ericsson + * based on shrm_protocol.c + * + * License terms: GNU General Public License (GPL) version 2 + * + * U9500 <-> M6718 IPC protocol implementation using SPI: + * netlink related functionality + */ +#include +#include +#include +#include +#include "modem_protocol.h" +#include "modem_private.h" +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE +#include "modem_state.h" +#endif + +static struct sock *netlink_sk; +struct modem_spi_dev *modem_dev; + +#define MAX_PAYLOAD 1024 + +/* + * Netlink broadcast message values: this must correspond to those values + * expected by userspace for the appropriate message. + */ +enum netlink_msg_id { + NETLINK_MODEM_RESET = 1, + NETLINK_MODEM_QUERY_STATE, + NETLINK_USER_REQUEST_MODEM_RESET, + NETLINK_MODEM_STATUS_ONLINE, + NETLINK_MODEM_STATUS_OFFLINE +}; + +static void netlink_multicast_tasklet(unsigned long data) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + enum netlink_msg_id nlmsg = (enum netlink_msg_id)data; + + if (netlink_sk == NULL) { + pr_err("could not send multicast, no socket\n"); + return; + } + + /* prepare netlink message */ + skb = alloc_skb(NLMSG_SPACE(MAX_PAYLOAD), GFP_ATOMIC); + if (!skb) { + pr_err("failed to allocate socket buffer\n"); + return; + } + + if (nlmsg == NETLINK_MODEM_RESET) + modem_isa_reset(modem_dev); + + nlh = (struct nlmsghdr *)skb->data; + nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD); + nlh->nlmsg_pid = 0; /* from kernel */ + nlh->nlmsg_flags = 0; + *(int *)NLMSG_DATA(nlh) = nlmsg; + 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 */ + pr_debug("sending netlink multicast message %d\n", nlmsg); + netlink_broadcast(netlink_sk, skb, 0, 1, GFP_ATOMIC); + +} + +static void send_unicast(int dst_pid) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + + if (netlink_sk == NULL) { + pr_err("could not send unicast, no socket\n"); + return; + } + + /* prepare the message for unicast */ + skb = alloc_skb(NLMSG_SPACE(MAX_PAYLOAD), GFP_KERNEL); + if (!skb) { + pr_err("failed to allocate socket buffer\n"); + return; + } + + nlh = (struct nlmsghdr *)skb->data; + nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD); + nlh->nlmsg_pid = 0; /* from kernel */ + nlh->nlmsg_flags = 0; + + if (modem_m6718_spi_is_boot_done()) { + pr_debug("sending netlink unicast message %d\n", + NETLINK_MODEM_STATUS_ONLINE); + *(int *)NLMSG_DATA(nlh) = NETLINK_MODEM_STATUS_ONLINE; + } else { + pr_debug("sending netlink unicast message %d\n", + NETLINK_MODEM_STATUS_OFFLINE); + *(int *)NLMSG_DATA(nlh) = NETLINK_MODEM_STATUS_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 process */ + netlink_unicast(netlink_sk, skb, dst_pid, MSG_DONTWAIT); +} + +static void netlink_receive(struct sk_buff *skb) +{ + struct nlmsghdr *nlh = NULL; + int msg; + + nlh = (struct nlmsghdr *)skb->data; + msg = *((int *)(NLMSG_DATA(nlh))); + switch (msg) { + case NETLINK_MODEM_QUERY_STATE: + send_unicast(nlh->nlmsg_pid); + break; + case NETLINK_USER_REQUEST_MODEM_RESET: + pr_info("user requested modem reset!\n"); +#ifdef CONFIG_DEBUG_FS + if (l1_context.msr_disable) { + pr_info("MSR is disabled, ignoring reset request\n"); + break; + } +#endif +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE + modem_state_force_reset(); +#else + pr_err("modestate integration is not enabled in IPC, " + "unable to reset modem\n"); +#endif + break; + default: + pr_debug("ignoring invalid netlink message\n"); + break; + } +} + +bool ipc_create_netlink_socket(struct ipc_link_context *context) +{ + if (netlink_sk != NULL) + return true; + + netlink_sk = netlink_kernel_create(NULL, NETLINK_MODEM, 1, + netlink_receive, NULL, THIS_MODULE); + if (netlink_sk == NULL) { + dev_err(&context->sdev->dev, + "failed to create netlink socket\n"); + return false; + } + modem_dev = spi_get_drvdata(context->sdev); + return true; +} + +DECLARE_TASKLET(modem_online_tasklet, netlink_multicast_tasklet, + NETLINK_MODEM_STATUS_ONLINE); +DECLARE_TASKLET(modem_reset_tasklet, netlink_multicast_tasklet, + NETLINK_MODEM_RESET); + +void ipc_broadcast_modem_online(struct ipc_link_context *context) +{ + dev_info(&context->sdev->dev, "broadcast modem online event!\n"); + tasklet_schedule(&modem_online_tasklet); +} + +void ipc_broadcast_modem_reset(struct ipc_link_context *context) +{ + dev_info(&context->sdev->dev, "broadcast modem reset event!\n"); + tasklet_schedule(&modem_reset_tasklet); +} + diff --git a/drivers/modem/m6718_spi/protocol.c b/drivers/modem/m6718_spi/protocol.c index a8e3ce42389..86b124120ce 100644 --- a/drivers/modem/m6718_spi/protocol.c +++ b/drivers/modem/m6718_spi/protocol.c @@ -13,6 +13,7 @@ #include "modem_util.h" #include "modem_queue.h" #include "modem_debug.h" +#include "modem_netlink.h" #ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE #include @@ -326,6 +327,7 @@ int modem_protocol_probe(struct spi_device *sdev) ipc_queue_init(context); ipc_dbg_debugfs_link_init(context); ipc_dbg_throughput_link_init(context); + ipc_create_netlink_socket(context); /* * For link0 (the handshake link) we force a state transition now so -- cgit v1.2.3 From 02d694f3b46da51fb4dec1c74690afdd82c62639 Mon Sep 17 00:00:00 2001 From: Chris Blair Date: Fri, 4 Nov 2011 14:19:57 +0000 Subject: modem: Add M6718 IPC SPI driver state machine Adds the implementation of the driver protocol state machine. ST-Ericsson ID: 369397 ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-12224 ST-Ericsson Linux next: NA Change-Id: Iffa99154ab2d3f0150c96c7bc535ffe26f6988db Signed-off-by: Chris Blair Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36501 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Jonas ABERG --- drivers/modem/m6718_spi/Makefile | 2 +- drivers/modem/m6718_spi/modem_statemachine.h | 7 + drivers/modem/m6718_spi/protocol.c | 23 +- drivers/modem/m6718_spi/statemachine.c | 1402 ++++++++++++++++++++++++++ 4 files changed, 1424 insertions(+), 10 deletions(-) create mode 100644 drivers/modem/m6718_spi/statemachine.c diff --git a/drivers/modem/m6718_spi/Makefile b/drivers/modem/m6718_spi/Makefile index 5cb265b00ac..a0a82c30b07 100644 --- a/drivers/modem/m6718_spi/Makefile +++ b/drivers/modem/m6718_spi/Makefile @@ -6,7 +6,7 @@ EXTRA_CFLAGS += -DDEBUG endif m6718_modem_spi-objs := modem_driver.o protocol.o util.o queue.o debug.o \ - netlink.o + netlink.o statemachine.o ifeq ($(CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE),y) m6718_modem_spi-objs += modem_state.o diff --git a/drivers/modem/m6718_spi/modem_statemachine.h b/drivers/modem/m6718_spi/modem_statemachine.h index bc737dbb7e8..55e4a520d3d 100644 --- a/drivers/modem/m6718_spi/modem_statemachine.h +++ b/drivers/modem/m6718_spi/modem_statemachine.h @@ -71,4 +71,11 @@ struct ipc_sm_state { u8 events; }; +const struct ipc_sm_state *ipc_sm_idle_state(struct ipc_link_context *context); +const struct ipc_sm_state *ipc_sm_init_state(struct ipc_link_context *context); +const struct ipc_sm_state *ipc_sm_state(u8 id); +bool ipc_sm_valid_for_state(u8 event, const struct ipc_sm_state *state); + +void ipc_sm_kick(u8 event, struct ipc_link_context *context); + #endif /* _MODEM_STATEMACHINE_H_ */ diff --git a/drivers/modem/m6718_spi/protocol.c b/drivers/modem/m6718_spi/protocol.c index 86b124120ce..aa21d4dbcde 100644 --- a/drivers/modem/m6718_spi/protocol.c +++ b/drivers/modem/m6718_spi/protocol.c @@ -79,12 +79,12 @@ bool modem_protocol_channel_is_open(u8 channel) void modem_comms_timeout(unsigned long data) { - /* TODO: statemachine will be kicked here */ + ipc_sm_kick(IPC_SM_RUN_COMMS_TMO, (struct ipc_link_context *)data); } void slave_stable_timeout(unsigned long data) { - /* TODO: statemachine will be kicked here */ + ipc_sm_kick(IPC_SM_RUN_STABLE_TMO, (struct ipc_link_context *)data); } /** @@ -150,7 +150,7 @@ int modem_m6718_spi_send(struct modem_spi_dev *modem_spi_dev, u8 channel, if (ipc_util_link_is_idle(context)) { dev_dbg(modem_spi_dev->dev, "link %d is idle, kicking\n", channels[channel].link); - /* TODO: statemachine will be kicked here */ + ipc_sm_kick(IPC_SM_RUN_TX_REQ, context); } else { dev_dbg(modem_spi_dev->dev, "link %d is already running\n", channels[channel].link); @@ -198,7 +198,8 @@ bool modem_protocol_is_busy(struct spi_device *sdev) static void spi_tfr_complete(void *context) { - /* TODO: statemachine will be kicked here */ + ipc_sm_kick(IPC_SM_RUN_TFR_COMPLETE, + (struct ipc_link_context *)context); } static irqreturn_t slave_ready_irq(int irq, void *dev) @@ -221,7 +222,7 @@ static irqreturn_t slave_ready_irq(int irq, void *dev) return IRQ_HANDLED; } #endif - /* TODO: statemachine will be kicked here */ + ipc_sm_kick(IPC_SM_RUN_SLAVE_IRQ, context); return IRQ_HANDLED; } @@ -243,12 +244,14 @@ static int modem_state_callback(unsigned long unused) */ for (i = 0; i < IPC_NBR_SUPPORTED_SPI_LINKS; i++) ipc_util_link_gpio_config(&contexts[i]); - /* TODO: statemachine will be kicked here */ + ipc_sm_kick(IPC_SM_RUN_INIT, &contexts[0]); break; case MODEM_STATE_OFF: case MODEM_STATE_RESET: case MODEM_STATE_CRASH: - /* TODO: statemachine will be kicked here */ + /* force all links to reset */ + for (i = 0; i < IPC_NBR_SUPPORTED_SPI_LINKS; i++) + ipc_sm_kick(IPC_SM_RUN_RESET, &contexts[i]); break; default: break; @@ -307,6 +310,7 @@ int modem_protocol_probe(struct spi_device *sdev) atomic_set(&context->state_int, ipc_util_int_level_inactive(context)); spin_lock_init(&context->sm_lock); + context->state = ipc_sm_init_state(context); ipc_util_spi_message_init(context, spi_tfr_complete); init_timer(&context->comms_timer); context->comms_timer.function = modem_comms_timeout; @@ -333,7 +337,8 @@ int modem_protocol_probe(struct spi_device *sdev) * For link0 (the handshake link) we force a state transition now so * that it prepares for boot sync. */ - /* TODO: statemachine will be kicked here */ + if (link->id == 0) + ipc_sm_kick(IPC_SM_RUN_INIT, context); /* * unlikely but possible: for links other than 0, check if handshake is @@ -345,7 +350,7 @@ int modem_protocol_probe(struct spi_device *sdev) dev_dbg(&sdev->dev, "link %d: boot sync is done, kicking state machine\n", link->id); - /* TODO: statemachine will be kicked here */ + ipc_sm_kick(IPC_SM_RUN_INIT, context); } return 0; } diff --git a/drivers/modem/m6718_spi/statemachine.c b/drivers/modem/m6718_spi/statemachine.c new file mode 100644 index 00000000000..a2092cd2f94 --- /dev/null +++ b/drivers/modem/m6718_spi/statemachine.c @@ -0,0 +1,1402 @@ +/* + * Copyright (C) ST-Ericsson SA 2010,2011 + * + * Author: Chris Blair for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * U9500 <-> M6718 IPC protocol implementation using SPI. + * state machine definition and functionality. + */ +#include +#include "modem_statemachine.h" +#include "modem_util.h" +#include "modem_netlink.h" +#include "modem_debug.h" +#include "modem_queue.h" +#include "modem_protocol.h" + +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE +#include "modem_state.h" +#endif + +#define CMD_BOOTREQ (1) +#define CMD_BOOTRESP (2) +#define CMD_WRITE (3) +#define CMD_READ (4) + +static u8 sm_init_enter(u8 event, struct ipc_link_context *context) +{ +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE + /* if modem is off un-configure the IPC GPIO pins for low-power */ + if (modem_state_get_state() == MODEM_STATE_OFF) { + dev_info(&context->sdev->dev, + "link %d: modem is off, un-configuring GPIO\n", + context->link->id); + ipc_util_link_gpio_unconfig(context); + } +#endif + /* nothing more to do until an event happens */ + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_init_exit(u8 event, + struct ipc_link_context *context) +{ + bool int_active = false; + + /* + * For reset event just re-enter init in case the modem has + * powered off - we need to reconfigure our GPIO pins + */ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_INIT); + + /* re-sample link INT pin */ + int_active = ipc_util_int_is_active(context); + atomic_set(&context->state_int, int_active); + + dev_info(&context->sdev->dev, + "link %d: link initialised; SS:INACTIVE(%d) INT:%s(%d)\n", + context->link->id, + ipc_util_ss_level_inactive(context), + int_active ? "ACTIVE" : "INACTIVE", + int_active ? ipc_util_int_level_active(context) : + ipc_util_int_level_inactive(context)); + + /* handshake is only on link 0 */ + if (context->link->id == 0) { + if (!int_active) { + dev_info(&context->sdev->dev, + "link %d: slave INT signal is inactive\n", + context->link->id); + /* start boot handshake */ + return ipc_sm_state(IPC_SM_SLW_TX_BOOTREQ); + } else { + /* wait for slave INT signal to stabilise inactive */ + return ipc_sm_state(IPC_SM_WAIT_SLAVE_STABLE); + } + } else { + dev_info(&context->sdev->dev, + "link %d: boot sync not needed, going idle\n", + context->link->id); + return ipc_sm_state(IPC_SM_IDL); + } +} + +static const struct ipc_sm_state *sm_init_aud_exit(u8 event, + struct ipc_link_context *context) +{ + bool int_active = false; + + /* + * For reset event just re-enter init in case the modem has + * powered off - we need to reconfigure our GPIO pins + */ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_INIT_AUD); + + /* re-sample link INT pin */ + int_active = ipc_util_int_is_active(context); + atomic_set(&context->state_int, int_active); + + dev_info(&context->sdev->dev, + "link %d: link initialised; SS:INACTIVE(%d) INT:%s(%d)\n", + context->link->id, + ipc_util_ss_level_inactive(context), + int_active ? "ACTIVE" : "INACTIVE", + int_active ? ipc_util_int_level_active(context) : + ipc_util_int_level_inactive(context)); + dev_info(&context->sdev->dev, + "link %d: boot sync not needed, going idle\n", + context->link->id); + return ipc_sm_state(IPC_SM_IDL_AUD); +} + +static u8 sm_wait_slave_stable_enter(u8 event, struct ipc_link_context *context) +{ + static unsigned long printk_warn_time; + if (printk_timed_ratelimit(&printk_warn_time, 60 * 1000)) + dev_info(&context->sdev->dev, + "link %d: waiting for stable inactive slave INT\n", + context->link->id); + ipc_util_start_slave_stable_timer(context); + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_wait_slave_stable_exit(u8 event, + struct ipc_link_context *context) +{ + if (!ipc_util_int_is_active(context)) { + dev_info(&context->sdev->dev, + "link %d: slave INT signal is stable inactive\n", + context->link->id); + return ipc_sm_state(IPC_SM_SLW_TX_BOOTREQ); + } else { + return ipc_sm_state(IPC_SM_WAIT_SLAVE_STABLE); + } +} + +static u8 sm_wait_handshake_inactive_enter(u8 event, + struct ipc_link_context *context) +{ + dev_info(&context->sdev->dev, + "link %d: waiting for stable inactive slave INT\n", + context->link->id); + ipc_util_start_slave_stable_timer(context); + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_wait_handshake_inactive_exit(u8 event, + struct ipc_link_context *context) +{ + int i; + + if (!ipc_util_int_is_active(context)) { + dev_info(&context->sdev->dev, + "link %d: slave INT signal is inactive, going idle\n", + context->link->id); + + /* modem sync is done */ + atomic_inc(&l1_context.boot_sync_done); + ipc_broadcast_modem_online(context); + + /* + * Kick the state machine for any initialised links - skip link0 + * since this link has just completed handshake + */ + for (i = 1; i < IPC_NBR_SUPPORTED_SPI_LINKS; i++) + if (l1_context.device_context[i].state != NULL) { + dev_dbg(&context->sdev->dev, + "link %d has already been probed, " + "kicking state machine\n", i); + ipc_sm_kick(IPC_SM_RUN_INIT, + &l1_context.device_context[i]); + } + return ipc_sm_state(IPC_SM_IDL); + } else { + return ipc_sm_state(IPC_SM_WAIT_HANDSHAKE_INACTIVE); + } +} + +static u8 sm_idl_enter(u8 event, struct ipc_link_context *context) +{ + ipc_util_deactivate_ss(context); + ipc_dbg_enter_idle(context); + + /* check if tx queue contains items */ + if (atomic_read(&context->tx_q_count) > 0) { + dev_dbg(&context->sdev->dev, + "link %d: tx queue contains items\n", + context->link->id); + return IPC_SM_RUN_TX_REQ; + } + + /* check if modem has already requested transaction start */ + if (atomic_read(&context->state_int)) { + dev_dbg(&context->sdev->dev, + "link %d: slave has already signalled ready\n", + context->link->id); + return IPC_SM_RUN_SLAVE_IRQ; + } + + dev_dbg(&context->sdev->dev, + "link %d: going idle\n", context->link->id); + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_idl_exit(u8 event, + struct ipc_link_context *context) +{ + ipc_dbg_exit_idle(context); + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET); + else if (event == IPC_SM_RUN_TX_REQ) + return ipc_sm_state(IPC_SM_SLW_TX_WR_CMD); + else if (event == IPC_SM_RUN_SLAVE_IRQ) + return ipc_sm_state(IPC_SM_SLW_TX_RD_CMD); + else + return ipc_sm_state(IPC_SM_HALT); +} + +static const struct ipc_sm_state *sm_idl_aud_exit(u8 event, + struct ipc_link_context *context) +{ + ipc_dbg_exit_idle(context); + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET_AUD); + + /* always transmit data first */ + return ipc_sm_state(IPC_SM_SLW_TX_WR_DAT_AUD); +} + +static u8 sm_slw_tx_wr_cmd_enter(u8 event, struct ipc_link_context *context) +{ + struct ipc_tx_queue *frame; + + /* get the frame from the head of the tx queue */ + if (ipc_queue_is_empty(context)) { + dev_err(&context->sdev->dev, + "link %d error: tx queue is empty!\n", + context->link->id); + return IPC_SM_RUN_ABORT; + } + frame = ipc_queue_get_frame(context); + ipc_dbg_dump_frame(&context->sdev->dev, context->link->id, frame, true); + + context->cmd = ipc_util_make_l1_header(CMD_WRITE, frame->counter, + frame->len); + + dev_dbg(&context->sdev->dev, + "link %d: TX FRAME cmd %08x (type %d counter %d len %d)\n", + context->link->id, + context->cmd, + ipc_util_get_l1_cmd(context->cmd), + ipc_util_get_l1_counter(context->cmd), + ipc_util_get_l1_length(context->cmd)); + + ipc_util_spi_message_prepare(context, &context->cmd, + NULL, IPC_L1_HDR_SIZE); + context->frame = frame; + + /* slave might already have signalled ready to transmit */ + if (atomic_read(&context->state_int)) { + dev_dbg(&context->sdev->dev, + "link %d: slave has already signalled ready\n", + context->link->id); + ipc_util_activate_ss(context); + return IPC_SM_RUN_SLAVE_IRQ; + } else { + ipc_util_activate_ss_with_tmo(context); + return IPC_SM_RUN_NONE; + } +} + +static const struct ipc_sm_state *sm_slw_tx_wr_cmd_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET); + else if (event == IPC_SM_RUN_COMMS_TMO) + return ipc_sm_state(IPC_SM_HALT); + else + return ipc_sm_state(IPC_SM_ACT_TX_WR_CMD); +} + +static u8 sm_act_tx_wr_cmd_enter(u8 event, struct ipc_link_context *context) +{ + int err; + + /* slave is ready - start the spi transfer */ + dev_dbg(&context->sdev->dev, + "link %d: starting spi tfr\n", context->link->id); + err = spi_async(context->sdev, &context->spi_message); + if (err < 0) { + dev_err(&context->sdev->dev, + "link %d error: spi tfr start failed, error %d\n", + context->link->id, err); + return IPC_SM_RUN_ABORT; + } + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_act_tx_wr_cmd_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET); + else + return ipc_sm_state(IPC_SM_SLW_TX_WR_DAT); +} + +static u8 sm_slw_tx_wr_dat_enter(u8 event, struct ipc_link_context *context) +{ + /* prepare to transfer the frame tx data */ + ipc_util_spi_message_prepare(context, context->frame->data, + NULL, context->frame->len); + + /* slave might already have signalled ready to transmit */ + if (atomic_read(&context->state_int)) { + dev_dbg(&context->sdev->dev, + "link %d: slave has already signalled ready\n", + context->link->id); + ipc_util_activate_ss(context); + return IPC_SM_RUN_SLAVE_IRQ; + } else { + ipc_util_activate_ss_with_tmo(context); + return IPC_SM_RUN_NONE; + } +} + +static u8 sm_slw_tx_wr_dat_aud_enter(u8 event, struct ipc_link_context *context) +{ + struct ipc_tx_queue *frame = NULL; + + /* check if there is a frame to be sent */ + if (!ipc_queue_is_empty(context)) { + frame = ipc_queue_get_frame(context); + } else { + /* no frame to send, create an empty one */ + dev_dbg(&context->sdev->dev, + "link %d: no frame to send, allocating dummy\n", + context->link->id); + frame = ipc_queue_new_frame(context, 0); + if (frame == NULL) + return IPC_SM_RUN_ABORT; + } + + ipc_dbg_dump_frame(&context->sdev->dev, context->link->id, frame, true); + + /* prepare to transfer the frame tx data */ + context->frame = frame; + ipc_util_spi_message_prepare(context, context->frame->data, + NULL, context->frame->len); + + /* slave might already have signalled ready to transmit */ + if (event == IPC_SM_RUN_SLAVE_IRQ || atomic_read(&context->state_int)) { + dev_dbg(&context->sdev->dev, + "link %d: slave has already signalled ready\n", + context->link->id); + ipc_util_activate_ss(context); + return IPC_SM_RUN_SLAVE_IRQ; + } else { + ipc_util_activate_ss_with_tmo(context); + return IPC_SM_RUN_NONE; + } +} + +static const struct ipc_sm_state *sm_slw_tx_wr_dat_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET); + else if (event == IPC_SM_RUN_COMMS_TMO) + return ipc_sm_state(IPC_SM_HALT); + else + return ipc_sm_state(IPC_SM_ACT_TX_WR_DAT); +} + +static const struct ipc_sm_state *sm_slw_tx_wr_dat_aud_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET_AUD); + else if (event == IPC_SM_RUN_COMMS_TMO) + return ipc_sm_state(IPC_SM_HALT_AUD); + else + return ipc_sm_state(IPC_SM_ACT_TX_WR_DAT_AUD); +} + +static u8 sm_act_tx_wr_dat_enter(u8 event, struct ipc_link_context *context) +{ + int err; + + /* slave is ready - start the spi transfer */ + dev_dbg(&context->sdev->dev, + "link %d: starting spi tfr\n", context->link->id); + err = spi_async(context->sdev, &context->spi_message); + if (err < 0) { + dev_err(&context->sdev->dev, + "link %d error: spi tfr start failed, error %d\n", + context->link->id, err); + return IPC_SM_RUN_ABORT; + } + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_act_tx_wr_dat_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET); + +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT + /* frame is sent, increment link tx counter */ + context->tx_bytes += context->frame->actual_len; +#endif +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_VERIFY_FRAMES + { + u8 channel; + + channel = ipc_util_get_l2_channel(*(u32 *)context->frame->data); + if (ipc_util_channel_is_loopback(channel)) { + context->last_frame = context->frame; + } else { + ipc_queue_delete_frame(context->frame); + context->frame = NULL; + } + } +#else + /* free the sent frame */ + ipc_queue_delete_frame(context->frame); + context->frame = NULL; +#endif + return ipc_sm_state(IPC_SM_SLW_TX_RD_CMD); +} + +static const struct ipc_sm_state *sm_act_tx_wr_dat_aud_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET_AUD); + +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT + /* frame is sent, increment link tx counter */ + context->tx_bytes += context->frame->actual_len; +#endif +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_VERIFY_FRAMES + { + u8 channel; + + channel = ipc_util_get_l2_channel(*(u32 *)context->frame->data); + if (ipc_util_channel_is_loopback(channel)) { + /* create a copy of the frame */ + context->last_frame = ipc_queue_new_frame(context, + context->frame->actual_len); + memcpy(context->last_frame->data, + context->frame->data, + context->frame->actual_len); + } + } +#endif + return ipc_sm_state(IPC_SM_SLW_RX_WR_DAT_AUD); +} + +static u8 sm_slw_tx_rd_cmd_enter(u8 event, struct ipc_link_context *context) +{ + context->cmd = ipc_util_make_l1_header(CMD_READ, 0, 0); + dev_dbg(&context->sdev->dev, + "link %d: cmd %08x (type %d)\n", + context->link->id, + context->cmd, + ipc_util_get_l1_cmd(context->cmd)); + + /* prepare the spi message to transfer */ + ipc_util_spi_message_prepare(context, &context->cmd, + NULL, IPC_L1_HDR_SIZE); + + /* check if the slave requested this transaction */ + if (event == IPC_SM_RUN_SLAVE_IRQ) { + dev_dbg(&context->sdev->dev, + "link %d: slave initiated transaction, continue\n", + context->link->id); + ipc_util_activate_ss(context); + return IPC_SM_RUN_SLAVE_IRQ; + } else { + /* slave might already have signalled ready to transmit */ + if (atomic_read(&context->state_int)) { + dev_dbg(&context->sdev->dev, + "link %d: slave has already signalled ready\n", + context->link->id); + ipc_util_activate_ss(context); + return IPC_SM_RUN_SLAVE_IRQ; + } else { + ipc_util_activate_ss_with_tmo(context); + return IPC_SM_RUN_NONE; + } + } +} + +static const struct ipc_sm_state *sm_slw_tx_rd_cmd_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET); + else if (event == IPC_SM_RUN_COMMS_TMO) + return ipc_sm_state(IPC_SM_HALT); + else + return ipc_sm_state(IPC_SM_ACT_TX_RD_CMD); +} + +static u8 sm_act_tx_rd_cmd_enter(u8 event, struct ipc_link_context *context) +{ + int err; + + /* slave is ready - start the spi transfer */ + dev_dbg(&context->sdev->dev, + "link %d: starting spi tfr\n", context->link->id); + err = spi_async(context->sdev, &context->spi_message); + if (err < 0) { + dev_err(&context->sdev->dev, + "link %d error: spi tfr start failed, error %d\n", + context->link->id, err); + return IPC_SM_RUN_ABORT; + } + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_act_tx_rd_cmd_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET); + else + return ipc_sm_state(IPC_SM_SLW_RX_WR_CMD); +} + +static u8 sm_slw_rx_wr_cmd_enter(u8 event, struct ipc_link_context *context) +{ + /* prepare to receive MESSAGE WRITE frame header */ + ipc_util_spi_message_prepare(context, NULL, + &context->cmd, IPC_L1_HDR_SIZE); + + /* slave might already have signalled ready to transmit */ + if (atomic_read(&context->state_int)) { + dev_dbg(&context->sdev->dev, + "link %d: slave has already signalled ready\n", + context->link->id); + ipc_util_activate_ss(context); + return IPC_SM_RUN_SLAVE_IRQ; + } else { + ipc_util_activate_ss_with_tmo(context); + return IPC_SM_RUN_NONE; + } +} + +static const struct ipc_sm_state *sm_slw_rx_wr_cmd_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET); + else if (event == IPC_SM_RUN_COMMS_TMO) + return ipc_sm_state(IPC_SM_HALT); + else + return ipc_sm_state(IPC_SM_ACT_RX_WR_CMD); +} + +static u8 sm_act_rx_wr_cmd_enter(u8 event, struct ipc_link_context *context) +{ + int err; + + /* slave is ready - start the spi transfer */ + dev_dbg(&context->sdev->dev, + "link %d: starting spi tfr\n", context->link->id); + err = spi_async(context->sdev, &context->spi_message); + if (err < 0) { + dev_err(&context->sdev->dev, + "link %d error: spi tfr start failed, error %d\n", + context->link->id, err); + return IPC_SM_RUN_ABORT; + } + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_act_rx_wr_cmd_exit(u8 event, + struct ipc_link_context *context) +{ + u8 cmd_type = ipc_util_get_l1_cmd(context->cmd); + int counter = ipc_util_get_l1_counter(context->cmd); + int length = ipc_util_get_l1_length(context->cmd); + + dev_dbg(&context->sdev->dev, + "link %d: RX HEADER %08x (type %d counter %d length %d)\n", + context->link->id, + context->cmd, + cmd_type, + counter, + length); + + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET); + + if (cmd_type == CMD_WRITE) { + /* slave has data to send - allocate a frame to hold it */ + context->frame = ipc_queue_new_frame(context, length); + if (context->frame == NULL) + return ipc_sm_state(IPC_SM_IDL); + + context->frame->counter = counter; + ipc_util_spi_message_prepare(context, NULL, + context->frame->data, context->frame->len); + return ipc_sm_state(IPC_SM_ACT_RX_WR_DAT); + } else { + if (cmd_type != 0) + dev_err(&context->sdev->dev, + "link %d error: received invalid frame type %x " + "(%08x)! assuming TRANSACTION_END...\n", + context->link->id, + cmd_type, + context->cmd); + + /* slave has no data to send */ + dev_dbg(&context->sdev->dev, + "link %d: slave has no data to send\n", + context->link->id); + return ipc_sm_state(IPC_SM_IDL); + } +} + +static u8 sm_slw_rx_wr_dat_aud_enter(u8 event, struct ipc_link_context *context) +{ + /* + * We're using the same frame buffer we just sent, so no need for a + * new allocation here, just prepare the spi message + */ + ipc_util_spi_message_prepare(context, NULL, + context->frame->data, context->frame->len); + + /* slave might already have signalled ready to transmit */ + if (atomic_read(&context->state_int)) { + dev_dbg(&context->sdev->dev, + "link %d: slave has already signalled ready\n", + context->link->id); + ipc_util_activate_ss(context); + return IPC_SM_RUN_SLAVE_IRQ; + } else { + ipc_util_activate_ss_with_tmo(context); + return IPC_SM_RUN_NONE; + } +} + +static const struct ipc_sm_state *sm_slw_rx_wr_dat_aud_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET_AUD); + else if (event == IPC_SM_RUN_COMMS_TMO) + return ipc_sm_state(IPC_SM_HALT_AUD); + else + return ipc_sm_state(IPC_SM_ACT_RX_WR_DAT_AUD); +} + +static u8 sm_act_rx_wr_dat_enter(u8 event, struct ipc_link_context *context) +{ + int err; + + /* assume slave is still ready - prepare and start the spi transfer */ + ipc_util_spi_message_prepare(context, NULL, + context->frame->data, context->frame->len); + + dev_dbg(&context->sdev->dev, + "link %d: starting spi tfr\n", context->link->id); + err = spi_async(context->sdev, &context->spi_message); + if (err < 0) { + dev_err(&context->sdev->dev, + "link %d error: spi tfr start failed, error %d\n", + context->link->id, err); + return IPC_SM_RUN_ABORT; + } + return IPC_SM_RUN_NONE; +} + +static u8 sm_act_rx_wr_dat_aud_enter(u8 event, struct ipc_link_context *context) +{ + int err; + + dev_dbg(&context->sdev->dev, + "link %d: starting spi tfr\n", context->link->id); + err = spi_async(context->sdev, &context->spi_message); + if (err < 0) { + dev_err(&context->sdev->dev, + "link %d error: spi tfr start failed, error %d\n", + context->link->id, err); + return IPC_SM_RUN_ABORT; + } + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_act_rx_wr_dat_exit(u8 event, + struct ipc_link_context *context) +{ + u32 frame_hdr; + unsigned char l2_header; + unsigned int l2_length; + u8 *l2_data; + + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET); + + dev_dbg(&context->sdev->dev, + "link %d: RX PAYLOAD %d bytes\n", + context->link->id, context->frame->len); + +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT + /* frame is received, increment link rx counter */ + context->rx_bytes += context->frame->len; +#endif + /* decode L2 header */ + frame_hdr = *(u32 *)context->frame->data; + l2_header = ipc_util_get_l2_channel(frame_hdr); + l2_length = ipc_util_get_l2_length(frame_hdr); + l2_data = (u8 *)context->frame->data + IPC_L2_HDR_SIZE; + + context->frame->actual_len = l2_length + IPC_L2_HDR_SIZE; + ipc_dbg_dump_frame(&context->sdev->dev, context->link->id, + context->frame, false); + + if (l2_length > (context->frame->len - 4)) { + dev_err(&context->sdev->dev, + "link %d: suspicious frame: L1 len %d L2 len %d\n", + context->link->id, context->frame->len, l2_length); + } + + dev_dbg(&context->sdev->dev, + "link %d: L2 PDU decode: header 0x%08x channel %d length %d " + "data[%02x%02x%02x...]\n", + context->link->id, frame_hdr, l2_header, l2_length, + l2_data[0], l2_data[1], l2_data[2]); + + if (ipc_util_channel_is_loopback(l2_header)) + ipc_dbg_verify_rx_frame(context); + + /* pass received frame up to L2mux layer */ + if (!modem_protocol_channel_is_open(l2_header)) { + dev_err(&context->sdev->dev, + "link %d error: received frame on invalid channel %d, " + "frame discarded\n", + context->link->id, l2_header); + } else { +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT + /* + * Discard loopback frames if we are taking throughput + * measurements - we'll be loading the links and so will likely + * overload the buffers. + */ + if (!ipc_util_channel_is_loopback(l2_header)) +#endif + modem_m6718_spi_receive(context->sdev, + l2_header, l2_length, l2_data); + } + + /* data is copied by L2mux so free the frame here */ + ipc_queue_delete_frame(context->frame); + context->frame = NULL; + + /* check tx queue for content */ + if (!ipc_queue_is_empty(context)) { + dev_dbg(&context->sdev->dev, + "link %d: tx queue not empty\n", context->link->id); + return ipc_sm_state(IPC_SM_SLW_TX_WR_CMD); + } else { + dev_dbg(&context->sdev->dev, + "link %d: tx queue empty\n", context->link->id); + return ipc_sm_state(IPC_SM_SLW_TX_RD_CMD); + } +} + +static const struct ipc_sm_state *sm_act_rx_wr_dat_aud_exit(u8 event, + struct ipc_link_context *context) +{ + u32 frame_hdr; + unsigned char l2_header; + unsigned int l2_length; + u8 *l2_data; + + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET_AUD); + + dev_dbg(&context->sdev->dev, + "link %d: RX PAYLOAD %d bytes\n", + context->link->id, context->frame->len); + + /* decode L2 header */ + frame_hdr = *(u32 *)context->frame->data; + l2_header = ipc_util_get_l2_channel(frame_hdr); + l2_length = ipc_util_get_l2_length(frame_hdr); + l2_data = (u8 *)context->frame->data + IPC_L2_HDR_SIZE; + +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT + /* frame is received, increment link rx counter */ + context->rx_bytes += l2_length; +#endif + if (frame_hdr != 0) + context->frame->actual_len = l2_length + IPC_L2_HDR_SIZE; + else + context->frame->actual_len = 0; + ipc_dbg_dump_frame(&context->sdev->dev, context->link->id, + context->frame, false); + + if (l2_length > (context->frame->len - 4)) + dev_err(&context->sdev->dev, + "link %d: suspicious frame: L1 len %d L2 len %d\n", + context->link->id, context->frame->len, l2_length); + + dev_dbg(&context->sdev->dev, + "link %d: L2 PDU decode: header 0x%08x channel %d length %d " + "data[%02x%02x%02x...]\n", + context->link->id, frame_hdr, l2_header, l2_length, + l2_data[0], l2_data[1], l2_data[2]); + + if (ipc_util_channel_is_loopback(l2_header)) + ipc_dbg_verify_rx_frame(context); + + /* did the slave actually have anything to send? */ + if (frame_hdr != 0) { + /* pass received frame up to L2mux layer */ + if (!modem_protocol_channel_is_open(l2_header)) { + dev_err(&context->sdev->dev, + "link %d error: received frame on invalid " + "channel %d, frame discarded\n", + context->link->id, l2_header); + } else { +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT + /* + * Discard loopback frames if we are taking throughput + * measurements - we'll be loading the links and so will + * likely overload the buffers. + */ + if (!ipc_util_channel_is_loopback(l2_header)) +#endif + modem_m6718_spi_receive(context->sdev, + l2_header, l2_length, l2_data); + } + } else { + dev_dbg(&context->sdev->dev, + "link %d: received dummy frame, discarding\n", + context->link->id); + } + + /* data is copied by L2mux so free the frame here */ + ipc_queue_delete_frame(context->frame); + context->frame = NULL; + + /* audio link goes idle ready for next transaction */ + return ipc_sm_state(IPC_SM_IDL_AUD); +} + +static u8 sm_halt_enter(u8 event, struct ipc_link_context *context) +{ + dev_err(&context->sdev->dev, + "link %d error: HALTED\n", context->link->id); + +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE + /* + * Force modem reset, this will cause a reset event from the modemstate + * driver which will reset the links. If debugfs is enabled then there + * is a userspace file which controls whether MSR is enabled or not. + */ +#ifdef CONFIG_DEBUG_FS + if (l1_context.msr_disable) { + dev_info(&context->sdev->dev, + "link %d: MSR is disabled by user, " + "not requesting modem reset\n", context->link->id); + return IPC_SM_RUN_RESET; + } +#endif + modem_state_force_reset(); +#endif + return IPC_SM_RUN_RESET; +} + +static const struct ipc_sm_state *sm_halt_exit(u8 event, + struct ipc_link_context *context) +{ + return ipc_sm_state(IPC_SM_RESET); +} + +static const struct ipc_sm_state *sm_halt_aud_exit(u8 event, + struct ipc_link_context *context) +{ + return ipc_sm_state(IPC_SM_RESET_AUD); +} + +static u8 sm_reset_enter(u8 event, struct ipc_link_context *context) +{ + dev_err(&context->sdev->dev, + "link %d resetting\n", context->link->id); + + if (context->link->id == 0) + ipc_broadcast_modem_reset(context); + + ipc_util_deactivate_ss(context); + ipc_queue_reset(context); + if (context->frame != NULL) { + ipc_queue_delete_frame(context->frame); + context->frame = NULL; + } +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_VERIFY_FRAMES + if (context->last_frame != NULL) { + ipc_queue_delete_frame(context->last_frame); + context->last_frame = NULL; + } +#endif + dev_dbg(&context->sdev->dev, + "link %d reset completed\n", context->link->id); + + return IPC_SM_RUN_RESET; +} + +static const struct ipc_sm_state *sm_reset_exit(u8 event, + struct ipc_link_context *context) +{ + return ipc_sm_state(IPC_SM_INIT); +} + +static const struct ipc_sm_state *sm_reset_aud_exit(u8 event, + struct ipc_link_context *context) +{ + return ipc_sm_state(IPC_SM_INIT_AUD); +} + +static u8 sm_slw_tx_bootreq_enter(u8 event, struct ipc_link_context *context) +{ + dev_info(&context->sdev->dev, + "link %d: waiting for boot sync\n", context->link->id); + + ipc_util_activate_ss(context); + context->cmd = ipc_util_make_l1_header(CMD_BOOTREQ, 0, + IPC_DRIVER_VERSION); + dev_dbg(&context->sdev->dev, + "link %d: TX HEADER cmd %08x (type %x)\n", + context->link->id, + context->cmd, + ipc_util_get_l1_cmd(context->cmd)); + ipc_util_spi_message_prepare(context, &context->cmd, + NULL, IPC_L1_HDR_SIZE); + + /* wait now for the slave to indicate ready... */ + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_slw_tx_bootreq_exit(u8 event, + struct ipc_link_context *context) +{ + return ipc_sm_state(IPC_SM_ACT_TX_BOOTREQ); +} + +static u8 sm_act_tx_bootreq_enter(u8 event, struct ipc_link_context *context) +{ + int err; + + /* slave is ready - start the spi transfer */ + dev_dbg(&context->sdev->dev, + "link %d: starting spi tfr\n", context->link->id); + err = spi_async(context->sdev, &context->spi_message); + if (err < 0) { + dev_err(&context->sdev->dev, + "link %d error: spi tfr start failed, error %d\n", + context->link->id, err); + return IPC_SM_RUN_ABORT; + } + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_act_tx_bootreq_exit(u8 event, + struct ipc_link_context *context) +{ + return ipc_sm_state(IPC_SM_SLW_RX_BOOTRESP); +} + +static u8 sm_slw_rx_bootresp_enter(u8 event, struct ipc_link_context *context) +{ + /* prepare to receive BOOTRESP frame header */ + ipc_util_spi_message_prepare(context, NULL, + &context->cmd, IPC_L1_HDR_SIZE); + + /* slave might already have signalled ready to transmit */ + if (atomic_read(&context->state_int)) { + dev_dbg(&context->sdev->dev, + "link %d: slave has already signalled ready\n", + context->link->id); + ipc_util_activate_ss(context); + return IPC_SM_RUN_SLAVE_IRQ; + } else { + ipc_util_activate_ss_with_tmo(context); + return IPC_SM_RUN_NONE; + } +} + +static const struct ipc_sm_state *sm_slw_rx_bootresp_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_COMMS_TMO) { + /* + * Modem timeout: was it really ready or just noise? + * Revert to waiting for handshake to start. + */ + ipc_util_deactivate_ss(context); + return ipc_sm_state(IPC_SM_SLW_TX_BOOTREQ); + } else { + return ipc_sm_state(IPC_SM_ACT_RX_BOOTRESP); + } +} + +static u8 sm_act_rx_bootresp_enter(u8 event, struct ipc_link_context *context) +{ + int err; + + /* slave is ready - start the spi transfer */ + dev_dbg(&context->sdev->dev, + "link %d: starting spi tfr\n", context->link->id); + err = spi_async(context->sdev, &context->spi_message); + if (err < 0) { + dev_err(&context->sdev->dev, + "link %d error: spi tfr start failed, error %d\n", + context->link->id, err); + return IPC_SM_RUN_ABORT; + } + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_act_rx_bootresp_exit(u8 event, + struct ipc_link_context *context) +{ + u8 cmd_type = ipc_util_get_l1_cmd(context->cmd); + u8 modem_ver; + + dev_dbg(&context->sdev->dev, + "link %d: RX HEADER %08x (type %d)\n", + context->link->id, context->cmd, cmd_type); + + if (cmd_type == CMD_BOOTRESP) { + modem_ver = ipc_util_get_l1_bootresp_ver(context->cmd); + + dev_info(&context->sdev->dev, + "link %d: boot sync done; " + "APE version %02x, MODEM version %02x\n", + context->link->id, IPC_DRIVER_VERSION, modem_ver); + + /* check for minimum required modem version */ + if (modem_ver < IPC_DRIVER_MODEM_MIN_VER) { + dev_warn(&context->sdev->dev, + "link %d warning: modem version mismatch! " + "minimum required version is %02x\n", + context->link->id, + IPC_DRIVER_MODEM_MIN_VER); + } + + return ipc_sm_state(IPC_SM_WAIT_HANDSHAKE_INACTIVE); + } else { + /* invalid response... this is not our slave */ + dev_err(&context->sdev->dev, + "link %d error: expected %x (BOOTRESP), received %x.\n", + context->link->id, + CMD_BOOTRESP, + cmd_type); + return ipc_sm_state(IPC_SM_HALT); + } +} + +/* the driver protocol state machine */ +static const struct ipc_sm_state state_machine[IPC_SM_STATE_ID_NBR] = { + [IPC_SM_INIT] = { + .id = IPC_SM_INIT, + .enter = sm_init_enter, + .exit = sm_init_exit, + .events = IPC_SM_RUN_INIT | IPC_SM_RUN_RESET + }, + [IPC_SM_HALT] = { + .id = IPC_SM_HALT, + .enter = sm_halt_enter, + .exit = sm_halt_exit, + .events = IPC_SM_RUN_RESET + }, + [IPC_SM_RESET] = { + .id = IPC_SM_RESET, + .enter = sm_reset_enter, + .exit = sm_reset_exit, + .events = IPC_SM_RUN_RESET + }, + [IPC_SM_WAIT_SLAVE_STABLE] = { + .id = IPC_SM_WAIT_SLAVE_STABLE, + .enter = sm_wait_slave_stable_enter, + .exit = sm_wait_slave_stable_exit, + .events = IPC_SM_RUN_STABLE_TMO + }, + [IPC_SM_WAIT_HANDSHAKE_INACTIVE] = { + .id = IPC_SM_WAIT_HANDSHAKE_INACTIVE, + .enter = sm_wait_handshake_inactive_enter, + .exit = sm_wait_handshake_inactive_exit, + .events = IPC_SM_RUN_STABLE_TMO + }, + [IPC_SM_SLW_TX_BOOTREQ] = { + .id = IPC_SM_SLW_TX_BOOTREQ, + .enter = sm_slw_tx_bootreq_enter, + .exit = sm_slw_tx_bootreq_exit, + .events = IPC_SM_RUN_SLAVE_IRQ + }, + [IPC_SM_ACT_TX_BOOTREQ] = { + .id = IPC_SM_ACT_TX_BOOTREQ, + .enter = sm_act_tx_bootreq_enter, + .exit = sm_act_tx_bootreq_exit, + .events = IPC_SM_RUN_TFR_COMPLETE + }, + [IPC_SM_SLW_RX_BOOTRESP] = { + .id = IPC_SM_SLW_RX_BOOTRESP, + .enter = sm_slw_rx_bootresp_enter, + .exit = sm_slw_rx_bootresp_exit, + .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_COMMS_TMO + }, + [IPC_SM_ACT_RX_BOOTRESP] = { + .id = IPC_SM_ACT_RX_BOOTRESP, + .enter = sm_act_rx_bootresp_enter, + .exit = sm_act_rx_bootresp_exit, + .events = IPC_SM_RUN_TFR_COMPLETE + }, + [IPC_SM_IDL] = { + .id = IPC_SM_IDL, + .enter = sm_idl_enter, + .exit = sm_idl_exit, + .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_TX_REQ | + IPC_SM_RUN_RESET + }, + [IPC_SM_SLW_TX_WR_CMD] = { + .id = IPC_SM_SLW_TX_WR_CMD, + .enter = sm_slw_tx_wr_cmd_enter, + .exit = sm_slw_tx_wr_cmd_exit, + .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_COMMS_TMO | + IPC_SM_RUN_RESET + }, + [IPC_SM_ACT_TX_WR_CMD] = { + .id = IPC_SM_ACT_TX_WR_CMD, + .enter = sm_act_tx_wr_cmd_enter, + .exit = sm_act_tx_wr_cmd_exit, + .events = IPC_SM_RUN_TFR_COMPLETE | IPC_SM_RUN_RESET + }, + [IPC_SM_SLW_TX_WR_DAT] = { + .id = IPC_SM_SLW_TX_WR_DAT, + .enter = sm_slw_tx_wr_dat_enter, + .exit = sm_slw_tx_wr_dat_exit, + .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_COMMS_TMO | + IPC_SM_RUN_RESET + }, + [IPC_SM_ACT_TX_WR_DAT] = { + .id = IPC_SM_ACT_TX_WR_DAT, + .enter = sm_act_tx_wr_dat_enter, + .exit = sm_act_tx_wr_dat_exit, + .events = IPC_SM_RUN_TFR_COMPLETE | IPC_SM_RUN_RESET + }, + [IPC_SM_SLW_TX_RD_CMD] = { + .id = IPC_SM_SLW_TX_RD_CMD, + .enter = sm_slw_tx_rd_cmd_enter, + .exit = sm_slw_tx_rd_cmd_exit, + .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_COMMS_TMO | + IPC_SM_RUN_RESET + }, + [IPC_SM_ACT_TX_RD_CMD] = { + .id = IPC_SM_ACT_TX_RD_CMD, + .enter = sm_act_tx_rd_cmd_enter, + .exit = sm_act_tx_rd_cmd_exit, + .events = IPC_SM_RUN_TFR_COMPLETE | IPC_SM_RUN_RESET + }, + [IPC_SM_SLW_RX_WR_CMD] = { + .id = IPC_SM_SLW_RX_WR_CMD, + .enter = sm_slw_rx_wr_cmd_enter, + .exit = sm_slw_rx_wr_cmd_exit, + .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_COMMS_TMO | + IPC_SM_RUN_RESET + }, + [IPC_SM_ACT_RX_WR_CMD] = { + .id = IPC_SM_ACT_RX_WR_CMD, + .enter = sm_act_rx_wr_cmd_enter, + .exit = sm_act_rx_wr_cmd_exit, + .events = IPC_SM_RUN_TFR_COMPLETE | IPC_SM_RUN_RESET + }, + [IPC_SM_ACT_RX_WR_DAT] = { + .id = IPC_SM_ACT_RX_WR_DAT, + .enter = sm_act_rx_wr_dat_enter, + .exit = sm_act_rx_wr_dat_exit, + .events = IPC_SM_RUN_TFR_COMPLETE | IPC_SM_RUN_RESET + }, + /* audio link states below */ + [IPC_SM_INIT_AUD] = { + .id = IPC_SM_INIT_AUD, + .enter = sm_init_enter, + .exit = sm_init_aud_exit, + .events = IPC_SM_RUN_INIT | IPC_SM_RUN_RESET + }, + [IPC_SM_HALT_AUD] = { + .id = IPC_SM_HALT_AUD, + .enter = sm_halt_enter, + .exit = sm_halt_aud_exit, + .events = IPC_SM_RUN_RESET + }, + [IPC_SM_RESET_AUD] = { + .id = IPC_SM_RESET_AUD, + .enter = sm_reset_enter, + .exit = sm_reset_aud_exit, + .events = IPC_SM_RUN_RESET + }, + [IPC_SM_IDL_AUD] = { + .id = IPC_SM_IDL_AUD, + .enter = sm_idl_enter, + .exit = sm_idl_aud_exit, + .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_TX_REQ | + IPC_SM_RUN_RESET + }, + [IPC_SM_SLW_TX_WR_DAT_AUD] = { + .id = IPC_SM_SLW_TX_WR_DAT_AUD, + .enter = sm_slw_tx_wr_dat_aud_enter, + .exit = sm_slw_tx_wr_dat_aud_exit, + .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_COMMS_TMO | + IPC_SM_RUN_RESET + }, + [IPC_SM_ACT_TX_WR_DAT_AUD] = { + .id = IPC_SM_ACT_TX_WR_DAT_AUD, + .enter = sm_act_tx_wr_dat_enter, + .exit = sm_act_tx_wr_dat_aud_exit, + .events = IPC_SM_RUN_TFR_COMPLETE | IPC_SM_RUN_RESET + }, + [IPC_SM_SLW_RX_WR_DAT_AUD] = { + .id = IPC_SM_SLW_RX_WR_DAT_AUD, + .enter = sm_slw_rx_wr_dat_aud_enter, + .exit = sm_slw_rx_wr_dat_aud_exit, + .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_COMMS_TMO | + IPC_SM_RUN_RESET + }, + [IPC_SM_ACT_RX_WR_DAT_AUD] = { + .id = IPC_SM_ACT_RX_WR_DAT_AUD, + .enter = sm_act_rx_wr_dat_aud_enter, + .exit = sm_act_rx_wr_dat_aud_exit, + .events = IPC_SM_RUN_TFR_COMPLETE | IPC_SM_RUN_RESET + } +}; + + +const struct ipc_sm_state *ipc_sm_idle_state(struct ipc_link_context *context) +{ + if (context->link->id == IPC_LINK_AUDIO) + return ipc_sm_state(IPC_SM_IDL_AUD); + else + return ipc_sm_state(IPC_SM_IDL); +} + +const struct ipc_sm_state *ipc_sm_init_state(struct ipc_link_context *context) +{ + if (context->link->id == IPC_LINK_AUDIO) + return ipc_sm_state(IPC_SM_INIT_AUD); + else + return ipc_sm_state(IPC_SM_INIT); +} + +const struct ipc_sm_state *ipc_sm_state(u8 id) +{ + BUG_ON(id >= IPC_SM_STATE_ID_NBR); + return &state_machine[id]; +} + +bool ipc_sm_valid_for_state(u8 event, const struct ipc_sm_state *state) +{ + return (state->events & event) == event; +} + +static void state_machine_run(struct ipc_link_context *context, u8 event) +{ + struct modem_m6718_spi_link_platform_data *link = context->link; + struct spi_device *sdev = context->sdev; + const struct ipc_sm_state *cur_state = context->state; + + /* some sanity checking */ + if (context == NULL || link == NULL || cur_state == NULL) { + pr_err("M6718 IPC protocol error: " + "inconsistent driver state, ignoring event\n"); + return; + } + + dev_dbg(&sdev->dev, "link %d: RUNNING in %s (%s)\n", link->id, + ipc_dbg_state_id(cur_state), ipc_dbg_event(event)); + + /* valid trigger event for current state? */ + if (!ipc_sm_valid_for_state(event, cur_state)) { + dev_dbg(&sdev->dev, + "link %d: ignoring invalid event\n", link->id); + ipc_dbg_ignoring_event(context, event); + return; + } + ipc_dbg_handling_event(context, event); + + /* run machine while state entry functions trigger new changes */ + do { + if (event == IPC_SM_RUN_SLAVE_IRQ && + !ipc_util_int_is_active(context)) { + dev_err(&sdev->dev, + "link %d error: slave is not ready! (%s)", + link->id, + ipc_dbg_state_id(cur_state)); + } + + if (event == IPC_SM_RUN_ABORT) { + dev_err(&sdev->dev, + "link %d error: abort event\n", link->id); + /* reset state to idle */ + context->state = ipc_sm_idle_state(context); + break; + } else { + /* exit current state */ + dev_dbg(&sdev->dev, "link %d: exit %s (%s)\n", + link->id, ipc_dbg_state_id(cur_state), + ipc_dbg_event(event)); + cur_state = cur_state->exit(event, context); + context->state = cur_state; + } + + /* reset state of slave irq to prepare for next event */ + if (event == IPC_SM_RUN_SLAVE_IRQ) + atomic_set(&context->state_int, 0); + + /* enter new state */ + dev_dbg(&sdev->dev, "link %d: enter %s (%s)\n", link->id, + ipc_dbg_state_id(cur_state), ipc_dbg_event(event)); + event = context->state->enter(event, context); + ipc_dbg_entering_state(context); + } while (event != IPC_SM_RUN_NONE); + + dev_dbg(&sdev->dev, "link %d: STOPPED in %s\n", link->id, + ipc_dbg_state_id(cur_state)); +} + +void ipc_sm_kick(u8 event, struct ipc_link_context *context) +{ + unsigned long flags; + struct modem_m6718_spi_link_platform_data *link = context->link; + struct spi_device *sdev = context->sdev; + struct spi_message *msg = &context->spi_message; + u8 i; + + spin_lock_irqsave(&context->sm_lock, flags); + switch (event) { + case IPC_SM_RUN_SLAVE_IRQ: + dev_dbg(&sdev->dev, + "link %d EVENT: slave-ready irq\n", link->id); + del_timer(&context->comms_timer); + atomic_set(&context->state_int, + ipc_util_int_is_active(context)); + break; + + case IPC_SM_RUN_TFR_COMPLETE: + dev_dbg(&sdev->dev, + "link %d EVENT: spi tfr complete (status %d len %d)\n", + link->id, msg->status, msg->actual_length); + ipc_dbg_dump_spi_tfr(context); + break; + + case IPC_SM_RUN_COMMS_TMO: + { + char *statestr; + struct ipc_link_context *contexts = l1_context.device_context; + + statestr = ipc_dbg_link_state_str(context); + dev_err(&sdev->dev, + "link %d EVENT: modem comms timeout (%s)!\n", + link->id, ipc_dbg_state_id(context->state)); + if (statestr != NULL) { + dev_err(&sdev->dev, "%s", statestr); + kfree(statestr); + } + + /* cancel all link timeout timers except this one */ + for (i = 0; i < IPC_NBR_SUPPORTED_SPI_LINKS; i++) + if (contexts[i].link->id != link->id) + del_timer(&contexts[i].comms_timer); + break; + } + + case IPC_SM_RUN_STABLE_TMO: + dev_dbg(&sdev->dev, + "link %d EVENT: slave-stable timeout\n", link->id); + break; + + case IPC_SM_RUN_RESET: + dev_dbg(&sdev->dev, + "link %d EVENT: reset\n", link->id); + del_timer(&context->comms_timer); + break; + + default: + break; + } + + state_machine_run(context, event); + spin_unlock_irqrestore(&context->sm_lock, flags); +} + -- cgit v1.2.3 From 61c8a3297f88a79648271a694a03fd44f29d40c8 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Tue, 20 Dec 2011 09:39:07 +0530 Subject: u8500-shrm: maintain sleep statemachine in real time ac_sleep_disable_count is flag used t control the power management state machine this counter is increased in 3 places ca_wake_req ac_msg_pend_notify_0 ac-msg_pend_notify_1 and decremented in 3 places ac_read_notify_0 ac_read_notify_1 ca_sleep_req ac_msg_pend_notify is called from 2 places, shm_write_msg ac_read_notify it gets called from "shm_write_msg" when there is only one message in the shared memory and otherwise from "ac_read_notify" LOGS <6>[ 72.793395] send_ac_msg_pend_notify_1_work I:2 <6>[ 72.793609] shm_ac_read_notif_1_tasklet D:1 <6>[ 72.793731] send_ac_msg_pend_notify_1_work I:2 <6>[ 72.793853] send_ac_msg_pend_notify_1_work I:3 <6>[ 72.793914] shm_ac_read_notif_1_tasklet D:2 <6>[ 72.804840] send_ac_msg_pend_notify_1_work I:3 <6>[ 72.805053] shm_ac_read_notif_1_tasklet D:2 From the above logs send_ac_msg_pend_notify_1_work is called twice. No problem if this is called twice, but for state machine counter ac_sleep_disable_count this causes problem which doesnt allow the system to enter sleep. This function is called twice in a real time seq, first time from the ac_read_notify, here assume that there is only one message, and hence after notifying modem will call ac_msg_pend_notify. Modem on receiving this interrupt will update the shared read pointer and send the ack. After updating the shared read pointer and before APE receiving the ACK, there is some msg write req coming to shrm. shm driver shm_write_msg is called. Here it finds that this is the only message in shared memory, because the shared read and write pointer are same. Hence send_ac_msg_pend_notify_1_work is called again. Even though msg_pend is notified twice we receive read_notification only onece. Counter ac_sleep_disable_count is incremented in ca_wake_req to 1. Then in ac_msg_pend_notify to 2, then decremented in ac_read_notify. Hence it doesnt increase beyond 2 Fix would be to check if ac_msg_pend is called twice before ac_read_notify is received, then in that case dont increment the ac_sleep_disable_count in ac_msg_pend_notify. ST-Ericsson Linux next: NA ST-Ericsson ID: 401826 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ie4ed0ae33b011a798d620a8db531fad8c23b1d7a Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/43169 Reviewed-by: Bibek BASU Reviewed-by: QABUILD Reviewed-by: Srinidhi KASAGAR --- drivers/modem/shrm/shrm_protocol.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index 6cc6e347188..cbb3a820317 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -48,6 +48,7 @@ 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 atomic_t ac_msg_pend_1 = ATOMIC_INIT(0); static struct shrm_dev *shm_dev; /* Spin lock and tasklet declaration */ @@ -440,6 +441,7 @@ void shm_ac_read_notif_1_tasklet(unsigned long tasklet_data) hrtimer_start(&timer, ktime_set(0, 10*NSEC_PER_MSEC), HRTIMER_MODE_REL); atomic_dec(&ac_sleep_disable_count); + atomic_dec(&ac_msg_pend_1); dev_dbg(shrm->dev, "%s OUT\n", __func__); } @@ -508,6 +510,7 @@ static int shrm_modem_reset_sequence(void) * for normal ac-wake and ac-sleep logic */ atomic_set(&ac_sleep_disable_count, 0); + atomic_set(&ac_msg_pend_1, 0); /* workaround for MSR */ queue_kthread_work(&shm_dev->shm_ac_wake_kw, @@ -665,7 +668,10 @@ static void send_ac_msg_pend_notify_1_work(struct kthread_work *work) update_ac_audio_shared_wptr(shrm); mutex_lock(&ac_state_mutex); - atomic_inc(&ac_sleep_disable_count); + if (!atomic_read(&ac_msg_pend_1)) { + atomic_inc(&ac_sleep_disable_count); + atomic_inc(&ac_msg_pend_1); + } modem_request(shrm->modem); mutex_unlock(&ac_state_mutex); -- cgit v1.2.3 From 4cf60a4301367bec5962ffb905c51a62c85bd1a6 Mon Sep 17 00:00:00 2001 From: Chris Blair Date: Wed, 4 Jan 2012 16:19:01 +0000 Subject: modem m6718: Handle modem irq before resume Handles the scenario where the modem wakes up the APE and the modem irq is serviced before the IPC driver is resumed. The event is regsitered and serviced later when the driver resume hook is called. ST-Ericsson ID: 405458 ST-Ericsson FOSS-OUT ID: Trivial ST-Ericsson Linux next: NA Change-Id: I441decea1cbc657e8dc13ca63a3116f50581c6a7 Signed-off-by: Chris Blair Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/44410 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Rickard EVERTSSON Reviewed-by: Linus WALLEIJ --- drivers/modem/m6718_spi/modem_driver.c | 34 +++++++++++++--- drivers/modem/m6718_spi/modem_private.h | 1 + drivers/modem/m6718_spi/modem_protocol.h | 2 + drivers/modem/m6718_spi/modem_util.h | 4 ++ drivers/modem/m6718_spi/protocol.c | 66 ++++++++++++++++++++++++++++++++ drivers/modem/m6718_spi/statemachine.c | 6 ++- drivers/modem/m6718_spi/util.c | 14 +++++++ 7 files changed, 121 insertions(+), 6 deletions(-) diff --git a/drivers/modem/m6718_spi/modem_driver.c b/drivers/modem/m6718_spi/modem_driver.c index 623a9191d27..8086e97aa7c 100644 --- a/drivers/modem/m6718_spi/modem_driver.c +++ b/drivers/modem/m6718_spi/modem_driver.c @@ -214,13 +214,25 @@ static int __exit spi_remove(struct spi_device *sdev) static int spi_suspend(struct spi_device *sdev, pm_message_t mesg) { bool busy; + int ret = -EBUSY; + dev_dbg(&sdev->dev, "suspend called\n"); busy = modem_protocol_is_busy(sdev); - dev_dbg(&sdev->dev, "suspend called, protocol busy:%d\n", busy); - if (!busy) - return modem_net_suspend(modem_driver_data.ndev); - else + if (busy) { + dev_warn(&sdev->dev, "suspend failed (protocol busy)\n"); return -EBUSY; + } + ret = modem_protocol_suspend(sdev); + if (ret) { + dev_warn(&sdev->dev, "suspend failed, (protocol suspend))\n"); + return ret; + } + ret = modem_net_suspend(modem_driver_data.ndev); + if (ret) { + dev_warn(&sdev->dev, "suspend failed, (netdev suspend)\n"); + return ret; + } + return 0; } /** @@ -229,8 +241,20 @@ static int spi_suspend(struct spi_device *sdev, pm_message_t mesg) */ static int spi_resume(struct spi_device *sdev) { + int ret; + dev_dbg(&sdev->dev, "resume called\n"); - return modem_net_resume(modem_driver_data.ndev); + ret = modem_protocol_resume(sdev); + if (ret) { + dev_warn(&sdev->dev, "resume failed, (protocol resume))\n"); + return ret; + } + ret = modem_net_resume(modem_driver_data.ndev); + if (ret) { + dev_warn(&sdev->dev, "resume failed, (netdev resume))\n"); + return ret; + } + return 0; } #endif /* CONFIG_PM */ diff --git a/drivers/modem/m6718_spi/modem_private.h b/drivers/modem/m6718_spi/modem_private.h index 929cb62d672..10e651c01ea 100644 --- a/drivers/modem/m6718_spi/modem_private.h +++ b/drivers/modem/m6718_spi/modem_private.h @@ -49,6 +49,7 @@ struct ipc_tx_queue { struct ipc_link_context { struct modem_m6718_spi_link_platform_data *link; struct spi_device *sdev; + atomic_t suspended; atomic_t gpio_configured; atomic_t state_int; spinlock_t sm_lock; diff --git a/drivers/modem/m6718_spi/modem_protocol.h b/drivers/modem/m6718_spi/modem_protocol.h index 69511945cb5..751dcba1087 100644 --- a/drivers/modem/m6718_spi/modem_protocol.h +++ b/drivers/modem/m6718_spi/modem_protocol.h @@ -18,5 +18,7 @@ int modem_protocol_probe(struct spi_device *sdev); void modem_protocol_exit(void); bool modem_protocol_is_busy(struct spi_device *sdev); bool modem_protocol_channel_is_open(u8 channel); +int modem_protocol_suspend(struct spi_device *sdev); +int modem_protocol_resume(struct spi_device *sdev); #endif /* _MODEM_PROTOCOL_H_ */ diff --git a/drivers/modem/m6718_spi/modem_util.h b/drivers/modem/m6718_spi/modem_util.h index 3219900a3c8..2d9e2e39abc 100644 --- a/drivers/modem/m6718_spi/modem_util.h +++ b/drivers/modem/m6718_spi/modem_util.h @@ -50,4 +50,8 @@ bool ipc_util_link_gpio_request(struct ipc_link_context *context, bool ipc_util_link_gpio_config(struct ipc_link_context *context); bool ipc_util_link_gpio_unconfig(struct ipc_link_context *context); +bool ipc_util_link_is_suspended(struct ipc_link_context *context); +void ipc_util_suspend_link(struct ipc_link_context *context); +void ipc_util_resume_link(struct ipc_link_context *context); + #endif /* _MODEM_UTIL_H_ */ diff --git a/drivers/modem/m6718_spi/protocol.c b/drivers/modem/m6718_spi/protocol.c index aa21d4dbcde..fa6b2528dd4 100644 --- a/drivers/modem/m6718_spi/protocol.c +++ b/drivers/modem/m6718_spi/protocol.c @@ -196,6 +196,71 @@ bool modem_protocol_is_busy(struct spi_device *sdev) return false; } +int modem_protocol_suspend(struct spi_device *sdev) +{ + struct modem_m6718_spi_link_platform_data *link = + sdev->dev.platform_data; + struct ipc_link_context *context; + int link_id; + + if (link == NULL) { + /* platform data missing in board config? */ + dev_err(&sdev->dev, "error: no platform data for link!\n"); + return -ENODEV; + } + + link_id = link->id; + context = &l1_context.device_context[link_id]; + + if (link_id >= IPC_NBR_SUPPORTED_SPI_LINKS) { + dev_err(&sdev->dev, + "link %d error: too many links! (max %d)\n", + link->id, IPC_NBR_SUPPORTED_SPI_LINKS); + return -ENODEV; + } + + ipc_util_suspend_link(context); + return 0; +} + +int modem_protocol_resume(struct spi_device *sdev) +{ + struct modem_m6718_spi_link_platform_data *link = + sdev->dev.platform_data; + struct ipc_link_context *context; + int link_id; + + if (link == NULL) { + /* platform data missing in board config? */ + dev_err(&sdev->dev, "error: no platform data for link!\n"); + return -ENODEV; + } + + link_id = link->id; + context = &l1_context.device_context[link_id]; + + if (link_id >= IPC_NBR_SUPPORTED_SPI_LINKS) { + dev_err(&sdev->dev, + "link %d error: too many links! (max %d)\n", + link->id, IPC_NBR_SUPPORTED_SPI_LINKS); + return -ENODEV; + } + + ipc_util_resume_link(context); + + /* + * If the resume event was an interrupt from the slave then the event + * is pending and we need to service it now. + */ + if (ipc_util_int_is_active(context)) { + dev_dbg(&sdev->dev, + "link %d: slave-ready is pending after resume\n", + link_id); + ipc_sm_kick(IPC_SM_RUN_SLAVE_IRQ, context); + } + return 0; +} + static void spi_tfr_complete(void *context) { ipc_sm_kick(IPC_SM_RUN_TFR_COMPLETE, @@ -306,6 +371,7 @@ int modem_protocol_probe(struct spi_device *sdev) /* init link context */ context->link = link; context->sdev = sdev; + ipc_util_resume_link(context); atomic_set(&context->gpio_configured, 0); atomic_set(&context->state_int, ipc_util_int_level_inactive(context)); diff --git a/drivers/modem/m6718_spi/statemachine.c b/drivers/modem/m6718_spi/statemachine.c index a2092cd2f94..a956661c3bf 100644 --- a/drivers/modem/m6718_spi/statemachine.c +++ b/drivers/modem/m6718_spi/statemachine.c @@ -1396,7 +1396,11 @@ void ipc_sm_kick(u8 event, struct ipc_link_context *context) break; } - state_machine_run(context, event); + if (!ipc_util_link_is_suspended(context)) + state_machine_run(context, event); + else + dev_dbg(&sdev->dev, + "link %d is suspended, waiting for resume\n", link->id); spin_unlock_irqrestore(&context->sm_lock, flags); } diff --git a/drivers/modem/m6718_spi/util.c b/drivers/modem/m6718_spi/util.c index 43862baff99..9c89eb9b34a 100644 --- a/drivers/modem/m6718_spi/util.c +++ b/drivers/modem/m6718_spi/util.c @@ -266,3 +266,17 @@ bool ipc_util_link_gpio_unconfig(struct ipc_link_context *context) return true; } +bool ipc_util_link_is_suspended(struct ipc_link_context *context) +{ + return atomic_read(&context->suspended) == 1; +} + +void ipc_util_suspend_link(struct ipc_link_context *context) +{ + atomic_set(&context->suspended, 1); +} + +void ipc_util_resume_link(struct ipc_link_context *context) +{ + atomic_set(&context->suspended, 0); +} -- cgit v1.2.3 From ecb5d200c0f2defd6890a192dc6681f38010becc Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Wed, 22 Feb 2012 15:46:16 +0100 Subject: mbox: Fix bad PRCMU include order Signed-off-by: Philippe Langlais --- drivers/misc/mbox.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/mbox.c b/drivers/misc/mbox.c index 4199f9b1247..d884496fa4c 100644 --- a/drivers/misc/mbox.c +++ b/drivers/misc/mbox.c @@ -41,8 +41,8 @@ #include #include #include -#include #include +#include #include #include -- cgit v1.2.3 From 67cbe0b8b9090fa4c8b91040714add0e3b02c3fd Mon Sep 17 00:00:00 2001 From: Chris Blair Date: Tue, 10 Jan 2012 09:53:46 +0000 Subject: modem m6718: Revert protocol to version 2 Changes the IPC protocol to version 0x02 which used the same protocol for both common and audio links. Since this protocol is not suitable for DMA transfers, the link must be configured in interrupt transfer mode. ST-Ericsson ID: 409301 ST-Ericsson FOSS-OUT ID: Trivial ST-Ericsson Linux next: NA Change-Id: Ifb7fc0ad008983e59bcfe97f59d9aea128613888 Signed-off-by: Chris Blair Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/44936 Reviewed-by: Srinidhi KASAGAR --- drivers/modem/m6718_spi/debug.c | 8 - drivers/modem/m6718_spi/modem_private.h | 4 +- drivers/modem/m6718_spi/modem_statemachine.h | 10 - drivers/modem/m6718_spi/protocol.c | 4 +- drivers/modem/m6718_spi/queue.c | 25 +-- drivers/modem/m6718_spi/statemachine.c | 325 +-------------------------- drivers/modem/m6718_spi/util.c | 1 - 7 files changed, 11 insertions(+), 366 deletions(-) diff --git a/drivers/modem/m6718_spi/debug.c b/drivers/modem/m6718_spi/debug.c index 522a37163c1..06ee4c34a5a 100644 --- a/drivers/modem/m6718_spi/debug.c +++ b/drivers/modem/m6718_spi/debug.c @@ -36,14 +36,6 @@ static const char * const sm_state_id_str[] = { "IPC_SLW_RX_WR_CMD", "IPC_ACT_RX_WR_CMD", "IPC_ACT_RX_WR_DAT", - "IPC_INIT_AUD", - "IPC_HALT_AUD", - "IPC_RESET_AUD", - "IPC_IDL_AUD", - "IPC_SLW_TX_WR_DAT_AUD", - "IPC_ACT_TX_WR_DAT_AUD", - "IPC_SLW_RX_WR_DAT_AUD", - "IPC_ACT_RX_WR_DAT_AUD", }; /* name of each state machine run cause */ diff --git a/drivers/modem/m6718_spi/modem_private.h b/drivers/modem/m6718_spi/modem_private.h index 10e651c01ea..a4de4ba9e93 100644 --- a/drivers/modem/m6718_spi/modem_private.h +++ b/drivers/modem/m6718_spi/modem_private.h @@ -24,8 +24,8 @@ #include "modem_protocol.h" #include "modem_statemachine.h" -#define IPC_DRIVER_VERSION (0x03) /* APE protocol version */ -#define IPC_DRIVER_MODEM_MIN_VER (0x03) /* version required from modem */ +#define IPC_DRIVER_VERSION (0x02) /* APE protocol version */ +#define IPC_DRIVER_MODEM_MIN_VER (0x02) /* version required from modem */ #define IPC_NBR_SUPPORTED_SPI_LINKS (2) #define IPC_LINK_COMMON (0) diff --git a/drivers/modem/m6718_spi/modem_statemachine.h b/drivers/modem/m6718_spi/modem_statemachine.h index 55e4a520d3d..6a2a32cad3a 100644 --- a/drivers/modem/m6718_spi/modem_statemachine.h +++ b/drivers/modem/m6718_spi/modem_statemachine.h @@ -15,7 +15,6 @@ /* valid states for the driver state machine */ enum ipc_sm_state_id { - /* common link and shared states below */ IPC_SM_INIT, IPC_SM_HALT, IPC_SM_RESET, @@ -35,15 +34,6 @@ enum ipc_sm_state_id { IPC_SM_SLW_RX_WR_CMD, IPC_SM_ACT_RX_WR_CMD, IPC_SM_ACT_RX_WR_DAT, - /* audio link states below */ - IPC_SM_INIT_AUD, - IPC_SM_HALT_AUD, - IPC_SM_RESET_AUD, - IPC_SM_IDL_AUD, - IPC_SM_SLW_TX_WR_DAT_AUD, - IPC_SM_ACT_TX_WR_DAT_AUD, - IPC_SM_SLW_RX_WR_DAT_AUD, - IPC_SM_ACT_RX_WR_DAT_AUD, IPC_SM_STATE_ID_NBR }; diff --git a/drivers/modem/m6718_spi/protocol.c b/drivers/modem/m6718_spi/protocol.c index fa6b2528dd4..38e9190e397 100644 --- a/drivers/modem/m6718_spi/protocol.c +++ b/drivers/modem/m6718_spi/protocol.c @@ -183,9 +183,7 @@ bool modem_protocol_is_busy(struct spi_device *sdev) for (i = 0; i < IPC_NBR_SUPPORTED_SPI_LINKS; i++) switch (l1_context.device_context[i].state->id) { case IPC_SM_IDL: - case IPC_SM_IDL_AUD: case IPC_SM_INIT: - case IPC_SM_INIT_AUD: case IPC_SM_WAIT_SLAVE_STABLE: /* not busy; continue checking */ break; @@ -280,7 +278,7 @@ static irqreturn_t slave_ready_irq(int irq, void *dev) } #ifdef WORKAROUND_DUPLICATED_IRQ - if (link->id != IPC_LINK_AUDIO && pl022_tfr_in_progress(sdev)) { + if (pl022_tfr_in_progress(sdev)) { dev_warn(&sdev->dev, "link %d warning: slave irq while transfer " "is active! discarding event\n", link->id); diff --git a/drivers/modem/m6718_spi/queue.c b/drivers/modem/m6718_spi/queue.c index 911d538ee82..fe23ac36736 100644 --- a/drivers/modem/m6718_spi/queue.c +++ b/drivers/modem/m6718_spi/queue.c @@ -14,9 +14,6 @@ #define FRAME_LENGTH_ALIGN (4) #define MAX_FRAME_COUNTER (256) -/* fixed L1 frame size for audio link: 4 byte L2 header + 664 byte L2 payload */ -#define FRAME_SIZE_AUDIO (668) - void ipc_queue_init(struct ipc_link_context *context) { spin_lock_init(&context->tx_q_update_lock); @@ -37,24 +34,10 @@ struct ipc_tx_queue *ipc_queue_new_frame(struct ipc_link_context *link_context, struct ipc_tx_queue *frame; u32 padded_len = l2_length; - /* audio link frames are always a fixed size */ - if (link_context->link->id == IPC_LINK_AUDIO) { - if (l2_length > FRAME_SIZE_AUDIO) { - dev_err(&link_context->sdev->dev, - "link %d error: invalid frame size %d " - "requested, max is %d\n", - link_context->link->id, - l2_length, - FRAME_SIZE_AUDIO); - return NULL; - } - padded_len = FRAME_SIZE_AUDIO; - } else { - /* frame length padded to alignment boundary */ - if (padded_len % FRAME_LENGTH_ALIGN) - padded_len += (FRAME_LENGTH_ALIGN - - (padded_len % FRAME_LENGTH_ALIGN)); - } + /* frame length padded to alignment boundary */ + if (padded_len % FRAME_LENGTH_ALIGN) + padded_len += (FRAME_LENGTH_ALIGN - + (padded_len % FRAME_LENGTH_ALIGN)); dev_dbg(&link_context->sdev->dev, "link %d: new frame: length %d, padded to %d\n", diff --git a/drivers/modem/m6718_spi/statemachine.c b/drivers/modem/m6718_spi/statemachine.c index a956661c3bf..bb047fcee18 100644 --- a/drivers/modem/m6718_spi/statemachine.c +++ b/drivers/modem/m6718_spi/statemachine.c @@ -84,35 +84,6 @@ static const struct ipc_sm_state *sm_init_exit(u8 event, } } -static const struct ipc_sm_state *sm_init_aud_exit(u8 event, - struct ipc_link_context *context) -{ - bool int_active = false; - - /* - * For reset event just re-enter init in case the modem has - * powered off - we need to reconfigure our GPIO pins - */ - if (event == IPC_SM_RUN_RESET) - return ipc_sm_state(IPC_SM_INIT_AUD); - - /* re-sample link INT pin */ - int_active = ipc_util_int_is_active(context); - atomic_set(&context->state_int, int_active); - - dev_info(&context->sdev->dev, - "link %d: link initialised; SS:INACTIVE(%d) INT:%s(%d)\n", - context->link->id, - ipc_util_ss_level_inactive(context), - int_active ? "ACTIVE" : "INACTIVE", - int_active ? ipc_util_int_level_active(context) : - ipc_util_int_level_inactive(context)); - dev_info(&context->sdev->dev, - "link %d: boot sync not needed, going idle\n", - context->link->id); - return ipc_sm_state(IPC_SM_IDL_AUD); -} - static u8 sm_wait_slave_stable_enter(u8 event, struct ipc_link_context *context) { static unsigned long printk_warn_time; @@ -219,17 +190,6 @@ static const struct ipc_sm_state *sm_idl_exit(u8 event, return ipc_sm_state(IPC_SM_HALT); } -static const struct ipc_sm_state *sm_idl_aud_exit(u8 event, - struct ipc_link_context *context) -{ - ipc_dbg_exit_idle(context); - if (event == IPC_SM_RUN_RESET) - return ipc_sm_state(IPC_SM_RESET_AUD); - - /* always transmit data first */ - return ipc_sm_state(IPC_SM_SLW_TX_WR_DAT_AUD); -} - static u8 sm_slw_tx_wr_cmd_enter(u8 event, struct ipc_link_context *context) { struct ipc_tx_queue *frame; @@ -328,43 +288,6 @@ static u8 sm_slw_tx_wr_dat_enter(u8 event, struct ipc_link_context *context) } } -static u8 sm_slw_tx_wr_dat_aud_enter(u8 event, struct ipc_link_context *context) -{ - struct ipc_tx_queue *frame = NULL; - - /* check if there is a frame to be sent */ - if (!ipc_queue_is_empty(context)) { - frame = ipc_queue_get_frame(context); - } else { - /* no frame to send, create an empty one */ - dev_dbg(&context->sdev->dev, - "link %d: no frame to send, allocating dummy\n", - context->link->id); - frame = ipc_queue_new_frame(context, 0); - if (frame == NULL) - return IPC_SM_RUN_ABORT; - } - - ipc_dbg_dump_frame(&context->sdev->dev, context->link->id, frame, true); - - /* prepare to transfer the frame tx data */ - context->frame = frame; - ipc_util_spi_message_prepare(context, context->frame->data, - NULL, context->frame->len); - - /* slave might already have signalled ready to transmit */ - if (event == IPC_SM_RUN_SLAVE_IRQ || atomic_read(&context->state_int)) { - dev_dbg(&context->sdev->dev, - "link %d: slave has already signalled ready\n", - context->link->id); - ipc_util_activate_ss(context); - return IPC_SM_RUN_SLAVE_IRQ; - } else { - ipc_util_activate_ss_with_tmo(context); - return IPC_SM_RUN_NONE; - } -} - static const struct ipc_sm_state *sm_slw_tx_wr_dat_exit(u8 event, struct ipc_link_context *context) { @@ -376,17 +299,6 @@ static const struct ipc_sm_state *sm_slw_tx_wr_dat_exit(u8 event, return ipc_sm_state(IPC_SM_ACT_TX_WR_DAT); } -static const struct ipc_sm_state *sm_slw_tx_wr_dat_aud_exit(u8 event, - struct ipc_link_context *context) -{ - if (event == IPC_SM_RUN_RESET) - return ipc_sm_state(IPC_SM_RESET_AUD); - else if (event == IPC_SM_RUN_COMMS_TMO) - return ipc_sm_state(IPC_SM_HALT_AUD); - else - return ipc_sm_state(IPC_SM_ACT_TX_WR_DAT_AUD); -} - static u8 sm_act_tx_wr_dat_enter(u8 event, struct ipc_link_context *context) { int err; @@ -434,34 +346,6 @@ static const struct ipc_sm_state *sm_act_tx_wr_dat_exit(u8 event, return ipc_sm_state(IPC_SM_SLW_TX_RD_CMD); } -static const struct ipc_sm_state *sm_act_tx_wr_dat_aud_exit(u8 event, - struct ipc_link_context *context) -{ - if (event == IPC_SM_RUN_RESET) - return ipc_sm_state(IPC_SM_RESET_AUD); - -#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT - /* frame is sent, increment link tx counter */ - context->tx_bytes += context->frame->actual_len; -#endif -#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_VERIFY_FRAMES - { - u8 channel; - - channel = ipc_util_get_l2_channel(*(u32 *)context->frame->data); - if (ipc_util_channel_is_loopback(channel)) { - /* create a copy of the frame */ - context->last_frame = ipc_queue_new_frame(context, - context->frame->actual_len); - memcpy(context->last_frame->data, - context->frame->data, - context->frame->actual_len); - } - } -#endif - return ipc_sm_state(IPC_SM_SLW_RX_WR_DAT_AUD); -} - static u8 sm_slw_tx_rd_cmd_enter(u8 event, struct ipc_link_context *context) { context->cmd = ipc_util_make_l1_header(CMD_READ, 0, 0); @@ -626,39 +510,6 @@ static const struct ipc_sm_state *sm_act_rx_wr_cmd_exit(u8 event, } } -static u8 sm_slw_rx_wr_dat_aud_enter(u8 event, struct ipc_link_context *context) -{ - /* - * We're using the same frame buffer we just sent, so no need for a - * new allocation here, just prepare the spi message - */ - ipc_util_spi_message_prepare(context, NULL, - context->frame->data, context->frame->len); - - /* slave might already have signalled ready to transmit */ - if (atomic_read(&context->state_int)) { - dev_dbg(&context->sdev->dev, - "link %d: slave has already signalled ready\n", - context->link->id); - ipc_util_activate_ss(context); - return IPC_SM_RUN_SLAVE_IRQ; - } else { - ipc_util_activate_ss_with_tmo(context); - return IPC_SM_RUN_NONE; - } -} - -static const struct ipc_sm_state *sm_slw_rx_wr_dat_aud_exit(u8 event, - struct ipc_link_context *context) -{ - if (event == IPC_SM_RUN_RESET) - return ipc_sm_state(IPC_SM_RESET_AUD); - else if (event == IPC_SM_RUN_COMMS_TMO) - return ipc_sm_state(IPC_SM_HALT_AUD); - else - return ipc_sm_state(IPC_SM_ACT_RX_WR_DAT_AUD); -} - static u8 sm_act_rx_wr_dat_enter(u8 event, struct ipc_link_context *context) { int err; @@ -679,22 +530,6 @@ static u8 sm_act_rx_wr_dat_enter(u8 event, struct ipc_link_context *context) return IPC_SM_RUN_NONE; } -static u8 sm_act_rx_wr_dat_aud_enter(u8 event, struct ipc_link_context *context) -{ - int err; - - dev_dbg(&context->sdev->dev, - "link %d: starting spi tfr\n", context->link->id); - err = spi_async(context->sdev, &context->spi_message); - if (err < 0) { - dev_err(&context->sdev->dev, - "link %d error: spi tfr start failed, error %d\n", - context->link->id, err); - return IPC_SM_RUN_ABORT; - } - return IPC_SM_RUN_NONE; -} - static const struct ipc_sm_state *sm_act_rx_wr_dat_exit(u8 event, struct ipc_link_context *context) { @@ -774,86 +609,6 @@ static const struct ipc_sm_state *sm_act_rx_wr_dat_exit(u8 event, } } -static const struct ipc_sm_state *sm_act_rx_wr_dat_aud_exit(u8 event, - struct ipc_link_context *context) -{ - u32 frame_hdr; - unsigned char l2_header; - unsigned int l2_length; - u8 *l2_data; - - if (event == IPC_SM_RUN_RESET) - return ipc_sm_state(IPC_SM_RESET_AUD); - - dev_dbg(&context->sdev->dev, - "link %d: RX PAYLOAD %d bytes\n", - context->link->id, context->frame->len); - - /* decode L2 header */ - frame_hdr = *(u32 *)context->frame->data; - l2_header = ipc_util_get_l2_channel(frame_hdr); - l2_length = ipc_util_get_l2_length(frame_hdr); - l2_data = (u8 *)context->frame->data + IPC_L2_HDR_SIZE; - -#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT - /* frame is received, increment link rx counter */ - context->rx_bytes += l2_length; -#endif - if (frame_hdr != 0) - context->frame->actual_len = l2_length + IPC_L2_HDR_SIZE; - else - context->frame->actual_len = 0; - ipc_dbg_dump_frame(&context->sdev->dev, context->link->id, - context->frame, false); - - if (l2_length > (context->frame->len - 4)) - dev_err(&context->sdev->dev, - "link %d: suspicious frame: L1 len %d L2 len %d\n", - context->link->id, context->frame->len, l2_length); - - dev_dbg(&context->sdev->dev, - "link %d: L2 PDU decode: header 0x%08x channel %d length %d " - "data[%02x%02x%02x...]\n", - context->link->id, frame_hdr, l2_header, l2_length, - l2_data[0], l2_data[1], l2_data[2]); - - if (ipc_util_channel_is_loopback(l2_header)) - ipc_dbg_verify_rx_frame(context); - - /* did the slave actually have anything to send? */ - if (frame_hdr != 0) { - /* pass received frame up to L2mux layer */ - if (!modem_protocol_channel_is_open(l2_header)) { - dev_err(&context->sdev->dev, - "link %d error: received frame on invalid " - "channel %d, frame discarded\n", - context->link->id, l2_header); - } else { -#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT - /* - * Discard loopback frames if we are taking throughput - * measurements - we'll be loading the links and so will - * likely overload the buffers. - */ - if (!ipc_util_channel_is_loopback(l2_header)) -#endif - modem_m6718_spi_receive(context->sdev, - l2_header, l2_length, l2_data); - } - } else { - dev_dbg(&context->sdev->dev, - "link %d: received dummy frame, discarding\n", - context->link->id); - } - - /* data is copied by L2mux so free the frame here */ - ipc_queue_delete_frame(context->frame); - context->frame = NULL; - - /* audio link goes idle ready for next transaction */ - return ipc_sm_state(IPC_SM_IDL_AUD); -} - static u8 sm_halt_enter(u8 event, struct ipc_link_context *context) { dev_err(&context->sdev->dev, @@ -884,12 +639,6 @@ static const struct ipc_sm_state *sm_halt_exit(u8 event, return ipc_sm_state(IPC_SM_RESET); } -static const struct ipc_sm_state *sm_halt_aud_exit(u8 event, - struct ipc_link_context *context) -{ - return ipc_sm_state(IPC_SM_RESET_AUD); -} - static u8 sm_reset_enter(u8 event, struct ipc_link_context *context) { dev_err(&context->sdev->dev, @@ -922,12 +671,6 @@ static const struct ipc_sm_state *sm_reset_exit(u8 event, return ipc_sm_state(IPC_SM_INIT); } -static const struct ipc_sm_state *sm_reset_aud_exit(u8 event, - struct ipc_link_context *context) -{ - return ipc_sm_state(IPC_SM_INIT_AUD); -} - static u8 sm_slw_tx_bootreq_enter(u8 event, struct ipc_link_context *context) { dev_info(&context->sdev->dev, @@ -1047,10 +790,10 @@ static const struct ipc_sm_state *sm_act_rx_bootresp_exit(u8 event, context->link->id, IPC_DRIVER_VERSION, modem_ver); /* check for minimum required modem version */ - if (modem_ver < IPC_DRIVER_MODEM_MIN_VER) { + if (modem_ver != IPC_DRIVER_MODEM_MIN_VER) { dev_warn(&context->sdev->dev, "link %d warning: modem version mismatch! " - "minimum required version is %02x\n", + "required version is %02x\n", context->link->id, IPC_DRIVER_MODEM_MIN_VER); } @@ -1188,75 +931,16 @@ static const struct ipc_sm_state state_machine[IPC_SM_STATE_ID_NBR] = { .exit = sm_act_rx_wr_dat_exit, .events = IPC_SM_RUN_TFR_COMPLETE | IPC_SM_RUN_RESET }, - /* audio link states below */ - [IPC_SM_INIT_AUD] = { - .id = IPC_SM_INIT_AUD, - .enter = sm_init_enter, - .exit = sm_init_aud_exit, - .events = IPC_SM_RUN_INIT | IPC_SM_RUN_RESET - }, - [IPC_SM_HALT_AUD] = { - .id = IPC_SM_HALT_AUD, - .enter = sm_halt_enter, - .exit = sm_halt_aud_exit, - .events = IPC_SM_RUN_RESET - }, - [IPC_SM_RESET_AUD] = { - .id = IPC_SM_RESET_AUD, - .enter = sm_reset_enter, - .exit = sm_reset_aud_exit, - .events = IPC_SM_RUN_RESET - }, - [IPC_SM_IDL_AUD] = { - .id = IPC_SM_IDL_AUD, - .enter = sm_idl_enter, - .exit = sm_idl_aud_exit, - .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_TX_REQ | - IPC_SM_RUN_RESET - }, - [IPC_SM_SLW_TX_WR_DAT_AUD] = { - .id = IPC_SM_SLW_TX_WR_DAT_AUD, - .enter = sm_slw_tx_wr_dat_aud_enter, - .exit = sm_slw_tx_wr_dat_aud_exit, - .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_COMMS_TMO | - IPC_SM_RUN_RESET - }, - [IPC_SM_ACT_TX_WR_DAT_AUD] = { - .id = IPC_SM_ACT_TX_WR_DAT_AUD, - .enter = sm_act_tx_wr_dat_enter, - .exit = sm_act_tx_wr_dat_aud_exit, - .events = IPC_SM_RUN_TFR_COMPLETE | IPC_SM_RUN_RESET - }, - [IPC_SM_SLW_RX_WR_DAT_AUD] = { - .id = IPC_SM_SLW_RX_WR_DAT_AUD, - .enter = sm_slw_rx_wr_dat_aud_enter, - .exit = sm_slw_rx_wr_dat_aud_exit, - .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_COMMS_TMO | - IPC_SM_RUN_RESET - }, - [IPC_SM_ACT_RX_WR_DAT_AUD] = { - .id = IPC_SM_ACT_RX_WR_DAT_AUD, - .enter = sm_act_rx_wr_dat_aud_enter, - .exit = sm_act_rx_wr_dat_aud_exit, - .events = IPC_SM_RUN_TFR_COMPLETE | IPC_SM_RUN_RESET - } }; - const struct ipc_sm_state *ipc_sm_idle_state(struct ipc_link_context *context) { - if (context->link->id == IPC_LINK_AUDIO) - return ipc_sm_state(IPC_SM_IDL_AUD); - else - return ipc_sm_state(IPC_SM_IDL); + return ipc_sm_state(IPC_SM_IDL); } const struct ipc_sm_state *ipc_sm_init_state(struct ipc_link_context *context) { - if (context->link->id == IPC_LINK_AUDIO) - return ipc_sm_state(IPC_SM_INIT_AUD); - else - return ipc_sm_state(IPC_SM_INIT); + return ipc_sm_state(IPC_SM_INIT); } const struct ipc_sm_state *ipc_sm_state(u8 id) @@ -1403,4 +1087,3 @@ void ipc_sm_kick(u8 event, struct ipc_link_context *context) "link %d is suspended, waiting for resume\n", link->id); spin_unlock_irqrestore(&context->sm_lock, flags); } - diff --git a/drivers/modem/m6718_spi/util.c b/drivers/modem/m6718_spi/util.c index 9c89eb9b34a..9026f4427dd 100644 --- a/drivers/modem/m6718_spi/util.c +++ b/drivers/modem/m6718_spi/util.c @@ -136,7 +136,6 @@ bool ipc_util_link_is_idle(struct ipc_link_context *context) switch (context->state->id) { case IPC_SM_IDL: - case IPC_SM_IDL_AUD: return true; default: return false; -- cgit v1.2.3 From aedb9867d5f6f65d908c3e5e130b37ceddcbeb03 Mon Sep 17 00:00:00 2001 From: Rikard Olsson Date: Wed, 11 Jan 2012 15:55:40 +0100 Subject: ARM: ux500: adds additional dump areas Adds additional dump area information which contains information of fuses in db8500. ST-Ericsson Linux next: - ST-Ericsson ID: 404278 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: If2aea9493c0942700acc676c40dc9b6e344ef891 Signed-off-by: Rikard Olsson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/45155 Reviewed-by: Srinidhi KASAGAR --- arch/arm/mach-ux500/mloader-db8500.c | 12 +++++++++++- drivers/misc/dbx500-mloader.c | 19 +++++++++++++++++++ include/linux/mloader.h | 1 + 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-ux500/mloader-db8500.c b/arch/arm/mach-ux500/mloader-db8500.c index b13652f55cb..8f08e6af969 100644 --- a/arch/arm/mach-ux500/mloader-db8500.c +++ b/arch/arm/mach-ux500/mloader-db8500.c @@ -12,6 +12,7 @@ #include #include +#include static struct dbx500_ml_area modem_areas[] = { { .name = "modem_trace", .start = 0x6000000, .size = 0xf00000 }, @@ -31,13 +32,22 @@ static struct dbx500_mloader_pdata mloader_fw_data = { .nr_areas = ARRAY_SIZE(modem_areas), }; +static struct resource mloader_fw_rsrc[] = { + { + .start = (U8500_BACKUPRAM1_BASE + 0xF70), + .end = (U8500_BACKUPRAM1_BASE + 0xF7C), + .flags = IORESOURCE_MEM + } +}; + struct platform_device mloader_fw_device = { .name = "dbx500_mloader_fw", .id = -1, .dev = { .platform_data = &mloader_fw_data, }, - .num_resources = 0, + .resource = mloader_fw_rsrc, + .num_resources = ARRAY_SIZE(mloader_fw_rsrc) }; /* Default areas can be overloaded in cmdline */ diff --git a/drivers/misc/dbx500-mloader.c b/drivers/misc/dbx500-mloader.c index c3ec8b67983..408d1a1a29e 100644 --- a/drivers/misc/dbx500-mloader.c +++ b/drivers/misc/dbx500-mloader.c @@ -22,6 +22,7 @@ #include #include +#include #define DEVICE_NAME "dbx500_mloader_fw" @@ -30,6 +31,8 @@ struct mloader_priv { struct dbx500_mloader_pdata *pdata; struct miscdevice misc_dev; u32 aeras_size; + void __iomem *uid_base; + u8 size; }; static struct mloader_priv *mloader_priv; @@ -165,6 +168,11 @@ static long mloader_fw_ioctl(struct file *filp, unsigned int cmd, kfree(dump_images); break; } + case ML_GET_FUSEINFO: { + ret = copy_to_user(argp, (void *) mloader_priv->uid_base, + mloader_priv->size) ? -EFAULT : 0; + break; + } default: ret = -EPERM; break; @@ -183,6 +191,7 @@ static int __devinit mloader_fw_probe(struct platform_device *pdev) { int ret = 0; int i; + struct resource *res = NULL; mloader_priv = kzalloc(sizeof(*mloader_priv), GFP_ATOMIC); if (!mloader_priv) { @@ -196,6 +205,16 @@ static int __devinit mloader_fw_probe(struct platform_device *pdev) mloader_priv->misc_dev.minor = MISC_DYNAMIC_MINOR; mloader_priv->misc_dev.name = DEVICE_NAME; mloader_priv->misc_dev.fops = &modem_fw_fops; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mloader_priv->size = resource_size(res); + mloader_priv->uid_base = ioremap(res->start, mloader_priv->size); + + if (!mloader_priv->uid_base) { + ret = -ENOMEM; + goto err_free_priv; + } + ret = misc_register(&mloader_priv->misc_dev); if (ret < 0) { dev_err(&pdev->dev, "can't misc_register\n"); diff --git a/include/linux/mloader.h b/include/linux/mloader.h index ceca3245856..c339a7f7a36 100644 --- a/include/linux/mloader.h +++ b/include/linux/mloader.h @@ -13,6 +13,7 @@ #define ML_UPLOAD _IO(ML_IO_NUMBER, 1) #define ML_GET_NBIMAGES _IOR(ML_IO_NUMBER, 2, int) #define ML_GET_DUMPINFO _IOR(ML_IO_NUMBER, 3, struct dump_image*) +#define ML_GET_FUSEINFO _IOR(ML_IO_NUMBER, 4, char*) #define MAX_NAME 16 -- cgit v1.2.3 From a14e91b4908c66917b2653179db226587ae6b874 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Mon, 19 Dec 2011 11:31:05 +0530 Subject: u8500-shrm: Ensure not to access GOP register while in MSR ST-Ericsson Linux next: NA ST-Ericsson ID: 402285 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I94eb660c8a06070954c1b8920042ff465a85db84 Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/43027 Reviewed-by: Ashok G X Tested-by: Ashok G X Reviewed-by: Srinidhi KASAGAR --- drivers/modem/shrm/shrm_protocol.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index cbb3a820317..cfa65cda18f 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -448,16 +448,22 @@ void shm_ac_read_notif_1_tasklet(unsigned long tasklet_data) void shm_ca_sleep_req_work(struct kthread_work *work) { + u8 bt_state; + unsigned long flags; + dev_dbg(shm_dev->dev, "%s:IRQ_PRCMU_CA_SLEEP\n", __func__); - shrm_common_rx_state = SHRM_IDLE; - shrm_audio_rx_state = SHRM_IDLE; + spin_lock_irqsave(&boot_lock, flags); + bt_state = boot_state; + spin_unlock_irqrestore(&boot_lock, flags); - if (check_modem_in_reset()) { + if (bt_state != BOOT_DONE) { dev_err(shm_dev->dev, "%s:Modem state reset or unknown\n", __func__); return; } + shrm_common_rx_state = SHRM_IDLE; + shrm_audio_rx_state = SHRM_IDLE; writel((1<intr_base + GOP_SET_REGISTER_BASE); @@ -504,6 +510,10 @@ static int shrm_modem_reset_sequence(void) unsigned long flags; hrtimer_cancel(&timer); + tasklet_disable_nosync(&shm_ac_read_0_tasklet); + tasklet_disable_nosync(&shm_ac_read_1_tasklet); + tasklet_disable_nosync(&shm_ca_0_tasklet); + tasklet_disable_nosync(&shm_ca_1_tasklet); /* * keep the count to 0 so that we can bring down the line @@ -536,6 +546,10 @@ static int shrm_modem_reset_sequence(void) boot_state = BOOT_INIT; spin_unlock_irqrestore(&boot_lock, flags); + tasklet_enable(&shm_ac_read_0_tasklet); + tasklet_enable(&shm_ac_read_1_tasklet); + tasklet_enable(&shm_ca_0_tasklet); + tasklet_enable(&shm_ca_1_tasklet); /* re-enable irqs */ enable_irq(shm_dev->ac_read_notif_0_irq); enable_irq(shm_dev->ac_read_notif_1_irq); -- cgit v1.2.3 From 5c509223e311766d48f03e1be403cf33ddd526de Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Tue, 10 Jan 2012 15:23:49 +0530 Subject: u8500-shrm: inactivity timer change the inactivity timer of audio and common messages to 25ms. Since there is audio patch every 20ms, the inactivity timer can be 25ms. ST-Ericsson Linux next: NA ST-Ericsson ID: 410076 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I9c66012b7283bcf60786ef3475111ce398cf9044 Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/45200 Reviewed-by: Bibek BASU Reviewed-by: QATOOLS Reviewed-by: QABUILD Tested-by: Bibek BASU Reviewed-by: Srinidhi KASAGAR --- drivers/modem/shrm/shrm_fifo.c | 4 ++-- drivers/modem/shrm/shrm_protocol.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/modem/shrm/shrm_fifo.c b/drivers/modem/shrm/shrm_fifo.c index 1804c1be69e..5fe9f3c5724 100644 --- a/drivers/modem/shrm/shrm_fifo.c +++ b/drivers/modem/shrm/shrm_fifo.c @@ -30,8 +30,8 @@ #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 */ +#define ca_ist_inactivity_timer 25 /*25ms */ +#define ca_csc_inactivity_timer 25 /*25ms */ static u8 msg_audio_counter; static u8 msg_common_counter; diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index cfa65cda18f..dfb8f14f0e2 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -400,7 +400,7 @@ void shm_ac_read_notif_0_tasklet(unsigned long tasklet_data) dev_err(shrm->dev, "Invalid boot state\n"); } /* start timer here */ - hrtimer_start(&timer, ktime_set(0, 10*NSEC_PER_MSEC), + hrtimer_start(&timer, ktime_set(0, 25*NSEC_PER_MSEC), HRTIMER_MODE_REL); atomic_dec(&ac_sleep_disable_count); @@ -438,7 +438,7 @@ void shm_ac_read_notif_1_tasklet(unsigned long tasklet_data) shrm_audio_tx_state = SHRM_IDLE; } /* start timer here */ - hrtimer_start(&timer, ktime_set(0, 10*NSEC_PER_MSEC), + hrtimer_start(&timer, ktime_set(0, 25*NSEC_PER_MSEC), HRTIMER_MODE_REL); atomic_dec(&ac_sleep_disable_count); atomic_dec(&ac_msg_pend_1); -- cgit v1.2.3 From 90f2a2c4152a251b0bfad40d7fdc512f543cd5ce Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Tue, 24 Jan 2012 15:57:26 +0530 Subject: u8500-shrm: add timer to check if modem has stuck or hang On APE sending msg_pend notification, the only way when APE gets to know that modem is not responsive, is when it ends up with a FIFO full. In order to get this FIFO full on common channel it might take 30min. Hence add a timer and start the timer on msg_pend_notification and cancel the timer at read_noti. If timer expires within this time then its for sure that modem is not responsive. Initiate MSR in that case. The current timer value is 6sec. Since there are two timers and on expiry of either of the timer, modem_reset is called, have added flag so as to ensure that modem_rest is not called simultaneously on exit of both the timer in short span of time. ST-Ericsson Linux next: NA ST-Ericsson ID: 407459 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: If3e7b61ee57ea4b1d386a50c8c4b99c2cd701e13 Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/46337 Reviewed-by: Ashok G X Tested-by: Ashok G X Reviewed-by: Srinidhi KASAGAR --- drivers/modem/shrm/shrm_protocol.c | 68 +++++++++++++++++++++++++++++++++- include/linux/modem/shrm/shrm_driver.h | 4 ++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index dfb8f14f0e2..5997c35297b 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -31,6 +31,7 @@ #define L2_HEADER_CIQ 0xC3 #define L2_HEADER_RTC_CALIBRATION 0xC8 #define MAX_PAYLOAD 1024 +#define MOD_STUCK_TIMEOUT 6 #define PRCM_HOSTACCESS_REQ 0x334 @@ -40,6 +41,8 @@ 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; +static struct hrtimer mod_stuck_timer_0; +static struct hrtimer mod_stuck_timer_1; struct sock *shrm_nl_sk; static char shrm_common_tx_state = SHRM_SLEEP_STATE; @@ -49,6 +52,7 @@ static char shrm_audio_rx_state = SHRM_SLEEP_STATE; static atomic_t ac_sleep_disable_count = ATOMIC_INIT(0); static atomic_t ac_msg_pend_1 = ATOMIC_INIT(0); +static atomic_t mod_stuck = ATOMIC_INIT(0); static struct shrm_dev *shm_dev; /* Spin lock and tasklet declaration */ @@ -63,6 +67,7 @@ static DEFINE_SPINLOCK(ca_common_lock); static DEFINE_SPINLOCK(ca_audio_lock); static DEFINE_SPINLOCK(ca_wake_req_lock); static DEFINE_SPINLOCK(boot_lock); +static DEFINE_SPINLOCK(mod_stuck_lock); enum shrm_nl { SHRM_NL_MOD_RESET = 1, @@ -102,6 +107,28 @@ static u32 get_host_accessport_val(void) return prcm_hostaccess; } + +static enum hrtimer_restart shm_mod_stuck_timeout(struct hrtimer *timer) +{ + unsigned long flags; + + spin_lock_irqsave(&mod_stuck_lock, flags); + /* Check MSR is already in progress */ + if (shm_dev->msr_flag || boot_state == BOOT_UNKNOWN || + atomic_read(&mod_stuck)) { + spin_unlock_irqrestore(&mod_stuck_lock, flags); + return HRTIMER_NORESTART; + } + atomic_set(&mod_stuck, 1); + spin_unlock_irqrestore(&mod_stuck_lock, flags); + dev_err(shm_dev->dev, "No response from modem, timeout %dsec\n", + MOD_STUCK_TIMEOUT); + dev_err(shm_dev->dev, "APE initiating MSR\n"); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + return HRTIMER_NORESTART; +} + static enum hrtimer_restart callback(struct hrtimer *timer) { unsigned long flags; @@ -510,6 +537,9 @@ static int shrm_modem_reset_sequence(void) unsigned long flags; hrtimer_cancel(&timer); + hrtimer_cancel(&mod_stuck_timer_0); + hrtimer_cancel(&mod_stuck_timer_1); + atomic_set(&mod_stuck, 0); tasklet_disable_nosync(&shm_ac_read_0_tasklet); tasklet_disable_nosync(&shm_ac_read_1_tasklet); tasklet_disable_nosync(&shm_ca_0_tasklet); @@ -666,6 +696,9 @@ static void send_ac_msg_pend_notify_0_work(struct kthread_work *work) writel((1<intr_base + GOP_SET_REGISTER_BASE); + /* timer to detect modem stuck or hang */ + hrtimer_start(&mod_stuck_timer_0, ktime_set(MOD_STUCK_TIMEOUT, 0), + HRTIMER_MODE_REL); if (shrm_common_tx_state == SHRM_PTR_FREE) shrm_common_tx_state = SHRM_PTR_BUSY; @@ -705,6 +738,9 @@ static void send_ac_msg_pend_notify_1_work(struct kthread_work *work) writel((1<intr_base + GOP_SET_REGISTER_BASE); + /* timer to detect modem stuck or hang */ + hrtimer_start(&mod_stuck_timer_1, ktime_set(MOD_STUCK_TIMEOUT, 0), + HRTIMER_MODE_REL); if (shrm_audio_tx_state == SHRM_PTR_FREE) shrm_audio_tx_state = SHRM_PTR_BUSY; @@ -729,7 +765,14 @@ void shm_nl_receive(struct sk_buff *skb) 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(); + if (atomic_read(&mod_stuck)) { + dev_info(shm_dev->dev, + "Modem reset already in progress\n"); + break; + } + atomic_set(&mod_stuck, 1); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); break; default: @@ -754,6 +797,10 @@ int shrm_protocol_init(struct shrm_dev *shrm, hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); timer.function = callback; + hrtimer_init(&mod_stuck_timer_0, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + mod_stuck_timer_0.function = shm_mod_stuck_timeout; + hrtimer_init(&mod_stuck_timer_1, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + mod_stuck_timer_1.function = shm_mod_stuck_timeout; init_kthread_worker(&shrm->shm_common_ch_wr_kw); shrm->shm_common_ch_wr_kw_task = kthread_run(kthread_worker_fn, @@ -809,6 +856,15 @@ int shrm_protocol_init(struct shrm_dev *shrm, err = -ENOMEM; goto free_kw4; } + init_kthread_worker(&shrm->shm_mod_stuck_kw); + shrm->shm_mod_stuck_kw_task = kthread_run(kthread_worker_fn, + &shrm->shm_mod_stuck_kw, + "shm_mod_reset_req"); + if (IS_ERR(shrm->shm_mod_stuck_kw_task)) { + dev_err(shrm->dev, "failed to create work task\n"); + return -ENOMEM; + goto free_kw5; + } init_kthread_work(&shrm->send_ac_msg_pend_notify_0, send_ac_msg_pend_notify_0_work); @@ -828,7 +884,7 @@ int shrm_protocol_init(struct shrm_dev *shrm, IRQF_NO_SUSPEND, "ca-sleep", shrm); if (err < 0) { dev_err(shm_dev->dev, "Failed alloc IRQ_PRCMU_CA_SLEEP.\n"); - goto free_kw5; + goto free_kw6; } err = request_irq(IRQ_PRCMU_CA_WAKE, shrm_prcmu_irq_handler, @@ -866,6 +922,8 @@ drop1: free_irq(IRQ_PRCMU_CA_WAKE, NULL); drop2: free_irq(IRQ_PRCMU_CA_SLEEP, NULL); +free_kw6: + kthread_stop(shrm->shm_mod_stuck_kw_task); free_kw5: kthread_stop(shrm->shm_ac_sleep_kw_task); free_kw4: @@ -889,11 +947,13 @@ void shrm_protocol_deinit(struct shrm_dev *shrm) flush_kthread_worker(&shrm->shm_ac_wake_kw); flush_kthread_worker(&shrm->shm_ca_wake_kw); flush_kthread_worker(&shrm->shm_ac_sleep_kw); + flush_kthread_worker(&shrm->shm_mod_stuck_kw); kthread_stop(shrm->shm_common_ch_wr_kw_task); kthread_stop(shrm->shm_audio_ch_wr_kw_task); kthread_stop(shrm->shm_ac_wake_kw_task); kthread_stop(shrm->shm_ca_wake_kw_task); kthread_stop(shrm->shm_ac_sleep_kw_task); + kthread_stop(shrm->shm_mod_stuck_kw_task); modem_put(shrm->modem); } @@ -933,6 +993,8 @@ 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__); + /* Cancel the modem stuck timer */ + hrtimer_cancel(&mod_stuck_timer_0); if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", @@ -962,6 +1024,8 @@ 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__); + /* Cancel the modem stuck timer */ + hrtimer_cancel(&mod_stuck_timer_1); if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", diff --git a/include/linux/modem/shrm/shrm_driver.h b/include/linux/modem/shrm/shrm_driver.h index 96b5c594d34..072a232d53d 100644 --- a/include/linux/modem/shrm/shrm_driver.h +++ b/include/linux/modem/shrm/shrm_driver.h @@ -76,6 +76,8 @@ * @shm_ca_wake_kw_task: task for receiving cmt-ape wake requests * @shm_ac_sleep_kw: kthread worker for recieving ape-cmt sleep requests * @shm_ac_sleep_kw_task: task for recieving ape-cmt sleep requests + * @shm_mod_stuck_kw: kthread worker to reset the modem + * &shm_mod_stuck_kw_task: task for sending modem reset request * @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 @@ -134,6 +136,8 @@ struct shrm_dev { struct task_struct *shm_ca_wake_kw_task; struct kthread_worker shm_ac_sleep_kw; struct task_struct *shm_ac_sleep_kw_task; + struct kthread_worker shm_mod_stuck_kw; + struct task_struct *shm_mod_stuck_kw_task; struct kthread_work send_ac_msg_pend_notify_0; struct kthread_work send_ac_msg_pend_notify_1; struct kthread_work shm_ac_wake_req; -- cgit v1.2.3 From 380168c494bf2f2caa00af62d42a6051d9643b90 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Tue, 24 Jan 2012 16:29:56 +0530 Subject: u8500-shrm: initiate MSR after fifo full On APE side after FIFO fill, shrm driver stops further receiving messages from the user space and keeps waiting indefinetly for the modem to read and empty the fifo. If modem is not responsive then it ends up in an indefinite loop. Hence after arriving at fifo full wait for some time say 1sec and then issue a modem reset which will turn out in a MSR. ST-Ericsson Linux next: NA ST-Ericsson ID: 407459 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I9ac5cc4029f6b092809297513810798805485b60 Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/46338 Reviewed-by: Ashok G X Tested-by: Ashok G X Reviewed-by: Srinidhi KASAGAR --- drivers/modem/shrm/shrm_protocol.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index 5997c35297b..b9613f544e5 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -32,6 +32,7 @@ #define L2_HEADER_RTC_CALIBRATION 0xC8 #define MAX_PAYLOAD 1024 #define MOD_STUCK_TIMEOUT 6 +#define FIFO_FULL_TIMEOUT 1 #define PRCM_HOSTACCESS_REQ 0x334 @@ -43,6 +44,7 @@ static received_msg_handler rx_audio_handler; static struct hrtimer timer; static struct hrtimer mod_stuck_timer_0; static struct hrtimer mod_stuck_timer_1; +static struct hrtimer fifo_full_timer; struct sock *shrm_nl_sk; static char shrm_common_tx_state = SHRM_SLEEP_STATE; @@ -53,6 +55,7 @@ static char shrm_audio_rx_state = SHRM_SLEEP_STATE; static atomic_t ac_sleep_disable_count = ATOMIC_INIT(0); static atomic_t ac_msg_pend_1 = ATOMIC_INIT(0); static atomic_t mod_stuck = ATOMIC_INIT(0); +static atomic_t fifo_full = ATOMIC_INIT(0); static struct shrm_dev *shm_dev; /* Spin lock and tasklet declaration */ @@ -108,6 +111,13 @@ static u32 get_host_accessport_val(void) return prcm_hostaccess; } +static enum hrtimer_restart shm_fifo_full_timeout(struct hrtimer *timer) +{ + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + return HRTIMER_NORESTART; +} + static enum hrtimer_restart shm_mod_stuck_timeout(struct hrtimer *timer) { unsigned long flags; @@ -115,7 +125,7 @@ static enum hrtimer_restart shm_mod_stuck_timeout(struct hrtimer *timer) spin_lock_irqsave(&mod_stuck_lock, flags); /* Check MSR is already in progress */ if (shm_dev->msr_flag || boot_state == BOOT_UNKNOWN || - atomic_read(&mod_stuck)) { + atomic_read(&mod_stuck) || atomic_read(&fifo_full)) { spin_unlock_irqrestore(&mod_stuck_lock, flags); return HRTIMER_NORESTART; } @@ -539,7 +549,9 @@ static int shrm_modem_reset_sequence(void) hrtimer_cancel(&timer); hrtimer_cancel(&mod_stuck_timer_0); hrtimer_cancel(&mod_stuck_timer_1); + hrtimer_cancel(&fifo_full_timer); atomic_set(&mod_stuck, 0); + atomic_set(&fifo_full, 0); tasklet_disable_nosync(&shm_ac_read_0_tasklet); tasklet_disable_nosync(&shm_ac_read_1_tasklet); tasklet_disable_nosync(&shm_ca_0_tasklet); @@ -801,6 +813,8 @@ int shrm_protocol_init(struct shrm_dev *shrm, mod_stuck_timer_0.function = shm_mod_stuck_timeout; hrtimer_init(&mod_stuck_timer_1, CLOCK_MONOTONIC, HRTIMER_MODE_REL); mod_stuck_timer_1.function = shm_mod_stuck_timeout; + hrtimer_init(&fifo_full_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + fifo_full_timer.function = shm_fifo_full_timeout; init_kthread_worker(&shrm->shm_common_ch_wr_kw); shrm->shm_common_ch_wr_kw_task = kthread_run(kthread_worker_fn, @@ -995,6 +1009,8 @@ irqreturn_t ac_read_notif_0_irq_handler(int irq, void *ctrlr) dev_dbg(shrm->dev, "%s IN\n", __func__); /* Cancel the modem stuck timer */ hrtimer_cancel(&mod_stuck_timer_0); + if(atomic_read(&fifo_full)) + hrtimer_cancel(&fifo_full_timer); if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", @@ -1026,6 +1042,8 @@ irqreturn_t ac_read_notif_1_irq_handler(int irq, void *ctrlr) dev_dbg(shrm->dev, "%s IN+\n", __func__); /* Cancel the modem stuck timer */ hrtimer_cancel(&mod_stuck_timer_1); + if(atomic_read(&fifo_full)) + hrtimer_cancel(&fifo_full_timer); if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", @@ -1165,6 +1183,15 @@ int shm_write_msg(struct shrm_dev *shrm, u8 l2_header, 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"); + if (ret == -EAGAIN) { + if(!atomic_read(&fifo_full)) { + /* Start a timer so as to handle this gently */ + atomic_set(&fifo_full, 1); + hrtimer_start(&fifo_full_timer, ktime_set( + FIFO_FULL_TIMEOUT, 0), + HRTIMER_MODE_REL); + } + } return ret; } /* -- cgit v1.2.3 From 2cb12e7c40790435d1ced4f711c164e7fb30f85a Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Wed, 25 Jan 2012 19:14:52 +0530 Subject: u8500-shrm: fix html docs build error /local/scratch/hudson_prod/sandbox/build-roots/u8500-lbp-dev/kernel/ Documentation/DocBook/shrm.xml:2153: parser error : EntityRef: expecting ';' &shm_mod_stuck_kw_task ^ unable to parse /local/scratch/hudson_prod/sandbox/build-roots/u8500-lbp-dev/ kernel/Documentation/DocBook/shrm.xml /bin/cp: cannot stat `*.*htm*': No such file or directory make[4]: *** [Documentation/DocBook/shrm.html] Error 1 make[4]: *** Waiting for unfinished jobs.... ST-Ericsson Linux next: NA ST-Ericsson ID: 407459 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I916a0aace48eb04b9707c3d2f3f6b4c47ce08b30 Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/46539 Reviewed-by: Maria STURESSON Reviewed-by: QATEST Reviewed-by: Henrik CARLING --- include/linux/modem/shrm/shrm_driver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/modem/shrm/shrm_driver.h b/include/linux/modem/shrm/shrm_driver.h index 072a232d53d..e8905e68778 100644 --- a/include/linux/modem/shrm/shrm_driver.h +++ b/include/linux/modem/shrm/shrm_driver.h @@ -77,7 +77,7 @@ * @shm_ac_sleep_kw: kthread worker for recieving ape-cmt sleep requests * @shm_ac_sleep_kw_task: task for recieving ape-cmt sleep requests * @shm_mod_stuck_kw: kthread worker to reset the modem - * &shm_mod_stuck_kw_task: task for sending modem reset request + * @shm_mod_stuck_kw_task: task for sending modem reset request * @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 -- cgit v1.2.3 From 83765c8325ac699f3236e8f428f6df0a7efff4b2 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Fri, 27 Jan 2012 20:38:10 +0530 Subject: modem: u8500-shrm: print prcmu and abb regs on stuck timeout ST-Ericsson Linux next: - ST-Ericsson ID: 402239 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ia08165f58c0d55db7ed97a0c74cd4b493cf14495 Signed-off-by: Arun Murthy Signed-off-by: Mian Yousaf Kaukab Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/47128 Reviewed-by: QABUILD Reviewed-by: QATEST Reviewed-by: Jonas ABERG --- drivers/modem/shrm/shrm_protocol.c | 14 ++++++++++++++ include/linux/modem/shrm/shrm_driver.h | 2 ++ 2 files changed, 16 insertions(+) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index b9613f544e5..40e77993613 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -17,8 +17,10 @@ #include #include #include +#include #include #include +#include #define L2_HEADER_ISI 0x0 #define L2_HEADER_RPC 0x1 @@ -80,6 +82,13 @@ enum shrm_nl { SHRM_NL_STATUS_MOD_OFFLINE, }; +void shm_print_dbg_info_work(struct kthread_work *work) +{ + abx500_dump_all_banks(); + prcmu_debug_dump_regs(); + prcmu_debug_dump_data_mem(); +} + void shm_mod_reset_req_work(struct kthread_work *work) { prcmu_modem_reset(); @@ -115,6 +124,8 @@ static enum hrtimer_restart shm_fifo_full_timeout(struct hrtimer *timer) { queue_kthread_work(&shm_dev->shm_mod_stuck_kw, &shm_dev->shm_mod_reset_req); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_print_dbg_info); return HRTIMER_NORESTART; } @@ -136,6 +147,8 @@ static enum hrtimer_restart shm_mod_stuck_timeout(struct hrtimer *timer) dev_err(shm_dev->dev, "APE initiating MSR\n"); queue_kthread_work(&shm_dev->shm_mod_stuck_kw, &shm_dev->shm_mod_reset_req); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_print_dbg_info); return HRTIMER_NORESTART; } @@ -889,6 +902,7 @@ int shrm_protocol_init(struct shrm_dev *shrm, init_kthread_work(&shrm->shm_ac_sleep_req, shm_ac_sleep_req_work); init_kthread_work(&shrm->shm_ac_wake_req, shm_ac_wake_req_work); init_kthread_work(&shrm->shm_mod_reset_req, shm_mod_reset_req_work); + init_kthread_work(&shrm->shm_print_dbg_info, shm_print_dbg_info_work); /* set tasklet data */ shm_ca_0_tasklet.data = (unsigned long)shrm; diff --git a/include/linux/modem/shrm/shrm_driver.h b/include/linux/modem/shrm/shrm_driver.h index e8905e68778..b6e5c354db5 100644 --- a/include/linux/modem/shrm/shrm_driver.h +++ b/include/linux/modem/shrm/shrm_driver.h @@ -87,6 +87,7 @@ * @shm_ca_sleep_req: work to send cmt-ape sleep request * @shm_ac_sleep_req: work to send ape-cmt sleep request * @shm_mod_reset_req: work to send a reset request to modem + * @shm_print_dbg_info: work function to print all prcmu/abb registers */ struct shrm_dev { u8 ca_wake_irq; @@ -145,6 +146,7 @@ struct shrm_dev { struct kthread_work shm_ca_sleep_req; struct kthread_work shm_ac_sleep_req; struct kthread_work shm_mod_reset_req; + struct kthread_work shm_print_dbg_info; }; /** -- cgit v1.2.3 From 18f638e206e2536b835e27fae5024eb13a6f9915 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Wed, 8 Feb 2012 17:06:31 +0530 Subject: u8500-shrm: ensure that timer is started after write to GOP After sendig AC_MSG_PEND timer is started and on getting AC_READ_NOTI timer is stopped. There are possibility of preemtion happening after writting to GOP register(for sending AC_MSG_PEND) and starting the timer. Hence acquire a spin lock to ensure that hrtimer is started after sending MSG_PEND interrupt. ST-Ericsson Linux next: NA ST-Ericsson ID: 413524 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ic3d5d6e9259dc22ca6a4741b1cdd3792f33777fb Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/48272 Reviewed-by: QABUILD Reviewed-by: Jonas ABERG Reviewed-by: Srinidhi KASAGAR --- drivers/modem/shrm/shrm_protocol.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index 40e77993613..d9187e36c68 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -73,6 +73,7 @@ static DEFINE_SPINLOCK(ca_audio_lock); static DEFINE_SPINLOCK(ca_wake_req_lock); static DEFINE_SPINLOCK(boot_lock); static DEFINE_SPINLOCK(mod_stuck_lock); +static DEFINE_SPINLOCK(start_timer_lock); enum shrm_nl { SHRM_NL_MOD_RESET = 1, @@ -694,6 +695,7 @@ static irqreturn_t shrm_prcmu_irq_handler(int irq, void *data) static void send_ac_msg_pend_notify_0_work(struct kthread_work *work) { + unsigned long flags; struct shrm_dev *shrm = container_of(work, struct shrm_dev, send_ac_msg_pend_notify_0); @@ -717,6 +719,7 @@ static void send_ac_msg_pend_notify_0_work(struct kthread_work *work) return; } + spin_lock_irqsave(&start_timer_lock, flags); /* Trigger AcMsgPendingNotification to CMU */ writel((1<intr_base + GOP_SET_REGISTER_BASE); @@ -724,6 +727,7 @@ static void send_ac_msg_pend_notify_0_work(struct kthread_work *work) /* timer to detect modem stuck or hang */ hrtimer_start(&mod_stuck_timer_0, ktime_set(MOD_STUCK_TIMEOUT, 0), HRTIMER_MODE_REL); + spin_unlock_irqrestore(&start_timer_lock, flags); if (shrm_common_tx_state == SHRM_PTR_FREE) shrm_common_tx_state = SHRM_PTR_BUSY; @@ -732,6 +736,7 @@ static void send_ac_msg_pend_notify_0_work(struct kthread_work *work) static void send_ac_msg_pend_notify_1_work(struct kthread_work *work) { + unsigned long flags; struct shrm_dev *shrm = container_of(work, struct shrm_dev, send_ac_msg_pend_notify_1); @@ -759,6 +764,7 @@ static void send_ac_msg_pend_notify_1_work(struct kthread_work *work) return; } + spin_lock_irqsave(&start_timer_lock, flags); /* Trigger AcMsgPendingNotification to CMU */ writel((1<intr_base + GOP_SET_REGISTER_BASE); @@ -766,6 +772,7 @@ static void send_ac_msg_pend_notify_1_work(struct kthread_work *work) /* timer to detect modem stuck or hang */ hrtimer_start(&mod_stuck_timer_1, ktime_set(MOD_STUCK_TIMEOUT, 0), HRTIMER_MODE_REL); + spin_unlock_irqrestore(&start_timer_lock, flags); if (shrm_audio_tx_state == SHRM_PTR_FREE) shrm_audio_tx_state = SHRM_PTR_BUSY; @@ -1018,11 +1025,14 @@ irqreturn_t ca_wake_irq_handler(int irq, void *ctrlr) irqreturn_t ac_read_notif_0_irq_handler(int irq, void *ctrlr) { + unsigned long flags; struct shrm_dev *shrm = ctrlr; dev_dbg(shrm->dev, "%s IN\n", __func__); /* Cancel the modem stuck timer */ + spin_lock_irqsave(&start_timer_lock, flags); hrtimer_cancel(&mod_stuck_timer_0); + spin_unlock_irqrestore(&start_timer_lock, flags); if(atomic_read(&fifo_full)) hrtimer_cancel(&fifo_full_timer); @@ -1051,11 +1061,14 @@ irqreturn_t ac_read_notif_0_irq_handler(int irq, void *ctrlr) irqreturn_t ac_read_notif_1_irq_handler(int irq, void *ctrlr) { + unsigned long flags; struct shrm_dev *shrm = ctrlr; dev_dbg(shrm->dev, "%s IN+\n", __func__); /* Cancel the modem stuck timer */ + spin_lock_irqsave(&start_timer_lock, flags); hrtimer_cancel(&mod_stuck_timer_1); + spin_unlock_irqrestore(&start_timer_lock, flags); if(atomic_read(&fifo_full)) hrtimer_cancel(&fifo_full_timer); -- cgit v1.2.3 From 40a824150688e73e6153cf15ffde55421a801ce8 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Wed, 8 Feb 2012 17:10:45 +0530 Subject: u8500-shrm: reset boot_state on ape initiated modem reset the variable boot_state is used to monitor the check the access for the GOP register. Hence on APE initiated modem reset, this flag must be reset as there can be possibility that very soon after ape initiated modem reset, there is some 100msec for modem to send to send modem reset ack and in the mean time if ape tries to access the GOP register then it might end up with system freeze. Hence reset the flag on ape initiated modem reset. ST-Ericsson Linux next: NA ST-Ericsson ID: 413508 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I8ce1c2b2df4608dc3cb4b7a0b3c6ed95857ea4a6 Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/48273 Reviewed-by: QABUILD Reviewed-by: Srinidhi KASAGAR --- drivers/modem/shrm/shrm_protocol.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index d9187e36c68..d6d9e605b5c 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -92,6 +92,13 @@ void shm_print_dbg_info_work(struct kthread_work *work) void shm_mod_reset_req_work(struct kthread_work *work) { + unsigned long flags; + + /* update the boot_state */ + spin_lock_irqsave(&boot_lock, flags); + boot_state = BOOT_UNKNOWN; + wmb(); + spin_unlock_irqrestore(&boot_lock, flags); prcmu_modem_reset(); } -- cgit v1.2.3 From 211d77b805e2c7f5f1ef02ecc6c777a313f5688d Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Wed, 8 Feb 2012 17:25:27 +0530 Subject: u8500-shrm: ensure hostaccess_port is high while accessing GOP In places of APE handling modem ISR, its clear that modem is awake since it has sent the interrupt and it should be waiting for the ACK for the interrupt. But still its better to check since there are cases where modem behaves unexpectedly. ST-Ericsson Linux next: NA ST-Ericsson ID: 413508 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: If9f13388aabeb6b9721ae7274b0d2216d1bca027 Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/48274 Reviewed-by: QABUILD Reviewed-by: Srinidhi KASAGAR --- drivers/modem/shrm/shrm_protocol.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index d6d9e605b5c..aad2431324d 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -523,6 +523,8 @@ void shm_ca_sleep_req_work(struct kthread_work *work) shrm_common_rx_state = SHRM_IDLE; shrm_audio_rx_state = SHRM_IDLE; + if (!get_host_accessport_val()) + BUG(); writel((1<intr_base + GOP_SET_REGISTER_BASE); @@ -558,6 +560,7 @@ void shm_ca_wake_req_work(struct kthread_work *work) return; } + /* send ca_wake_ack_interrupt to CMU */ writel((1<intr_base + GOP_SET_REGISTER_BASE); } @@ -1058,6 +1061,8 @@ irqreturn_t ac_read_notif_0_irq_handler(int irq, void *ctrlr) return IRQ_HANDLED; } + if (!get_host_accessport_val()) + BUG(); /* Clear the interrupt */ writel((1 << GOP_COMMON_AC_READ_NOTIFICATION_BIT), shrm->intr_base + GOP_CLEAR_REGISTER_BASE); @@ -1094,6 +1099,8 @@ irqreturn_t ac_read_notif_1_irq_handler(int irq, void *ctrlr) return IRQ_HANDLED; } + if (!get_host_accessport_val()) + BUG(); /* Clear the interrupt */ writel((1 << GOP_AUDIO_AC_READ_NOTIFICATION_BIT), shrm->intr_base + GOP_CLEAR_REGISTER_BASE); @@ -1122,6 +1129,8 @@ irqreturn_t ca_msg_pending_notif_0_irq_handler(int irq, void *ctrlr) return IRQ_HANDLED; } + if (!get_host_accessport_val()) + BUG(); /* Clear the interrupt */ writel((1 << GOP_COMMON_CA_MSG_PENDING_NOTIFICATION_BIT), shrm->intr_base + GOP_CLEAR_REGISTER_BASE); @@ -1150,6 +1159,8 @@ irqreturn_t ca_msg_pending_notif_1_irq_handler(int irq, void *ctrlr) return IRQ_HANDLED; } + if (!get_host_accessport_val()) + BUG(); /* Clear the interrupt */ writel((1<intr_base+GOP_CLEAR_REGISTER_BASE); @@ -1264,6 +1275,8 @@ void ca_msg_read_notification_0(struct shrm_dev *shrm) return; } + if (!get_host_accessport_val()) + BUG(); /* Trigger CaMsgReadNotification to CMU */ writel((1 << GOP_COMMON_CA_READ_NOTIFICATION_BIT), shrm->intr_base + GOP_SET_REGISTER_BASE); @@ -1287,6 +1300,8 @@ void ca_msg_read_notification_1(struct shrm_dev *shrm) return; } + if (!get_host_accessport_val()) + BUG(); /* Trigger CaMsgReadNotification to CMU */ writel((1<intr_base+GOP_SET_REGISTER_BASE); -- cgit v1.2.3 From a08ca73e20e0ffd54c210e0d44b2dc217d7dfea8 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Wed, 8 Feb 2012 17:31:03 +0530 Subject: u8500-shrm: fix potential issues while asserting modem reset ST-Ericsson Linux next: NA ST-Ericsson ID: 413508 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ie34c827ba78398ce81403c6ee24cd82b4f2100c1 Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/48275 Reviewed-by: QABUILD Reviewed-by: Srinidhi KASAGAR --- drivers/modem/shrm/shrm_protocol.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index aad2431324d..151ccf4c5a0 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -807,7 +807,7 @@ void shm_nl_receive(struct sk_buff *skb) 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"); - if (atomic_read(&mod_stuck)) { + if (atomic_read(&mod_stuck) || atomic_read(&fifo_full)) { dev_info(shm_dev->dev, "Modem reset already in progress\n"); break; @@ -1043,8 +1043,10 @@ irqreturn_t ac_read_notif_0_irq_handler(int irq, void *ctrlr) spin_lock_irqsave(&start_timer_lock, flags); hrtimer_cancel(&mod_stuck_timer_0); spin_unlock_irqrestore(&start_timer_lock, flags); - if(atomic_read(&fifo_full)) + if(atomic_read(&fifo_full)) { + atomic_set(&fifo_full, 0); hrtimer_cancel(&fifo_full_timer); + } if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", @@ -1081,8 +1083,10 @@ irqreturn_t ac_read_notif_1_irq_handler(int irq, void *ctrlr) spin_lock_irqsave(&start_timer_lock, flags); hrtimer_cancel(&mod_stuck_timer_1); spin_unlock_irqrestore(&start_timer_lock, flags); - if(atomic_read(&fifo_full)) + if(atomic_read(&fifo_full)) { + atomic_set(&fifo_full, 0); hrtimer_cancel(&fifo_full_timer); + } if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", -- cgit v1.2.3 From b3a75be74808170515cae50fffa537dd7b8bd932 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Wed, 8 Feb 2012 17:45:15 +0530 Subject: u8500-shrm: secure the access of GOP register Before accessing the GOP registers checks are present to verify the modem reset status and the availability of hostaccess_port. There are chances of preemtion happening between the check and access to GOP during which if modem has reset, then we will end up with a system freeze. Hence secure that the check and access to GOP happends at a time without preemption. ST-Ericsson Linux next: NA ST-Ericsson ID: 413508 ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Arun Murthy Change-Id: I481e925b3a457ad3c8c82a5b7986dba6b2a4457b Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/48276 Reviewed-by: QABUILD Reviewed-by: Srinidhi KASAGAR --- drivers/modem/shrm/shrm_protocol.c | 174 ++++++++++++++++++++++++++++++------- 1 file changed, 143 insertions(+), 31 deletions(-) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index 151ccf4c5a0..e3daeda4348 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -83,6 +83,8 @@ enum shrm_nl { SHRM_NL_STATUS_MOD_OFFLINE, }; +static int check_modem_in_reset(void); + void shm_print_dbg_info_work(struct kthread_work *work) { abx500_dump_all_banks(); @@ -96,6 +98,11 @@ void shm_mod_reset_req_work(struct kthread_work *work) /* update the boot_state */ spin_lock_irqsave(&boot_lock, flags); + if (boot_state != BOOT_DONE) { + dev_info(shm_dev->dev, "Modem in reset state\n"); + spin_unlock_irqrestore(&boot_lock, flags); + return; + } boot_state = BOOT_UNKNOWN; wmb(); spin_unlock_irqrestore(&boot_lock, flags); @@ -515,18 +522,30 @@ void shm_ca_sleep_req_work(struct kthread_work *work) bt_state = boot_state; spin_unlock_irqrestore(&boot_lock, flags); + local_irq_save(flags); + preempt_disable(); if (bt_state != BOOT_DONE) { dev_err(shm_dev->dev, "%s:Modem state reset or unknown\n", __func__); + preempt_enable(); + local_irq_restore(flags); return; } shrm_common_rx_state = SHRM_IDLE; shrm_audio_rx_state = SHRM_IDLE; - if (!get_host_accessport_val()) - BUG(); + if (!get_host_accessport_val()) { + dev_err(shm_dev->dev, "%s: host_accessport is low\n", __func__); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + preempt_enable(); + local_irq_restore(flags); + return; + } writel((1<intr_base + GOP_SET_REGISTER_BASE); + preempt_enable(); + local_irq_restore(flags); hrtimer_start(&timer, ktime_set(0, 10*NSEC_PER_MSEC), HRTIMER_MODE_REL); @@ -536,6 +555,7 @@ void shm_ca_sleep_req_work(struct kthread_work *work) void shm_ca_wake_req_work(struct kthread_work *work) { + unsigned long flags; struct shrm_dev *shrm = container_of(work, struct shrm_dev, shm_ca_wake_req); @@ -547,22 +567,30 @@ void shm_ca_wake_req_work(struct kthread_work *work) modem_request(shrm->modem); mutex_unlock(&ac_state_mutex); + local_irq_save(flags); + preempt_disable(); /* send ca_wake_ack_interrupt to CMU */ - if (!get_host_accessport_val()) { - dev_crit(shrm->dev, "get_host_accessport failed\n"); - dev_info(shrm->dev, "Initiating a modem reset\n"); - prcmu_modem_reset(); - } - if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown\n", __func__); + preempt_enable(); + local_irq_restore(flags); return; } + if (!get_host_accessport_val()) { + dev_err(shrm->dev, "%s: host_accessport is low\n", __func__); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + preempt_enable(); + local_irq_restore(flags); + } + /* send ca_wake_ack_interrupt to CMU */ writel((1<intr_base + GOP_SET_REGISTER_BASE); + preempt_enable(); + local_irq_restore(flags); } #ifdef CONFIG_U8500_SHRM_MODEM_SILENT_RESET static int shrm_modem_reset_sequence(void) @@ -717,19 +745,22 @@ static void send_ac_msg_pend_notify_0_work(struct kthread_work *work) modem_request(shrm->modem); mutex_unlock(&ac_state_mutex); + spin_lock_irqsave(&start_timer_lock, flags); if (!get_host_accessport_val()) { - dev_crit(shrm->dev, "get_host_accessport failed\n"); - dev_info(shrm->dev, "Initiating a modem reset\n"); - prcmu_modem_reset(); + dev_err(shrm->dev, "%s: host_accessport is low\n", __func__); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + spin_unlock_irqrestore(&start_timer_lock, flags); + return; } if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", __func__); + spin_unlock_irqrestore(&start_timer_lock, flags); return; } - spin_lock_irqsave(&start_timer_lock, flags); /* Trigger AcMsgPendingNotification to CMU */ writel((1<intr_base + GOP_SET_REGISTER_BASE); @@ -762,19 +793,22 @@ static void send_ac_msg_pend_notify_1_work(struct kthread_work *work) modem_request(shrm->modem); mutex_unlock(&ac_state_mutex); + spin_lock_irqsave(&start_timer_lock, flags); if (!get_host_accessport_val()) { - dev_crit(shrm->dev, "get_host_accessport failed\n"); - dev_info(shrm->dev, "Initiating a modem reset\n"); - prcmu_modem_reset(); + dev_err(shrm->dev, "%s: host_accessport is low\n", __func__); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + spin_unlock_irqrestore(&start_timer_lock, flags); + return; } if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", __func__); + spin_unlock_irqrestore(&start_timer_lock, flags); return; } - spin_lock_irqsave(&start_timer_lock, flags); /* Trigger AcMsgPendingNotification to CMU */ writel((1<intr_base + GOP_SET_REGISTER_BASE); @@ -1043,7 +1077,7 @@ irqreturn_t ac_read_notif_0_irq_handler(int irq, void *ctrlr) spin_lock_irqsave(&start_timer_lock, flags); hrtimer_cancel(&mod_stuck_timer_0); spin_unlock_irqrestore(&start_timer_lock, flags); - if(atomic_read(&fifo_full)) { + if (atomic_read(&fifo_full)) { atomic_set(&fifo_full, 0); hrtimer_cancel(&fifo_full_timer); } @@ -1057,17 +1091,29 @@ irqreturn_t ac_read_notif_0_irq_handler(int irq, void *ctrlr) shm_ac_read_0_tasklet.data = (unsigned long)shrm; tasklet_schedule(&shm_ac_read_0_tasklet); + local_irq_save(flags); + preempt_disable(); if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", __func__); + preempt_enable(); + local_irq_restore(flags); return IRQ_HANDLED; } - if (!get_host_accessport_val()) - BUG(); + if (!get_host_accessport_val()) { + dev_err(shrm->dev, "%s: host_accessport is low\n", __func__); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + preempt_enable(); + local_irq_restore(flags); + return IRQ_HANDLED; + } /* Clear the interrupt */ writel((1 << GOP_COMMON_AC_READ_NOTIFICATION_BIT), shrm->intr_base + GOP_CLEAR_REGISTER_BASE); + preempt_enable(); + local_irq_restore(flags); dev_dbg(shrm->dev, "%s OUT\n", __func__); return IRQ_HANDLED; @@ -1083,7 +1129,7 @@ irqreturn_t ac_read_notif_1_irq_handler(int irq, void *ctrlr) spin_lock_irqsave(&start_timer_lock, flags); hrtimer_cancel(&mod_stuck_timer_1); spin_unlock_irqrestore(&start_timer_lock, flags); - if(atomic_read(&fifo_full)) { + if (atomic_read(&fifo_full)) { atomic_set(&fifo_full, 0); hrtimer_cancel(&fifo_full_timer); } @@ -1097,17 +1143,29 @@ irqreturn_t ac_read_notif_1_irq_handler(int irq, void *ctrlr) shm_ac_read_1_tasklet.data = (unsigned long)shrm; tasklet_schedule(&shm_ac_read_1_tasklet); + local_irq_save(flags); + preempt_disable(); if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", __func__); + preempt_enable(); + local_irq_restore(flags); return IRQ_HANDLED; } - if (!get_host_accessport_val()) - BUG(); + if (!get_host_accessport_val()) { + dev_err(shrm->dev, "%s: host_accessport is low\n", __func__); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + preempt_enable(); + local_irq_restore(flags); + return IRQ_HANDLED; + } /* Clear the interrupt */ writel((1 << GOP_AUDIO_AC_READ_NOTIFICATION_BIT), shrm->intr_base + GOP_CLEAR_REGISTER_BASE); + preempt_enable(); + local_irq_restore(flags); dev_dbg(shrm->dev, "%s OUT\n", __func__); return IRQ_HANDLED; @@ -1115,6 +1173,7 @@ 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) { + unsigned long flags; struct shrm_dev *shrm = ctrlr; dev_dbg(shrm->dev, "%s IN\n", __func__); @@ -1127,17 +1186,29 @@ irqreturn_t ca_msg_pending_notif_0_irq_handler(int irq, void *ctrlr) tasklet_schedule(&shm_ca_0_tasklet); + local_irq_save(flags); + preempt_disable(); if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", __func__); + preempt_enable(); + local_irq_restore(flags); return IRQ_HANDLED; } - if (!get_host_accessport_val()) - BUG(); + if (!get_host_accessport_val()) { + dev_err(shrm->dev, "%s: host_accessport is low\n", __func__); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + preempt_enable(); + local_irq_restore(flags); + return IRQ_HANDLED; + } /* Clear the interrupt */ writel((1 << GOP_COMMON_CA_MSG_PENDING_NOTIFICATION_BIT), shrm->intr_base + GOP_CLEAR_REGISTER_BASE); + preempt_enable(); + local_irq_restore(flags); dev_dbg(shrm->dev, "%s OUT\n", __func__); return IRQ_HANDLED; @@ -1145,6 +1216,7 @@ 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) { + unsigned long flags; struct shrm_dev *shrm = ctrlr; dev_dbg(shrm->dev, "%s IN\n", __func__); @@ -1157,17 +1229,29 @@ irqreturn_t ca_msg_pending_notif_1_irq_handler(int irq, void *ctrlr) tasklet_schedule(&shm_ca_1_tasklet); + local_irq_save(flags); + preempt_disable(); if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", __func__); + preempt_enable(); + local_irq_restore(flags); return IRQ_HANDLED; } - if (!get_host_accessport_val()) - BUG(); + if (!get_host_accessport_val()) { + dev_err(shrm->dev, "%s: host_accessport is low\n", __func__); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + preempt_enable(); + local_irq_restore(flags); + return IRQ_HANDLED; + } /* Clear the interrupt */ writel((1<intr_base+GOP_CLEAR_REGISTER_BASE); + preempt_enable(); + local_irq_restore(flags); dev_dbg(shrm->dev, "%s OUT\n", __func__); return IRQ_HANDLED; @@ -1233,7 +1317,7 @@ int shm_write_msg(struct shrm_dev *shrm, u8 l2_header, if (ret < 0) { dev_err(shrm->dev, "write message to fifo failed\n"); if (ret == -EAGAIN) { - if(!atomic_read(&fifo_full)) { + if (!atomic_read(&fifo_full)) { /* Start a timer so as to handle this gently */ atomic_set(&fifo_full, 1); hrtimer_start(&fifo_full_timer, ktime_set( @@ -1268,22 +1352,36 @@ out: void ca_msg_read_notification_0(struct shrm_dev *shrm) { + unsigned long flags; dev_dbg(shrm->dev, "%s IN\n", __func__); if (get_ca_msg_0_read_notif_send() == 0) { update_ca_common_shared_rptr(shrm); + local_irq_save(flags); + preempt_disable(); if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", __func__); + preempt_enable(); + local_irq_restore(flags); return; } - if (!get_host_accessport_val()) - BUG(); + if (!get_host_accessport_val()) { + dev_err(shrm->dev, "%s: host_accessport is low\n", + __func__); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + preempt_enable(); + local_irq_restore(flags); + return; + } /* Trigger CaMsgReadNotification to CMU */ writel((1 << GOP_COMMON_CA_READ_NOTIFICATION_BIT), shrm->intr_base + GOP_SET_REGISTER_BASE); + preempt_enable(); + local_irq_restore(flags); set_ca_msg_0_read_notif_send(1); shrm_common_rx_state = SHRM_PTR_BUSY; } @@ -1293,22 +1391,36 @@ void ca_msg_read_notification_0(struct shrm_dev *shrm) void ca_msg_read_notification_1(struct shrm_dev *shrm) { + unsigned long flags; dev_dbg(shrm->dev, "%s IN\n", __func__); if (get_ca_msg_1_read_notif_send() == 0) { update_ca_audio_shared_rptr(shrm); + local_irq_save(flags); + preempt_disable(); if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", __func__); + preempt_enable(); + local_irq_restore(flags); return; } - if (!get_host_accessport_val()) - BUG(); + if (!get_host_accessport_val()) { + dev_err(shrm->dev, "%s: host_accessport is low\n", + __func__); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + preempt_enable(); + local_irq_restore(flags); + return; + } /* Trigger CaMsgReadNotification to CMU */ writel((1<intr_base+GOP_SET_REGISTER_BASE); + preempt_enable(); + local_irq_restore(flags); set_ca_msg_1_read_notif_send(1); shrm_audio_rx_state = SHRM_PTR_BUSY; } -- cgit v1.2.3 From cad90b212c6cdc03a9f51859112ec7d087a70930 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Wed, 8 Feb 2012 18:57:14 +0530 Subject: u8500-shrm: check for modem awake along with hostaccess_port Before writing to GOP we mush ensure that hostaccess_port is high. Along with that its better to check the modem awake status, to confirm that modem is awake and we dont end up with a freeze on writing to GOP. ST-Ericsson Linux next: NA ST-Ericsson ID: 413508 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ia1bfa7adb8b472745f53ddd03878f25b3ed62375 Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/48277 Reviewed-by: QABUILD Reviewed-by: Srinidhi KASAGAR --- drivers/modem/shrm/shrm_protocol.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index e3daeda4348..fa4251cdbbd 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -35,8 +35,12 @@ #define MAX_PAYLOAD 1024 #define MOD_STUCK_TIMEOUT 6 #define FIFO_FULL_TIMEOUT 1 +#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE BIT(0) +#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE BIT(1) +#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_VMODEM_OFF_ISO BIT(2) #define PRCM_HOSTACCESS_REQ 0x334 +#define PRCM_MOD_AWAKE_STATUS 0x4A0 static u8 boot_state = BOOT_INIT; static u8 recieve_common_msg[8*1024]; @@ -127,10 +131,14 @@ static void shm_ac_wake_req_work(struct kthread_work *work) static u32 get_host_accessport_val(void) { u32 prcm_hostaccess; + u32 status; + status = (prcmu_read(PRCM_MOD_AWAKE_STATUS) & 0x03); prcm_hostaccess = prcmu_read(PRCM_HOSTACCESS_REQ); wmb(); - prcm_hostaccess = prcm_hostaccess & 0x01; + prcm_hostaccess = (prcm_hostaccess & 0x01) && + (status == (PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE | + PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE)); return prcm_hostaccess; } -- cgit v1.2.3 From 7f57b41ebfac95e8de4120c87272cfd895b2a21f Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Wed, 8 Feb 2012 19:05:44 +0530 Subject: u8500-shrm: before accessing GOP check modem reset Apart from checking the modem reset based on the shrm protocol internal state machine, PRCMU registers can also be checked to know the modem reset status before accessing the GOP registers. ST-Ericsson Linux next: NA ST-Ericsson ID: 413508 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I3fefea9d001ccd7a0478967706fec083085ac7f3 Signed-off-by: Arun Murthy Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/48278 Reviewed-by: QABUILD Reviewed-by: Srinidhi KASAGAR --- drivers/modem/shrm/shrm_protocol.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index fa4251cdbbd..df277dc9dfd 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -38,9 +38,12 @@ #define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE BIT(0) #define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE BIT(1) #define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_VMODEM_OFF_ISO BIT(2) +#define PRCM_MOD_PURESET BIT(0) +#define PRCM_MOD_SW_RESET BIT(1) -#define PRCM_HOSTACCESS_REQ 0x334 -#define PRCM_MOD_AWAKE_STATUS 0x4A0 +#define PRCM_HOSTACCESS_REQ 0x334 +#define PRCM_MOD_AWAKE_STATUS 0x4A0 +#define PRCM_MOD_RESETN_VAL 0x204 static u8 boot_state = BOOT_INIT; static u8 recieve_common_msg[8*1024]; @@ -132,13 +135,16 @@ static u32 get_host_accessport_val(void) { u32 prcm_hostaccess; u32 status; + u32 reset_stats; status = (prcmu_read(PRCM_MOD_AWAKE_STATUS) & 0x03); + reset_stats = (prcmu_read(PRCM_MOD_RESETN_VAL) & 0x03); prcm_hostaccess = prcmu_read(PRCM_HOSTACCESS_REQ); wmb(); - prcm_hostaccess = (prcm_hostaccess & 0x01) && + prcm_hostaccess = ((prcm_hostaccess & 0x01) && (status == (PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE | - PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE)); + PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE)) && + (reset_stats == (PRCM_MOD_SW_RESET | PRCM_MOD_PURESET))); return prcm_hostaccess; } -- cgit v1.2.3 From 30713fa549ec7984e6755de8df363c6871723c88 Mon Sep 17 00:00:00 2001 From: Ashok G Date: Mon, 27 Feb 2012 11:17:28 +0530 Subject: u8500: shrm: Wrongly placed return statement St-Ericsson Linux next: NA St-Ericsson Id: 419832 St-Ericsson FOSS-OUT ID: Trivial Change-Id: I5a90032c32da0ae9c1963fcd132b5506ec75e591 Signed-off-by: Ashok G Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/50523 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Bibek BASU Reviewed-by: Arun MURTHY Reviewed-by: Srinidhi KASAGAR --- drivers/modem/shrm/shrm_protocol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index df277dc9dfd..f029f4e0cfa 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -954,7 +954,7 @@ int shrm_protocol_init(struct shrm_dev *shrm, "shm_mod_reset_req"); if (IS_ERR(shrm->shm_mod_stuck_kw_task)) { dev_err(shrm->dev, "failed to create work task\n"); - return -ENOMEM; + err = -ENOMEM; goto free_kw5; } -- 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(-) 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 From adf9fa7b2e81465eead46f8b6e8737d559da9e60 Mon Sep 17 00:00:00 2001 From: Mattias Wallin Date: Thu, 1 Mar 2012 11:06:06 +0100 Subject: sim_detect: file access for SIM voltage changed Allow userspace not beeing root to update the SIM voltage level through sysfs. Modem needs to go via userspace to update the voltage level and that program is currently not root. Also change sysfs return value when a faulty input is given. ST-Ericsson ID: 411141 ST-Ericsson FOSS-OUT ID: Trivial ST-Ericsson Linux next: - Change-Id: I3ba59721197b8bd10482806a5471ad635d045bf1 Signed-off-by: Mattias Wallin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/51080 Reviewed-by: Teddie STENVI Reviewed-by: Bibek BASU Reviewed-by: Bengt JONSSON --- drivers/misc/sim_detect.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/misc/sim_detect.c b/drivers/misc/sim_detect.c index 6e6446d0fc5..20975c8d4b5 100644 --- a/drivers/misc/sim_detect.c +++ b/drivers/misc/sim_detect.c @@ -64,7 +64,7 @@ static ssize_t write_voltage(struct device *dev, struct device_attribute *attr, /* check input */ if (strict_strtol(buf, 0, &val) != 0) { dev_err(dev, "Invalid voltage class configured.\n"); - return count; + return -EINVAL; } switch (val) { @@ -75,7 +75,7 @@ static ssize_t write_voltage(struct device *dev, struct device_attribute *attr, break; default: dev_err(dev, "Invalid voltage class configured.\n"); - return count; + return -EINVAL; } /* lock */ @@ -117,7 +117,7 @@ out_unlock: return count; } -static DEVICE_ATTR(voltage, S_IWUSR | S_IRUGO, show_voltage, write_voltage); +static DEVICE_ATTR(voltage, S_IWUGO | S_IRUGO, show_voltage, write_voltage); static struct attribute *sim_attributes[] = { &dev_attr_voltage.attr, -- cgit v1.2.3 From 7338b0ab88d72b11989f40394069164d9326afbf Mon Sep 17 00:00:00 2001 From: Paer-Olof Haakansson Date: Tue, 28 Feb 2012 16:45:36 +0100 Subject: U8500: Make SIM detect work on resume The sim detect irq was not enabled during suspend which meant that the first irq on wakeup was lost. ST-Ericsson Linux next: NA ST-Ericsson ID: 399003 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I5ae895adc38fd2128953010342930cd0c92d27be Signed-off-by: Paer-Olof Haakansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/50784 Reviewed-by: QATOOLS Reviewed-by: Jonas ABERG --- drivers/misc/sim_detect.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/misc/sim_detect.c b/drivers/misc/sim_detect.c index 20975c8d4b5..e67f4fad3db 100644 --- a/drivers/misc/sim_detect.c +++ b/drivers/misc/sim_detect.c @@ -223,7 +223,9 @@ static int __devinit sim_detect_probe(struct platform_device *pdev) /* request irq */ ret = request_threaded_irq(plat->irq_num, NULL, sim_activity_irq, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + IRQF_TRIGGER_FALLING | + IRQF_TRIGGER_RISING | + IRQF_NO_SUSPEND, "sim activity", sim_detect); if (ret < 0) goto out_put_modem; -- cgit v1.2.3