blob: f6a4f32c8810d45332c13eeb9cca132cae79f896 [file] [log] [blame]
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001/* arch/arm/mach-msm/smd.c
2 *
3 * Copyright (C) 2007 Google, Inc.
Eric Holmbergc7e8daf2011-12-28 11:49:21 -07004 * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
Brian Swetland2eb44eb2008-09-29 16:00:48 -07005 * Author: Brian Swetland <swetland@google.com>
6 *
7 * This software is licensed under the terms of the GNU General Public
8 * License version 2, as published by the Free Software Foundation, and
9 * may be copied, distributed, and modified under those terms.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 */
17
18#include <linux/platform_device.h>
19#include <linux/module.h>
20#include <linux/fs.h>
21#include <linux/cdev.h>
22#include <linux/device.h>
23#include <linux/wait.h>
24#include <linux/interrupt.h>
25#include <linux/irq.h>
26#include <linux/list.h>
27#include <linux/slab.h>
Brian Swetland2eb44eb2008-09-29 16:00:48 -070028#include <linux/delay.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070029#include <linux/io.h>
30#include <linux/termios.h>
31#include <linux/ctype.h>
32#include <linux/remote_spinlock.h>
33#include <linux/uaccess.h>
Eric Holmbergc7e8daf2011-12-28 11:49:21 -070034#include <linux/kfifo.h>
Brian Swetland2eb44eb2008-09-29 16:00:48 -070035#include <mach/msm_smd.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070036#include <mach/msm_iomap.h>
Brian Swetland2eb44eb2008-09-29 16:00:48 -070037#include <mach/system.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070038#include <mach/subsystem_notif.h>
Angshuman Sarkaread67bd2011-09-21 20:13:12 +053039#include <mach/socinfo.h>
Brian Swetland2eb44eb2008-09-29 16:00:48 -070040
41#include "smd_private.h"
42#include "proc_comm.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070043#include "modem_notifier.h"
Brian Swetland2eb44eb2008-09-29 16:00:48 -070044
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070045#if defined(CONFIG_ARCH_QSD8X50) || defined(CONFIG_ARCH_MSM8X60) \
Jeff Hugo56b933a2011-09-28 14:42:05 -060046 || defined(CONFIG_ARCH_MSM8960) || defined(CONFIG_ARCH_FSM9XXX) \
Jeff Hugo0c0f5e92011-09-28 13:55:45 -060047 || defined(CONFIG_ARCH_MSM9615) || defined(CONFIG_ARCH_APQ8064)
Brian Swetland37521a32009-07-01 18:30:47 -070048#define CONFIG_QDSP6 1
49#endif
50
Jeff Hugo0c0f5e92011-09-28 13:55:45 -060051#if defined(CONFIG_ARCH_MSM8X60) || defined(CONFIG_ARCH_MSM8960) \
52 || defined(CONFIG_ARCH_APQ8064)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070053#define CONFIG_DSPS 1
54#endif
55
Jeff Hugo0c0f5e92011-09-28 13:55:45 -060056#if defined(CONFIG_ARCH_MSM8960) \
57 || defined(CONFIG_ARCH_APQ8064)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070058#define CONFIG_WCNSS 1
Jeff Hugo6a8057c2011-08-16 13:47:12 -060059#define CONFIG_DSPS_SMSM 1
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070060#endif
Brian Swetland2eb44eb2008-09-29 16:00:48 -070061
62#define MODULE_NAME "msm_smd"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070063#define SMEM_VERSION 0x000B
64#define SMD_VERSION 0x00020000
Eric Holmbergc7e8daf2011-12-28 11:49:21 -070065#define SMSM_SNAPSHOT_CNT 64
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070066
67uint32_t SMSM_NUM_ENTRIES = 8;
68uint32_t SMSM_NUM_HOSTS = 3;
Brian Swetland2eb44eb2008-09-29 16:00:48 -070069
70enum {
71 MSM_SMD_DEBUG = 1U << 0,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070072 MSM_SMSM_DEBUG = 1U << 1,
73 MSM_SMD_INFO = 1U << 2,
74 MSM_SMSM_INFO = 1U << 3,
75};
76
77struct smsm_shared_info {
78 uint32_t *state;
79 uint32_t *intr_mask;
80 uint32_t *intr_mux;
81};
82
83static struct smsm_shared_info smsm_info;
Eric Holmbergc7e8daf2011-12-28 11:49:21 -070084struct kfifo smsm_snapshot_fifo;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070085
86struct smsm_size_info_type {
87 uint32_t num_hosts;
88 uint32_t num_entries;
89 uint32_t reserved0;
90 uint32_t reserved1;
91};
92
93struct smsm_state_cb_info {
94 struct list_head cb_list;
95 uint32_t mask;
96 void *data;
97 void (*notify)(void *data, uint32_t old_state, uint32_t new_state);
98};
99
100struct smsm_state_info {
101 struct list_head callbacks;
102 uint32_t last_value;
103};
104
Angshuman Sarkarbad32df2012-02-01 19:52:52 +0530105struct interrupt_config_item {
106 /* must be initialized */
107 irqreturn_t (*irq_handler)(int req, void *data);
108 /* outgoing interrupt config (set from platform data) */
109 uint32_t out_bit_pos;
110 void __iomem *out_base;
111 uint32_t out_offset;
112};
113
114struct interrupt_config {
115 struct interrupt_config_item smd;
116 struct interrupt_config_item smsm;
117};
118
119static irqreturn_t smd_modem_irq_handler(int irq, void *data);
120static irqreturn_t smd_dsp_irq_handler(int irq, void *data);
121static irqreturn_t smd_dsps_irq_handler(int irq, void *data);
122static irqreturn_t smd_wcnss_irq_handler(int irq, void *data);
123static irqreturn_t smsm_irq_handler(int irq, void *data);
124
125static struct interrupt_config private_intr_config[NUM_SMD_SUBSYSTEMS] = {
126 [SMD_MODEM] = {
127 .smd.irq_handler = smd_modem_irq_handler,
128 .smsm.irq_handler = smsm_irq_handler,
129 },
130 [SMD_Q6] = {
131 .smd.irq_handler = smd_dsp_irq_handler,
132 .smsm.irq_handler = smsm_irq_handler,
133 },
134 [SMD_DSPS] = {
135 .smd.irq_handler = smd_dsps_irq_handler,
136 .smsm.irq_handler = smsm_irq_handler,
137 },
138 [SMD_WCNSS] = {
139 .smd.irq_handler = smd_wcnss_irq_handler,
140 .smsm.irq_handler = smsm_irq_handler,
141 },
142};
143
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700144#define SMSM_STATE_ADDR(entry) (smsm_info.state + entry)
145#define SMSM_INTR_MASK_ADDR(entry, host) (smsm_info.intr_mask + \
146 entry * SMSM_NUM_HOSTS + host)
147#define SMSM_INTR_MUX_ADDR(entry) (smsm_info.intr_mux + entry)
148
149/* Internal definitions which are not exported in some targets */
150enum {
151 SMSM_APPS_DEM_I = 3,
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700152};
153
154static int msm_smd_debug_mask;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700155module_param_named(debug_mask, msm_smd_debug_mask,
156 int, S_IRUGO | S_IWUSR | S_IWGRP);
157
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700158#if defined(CONFIG_MSM_SMD_DEBUG)
159#define SMD_DBG(x...) do { \
160 if (msm_smd_debug_mask & MSM_SMD_DEBUG) \
161 printk(KERN_DEBUG x); \
162 } while (0)
163
164#define SMSM_DBG(x...) do { \
165 if (msm_smd_debug_mask & MSM_SMSM_DEBUG) \
166 printk(KERN_DEBUG x); \
167 } while (0)
168
169#define SMD_INFO(x...) do { \
170 if (msm_smd_debug_mask & MSM_SMD_INFO) \
171 printk(KERN_INFO x); \
172 } while (0)
173
174#define SMSM_INFO(x...) do { \
175 if (msm_smd_debug_mask & MSM_SMSM_INFO) \
176 printk(KERN_INFO x); \
177 } while (0)
178#else
179#define SMD_DBG(x...) do { } while (0)
180#define SMSM_DBG(x...) do { } while (0)
181#define SMD_INFO(x...) do { } while (0)
182#define SMSM_INFO(x...) do { } while (0)
183#endif
184
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700185static unsigned last_heap_free = 0xffffffff;
186
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700187static inline void smd_write_intr(unsigned int val,
188 const void __iomem *addr);
189
190#if defined(CONFIG_ARCH_MSM7X30)
191#define MSM_TRIG_A2M_SMD_INT \
192 (smd_write_intr(1 << 0, MSM_GCC_BASE + 0x8))
193#define MSM_TRIG_A2Q6_SMD_INT \
194 (smd_write_intr(1 << 8, MSM_GCC_BASE + 0x8))
195#define MSM_TRIG_A2M_SMSM_INT \
196 (smd_write_intr(1 << 5, MSM_GCC_BASE + 0x8))
197#define MSM_TRIG_A2Q6_SMSM_INT \
198 (smd_write_intr(1 << 8, MSM_GCC_BASE + 0x8))
199#define MSM_TRIG_A2DSPS_SMD_INT
Jeff Hugo6a8057c2011-08-16 13:47:12 -0600200#define MSM_TRIG_A2DSPS_SMSM_INT
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700201#define MSM_TRIG_A2WCNSS_SMD_INT
202#define MSM_TRIG_A2WCNSS_SMSM_INT
203#elif defined(CONFIG_ARCH_MSM8X60)
204#define MSM_TRIG_A2M_SMD_INT \
205 (smd_write_intr(1 << 3, MSM_GCC_BASE + 0x8))
206#define MSM_TRIG_A2Q6_SMD_INT \
207 (smd_write_intr(1 << 15, MSM_GCC_BASE + 0x8))
208#define MSM_TRIG_A2M_SMSM_INT \
209 (smd_write_intr(1 << 4, MSM_GCC_BASE + 0x8))
210#define MSM_TRIG_A2Q6_SMSM_INT \
211 (smd_write_intr(1 << 14, MSM_GCC_BASE + 0x8))
212#define MSM_TRIG_A2DSPS_SMD_INT \
213 (smd_write_intr(1, MSM_SIC_NON_SECURE_BASE + 0x4080))
Jeff Hugo6a8057c2011-08-16 13:47:12 -0600214#define MSM_TRIG_A2DSPS_SMSM_INT
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700215#define MSM_TRIG_A2WCNSS_SMD_INT
216#define MSM_TRIG_A2WCNSS_SMSM_INT
Stepan Moskovchenko0dd0eba2011-10-20 18:20:59 -0700217#elif defined(CONFIG_ARCH_MSM8960) || defined(CONFIG_ARCH_MSM8930) || \
218 defined(CONFIG_ARCH_APQ8064)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700219#define MSM_TRIG_A2M_SMD_INT \
220 (smd_write_intr(1 << 3, MSM_APCS_GCC_BASE + 0x8))
221#define MSM_TRIG_A2Q6_SMD_INT \
222 (smd_write_intr(1 << 15, MSM_APCS_GCC_BASE + 0x8))
223#define MSM_TRIG_A2M_SMSM_INT \
224 (smd_write_intr(1 << 4, MSM_APCS_GCC_BASE + 0x8))
225#define MSM_TRIG_A2Q6_SMSM_INT \
226 (smd_write_intr(1 << 14, MSM_APCS_GCC_BASE + 0x8))
227#define MSM_TRIG_A2DSPS_SMD_INT \
228 (smd_write_intr(1, MSM_SIC_NON_SECURE_BASE + 0x4080))
Jeff Hugo6a8057c2011-08-16 13:47:12 -0600229#define MSM_TRIG_A2DSPS_SMSM_INT \
230 (smd_write_intr(1, MSM_SIC_NON_SECURE_BASE + 0x4094))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700231#define MSM_TRIG_A2WCNSS_SMD_INT \
232 (smd_write_intr(1 << 25, MSM_APCS_GCC_BASE + 0x8))
233#define MSM_TRIG_A2WCNSS_SMSM_INT \
234 (smd_write_intr(1 << 23, MSM_APCS_GCC_BASE + 0x8))
Jeff Hugo56b933a2011-09-28 14:42:05 -0600235#elif defined(CONFIG_ARCH_MSM9615)
236#define MSM_TRIG_A2M_SMD_INT \
237 (smd_write_intr(1 << 3, MSM_APCS_GCC_BASE + 0x8))
238#define MSM_TRIG_A2Q6_SMD_INT \
239 (smd_write_intr(1 << 15, MSM_APCS_GCC_BASE + 0x8))
240#define MSM_TRIG_A2M_SMSM_INT \
241 (smd_write_intr(1 << 4, MSM_APCS_GCC_BASE + 0x8))
242#define MSM_TRIG_A2Q6_SMSM_INT \
243 (smd_write_intr(1 << 14, MSM_APCS_GCC_BASE + 0x8))
244#define MSM_TRIG_A2DSPS_SMD_INT
245#define MSM_TRIG_A2DSPS_SMSM_INT
246#define MSM_TRIG_A2WCNSS_SMD_INT
247#define MSM_TRIG_A2WCNSS_SMSM_INT
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700248#elif defined(CONFIG_ARCH_FSM9XXX)
249#define MSM_TRIG_A2Q6_SMD_INT \
250 (smd_write_intr(1 << 10, MSM_GCC_BASE + 0x8))
251#define MSM_TRIG_A2Q6_SMSM_INT \
252 (smd_write_intr(1 << 10, MSM_GCC_BASE + 0x8))
253#define MSM_TRIG_A2M_SMD_INT \
254 (smd_write_intr(1 << 0, MSM_GCC_BASE + 0x8))
255#define MSM_TRIG_A2M_SMSM_INT \
256 (smd_write_intr(1 << 5, MSM_GCC_BASE + 0x8))
257#define MSM_TRIG_A2DSPS_SMD_INT
Jeff Hugo6a8057c2011-08-16 13:47:12 -0600258#define MSM_TRIG_A2DSPS_SMSM_INT
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700259#define MSM_TRIG_A2WCNSS_SMD_INT
260#define MSM_TRIG_A2WCNSS_SMSM_INT
Eric Holmberg73d45462012-02-28 10:41:31 -0700261#elif defined(CONFIG_ARCH_MSM7X01A) || defined(CONFIG_ARCH_MSM7x25)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700262#define MSM_TRIG_A2M_SMD_INT \
263 (smd_write_intr(1, MSM_CSR_BASE + 0x400 + (0) * 4))
Eric Holmberg73d45462012-02-28 10:41:31 -0700264#define MSM_TRIG_A2Q6_SMD_INT
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700265#define MSM_TRIG_A2M_SMSM_INT \
266 (smd_write_intr(1, MSM_CSR_BASE + 0x400 + (5) * 4))
Eric Holmberg73d45462012-02-28 10:41:31 -0700267#define MSM_TRIG_A2Q6_SMSM_INT
268#define MSM_TRIG_A2DSPS_SMD_INT
269#define MSM_TRIG_A2DSPS_SMSM_INT
270#define MSM_TRIG_A2WCNSS_SMD_INT
271#define MSM_TRIG_A2WCNSS_SMSM_INT
272#elif defined(CONFIG_ARCH_MSM7X27) || defined(CONFIG_ARCH_MSM7X27A)
273#define MSM_TRIG_A2M_SMD_INT \
274 (smd_write_intr(1, MSM_CSR_BASE + 0x400 + (0) * 4))
275#define MSM_TRIG_A2Q6_SMD_INT
276#define MSM_TRIG_A2M_SMSM_INT \
277 (smd_write_intr(1, MSM_CSR_BASE + 0x400 + (5) * 4))
278#define MSM_TRIG_A2Q6_SMSM_INT
279#define MSM_TRIG_A2DSPS_SMD_INT
280#define MSM_TRIG_A2DSPS_SMSM_INT
281#define MSM_TRIG_A2WCNSS_SMD_INT
282#define MSM_TRIG_A2WCNSS_SMSM_INT
283#else /* use platform device / device tree configuration */
284#define MSM_TRIG_A2M_SMD_INT
285#define MSM_TRIG_A2Q6_SMD_INT
286#define MSM_TRIG_A2M_SMSM_INT
287#define MSM_TRIG_A2Q6_SMSM_INT
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700288#define MSM_TRIG_A2DSPS_SMD_INT
Jeff Hugo6a8057c2011-08-16 13:47:12 -0600289#define MSM_TRIG_A2DSPS_SMSM_INT
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700290#define MSM_TRIG_A2WCNSS_SMD_INT
291#define MSM_TRIG_A2WCNSS_SMSM_INT
Brian Swetland37521a32009-07-01 18:30:47 -0700292#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700293
Jeff Hugoee40b152012-02-09 17:39:47 -0700294/*
295 * stub out legacy macros if they are not being used so that the legacy
296 * code compiles even though it is not used
297 *
298 * these definitions should not be used in active code and will cause
299 * an early failure
300 */
301#ifndef INT_A9_M2A_0
302#define INT_A9_M2A_0 -1
303#endif
304#ifndef INT_A9_M2A_5
305#define INT_A9_M2A_5 -1
306#endif
307#ifndef INT_ADSP_A11
308#define INT_ADSP_A11 -1
309#endif
310#ifndef INT_ADSP_A11_SMSM
311#define INT_ADSP_A11_SMSM -1
312#endif
313#ifndef INT_DSPS_A11
314#define INT_DSPS_A11 -1
315#endif
316#ifndef INT_DSPS_A11_SMSM
317#define INT_DSPS_A11_SMSM -1
318#endif
319#ifndef INT_WCNSS_A11
320#define INT_WCNSS_A11 -1
321#endif
322#ifndef INT_WCNSS_A11_SMSM
323#define INT_WCNSS_A11_SMSM -1
324#endif
325
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700326#define SMD_LOOPBACK_CID 100
327
Eric Holmbergf6d7d1a2011-09-23 18:31:04 -0600328#define SMEM_SPINLOCK_SMEM_ALLOC "S:3"
329static remote_spinlock_t remote_spinlock;
330
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700331static LIST_HEAD(smd_ch_list_loopback);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700332static void smd_fake_irq_handler(unsigned long arg);
Eric Holmbergc7e8daf2011-12-28 11:49:21 -0700333static void smsm_cb_snapshot(void);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700334
335static void notify_smsm_cb_clients_worker(struct work_struct *work);
336static DECLARE_WORK(smsm_cb_work, notify_smsm_cb_clients_worker);
Eric Holmbergc8002902011-09-16 13:55:57 -0600337static DEFINE_MUTEX(smsm_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700338static struct smsm_state_info *smsm_states;
Angshuman Sarkar7ee0dca2011-08-22 21:37:34 +0530339static int spinlocks_initialized;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700340
341static inline void smd_write_intr(unsigned int val,
342 const void __iomem *addr)
343{
344 wmb();
345 __raw_writel(val, addr);
346}
347
348#ifdef CONFIG_WCNSS
349static inline void wakeup_v1_riva(void)
350{
351 /*
352 * workaround hack for RIVA v1 hardware bug
353 * trigger GPIO 40 to wake up RIVA from power collaspe
354 * not to be sent to customers
355 */
Jeff Hugoafb8c4a2011-10-27 15:57:27 -0600356 if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 1) {
357 __raw_writel(0x0, MSM_TLMM_BASE + 0x1284);
358 __raw_writel(0x2, MSM_TLMM_BASE + 0x1284);
359 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700360 /* end workaround */
361}
362#else
363static inline void wakeup_v1_riva(void) {}
364#endif
365
Angshuman Sarkarbad32df2012-02-01 19:52:52 +0530366static inline void notify_modem_smd(void)
367{
368 static const struct interrupt_config_item *intr
369 = &private_intr_config[SMD_MODEM].smd;
370 if (intr->out_base)
371 smd_write_intr(intr->out_bit_pos,
372 intr->out_base + intr->out_offset);
373 else
374 MSM_TRIG_A2M_SMD_INT;
375}
376
377static inline void notify_dsp_smd(void)
378{
379 static const struct interrupt_config_item *intr
380 = &private_intr_config[SMD_Q6].smd;
381 if (intr->out_base)
382 smd_write_intr(intr->out_bit_pos,
383 intr->out_base + intr->out_offset);
384 else
385 MSM_TRIG_A2Q6_SMD_INT;
386}
387
388static inline void notify_dsps_smd(void)
389{
390 static const struct interrupt_config_item *intr
391 = &private_intr_config[SMD_DSPS].smd;
392 if (intr->out_base)
393 smd_write_intr(intr->out_bit_pos,
394 intr->out_base + intr->out_offset);
395 else
396 MSM_TRIG_A2DSPS_SMD_INT;
397}
398
399static inline void notify_wcnss_smd(void)
400{
401 static const struct interrupt_config_item *intr
402 = &private_intr_config[SMD_WCNSS].smd;
403 wakeup_v1_riva();
404
405 if (intr->out_base)
406 smd_write_intr(intr->out_bit_pos,
407 intr->out_base + intr->out_offset);
408 else
409 MSM_TRIG_A2WCNSS_SMD_INT;
410}
411
412static inline void notify_modem_smsm(void)
413{
414 static const struct interrupt_config_item *intr
415 = &private_intr_config[SMD_MODEM].smsm;
416 if (intr->out_base)
417 smd_write_intr(intr->out_bit_pos,
418 intr->out_base + intr->out_offset);
419 else
420 MSM_TRIG_A2M_SMSM_INT;
421}
422
423static inline void notify_dsp_smsm(void)
424{
425 static const struct interrupt_config_item *intr
426 = &private_intr_config[SMD_Q6].smsm;
427 if (intr->out_base)
428 smd_write_intr(intr->out_bit_pos,
429 intr->out_base + intr->out_offset);
430 else
431 MSM_TRIG_A2Q6_SMSM_INT;
432}
433
434static inline void notify_dsps_smsm(void)
435{
436 static const struct interrupt_config_item *intr
437 = &private_intr_config[SMD_DSPS].smsm;
438 if (intr->out_base)
439 smd_write_intr(intr->out_bit_pos,
440 intr->out_base + intr->out_offset);
441 else
442 MSM_TRIG_A2DSPS_SMSM_INT;
443}
444
445static inline void notify_wcnss_smsm(void)
446{
447 static const struct interrupt_config_item *intr
448 = &private_intr_config[SMD_WCNSS].smsm;
449 wakeup_v1_riva();
450
451 if (intr->out_base)
452 smd_write_intr(intr->out_bit_pos,
453 intr->out_base + intr->out_offset);
454 else
455 MSM_TRIG_A2WCNSS_SMSM_INT;
456}
457
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700458static void notify_other_smsm(uint32_t smsm_entry, uint32_t notify_mask)
459{
460 /* older protocol don't use smsm_intr_mask,
461 but still communicates with modem */
462 if (!smsm_info.intr_mask ||
463 (__raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_MODEM))
464 & notify_mask))
Angshuman Sarkarbad32df2012-02-01 19:52:52 +0530465 notify_modem_smsm();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700466
467 if (smsm_info.intr_mask &&
468 (__raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_Q6))
469 & notify_mask)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700470 uint32_t mux_val;
471
Eric Holmberg6282c5d2011-10-27 17:30:57 -0600472 if (cpu_is_qsd8x50() && smsm_info.intr_mux) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700473 mux_val = __raw_readl(
474 SMSM_INTR_MUX_ADDR(SMEM_APPS_Q6_SMSM));
475 mux_val++;
476 __raw_writel(mux_val,
477 SMSM_INTR_MUX_ADDR(SMEM_APPS_Q6_SMSM));
478 }
Angshuman Sarkarbad32df2012-02-01 19:52:52 +0530479 notify_dsp_smsm();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700480 }
481
482 if (smsm_info.intr_mask &&
483 (__raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_WCNSS))
484 & notify_mask)) {
Angshuman Sarkarbad32df2012-02-01 19:52:52 +0530485 notify_wcnss_smsm();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700486 }
487
Jeff Hugo6a8057c2011-08-16 13:47:12 -0600488 if (smsm_info.intr_mask &&
489 (__raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_DSPS))
490 & notify_mask)) {
Angshuman Sarkarbad32df2012-02-01 19:52:52 +0530491 notify_dsps_smsm();
Jeff Hugo6a8057c2011-08-16 13:47:12 -0600492 }
493
Eric Holmbergc7e8daf2011-12-28 11:49:21 -0700494 smsm_cb_snapshot();
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700495}
496
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700497void smd_diag(void)
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700498{
499 char *x;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700500 int size;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700501
502 x = smem_find(ID_DIAG_ERR_MSG, SZ_DIAG_ERR_MSG);
503 if (x != 0) {
504 x[SZ_DIAG_ERR_MSG - 1] = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700505 SMD_INFO("smem: DIAG '%s'\n", x);
506 }
507
508 x = smem_get_entry(SMEM_ERR_CRASH_LOG, &size);
509 if (x != 0) {
510 x[size - 1] = 0;
511 pr_err("smem: CRASH LOG\n'%s'\n", x);
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700512 }
513}
514
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700515
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700516static void handle_modem_crash(void)
517{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700518 pr_err("MODEM/AMSS has CRASHED\n");
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700519 smd_diag();
520
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700521 /* hard reboot if possible FIXME
522 if (msm_reset_hook)
523 msm_reset_hook();
524 */
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700525
526 /* in this case the modem or watchdog should reboot us */
527 for (;;)
528 ;
529}
530
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700531int smsm_check_for_modem_crash(void)
Arve Hjønnevåg28379412009-05-20 16:52:36 -0700532{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700533 /* if the modem's not ready yet, we have to hope for the best */
534 if (!smsm_info.state)
535 return 0;
Arve Hjønnevåg28379412009-05-20 16:52:36 -0700536
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700537 if (__raw_readl(SMSM_STATE_ADDR(SMSM_MODEM_STATE)) & SMSM_RESET) {
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700538 handle_modem_crash();
539 return -1;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700540 }
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700541 return 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700542}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700543EXPORT_SYMBOL(smsm_check_for_modem_crash);
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700544
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700545/* the spinlock is used to synchronize between the
Brian Swetland03e00cd2009-07-01 17:58:37 -0700546 * irq handler and code that mutates the channel
547 * list or fiddles with channel state
548 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700549static DEFINE_SPINLOCK(smd_lock);
Brian Swetland03e00cd2009-07-01 17:58:37 -0700550DEFINE_SPINLOCK(smem_lock);
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700551
552/* the mutex is used during open() and close()
Brian Swetland03e00cd2009-07-01 17:58:37 -0700553 * operations to avoid races while creating or
554 * destroying smd_channel structures
555 */
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700556static DEFINE_MUTEX(smd_creation_mutex);
557
558static int smd_initialized;
559
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700560struct smd_shared_v1 {
561 struct smd_half_channel ch0;
562 unsigned char data0[SMD_BUF_SIZE];
563 struct smd_half_channel ch1;
564 unsigned char data1[SMD_BUF_SIZE];
565};
566
567struct smd_shared_v2 {
568 struct smd_half_channel ch0;
569 struct smd_half_channel ch1;
570};
571
572struct smd_channel {
573 volatile struct smd_half_channel *send;
574 volatile struct smd_half_channel *recv;
575 unsigned char *send_data;
576 unsigned char *recv_data;
577 unsigned fifo_size;
578 unsigned fifo_mask;
579 struct list_head ch_list;
580
581 unsigned current_packet;
582 unsigned n;
583 void *priv;
584 void (*notify)(void *priv, unsigned flags);
585
586 int (*read)(smd_channel_t *ch, void *data, int len, int user_buf);
587 int (*write)(smd_channel_t *ch, const void *data, int len,
588 int user_buf);
589 int (*read_avail)(smd_channel_t *ch);
590 int (*write_avail)(smd_channel_t *ch);
591 int (*read_from_cb)(smd_channel_t *ch, void *data, int len,
592 int user_buf);
593
594 void (*update_state)(smd_channel_t *ch);
595 unsigned last_state;
596 void (*notify_other_cpu)(void);
597
598 char name[20];
599 struct platform_device pdev;
600 unsigned type;
601
602 int pending_pkt_sz;
603
604 char is_pkt_ch;
605};
606
607struct edge_to_pid {
608 uint32_t local_pid;
609 uint32_t remote_pid;
610};
611
612/**
613 * Maps edge type to local and remote processor ID's.
614 */
615static struct edge_to_pid edge_to_pids[] = {
616 [SMD_APPS_MODEM] = {SMSM_APPS, SMSM_MODEM},
617 [SMD_APPS_QDSP] = {SMSM_APPS, SMSM_Q6},
618 [SMD_MODEM_QDSP] = {SMSM_MODEM, SMSM_Q6},
619 [SMD_APPS_DSPS] = {SMSM_APPS, SMSM_DSPS},
620 [SMD_MODEM_DSPS] = {SMSM_MODEM, SMSM_DSPS},
621 [SMD_QDSP_DSPS] = {SMSM_Q6, SMSM_DSPS},
622 [SMD_APPS_WCNSS] = {SMSM_APPS, SMSM_WCNSS},
623 [SMD_MODEM_WCNSS] = {SMSM_MODEM, SMSM_WCNSS},
624 [SMD_QDSP_WCNSS] = {SMSM_Q6, SMSM_WCNSS},
625 [SMD_DSPS_WCNSS] = {SMSM_DSPS, SMSM_WCNSS},
Eric Holmberg7dfd1972011-09-09 16:07:57 -0600626 [SMD_APPS_Q6FW] = {SMSM_APPS, SMD_MODEM_Q6_FW},
627 [SMD_MODEM_Q6FW] = {SMSM_MODEM, SMD_MODEM_Q6_FW},
628 [SMD_QDSP_Q6FW] = {SMSM_Q6, SMD_MODEM_Q6_FW},
629 [SMD_DSPS_Q6FW] = {SMSM_DSPS, SMD_MODEM_Q6_FW},
630 [SMD_WCNSS_Q6FW] = {SMSM_WCNSS, SMD_MODEM_Q6_FW},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700631};
632
633struct restart_notifier_block {
634 unsigned processor;
635 char *name;
636 struct notifier_block nb;
637};
638
639static struct platform_device loopback_tty_pdev = {.name = "LOOPBACK_TTY"};
640
641static LIST_HEAD(smd_ch_closed_list);
642static LIST_HEAD(smd_ch_closing_list);
643static LIST_HEAD(smd_ch_to_close_list);
644static LIST_HEAD(smd_ch_list_modem);
645static LIST_HEAD(smd_ch_list_dsp);
646static LIST_HEAD(smd_ch_list_dsps);
647static LIST_HEAD(smd_ch_list_wcnss);
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700648
649static unsigned char smd_ch_allocated[64];
650static struct work_struct probe_work;
651
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700652static void finalize_channel_close_fn(struct work_struct *work);
653static DECLARE_WORK(finalize_channel_close_work, finalize_channel_close_fn);
654static struct workqueue_struct *channel_close_wq;
655
656static int smd_alloc_channel(struct smd_alloc_elm *alloc_elm);
657
658/* on smp systems, the probe might get called from multiple cores,
659 hence use a lock */
660static DEFINE_MUTEX(smd_probe_lock);
661
662static void smd_channel_probe_worker(struct work_struct *work)
663{
664 struct smd_alloc_elm *shared;
665 unsigned n;
666 uint32_t type;
667
668 shared = smem_find(ID_CH_ALLOC_TBL, sizeof(*shared) * 64);
669
670 if (!shared) {
671 pr_err("%s: allocation table not initialized\n", __func__);
672 return;
673 }
674
675 mutex_lock(&smd_probe_lock);
676 for (n = 0; n < 64; n++) {
677 if (smd_ch_allocated[n])
678 continue;
679
680 /* channel should be allocated only if APPS
681 processor is involved */
682 type = SMD_CHANNEL_TYPE(shared[n].type);
683 if ((type != SMD_APPS_MODEM) && (type != SMD_APPS_QDSP) &&
684 (type != SMD_APPS_DSPS) && (type != SMD_APPS_WCNSS))
685 continue;
686 if (!shared[n].ref_count)
687 continue;
688 if (!shared[n].name[0])
689 continue;
690
691 if (!smd_alloc_channel(&shared[n]))
692 smd_ch_allocated[n] = 1;
693 else
694 SMD_INFO("Probe skipping ch %d, not allocated\n", n);
695 }
696 mutex_unlock(&smd_probe_lock);
697}
698
699/**
700 * Lookup processor ID and determine if it belongs to the proved edge
701 * type.
702 *
703 * @shared2: Pointer to v2 shared channel structure
704 * @type: Edge type
705 * @pid: Processor ID of processor on edge
706 * @local_ch: Channel that belongs to processor @pid
707 * @remote_ch: Other side of edge contained @pid
708 *
709 * Returns 0 for not on edge, 1 for found on edge
710 */
711static int pid_is_on_edge(struct smd_shared_v2 *shared2,
712 uint32_t type, uint32_t pid,
713 struct smd_half_channel **local_ch,
714 struct smd_half_channel **remote_ch
715 )
716{
717 int ret = 0;
718 struct edge_to_pid *edge;
719
720 *local_ch = 0;
721 *remote_ch = 0;
722
723 if (!shared2 || (type >= ARRAY_SIZE(edge_to_pids)))
724 return 0;
725
726 edge = &edge_to_pids[type];
727 if (edge->local_pid != edge->remote_pid) {
728 if (pid == edge->local_pid) {
729 *local_ch = &shared2->ch0;
730 *remote_ch = &shared2->ch1;
731 ret = 1;
732 } else if (pid == edge->remote_pid) {
733 *local_ch = &shared2->ch1;
734 *remote_ch = &shared2->ch0;
735 ret = 1;
736 }
737 }
738
739 return ret;
740}
741
Eric Holmberg2a563c32011-10-05 14:51:43 -0600742static void smd_reset_edge(struct smd_half_channel *ch, unsigned new_state)
743{
744 if (ch->state != SMD_SS_CLOSED) {
745 ch->state = new_state;
746 ch->fDSR = 0;
747 ch->fCTS = 0;
748 ch->fCD = 0;
749 ch->fSTATE = 1;
750 }
751}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700752
753static void smd_channel_reset_state(struct smd_alloc_elm *shared,
754 unsigned new_state, unsigned pid)
755{
756 unsigned n;
757 struct smd_shared_v2 *shared2;
758 uint32_t type;
759 struct smd_half_channel *local_ch;
760 struct smd_half_channel *remote_ch;
761
762 for (n = 0; n < SMD_CHANNELS; n++) {
763 if (!shared[n].ref_count)
764 continue;
765 if (!shared[n].name[0])
766 continue;
767
768 type = SMD_CHANNEL_TYPE(shared[n].type);
769 shared2 = smem_alloc(SMEM_SMD_BASE_ID + n, sizeof(*shared2));
770 if (!shared2)
771 continue;
772
Eric Holmberg2a563c32011-10-05 14:51:43 -0600773 if (pid_is_on_edge(shared2, type, pid, &local_ch, &remote_ch))
774 smd_reset_edge(local_ch, new_state);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700775
Eric Holmberg2a563c32011-10-05 14:51:43 -0600776 /*
777 * ModemFW is in the same subsystem as ModemSW, but has
778 * separate SMD edges that need to be reset.
779 */
780 if (pid == SMSM_MODEM &&
781 pid_is_on_edge(shared2, type, SMD_MODEM_Q6_FW,
782 &local_ch, &remote_ch))
783 smd_reset_edge(local_ch, new_state);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700784 }
785}
786
787
788void smd_channel_reset(uint32_t restart_pid)
789{
790 struct smd_alloc_elm *shared;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700791 unsigned long flags;
792
793 SMD_DBG("%s: starting reset\n", __func__);
794 shared = smem_find(ID_CH_ALLOC_TBL, sizeof(*shared) * 64);
795 if (!shared) {
796 pr_err("%s: allocation table not initialized\n", __func__);
797 return;
798 }
799
Eric Holmbergf6d7d1a2011-09-23 18:31:04 -0600800 /* release any held spinlocks */
801 remote_spin_release(&remote_spinlock, restart_pid);
802 remote_spin_release_all(restart_pid);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700803
804 /* reset SMSM entry */
805 if (smsm_info.state) {
806 writel_relaxed(0, SMSM_STATE_ADDR(restart_pid));
807
Eric Holmberg351a63c2011-12-02 17:49:43 -0700808 /* restart SMSM init handshake */
809 if (restart_pid == SMSM_MODEM) {
810 smsm_change_state(SMSM_APPS_STATE,
Eric Holmberg6b2f80e2012-01-09 12:22:52 -0700811 SMSM_INIT | SMSM_SMD_LOOPBACK | SMSM_RESET,
812 0);
Eric Holmberg351a63c2011-12-02 17:49:43 -0700813 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700814
815 /* notify SMSM processors */
816 smsm_irq_handler(0, 0);
817 MSM_TRIG_A2M_SMSM_INT;
818 MSM_TRIG_A2Q6_SMSM_INT;
Jeff Hugo6a8057c2011-08-16 13:47:12 -0600819 MSM_TRIG_A2DSPS_SMSM_INT;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700820 }
821
822 /* change all remote states to CLOSING */
823 mutex_lock(&smd_probe_lock);
824 spin_lock_irqsave(&smd_lock, flags);
825 smd_channel_reset_state(shared, SMD_SS_CLOSING, restart_pid);
826 spin_unlock_irqrestore(&smd_lock, flags);
827 mutex_unlock(&smd_probe_lock);
828
829 /* notify SMD processors */
830 mb();
831 smd_fake_irq_handler(0);
832 notify_modem_smd();
833 notify_dsp_smd();
834 notify_dsps_smd();
835 notify_wcnss_smd();
836
837 /* change all remote states to CLOSED */
838 mutex_lock(&smd_probe_lock);
839 spin_lock_irqsave(&smd_lock, flags);
840 smd_channel_reset_state(shared, SMD_SS_CLOSED, restart_pid);
841 spin_unlock_irqrestore(&smd_lock, flags);
842 mutex_unlock(&smd_probe_lock);
843
844 /* notify SMD processors */
845 mb();
846 smd_fake_irq_handler(0);
847 notify_modem_smd();
848 notify_dsp_smd();
849 notify_dsps_smd();
850 notify_wcnss_smd();
851
852 SMD_DBG("%s: finished reset\n", __func__);
853}
854
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700855/* how many bytes are available for reading */
856static int smd_stream_read_avail(struct smd_channel *ch)
857{
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700858 return (ch->recv->head - ch->recv->tail) & ch->fifo_mask;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700859}
860
861/* how many bytes we are free to write */
862static int smd_stream_write_avail(struct smd_channel *ch)
863{
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700864 return ch->fifo_mask -
865 ((ch->send->head - ch->send->tail) & ch->fifo_mask);
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700866}
867
868static int smd_packet_read_avail(struct smd_channel *ch)
869{
870 if (ch->current_packet) {
871 int n = smd_stream_read_avail(ch);
872 if (n > ch->current_packet)
873 n = ch->current_packet;
874 return n;
875 } else {
876 return 0;
877 }
878}
879
880static int smd_packet_write_avail(struct smd_channel *ch)
881{
882 int n = smd_stream_write_avail(ch);
883 return n > SMD_HEADER_SIZE ? n - SMD_HEADER_SIZE : 0;
884}
885
886static int ch_is_open(struct smd_channel *ch)
887{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700888 return (ch->recv->state == SMD_SS_OPENED ||
889 ch->recv->state == SMD_SS_FLUSHING)
890 && (ch->send->state == SMD_SS_OPENED);
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700891}
892
893/* provide a pointer and length to readable data in the fifo */
894static unsigned ch_read_buffer(struct smd_channel *ch, void **ptr)
895{
896 unsigned head = ch->recv->head;
897 unsigned tail = ch->recv->tail;
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700898 *ptr = (void *) (ch->recv_data + tail);
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700899
900 if (tail <= head)
901 return head - tail;
902 else
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700903 return ch->fifo_size - tail;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700904}
905
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700906static int read_intr_blocked(struct smd_channel *ch)
907{
908 return ch->recv->fBLOCKREADINTR;
909}
910
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700911/* advance the fifo read pointer after data from ch_read_buffer is consumed */
912static void ch_read_done(struct smd_channel *ch, unsigned count)
913{
914 BUG_ON(count > smd_stream_read_avail(ch));
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700915 ch->recv->tail = (ch->recv->tail + count) & ch->fifo_mask;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700916 wmb();
Haley Teng7632fba2009-10-12 10:38:10 -0700917 ch->send->fTAIL = 1;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700918}
919
920/* basic read interface to ch_read_{buffer,done} used
Brian Swetland03e00cd2009-07-01 17:58:37 -0700921 * by smd_*_read() and update_packet_state()
922 * will read-and-discard if the _data pointer is null
923 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700924static int ch_read(struct smd_channel *ch, void *_data, int len, int user_buf)
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700925{
926 void *ptr;
927 unsigned n;
928 unsigned char *data = _data;
929 int orig_len = len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700930 int r = 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700931
932 while (len > 0) {
933 n = ch_read_buffer(ch, &ptr);
934 if (n == 0)
935 break;
936
937 if (n > len)
938 n = len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700939 if (_data) {
940 if (user_buf) {
941 r = copy_to_user(data, ptr, n);
942 if (r > 0) {
943 pr_err("%s: "
944 "copy_to_user could not copy "
945 "%i bytes.\n",
946 __func__,
947 r);
948 }
949 } else
950 memcpy(data, ptr, n);
951 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700952
953 data += n;
954 len -= n;
955 ch_read_done(ch, n);
956 }
957
958 return orig_len - len;
959}
960
961static void update_stream_state(struct smd_channel *ch)
962{
963 /* streams have no special state requiring updating */
964}
965
966static void update_packet_state(struct smd_channel *ch)
967{
968 unsigned hdr[5];
969 int r;
970
971 /* can't do anything if we're in the middle of a packet */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700972 while (ch->current_packet == 0) {
973 /* discard 0 length packets if any */
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700974
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700975 /* don't bother unless we can get the full header */
976 if (smd_stream_read_avail(ch) < SMD_HEADER_SIZE)
977 return;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700978
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700979 r = ch_read(ch, hdr, SMD_HEADER_SIZE, 0);
980 BUG_ON(r != SMD_HEADER_SIZE);
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700981
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700982 ch->current_packet = hdr[0];
983 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700984}
985
986/* provide a pointer and length to next free space in the fifo */
987static unsigned ch_write_buffer(struct smd_channel *ch, void **ptr)
988{
989 unsigned head = ch->send->head;
990 unsigned tail = ch->send->tail;
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700991 *ptr = (void *) (ch->send_data + head);
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700992
993 if (head < tail) {
994 return tail - head - 1;
995 } else {
996 if (tail == 0)
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700997 return ch->fifo_size - head - 1;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700998 else
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700999 return ch->fifo_size - head;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001000 }
1001}
1002
1003/* advace the fifo write pointer after freespace
1004 * from ch_write_buffer is filled
1005 */
1006static void ch_write_done(struct smd_channel *ch, unsigned count)
1007{
1008 BUG_ON(count > smd_stream_write_avail(ch));
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001009 ch->send->head = (ch->send->head + count) & ch->fifo_mask;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001010 wmb();
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001011 ch->send->fHEAD = 1;
1012}
1013
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001014static void ch_set_state(struct smd_channel *ch, unsigned n)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001015{
1016 if (n == SMD_SS_OPENED) {
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001017 ch->send->fDSR = 1;
1018 ch->send->fCTS = 1;
1019 ch->send->fCD = 1;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001020 } else {
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001021 ch->send->fDSR = 0;
1022 ch->send->fCTS = 0;
1023 ch->send->fCD = 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001024 }
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001025 ch->send->state = n;
1026 ch->send->fSTATE = 1;
1027 ch->notify_other_cpu();
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001028}
1029
1030static void do_smd_probe(void)
1031{
1032 struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE;
1033 if (shared->heap_info.free_offset != last_heap_free) {
1034 last_heap_free = shared->heap_info.free_offset;
1035 schedule_work(&probe_work);
1036 }
1037}
1038
1039static void smd_state_change(struct smd_channel *ch,
1040 unsigned last, unsigned next)
1041{
1042 ch->last_state = next;
1043
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001044 SMD_INFO("SMD: ch %d %d -> %d\n", ch->n, last, next);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001045
1046 switch (next) {
1047 case SMD_SS_OPENING:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001048 if (ch->send->state == SMD_SS_CLOSING ||
1049 ch->send->state == SMD_SS_CLOSED) {
1050 ch->recv->tail = 0;
1051 ch->send->head = 0;
1052 ch->send->fBLOCKREADINTR = 0;
1053 ch_set_state(ch, SMD_SS_OPENING);
1054 }
1055 break;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001056 case SMD_SS_OPENED:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001057 if (ch->send->state == SMD_SS_OPENING) {
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001058 ch_set_state(ch, SMD_SS_OPENED);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001059 ch->notify(ch->priv, SMD_EVENT_OPEN);
1060 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001061 break;
1062 case SMD_SS_FLUSHING:
1063 case SMD_SS_RESET:
1064 /* we should force them to close? */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001065 break;
1066 case SMD_SS_CLOSED:
1067 if (ch->send->state == SMD_SS_OPENED) {
1068 ch_set_state(ch, SMD_SS_CLOSING);
1069 ch->current_packet = 0;
Eric Holmbergad4fa8d2011-11-11 16:55:13 -07001070 ch->pending_pkt_sz = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001071 ch->notify(ch->priv, SMD_EVENT_CLOSE);
1072 }
1073 break;
1074 case SMD_SS_CLOSING:
1075 if (ch->send->state == SMD_SS_CLOSED) {
1076 list_move(&ch->ch_list,
1077 &smd_ch_to_close_list);
1078 queue_work(channel_close_wq,
1079 &finalize_channel_close_work);
1080 }
1081 break;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001082 }
1083}
1084
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001085static void handle_smd_irq_closing_list(void)
1086{
1087 unsigned long flags;
1088 struct smd_channel *ch;
1089 struct smd_channel *index;
1090 unsigned tmp;
1091
1092 spin_lock_irqsave(&smd_lock, flags);
1093 list_for_each_entry_safe(ch, index, &smd_ch_closing_list, ch_list) {
1094 if (ch->recv->fSTATE)
1095 ch->recv->fSTATE = 0;
1096 tmp = ch->recv->state;
1097 if (tmp != ch->last_state)
1098 smd_state_change(ch, ch->last_state, tmp);
1099 }
1100 spin_unlock_irqrestore(&smd_lock, flags);
1101}
1102
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001103static void handle_smd_irq(struct list_head *list, void (*notify)(void))
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001104{
1105 unsigned long flags;
1106 struct smd_channel *ch;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001107 unsigned ch_flags;
1108 unsigned tmp;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001109 unsigned char state_change;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001110
1111 spin_lock_irqsave(&smd_lock, flags);
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001112 list_for_each_entry(ch, list, ch_list) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001113 state_change = 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001114 ch_flags = 0;
1115 if (ch_is_open(ch)) {
1116 if (ch->recv->fHEAD) {
1117 ch->recv->fHEAD = 0;
1118 ch_flags |= 1;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001119 }
1120 if (ch->recv->fTAIL) {
1121 ch->recv->fTAIL = 0;
1122 ch_flags |= 2;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001123 }
1124 if (ch->recv->fSTATE) {
1125 ch->recv->fSTATE = 0;
1126 ch_flags |= 4;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001127 }
1128 }
1129 tmp = ch->recv->state;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001130 if (tmp != ch->last_state) {
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001131 smd_state_change(ch, ch->last_state, tmp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001132 state_change = 1;
1133 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001134 if (ch_flags) {
1135 ch->update_state(ch);
1136 ch->notify(ch->priv, SMD_EVENT_DATA);
1137 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001138 if (ch_flags & 0x4 && !state_change)
1139 ch->notify(ch->priv, SMD_EVENT_STATUS);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001140 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001141 spin_unlock_irqrestore(&smd_lock, flags);
1142 do_smd_probe();
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001143}
1144
Brian Swetland37521a32009-07-01 18:30:47 -07001145static irqreturn_t smd_modem_irq_handler(int irq, void *data)
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001146{
Brian Swetland37521a32009-07-01 18:30:47 -07001147 handle_smd_irq(&smd_ch_list_modem, notify_modem_smd);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001148 handle_smd_irq_closing_list();
Brian Swetland37521a32009-07-01 18:30:47 -07001149 return IRQ_HANDLED;
1150}
1151
1152static irqreturn_t smd_dsp_irq_handler(int irq, void *data)
1153{
1154 handle_smd_irq(&smd_ch_list_dsp, notify_dsp_smd);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001155 handle_smd_irq_closing_list();
1156 return IRQ_HANDLED;
1157}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001158
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001159static irqreturn_t smd_dsps_irq_handler(int irq, void *data)
1160{
1161 handle_smd_irq(&smd_ch_list_dsps, notify_dsps_smd);
1162 handle_smd_irq_closing_list();
1163 return IRQ_HANDLED;
1164}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001165
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001166static irqreturn_t smd_wcnss_irq_handler(int irq, void *data)
1167{
1168 handle_smd_irq(&smd_ch_list_wcnss, notify_wcnss_smd);
1169 handle_smd_irq_closing_list();
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001170 return IRQ_HANDLED;
1171}
1172
1173static void smd_fake_irq_handler(unsigned long arg)
1174{
Brian Swetland37521a32009-07-01 18:30:47 -07001175 handle_smd_irq(&smd_ch_list_modem, notify_modem_smd);
1176 handle_smd_irq(&smd_ch_list_dsp, notify_dsp_smd);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001177 handle_smd_irq(&smd_ch_list_dsps, notify_dsps_smd);
1178 handle_smd_irq(&smd_ch_list_wcnss, notify_wcnss_smd);
1179 handle_smd_irq_closing_list();
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001180}
1181
1182static DECLARE_TASKLET(smd_fake_irq_tasklet, smd_fake_irq_handler, 0);
1183
Brian Swetland37521a32009-07-01 18:30:47 -07001184static inline int smd_need_int(struct smd_channel *ch)
1185{
1186 if (ch_is_open(ch)) {
1187 if (ch->recv->fHEAD || ch->recv->fTAIL || ch->recv->fSTATE)
1188 return 1;
1189 if (ch->recv->state != ch->last_state)
1190 return 1;
1191 }
1192 return 0;
1193}
1194
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001195void smd_sleep_exit(void)
1196{
1197 unsigned long flags;
1198 struct smd_channel *ch;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001199 int need_int = 0;
1200
1201 spin_lock_irqsave(&smd_lock, flags);
Brian Swetland37521a32009-07-01 18:30:47 -07001202 list_for_each_entry(ch, &smd_ch_list_modem, ch_list) {
1203 if (smd_need_int(ch)) {
1204 need_int = 1;
1205 break;
1206 }
1207 }
1208 list_for_each_entry(ch, &smd_ch_list_dsp, ch_list) {
1209 if (smd_need_int(ch)) {
1210 need_int = 1;
1211 break;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001212 }
1213 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001214 list_for_each_entry(ch, &smd_ch_list_dsps, ch_list) {
1215 if (smd_need_int(ch)) {
1216 need_int = 1;
1217 break;
1218 }
1219 }
1220 list_for_each_entry(ch, &smd_ch_list_wcnss, ch_list) {
1221 if (smd_need_int(ch)) {
1222 need_int = 1;
1223 break;
1224 }
1225 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001226 spin_unlock_irqrestore(&smd_lock, flags);
1227 do_smd_probe();
Brian Swetland37521a32009-07-01 18:30:47 -07001228
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001229 if (need_int) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001230 SMD_DBG("smd_sleep_exit need interrupt\n");
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001231 tasklet_schedule(&smd_fake_irq_tasklet);
1232 }
1233}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001234EXPORT_SYMBOL(smd_sleep_exit);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001235
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001236static int smd_is_packet(struct smd_alloc_elm *alloc_elm)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001237{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001238 if (SMD_XFER_TYPE(alloc_elm->type) == 1)
1239 return 0;
1240 else if (SMD_XFER_TYPE(alloc_elm->type) == 2)
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001241 return 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001242
1243 /* for cases where xfer type is 0 */
1244 if (!strncmp(alloc_elm->name, "DAL", 3))
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001245 return 0;
1246
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001247 /* for cases where xfer type is 0 */
1248 if (!strncmp(alloc_elm->name, "RPCCALL_QDSP", 12))
1249 return 0;
1250
1251 if (alloc_elm->cid > 4 || alloc_elm->cid == 1)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001252 return 1;
1253 else
1254 return 0;
1255}
1256
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001257static int smd_stream_write(smd_channel_t *ch, const void *_data, int len,
1258 int user_buf)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001259{
1260 void *ptr;
1261 const unsigned char *buf = _data;
1262 unsigned xfer;
1263 int orig_len = len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001264 int r = 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001265
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001266 SMD_DBG("smd_stream_write() %d -> ch%d\n", len, ch->n);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001267 if (len < 0)
1268 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001269 else if (len == 0)
1270 return 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001271
1272 while ((xfer = ch_write_buffer(ch, &ptr)) != 0) {
Eric Holmberg7a717872012-02-03 11:58:04 -07001273 if (!ch_is_open(ch)) {
1274 len = orig_len;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001275 break;
Eric Holmberg7a717872012-02-03 11:58:04 -07001276 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001277 if (xfer > len)
1278 xfer = len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001279 if (user_buf) {
1280 r = copy_from_user(ptr, buf, xfer);
1281 if (r > 0) {
1282 pr_err("%s: "
1283 "copy_from_user could not copy %i "
1284 "bytes.\n",
1285 __func__,
1286 r);
1287 }
1288 } else
1289 memcpy(ptr, buf, xfer);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001290 ch_write_done(ch, xfer);
1291 len -= xfer;
1292 buf += xfer;
1293 if (len == 0)
1294 break;
1295 }
1296
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001297 if (orig_len - len)
1298 ch->notify_other_cpu();
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001299
1300 return orig_len - len;
1301}
1302
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001303static int smd_packet_write(smd_channel_t *ch, const void *_data, int len,
1304 int user_buf)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001305{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001306 int ret;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001307 unsigned hdr[5];
1308
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001309 SMD_DBG("smd_packet_write() %d -> ch%d\n", len, ch->n);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001310 if (len < 0)
1311 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001312 else if (len == 0)
1313 return 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001314
1315 if (smd_stream_write_avail(ch) < (len + SMD_HEADER_SIZE))
1316 return -ENOMEM;
1317
1318 hdr[0] = len;
1319 hdr[1] = hdr[2] = hdr[3] = hdr[4] = 0;
1320
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001321
1322 ret = smd_stream_write(ch, hdr, sizeof(hdr), 0);
1323 if (ret < 0 || ret != sizeof(hdr)) {
1324 SMD_DBG("%s failed to write pkt header: "
1325 "%d returned\n", __func__, ret);
1326 return -1;
1327 }
1328
1329
1330 ret = smd_stream_write(ch, _data, len, user_buf);
1331 if (ret < 0 || ret != len) {
1332 SMD_DBG("%s failed to write pkt data: "
1333 "%d returned\n", __func__, ret);
1334 return ret;
1335 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001336
1337 return len;
1338}
1339
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001340static int smd_stream_read(smd_channel_t *ch, void *data, int len, int user_buf)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001341{
1342 int r;
1343
1344 if (len < 0)
1345 return -EINVAL;
1346
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001347 r = ch_read(ch, data, len, user_buf);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001348 if (r > 0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001349 if (!read_intr_blocked(ch))
1350 ch->notify_other_cpu();
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001351
1352 return r;
1353}
1354
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001355static int smd_packet_read(smd_channel_t *ch, void *data, int len, int user_buf)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001356{
1357 unsigned long flags;
1358 int r;
1359
1360 if (len < 0)
1361 return -EINVAL;
1362
1363 if (len > ch->current_packet)
1364 len = ch->current_packet;
1365
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001366 r = ch_read(ch, data, len, user_buf);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001367 if (r > 0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001368 if (!read_intr_blocked(ch))
1369 ch->notify_other_cpu();
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001370
1371 spin_lock_irqsave(&smd_lock, flags);
1372 ch->current_packet -= r;
1373 update_packet_state(ch);
1374 spin_unlock_irqrestore(&smd_lock, flags);
1375
1376 return r;
1377}
1378
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001379static int smd_packet_read_from_cb(smd_channel_t *ch, void *data, int len,
1380 int user_buf)
1381{
1382 int r;
1383
1384 if (len < 0)
1385 return -EINVAL;
1386
1387 if (len > ch->current_packet)
1388 len = ch->current_packet;
1389
1390 r = ch_read(ch, data, len, user_buf);
1391 if (r > 0)
1392 if (!read_intr_blocked(ch))
1393 ch->notify_other_cpu();
1394
1395 ch->current_packet -= r;
1396 update_packet_state(ch);
1397
1398 return r;
1399}
1400
Angshuman Sarkarac7d6252011-09-30 18:20:59 +05301401#if (defined(CONFIG_MSM_SMD_PKG4) || defined(CONFIG_MSM_SMD_PKG3))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001402static int smd_alloc_v2(struct smd_channel *ch)
1403{
1404 struct smd_shared_v2 *shared2;
1405 void *buffer;
1406 unsigned buffer_sz;
1407
1408 shared2 = smem_alloc(SMEM_SMD_BASE_ID + ch->n, sizeof(*shared2));
1409 if (!shared2) {
1410 SMD_INFO("smem_alloc failed ch=%d\n", ch->n);
Angshuman Sarkarac7d6252011-09-30 18:20:59 +05301411 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001412 }
1413 buffer = smem_get_entry(SMEM_SMD_FIFO_BASE_ID + ch->n, &buffer_sz);
1414 if (!buffer) {
Angshuman Sarkarac7d6252011-09-30 18:20:59 +05301415 SMD_INFO("smem_get_entry failed\n");
1416 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001417 }
1418
1419 /* buffer must be a power-of-two size */
Angshuman Sarkarac7d6252011-09-30 18:20:59 +05301420 if (buffer_sz & (buffer_sz - 1)) {
1421 SMD_INFO("Buffer size: %u not power of two\n", buffer_sz);
1422 return -EINVAL;
1423 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001424 buffer_sz /= 2;
1425 ch->send = &shared2->ch0;
1426 ch->recv = &shared2->ch1;
1427 ch->send_data = buffer;
1428 ch->recv_data = buffer + buffer_sz;
1429 ch->fifo_size = buffer_sz;
1430 return 0;
1431}
1432
1433static int smd_alloc_v1(struct smd_channel *ch)
1434{
Angshuman Sarkarac7d6252011-09-30 18:20:59 +05301435 return -EINVAL;
1436}
1437
1438#else /* define v1 for older targets */
1439static int smd_alloc_v2(struct smd_channel *ch)
1440{
1441 return -EINVAL;
1442}
1443
1444static int smd_alloc_v1(struct smd_channel *ch)
1445{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001446 struct smd_shared_v1 *shared1;
1447 shared1 = smem_alloc(ID_SMD_CHANNELS + ch->n, sizeof(*shared1));
1448 if (!shared1) {
1449 pr_err("smd_alloc_channel() cid %d does not exist\n", ch->n);
Angshuman Sarkarac7d6252011-09-30 18:20:59 +05301450 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001451 }
1452 ch->send = &shared1->ch0;
1453 ch->recv = &shared1->ch1;
1454 ch->send_data = shared1->data0;
1455 ch->recv_data = shared1->data1;
1456 ch->fifo_size = SMD_BUF_SIZE;
1457 return 0;
1458}
1459
Angshuman Sarkarac7d6252011-09-30 18:20:59 +05301460#endif
1461
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001462static int smd_alloc_channel(struct smd_alloc_elm *alloc_elm)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001463{
1464 struct smd_channel *ch;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001465
1466 ch = kzalloc(sizeof(struct smd_channel), GFP_KERNEL);
1467 if (ch == 0) {
1468 pr_err("smd_alloc_channel() out of memory\n");
Brian Swetland34f719b2009-10-30 16:22:05 -07001469 return -1;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001470 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001471 ch->n = alloc_elm->cid;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001472
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001473 if (smd_alloc_v2(ch) && smd_alloc_v1(ch)) {
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001474 kfree(ch);
Brian Swetland34f719b2009-10-30 16:22:05 -07001475 return -1;
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001476 }
1477
1478 ch->fifo_mask = ch->fifo_size - 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001479 ch->type = SMD_CHANNEL_TYPE(alloc_elm->type);
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001480
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001481 if (ch->type == SMD_APPS_MODEM)
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001482 ch->notify_other_cpu = notify_modem_smd;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001483 else if (ch->type == SMD_APPS_QDSP)
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001484 ch->notify_other_cpu = notify_dsp_smd;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001485 else if (ch->type == SMD_APPS_DSPS)
1486 ch->notify_other_cpu = notify_dsps_smd;
1487 else
1488 ch->notify_other_cpu = notify_wcnss_smd;
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001489
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001490 if (smd_is_packet(alloc_elm)) {
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001491 ch->read = smd_packet_read;
1492 ch->write = smd_packet_write;
1493 ch->read_avail = smd_packet_read_avail;
1494 ch->write_avail = smd_packet_write_avail;
1495 ch->update_state = update_packet_state;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001496 ch->read_from_cb = smd_packet_read_from_cb;
1497 ch->is_pkt_ch = 1;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001498 } else {
1499 ch->read = smd_stream_read;
1500 ch->write = smd_stream_write;
1501 ch->read_avail = smd_stream_read_avail;
1502 ch->write_avail = smd_stream_write_avail;
1503 ch->update_state = update_stream_state;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001504 ch->read_from_cb = smd_stream_read;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001505 }
1506
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001507 memcpy(ch->name, alloc_elm->name, SMD_MAX_CH_NAME_LEN);
1508 ch->name[SMD_MAX_CH_NAME_LEN-1] = 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001509
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001510 ch->pdev.name = ch->name;
1511 ch->pdev.id = ch->type;
1512
1513 SMD_INFO("smd_alloc_channel() '%s' cid=%d\n",
1514 ch->name, ch->n);
1515
1516 mutex_lock(&smd_creation_mutex);
1517 list_add(&ch->ch_list, &smd_ch_closed_list);
1518 mutex_unlock(&smd_creation_mutex);
1519
1520 platform_device_register(&ch->pdev);
1521 if (!strncmp(ch->name, "LOOPBACK", 8) && ch->type == SMD_APPS_MODEM) {
1522 /* create a platform driver to be used by smd_tty driver
1523 * so that it can access the loopback port
1524 */
1525 loopback_tty_pdev.id = ch->type;
1526 platform_device_register(&loopback_tty_pdev);
1527 }
1528 return 0;
1529}
1530
1531static inline void notify_loopback_smd(void)
1532{
1533 unsigned long flags;
1534 struct smd_channel *ch;
1535
1536 spin_lock_irqsave(&smd_lock, flags);
1537 list_for_each_entry(ch, &smd_ch_list_loopback, ch_list) {
1538 ch->notify(ch->priv, SMD_EVENT_DATA);
1539 }
1540 spin_unlock_irqrestore(&smd_lock, flags);
1541}
1542
1543static int smd_alloc_loopback_channel(void)
1544{
1545 static struct smd_half_channel smd_loopback_ctl;
1546 static char smd_loopback_data[SMD_BUF_SIZE];
1547 struct smd_channel *ch;
1548
1549 ch = kzalloc(sizeof(struct smd_channel), GFP_KERNEL);
1550 if (ch == 0) {
1551 pr_err("%s: out of memory\n", __func__);
1552 return -1;
1553 }
1554 ch->n = SMD_LOOPBACK_CID;
1555
1556 ch->send = &smd_loopback_ctl;
1557 ch->recv = &smd_loopback_ctl;
1558 ch->send_data = smd_loopback_data;
1559 ch->recv_data = smd_loopback_data;
1560 ch->fifo_size = SMD_BUF_SIZE;
1561
1562 ch->fifo_mask = ch->fifo_size - 1;
1563 ch->type = SMD_LOOPBACK_TYPE;
1564 ch->notify_other_cpu = notify_loopback_smd;
1565
1566 ch->read = smd_stream_read;
1567 ch->write = smd_stream_write;
1568 ch->read_avail = smd_stream_read_avail;
1569 ch->write_avail = smd_stream_write_avail;
1570 ch->update_state = update_stream_state;
1571 ch->read_from_cb = smd_stream_read;
1572
1573 memset(ch->name, 0, 20);
1574 memcpy(ch->name, "local_loopback", 14);
1575
1576 ch->pdev.name = ch->name;
1577 ch->pdev.id = ch->type;
1578
1579 SMD_INFO("%s: '%s' cid=%d\n", __func__, ch->name, ch->n);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001580
1581 mutex_lock(&smd_creation_mutex);
1582 list_add(&ch->ch_list, &smd_ch_closed_list);
1583 mutex_unlock(&smd_creation_mutex);
1584
1585 platform_device_register(&ch->pdev);
Brian Swetland34f719b2009-10-30 16:22:05 -07001586 return 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001587}
1588
1589static void do_nothing_notify(void *priv, unsigned flags)
1590{
1591}
1592
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001593static void finalize_channel_close_fn(struct work_struct *work)
1594{
1595 unsigned long flags;
1596 struct smd_channel *ch;
1597 struct smd_channel *index;
1598
Eric Holmbergbb2b1fa2011-10-12 16:41:37 -06001599 mutex_lock(&smd_creation_mutex);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001600 spin_lock_irqsave(&smd_lock, flags);
1601 list_for_each_entry_safe(ch, index, &smd_ch_to_close_list, ch_list) {
1602 list_del(&ch->ch_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001603 list_add(&ch->ch_list, &smd_ch_closed_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001604 ch->notify(ch->priv, SMD_EVENT_REOPEN_READY);
1605 ch->notify = do_nothing_notify;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001606 }
1607 spin_unlock_irqrestore(&smd_lock, flags);
Eric Holmbergbb2b1fa2011-10-12 16:41:37 -06001608 mutex_unlock(&smd_creation_mutex);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001609}
1610
1611struct smd_channel *smd_get_channel(const char *name, uint32_t type)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001612{
1613 struct smd_channel *ch;
1614
1615 mutex_lock(&smd_creation_mutex);
1616 list_for_each_entry(ch, &smd_ch_closed_list, ch_list) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001617 if (!strcmp(name, ch->name) &&
1618 (type == ch->type)) {
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001619 list_del(&ch->ch_list);
1620 mutex_unlock(&smd_creation_mutex);
1621 return ch;
1622 }
1623 }
1624 mutex_unlock(&smd_creation_mutex);
1625
1626 return NULL;
1627}
1628
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001629int smd_named_open_on_edge(const char *name, uint32_t edge,
1630 smd_channel_t **_ch,
1631 void *priv, void (*notify)(void *, unsigned))
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001632{
1633 struct smd_channel *ch;
1634 unsigned long flags;
1635
1636 if (smd_initialized == 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001637 SMD_INFO("smd_open() before smd_init()\n");
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001638 return -ENODEV;
1639 }
1640
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001641 SMD_DBG("smd_open('%s', %p, %p)\n", name, priv, notify);
1642
1643 ch = smd_get_channel(name, edge);
Eric Holmbergbb2b1fa2011-10-12 16:41:37 -06001644 if (!ch) {
Eric Holmbergbb2b1fa2011-10-12 16:41:37 -06001645 /* check closing list for port */
1646 spin_lock_irqsave(&smd_lock, flags);
1647 list_for_each_entry(ch, &smd_ch_closing_list, ch_list) {
1648 if (!strncmp(name, ch->name, 20) &&
1649 (edge == ch->type)) {
1650 /* channel exists, but is being closed */
1651 spin_unlock_irqrestore(&smd_lock, flags);
1652 return -EAGAIN;
1653 }
1654 }
1655
1656 /* check closing workqueue list for port */
1657 list_for_each_entry(ch, &smd_ch_to_close_list, ch_list) {
1658 if (!strncmp(name, ch->name, 20) &&
1659 (edge == ch->type)) {
1660 /* channel exists, but is being closed */
1661 spin_unlock_irqrestore(&smd_lock, flags);
1662 return -EAGAIN;
1663 }
1664 }
1665 spin_unlock_irqrestore(&smd_lock, flags);
1666
1667 /* one final check to handle closing->closed race condition */
1668 ch = smd_get_channel(name, edge);
1669 if (!ch)
1670 return -ENODEV;
1671 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001672
1673 if (notify == 0)
1674 notify = do_nothing_notify;
1675
1676 ch->notify = notify;
1677 ch->current_packet = 0;
1678 ch->last_state = SMD_SS_CLOSED;
1679 ch->priv = priv;
1680
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001681 if (edge == SMD_LOOPBACK_TYPE) {
1682 ch->last_state = SMD_SS_OPENED;
1683 ch->send->state = SMD_SS_OPENED;
1684 ch->send->fDSR = 1;
1685 ch->send->fCTS = 1;
1686 ch->send->fCD = 1;
1687 }
1688
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001689 *_ch = ch;
1690
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001691 SMD_DBG("smd_open: opening '%s'\n", ch->name);
1692
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001693 spin_lock_irqsave(&smd_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001694 if (SMD_CHANNEL_TYPE(ch->type) == SMD_APPS_MODEM)
Brian Swetland37521a32009-07-01 18:30:47 -07001695 list_add(&ch->ch_list, &smd_ch_list_modem);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001696 else if (SMD_CHANNEL_TYPE(ch->type) == SMD_APPS_QDSP)
Brian Swetland37521a32009-07-01 18:30:47 -07001697 list_add(&ch->ch_list, &smd_ch_list_dsp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001698 else if (SMD_CHANNEL_TYPE(ch->type) == SMD_APPS_DSPS)
1699 list_add(&ch->ch_list, &smd_ch_list_dsps);
1700 else if (SMD_CHANNEL_TYPE(ch->type) == SMD_APPS_WCNSS)
1701 list_add(&ch->ch_list, &smd_ch_list_wcnss);
1702 else
1703 list_add(&ch->ch_list, &smd_ch_list_loopback);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001704
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001705 SMD_DBG("%s: opening ch %d\n", __func__, ch->n);
1706
1707 if (edge != SMD_LOOPBACK_TYPE)
1708 smd_state_change(ch, ch->last_state, SMD_SS_OPENING);
1709
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001710 spin_unlock_irqrestore(&smd_lock, flags);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001711
1712 return 0;
1713}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001714EXPORT_SYMBOL(smd_named_open_on_edge);
1715
1716
1717int smd_open(const char *name, smd_channel_t **_ch,
1718 void *priv, void (*notify)(void *, unsigned))
1719{
1720 return smd_named_open_on_edge(name, SMD_APPS_MODEM, _ch, priv,
1721 notify);
1722}
1723EXPORT_SYMBOL(smd_open);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001724
1725int smd_close(smd_channel_t *ch)
1726{
1727 unsigned long flags;
1728
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001729 if (ch == 0)
1730 return -1;
1731
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001732 SMD_INFO("smd_close(%s)\n", ch->name);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001733
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001734 spin_lock_irqsave(&smd_lock, flags);
1735 list_del(&ch->ch_list);
1736 if (ch->n == SMD_LOOPBACK_CID) {
1737 ch->send->fDSR = 0;
1738 ch->send->fCTS = 0;
1739 ch->send->fCD = 0;
1740 ch->send->state = SMD_SS_CLOSED;
1741 } else
1742 ch_set_state(ch, SMD_SS_CLOSED);
1743
1744 if (ch->recv->state == SMD_SS_OPENED) {
1745 list_add(&ch->ch_list, &smd_ch_closing_list);
1746 spin_unlock_irqrestore(&smd_lock, flags);
1747 } else {
1748 spin_unlock_irqrestore(&smd_lock, flags);
1749 ch->notify = do_nothing_notify;
1750 mutex_lock(&smd_creation_mutex);
1751 list_add(&ch->ch_list, &smd_ch_closed_list);
1752 mutex_unlock(&smd_creation_mutex);
1753 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001754
1755 return 0;
1756}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001757EXPORT_SYMBOL(smd_close);
1758
1759int smd_write_start(smd_channel_t *ch, int len)
1760{
1761 int ret;
1762 unsigned hdr[5];
1763
1764 if (!ch) {
1765 pr_err("%s: Invalid channel specified\n", __func__);
1766 return -ENODEV;
1767 }
1768 if (!ch->is_pkt_ch) {
1769 pr_err("%s: non-packet channel specified\n", __func__);
1770 return -EACCES;
1771 }
1772 if (len < 1) {
1773 pr_err("%s: invalid length: %d\n", __func__, len);
1774 return -EINVAL;
1775 }
1776
1777 if (ch->pending_pkt_sz) {
1778 pr_err("%s: packet of size: %d in progress\n", __func__,
1779 ch->pending_pkt_sz);
1780 return -EBUSY;
1781 }
1782 ch->pending_pkt_sz = len;
1783
1784 if (smd_stream_write_avail(ch) < (SMD_HEADER_SIZE)) {
1785 ch->pending_pkt_sz = 0;
1786 SMD_DBG("%s: no space to write packet header\n", __func__);
1787 return -EAGAIN;
1788 }
1789
1790 hdr[0] = len;
1791 hdr[1] = hdr[2] = hdr[3] = hdr[4] = 0;
1792
1793
1794 ret = smd_stream_write(ch, hdr, sizeof(hdr), 0);
1795 if (ret < 0 || ret != sizeof(hdr)) {
1796 ch->pending_pkt_sz = 0;
1797 pr_err("%s: packet header failed to write\n", __func__);
1798 return -EPERM;
1799 }
1800 return 0;
1801}
1802EXPORT_SYMBOL(smd_write_start);
1803
1804int smd_write_segment(smd_channel_t *ch, void *data, int len, int user_buf)
1805{
1806 int bytes_written;
1807
1808 if (!ch) {
1809 pr_err("%s: Invalid channel specified\n", __func__);
1810 return -ENODEV;
1811 }
1812 if (len < 1) {
1813 pr_err("%s: invalid length: %d\n", __func__, len);
1814 return -EINVAL;
1815 }
1816
1817 if (!ch->pending_pkt_sz) {
1818 pr_err("%s: no transaction in progress\n", __func__);
1819 return -ENOEXEC;
1820 }
1821 if (ch->pending_pkt_sz - len < 0) {
1822 pr_err("%s: segment of size: %d will make packet go over "
1823 "length\n", __func__, len);
1824 return -EINVAL;
1825 }
1826
1827 bytes_written = smd_stream_write(ch, data, len, user_buf);
1828
1829 ch->pending_pkt_sz -= bytes_written;
1830
1831 return bytes_written;
1832}
1833EXPORT_SYMBOL(smd_write_segment);
1834
1835int smd_write_end(smd_channel_t *ch)
1836{
1837
1838 if (!ch) {
1839 pr_err("%s: Invalid channel specified\n", __func__);
1840 return -ENODEV;
1841 }
1842 if (ch->pending_pkt_sz) {
1843 pr_err("%s: current packet not completely written\n", __func__);
1844 return -E2BIG;
1845 }
1846
1847 return 0;
1848}
1849EXPORT_SYMBOL(smd_write_end);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001850
1851int smd_read(smd_channel_t *ch, void *data, int len)
1852{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001853 return ch->read(ch, data, len, 0);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001854}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001855EXPORT_SYMBOL(smd_read);
1856
1857int smd_read_user_buffer(smd_channel_t *ch, void *data, int len)
1858{
1859 return ch->read(ch, data, len, 1);
1860}
1861EXPORT_SYMBOL(smd_read_user_buffer);
1862
1863int smd_read_from_cb(smd_channel_t *ch, void *data, int len)
1864{
1865 return ch->read_from_cb(ch, data, len, 0);
1866}
1867EXPORT_SYMBOL(smd_read_from_cb);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001868
1869int smd_write(smd_channel_t *ch, const void *data, int len)
1870{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001871 return ch->pending_pkt_sz ? -EBUSY : ch->write(ch, data, len, 0);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001872}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001873EXPORT_SYMBOL(smd_write);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001874
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001875int smd_write_user_buffer(smd_channel_t *ch, const void *data, int len)
Brian Swetland636eb9c2009-12-07 15:28:08 -08001876{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001877 return ch->pending_pkt_sz ? -EBUSY : ch->write(ch, data, len, 1);
Brian Swetland636eb9c2009-12-07 15:28:08 -08001878}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001879EXPORT_SYMBOL(smd_write_user_buffer);
Brian Swetland636eb9c2009-12-07 15:28:08 -08001880
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001881int smd_read_avail(smd_channel_t *ch)
1882{
1883 return ch->read_avail(ch);
1884}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001885EXPORT_SYMBOL(smd_read_avail);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001886
1887int smd_write_avail(smd_channel_t *ch)
1888{
1889 return ch->write_avail(ch);
1890}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001891EXPORT_SYMBOL(smd_write_avail);
1892
1893void smd_enable_read_intr(smd_channel_t *ch)
1894{
1895 if (ch)
1896 ch->send->fBLOCKREADINTR = 0;
1897}
1898EXPORT_SYMBOL(smd_enable_read_intr);
1899
1900void smd_disable_read_intr(smd_channel_t *ch)
1901{
1902 if (ch)
1903 ch->send->fBLOCKREADINTR = 1;
1904}
1905EXPORT_SYMBOL(smd_disable_read_intr);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001906
1907int smd_wait_until_readable(smd_channel_t *ch, int bytes)
1908{
1909 return -1;
1910}
1911
1912int smd_wait_until_writable(smd_channel_t *ch, int bytes)
1913{
1914 return -1;
1915}
1916
1917int smd_cur_packet_size(smd_channel_t *ch)
1918{
1919 return ch->current_packet;
1920}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001921EXPORT_SYMBOL(smd_cur_packet_size);
1922
1923int smd_tiocmget(smd_channel_t *ch)
1924{
1925 return (ch->recv->fDSR ? TIOCM_DSR : 0) |
1926 (ch->recv->fCTS ? TIOCM_CTS : 0) |
1927 (ch->recv->fCD ? TIOCM_CD : 0) |
1928 (ch->recv->fRI ? TIOCM_RI : 0) |
1929 (ch->send->fCTS ? TIOCM_RTS : 0) |
1930 (ch->send->fDSR ? TIOCM_DTR : 0);
1931}
1932EXPORT_SYMBOL(smd_tiocmget);
1933
Vamsi Krishnacb12a102011-08-17 15:18:26 -07001934/* this api will be called while holding smd_lock */
1935int
1936smd_tiocmset_from_cb(smd_channel_t *ch, unsigned int set, unsigned int clear)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001937{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001938 if (set & TIOCM_DTR)
1939 ch->send->fDSR = 1;
1940
1941 if (set & TIOCM_RTS)
1942 ch->send->fCTS = 1;
1943
1944 if (clear & TIOCM_DTR)
1945 ch->send->fDSR = 0;
1946
1947 if (clear & TIOCM_RTS)
1948 ch->send->fCTS = 0;
1949
1950 ch->send->fSTATE = 1;
1951 barrier();
1952 ch->notify_other_cpu();
Vamsi Krishnacb12a102011-08-17 15:18:26 -07001953
1954 return 0;
1955}
1956EXPORT_SYMBOL(smd_tiocmset_from_cb);
1957
1958int smd_tiocmset(smd_channel_t *ch, unsigned int set, unsigned int clear)
1959{
1960 unsigned long flags;
1961
1962 spin_lock_irqsave(&smd_lock, flags);
1963 smd_tiocmset_from_cb(ch, set, clear);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001964 spin_unlock_irqrestore(&smd_lock, flags);
1965
1966 return 0;
1967}
1968EXPORT_SYMBOL(smd_tiocmset);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001969
1970
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001971/* -------------------------------------------------------------------------- */
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001972
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001973/* smem_alloc returns the pointer to smem item if it is already allocated.
1974 * Otherwise, it returns NULL.
1975 */
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001976void *smem_alloc(unsigned id, unsigned size)
1977{
1978 return smem_find(id, size);
1979}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001980EXPORT_SYMBOL(smem_alloc);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001981
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001982/* smem_alloc2 returns the pointer to smem item. If it is not allocated,
1983 * it allocates it and then returns the pointer to it.
1984 */
Angshuman Sarkar4eade0d2011-08-17 14:06:23 +05301985void *smem_alloc2(unsigned id, unsigned size_in)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001986{
1987 struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE;
1988 struct smem_heap_entry *toc = shared->heap_toc;
1989 unsigned long flags;
1990 void *ret = NULL;
1991
1992 if (!shared->heap_info.initialized) {
1993 pr_err("%s: smem heap info not initialized\n", __func__);
1994 return NULL;
1995 }
1996
1997 if (id >= SMEM_NUM_ITEMS)
1998 return NULL;
1999
2000 size_in = ALIGN(size_in, 8);
2001 remote_spin_lock_irqsave(&remote_spinlock, flags);
2002 if (toc[id].allocated) {
2003 SMD_DBG("%s: %u already allocated\n", __func__, id);
2004 if (size_in != toc[id].size)
2005 pr_err("%s: wrong size %u (expected %u)\n",
2006 __func__, toc[id].size, size_in);
2007 else
2008 ret = (void *)(MSM_SHARED_RAM_BASE + toc[id].offset);
2009 } else if (id > SMEM_FIXED_ITEM_LAST) {
2010 SMD_DBG("%s: allocating %u\n", __func__, id);
2011 if (shared->heap_info.heap_remaining >= size_in) {
2012 toc[id].offset = shared->heap_info.free_offset;
2013 toc[id].size = size_in;
2014 wmb();
2015 toc[id].allocated = 1;
2016
2017 shared->heap_info.free_offset += size_in;
2018 shared->heap_info.heap_remaining -= size_in;
2019 ret = (void *)(MSM_SHARED_RAM_BASE + toc[id].offset);
2020 } else
2021 pr_err("%s: not enough memory %u (required %u)\n",
2022 __func__, shared->heap_info.heap_remaining,
2023 size_in);
2024 }
2025 wmb();
2026 remote_spin_unlock_irqrestore(&remote_spinlock, flags);
2027 return ret;
2028}
Angshuman Sarkar4eade0d2011-08-17 14:06:23 +05302029EXPORT_SYMBOL(smem_alloc2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002030
2031void *smem_get_entry(unsigned id, unsigned *size)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002032{
2033 struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE;
2034 struct smem_heap_entry *toc = shared->heap_toc;
Angshuman Sarkar7ee0dca2011-08-22 21:37:34 +05302035 int use_spinlocks = spinlocks_initialized;
2036 void *ret = 0;
2037 unsigned long flags = 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002038
2039 if (id >= SMEM_NUM_ITEMS)
Angshuman Sarkar7ee0dca2011-08-22 21:37:34 +05302040 return ret;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002041
Angshuman Sarkar7ee0dca2011-08-22 21:37:34 +05302042 if (use_spinlocks)
2043 remote_spin_lock_irqsave(&remote_spinlock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002044 /* toc is in device memory and cannot be speculatively accessed */
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002045 if (toc[id].allocated) {
2046 *size = toc[id].size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002047 barrier();
Angshuman Sarkar7ee0dca2011-08-22 21:37:34 +05302048 ret = (void *) (MSM_SHARED_RAM_BASE + toc[id].offset);
Brian Swetland5b0f5a32009-04-26 18:38:49 -07002049 } else {
2050 *size = 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002051 }
Angshuman Sarkar7ee0dca2011-08-22 21:37:34 +05302052 if (use_spinlocks)
2053 remote_spin_unlock_irqrestore(&remote_spinlock, flags);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002054
Angshuman Sarkar7ee0dca2011-08-22 21:37:34 +05302055 return ret;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002056}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002057EXPORT_SYMBOL(smem_get_entry);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002058
2059void *smem_find(unsigned id, unsigned size_in)
2060{
2061 unsigned size;
2062 void *ptr;
2063
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002064 ptr = smem_get_entry(id, &size);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002065 if (!ptr)
2066 return 0;
2067
2068 size_in = ALIGN(size_in, 8);
2069 if (size_in != size) {
2070 pr_err("smem_find(%d, %d): wrong size %d\n",
2071 id, size_in, size);
2072 return 0;
2073 }
2074
2075 return ptr;
2076}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002077EXPORT_SYMBOL(smem_find);
2078
2079static int smsm_cb_init(void)
2080{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002081 struct smsm_state_info *state_info;
2082 int n;
2083 int ret = 0;
2084
2085 smsm_states = kmalloc(sizeof(struct smsm_state_info)*SMSM_NUM_ENTRIES,
2086 GFP_KERNEL);
2087
2088 if (!smsm_states) {
2089 pr_err("%s: SMSM init failed\n", __func__);
2090 return -ENOMEM;
2091 }
2092
Eric Holmbergc8002902011-09-16 13:55:57 -06002093 mutex_lock(&smsm_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002094 for (n = 0; n < SMSM_NUM_ENTRIES; n++) {
2095 state_info = &smsm_states[n];
2096 state_info->last_value = __raw_readl(SMSM_STATE_ADDR(n));
2097 INIT_LIST_HEAD(&state_info->callbacks);
2098 }
Eric Holmbergc8002902011-09-16 13:55:57 -06002099 mutex_unlock(&smsm_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002100
2101 return ret;
2102}
2103
2104static int smsm_init(void)
2105{
2106 struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE;
2107 int i;
2108 struct smsm_size_info_type *smsm_size_info;
2109
2110 i = remote_spin_lock_init(&remote_spinlock, SMEM_SPINLOCK_SMEM_ALLOC);
2111 if (i) {
2112 pr_err("%s: remote spinlock init failed %d\n", __func__, i);
2113 return i;
2114 }
Angshuman Sarkar7ee0dca2011-08-22 21:37:34 +05302115 spinlocks_initialized = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002116
2117 smsm_size_info = smem_alloc(SMEM_SMSM_SIZE_INFO,
2118 sizeof(struct smsm_size_info_type));
2119 if (smsm_size_info) {
2120 SMSM_NUM_ENTRIES = smsm_size_info->num_entries;
2121 SMSM_NUM_HOSTS = smsm_size_info->num_hosts;
2122 }
2123
Eric Holmbergc7e8daf2011-12-28 11:49:21 -07002124 i = kfifo_alloc(&smsm_snapshot_fifo,
2125 sizeof(uint32_t) * SMSM_NUM_ENTRIES * SMSM_SNAPSHOT_CNT,
2126 GFP_KERNEL);
2127 if (i) {
2128 pr_err("%s: SMSM state fifo alloc failed %d\n", __func__, i);
2129 return i;
2130 }
2131
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002132 if (!smsm_info.state) {
2133 smsm_info.state = smem_alloc2(ID_SHARED_STATE,
2134 SMSM_NUM_ENTRIES *
2135 sizeof(uint32_t));
2136
2137 if (smsm_info.state) {
2138 __raw_writel(0, SMSM_STATE_ADDR(SMSM_APPS_STATE));
2139 if ((shared->version[VERSION_MODEM] >> 16) >= 0xB)
2140 __raw_writel(0, \
2141 SMSM_STATE_ADDR(SMSM_APPS_DEM_I));
2142 }
2143 }
2144
2145 if (!smsm_info.intr_mask) {
2146 smsm_info.intr_mask = smem_alloc2(SMEM_SMSM_CPU_INTR_MASK,
2147 SMSM_NUM_ENTRIES *
2148 SMSM_NUM_HOSTS *
2149 sizeof(uint32_t));
2150
2151 if (smsm_info.intr_mask)
2152 for (i = 0; i < SMSM_NUM_ENTRIES; i++)
2153 __raw_writel(0xffffffff,
2154 SMSM_INTR_MASK_ADDR(i, SMSM_APPS));
2155 }
2156
2157 if (!smsm_info.intr_mux)
2158 smsm_info.intr_mux = smem_alloc2(SMEM_SMD_SMSM_INTR_MUX,
2159 SMSM_NUM_INTR_MUX *
2160 sizeof(uint32_t));
2161
2162 i = smsm_cb_init();
2163 if (i)
2164 return i;
2165
2166 wmb();
2167 return 0;
2168}
2169
2170void smsm_reset_modem(unsigned mode)
2171{
2172 if (mode == SMSM_SYSTEM_DOWNLOAD) {
2173 mode = SMSM_RESET | SMSM_SYSTEM_DOWNLOAD;
2174 } else if (mode == SMSM_MODEM_WAIT) {
2175 mode = SMSM_RESET | SMSM_MODEM_WAIT;
2176 } else { /* reset_mode is SMSM_RESET or default */
2177 mode = SMSM_RESET;
2178 }
2179
2180 smsm_change_state(SMSM_APPS_STATE, mode, mode);
2181}
2182EXPORT_SYMBOL(smsm_reset_modem);
2183
2184void smsm_reset_modem_cont(void)
2185{
2186 unsigned long flags;
2187 uint32_t state;
2188
2189 if (!smsm_info.state)
2190 return;
2191
2192 spin_lock_irqsave(&smem_lock, flags);
2193 state = __raw_readl(SMSM_STATE_ADDR(SMSM_APPS_STATE)) \
2194 & ~SMSM_MODEM_WAIT;
2195 __raw_writel(state, SMSM_STATE_ADDR(SMSM_APPS_STATE));
2196 wmb();
2197 spin_unlock_irqrestore(&smem_lock, flags);
2198}
2199EXPORT_SYMBOL(smsm_reset_modem_cont);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002200
Eric Holmbergc7e8daf2011-12-28 11:49:21 -07002201static void smsm_cb_snapshot(void)
2202{
2203 int n;
2204 uint32_t new_state;
2205 int ret;
2206
2207 ret = kfifo_avail(&smsm_snapshot_fifo);
2208 if (ret < (SMSM_NUM_ENTRIES * 4)) {
2209 pr_err("%s: SMSM snapshot full %d\n", __func__, ret);
2210 return;
2211 }
2212
2213 for (n = 0; n < SMSM_NUM_ENTRIES; n++) {
2214 new_state = __raw_readl(SMSM_STATE_ADDR(n));
2215
2216 ret = kfifo_in(&smsm_snapshot_fifo,
2217 &new_state, sizeof(new_state));
2218 if (ret != sizeof(new_state)) {
2219 pr_err("%s: SMSM snapshot failure %d\n", __func__, ret);
2220 return;
2221 }
2222 }
2223 schedule_work(&smsm_cb_work);
2224}
2225
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002226static irqreturn_t smsm_irq_handler(int irq, void *data)
2227{
2228 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002229
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002230 if (irq == INT_ADSP_A11_SMSM) {
Eric Holmberg6282c5d2011-10-27 17:30:57 -06002231 uint32_t mux_val;
2232 static uint32_t prev_smem_q6_apps_smsm;
2233
2234 if (smsm_info.intr_mux && cpu_is_qsd8x50()) {
2235 mux_val = __raw_readl(
2236 SMSM_INTR_MUX_ADDR(SMEM_Q6_APPS_SMSM));
2237 if (mux_val != prev_smem_q6_apps_smsm)
2238 prev_smem_q6_apps_smsm = mux_val;
2239 }
2240
Eric Holmbergc7e8daf2011-12-28 11:49:21 -07002241 spin_lock_irqsave(&smem_lock, flags);
2242 smsm_cb_snapshot();
2243 spin_unlock_irqrestore(&smem_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002244 return IRQ_HANDLED;
2245 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002246
2247 spin_lock_irqsave(&smem_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002248 if (!smsm_info.state) {
2249 SMSM_INFO("<SM NO STATE>\n");
2250 } else {
2251 unsigned old_apps, apps;
2252 unsigned modm = __raw_readl(SMSM_STATE_ADDR(SMSM_MODEM_STATE));
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002253
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002254 old_apps = apps = __raw_readl(SMSM_STATE_ADDR(SMSM_APPS_STATE));
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002255
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002256 SMSM_DBG("<SM %08x %08x>\n", apps, modm);
2257 if (apps & SMSM_RESET) {
2258 /* If we get an interrupt and the apps SMSM_RESET
2259 bit is already set, the modem is acking the
2260 app's reset ack. */
Stepan Moskovchenkoa1ca7582011-10-25 14:45:09 -07002261 if (!cpu_is_msm8960() && !cpu_is_msm8930())
Angshuman Sarkaread67bd2011-09-21 20:13:12 +05302262 apps &= ~SMSM_RESET;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002263 /* Issue a fake irq to handle any
2264 * smd state changes during reset
2265 */
2266 smd_fake_irq_handler(0);
Brian Swetland5b0f5a32009-04-26 18:38:49 -07002267
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002268 /* queue modem restart notify chain */
2269 modem_queue_start_reset_notify();
2270
2271 } else if (modm & SMSM_RESET) {
Stepan Moskovchenkoa1ca7582011-10-25 14:45:09 -07002272 if (!cpu_is_msm8960() && !cpu_is_msm8930())
Angshuman Sarkaread67bd2011-09-21 20:13:12 +05302273 apps |= SMSM_RESET;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002274
2275 pr_err("\nSMSM: Modem SMSM state changed to SMSM_RESET.");
2276 modem_queue_start_reset_notify();
2277
2278 } else if (modm & SMSM_INIT) {
2279 if (!(apps & SMSM_INIT)) {
2280 apps |= SMSM_INIT;
2281 modem_queue_smsm_init_notify();
2282 }
2283
2284 if (modm & SMSM_SMDINIT)
2285 apps |= SMSM_SMDINIT;
2286 if ((apps & (SMSM_INIT | SMSM_SMDINIT | SMSM_RPCINIT)) ==
2287 (SMSM_INIT | SMSM_SMDINIT | SMSM_RPCINIT))
2288 apps |= SMSM_RUN;
2289 } else if (modm & SMSM_SYSTEM_DOWNLOAD) {
2290 pr_err("\nSMSM: Modem SMSM state changed to SMSM_SYSTEM_DOWNLOAD.");
2291 modem_queue_start_reset_notify();
2292 }
2293
2294 if (old_apps != apps) {
2295 SMSM_DBG("<SM %08x NOTIFY>\n", apps);
2296 __raw_writel(apps, SMSM_STATE_ADDR(SMSM_APPS_STATE));
2297 do_smd_probe();
2298 notify_other_smsm(SMSM_APPS_STATE, (old_apps ^ apps));
2299 }
2300
Eric Holmbergc7e8daf2011-12-28 11:49:21 -07002301 smsm_cb_snapshot();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002302 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002303 spin_unlock_irqrestore(&smem_lock, flags);
2304 return IRQ_HANDLED;
2305}
2306
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002307int smsm_change_intr_mask(uint32_t smsm_entry,
2308 uint32_t clear_mask, uint32_t set_mask)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002309{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002310 uint32_t old_mask, new_mask;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002311 unsigned long flags;
Brian Swetland5b0f5a32009-04-26 18:38:49 -07002312
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002313 if (smsm_entry >= SMSM_NUM_ENTRIES) {
2314 pr_err("smsm_change_state: Invalid entry %d\n",
2315 smsm_entry);
2316 return -EINVAL;
2317 }
2318
2319 if (!smsm_info.intr_mask) {
2320 pr_err("smsm_change_intr_mask <SM NO STATE>\n");
Brian Swetland5b0f5a32009-04-26 18:38:49 -07002321 return -EIO;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002322 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002323
2324 spin_lock_irqsave(&smem_lock, flags);
2325
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002326 old_mask = __raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_APPS));
2327 new_mask = (old_mask & ~clear_mask) | set_mask;
2328 __raw_writel(new_mask, SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_APPS));
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002329
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002330 wmb();
2331 spin_unlock_irqrestore(&smem_lock, flags);
Brian Swetland5b0f5a32009-04-26 18:38:49 -07002332
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002333 return 0;
2334}
2335EXPORT_SYMBOL(smsm_change_intr_mask);
2336
2337int smsm_get_intr_mask(uint32_t smsm_entry, uint32_t *intr_mask)
2338{
2339 if (smsm_entry >= SMSM_NUM_ENTRIES) {
2340 pr_err("smsm_change_state: Invalid entry %d\n",
2341 smsm_entry);
2342 return -EINVAL;
2343 }
2344
2345 if (!smsm_info.intr_mask) {
2346 pr_err("smsm_change_intr_mask <SM NO STATE>\n");
2347 return -EIO;
2348 }
2349
2350 *intr_mask = __raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_APPS));
2351 return 0;
2352}
2353EXPORT_SYMBOL(smsm_get_intr_mask);
2354
2355int smsm_change_state(uint32_t smsm_entry,
2356 uint32_t clear_mask, uint32_t set_mask)
2357{
2358 unsigned long flags;
2359 uint32_t old_state, new_state;
2360
2361 if (smsm_entry >= SMSM_NUM_ENTRIES) {
2362 pr_err("smsm_change_state: Invalid entry %d",
2363 smsm_entry);
2364 return -EINVAL;
2365 }
2366
2367 if (!smsm_info.state) {
2368 pr_err("smsm_change_state <SM NO STATE>\n");
2369 return -EIO;
2370 }
2371 spin_lock_irqsave(&smem_lock, flags);
2372
2373 old_state = __raw_readl(SMSM_STATE_ADDR(smsm_entry));
2374 new_state = (old_state & ~clear_mask) | set_mask;
2375 __raw_writel(new_state, SMSM_STATE_ADDR(smsm_entry));
2376 SMSM_DBG("smsm_change_state %x\n", new_state);
2377 notify_other_smsm(SMSM_APPS_STATE, (old_state ^ new_state));
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002378
2379 spin_unlock_irqrestore(&smem_lock, flags);
2380
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002381 return 0;
2382}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002383EXPORT_SYMBOL(smsm_change_state);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002384
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002385uint32_t smsm_get_state(uint32_t smsm_entry)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002386{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002387 uint32_t rv = 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002388
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002389 /* needs interface change to return error code */
2390 if (smsm_entry >= SMSM_NUM_ENTRIES) {
2391 pr_err("smsm_change_state: Invalid entry %d",
2392 smsm_entry);
2393 return 0;
2394 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002395
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002396 if (!smsm_info.state) {
2397 pr_err("smsm_get_state <SM NO STATE>\n");
2398 } else {
2399 rv = __raw_readl(SMSM_STATE_ADDR(smsm_entry));
2400 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002401
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002402 return rv;
2403}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002404EXPORT_SYMBOL(smsm_get_state);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002405
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002406/**
2407 * Performs SMSM callback client notifiction.
2408 */
2409void notify_smsm_cb_clients_worker(struct work_struct *work)
Arve Hjønnevågec9d3d12009-06-16 14:48:21 -07002410{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002411 struct smsm_state_cb_info *cb_info;
2412 struct smsm_state_info *state_info;
2413 int n;
2414 uint32_t new_state;
2415 uint32_t state_changes;
Eric Holmbergc7e8daf2011-12-28 11:49:21 -07002416 int ret;
2417 int snapshot_size = SMSM_NUM_ENTRIES * sizeof(uint32_t);
Brian Swetland03e00cd2009-07-01 17:58:37 -07002418
Eric Holmbergc7e8daf2011-12-28 11:49:21 -07002419 if (!smd_initialized)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002420 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002421
Eric Holmbergc7e8daf2011-12-28 11:49:21 -07002422 while (kfifo_len(&smsm_snapshot_fifo) >= snapshot_size) {
2423 mutex_lock(&smsm_lock);
2424 for (n = 0; n < SMSM_NUM_ENTRIES; n++) {
2425 state_info = &smsm_states[n];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002426
Eric Holmbergc7e8daf2011-12-28 11:49:21 -07002427 ret = kfifo_out(&smsm_snapshot_fifo, &new_state,
2428 sizeof(new_state));
2429 if (ret != sizeof(new_state)) {
2430 pr_err("%s: snapshot underflow %d\n",
2431 __func__, ret);
2432 mutex_unlock(&smsm_lock);
2433 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002434 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002435
Eric Holmbergc7e8daf2011-12-28 11:49:21 -07002436 state_changes = state_info->last_value ^ new_state;
2437 if (state_changes) {
2438 list_for_each_entry(cb_info,
2439 &state_info->callbacks, cb_list) {
2440
2441 if (cb_info->mask & state_changes)
2442 cb_info->notify(cb_info->data,
2443 state_info->last_value,
2444 new_state);
2445 }
2446 state_info->last_value = new_state;
2447 }
2448 }
2449 mutex_unlock(&smsm_lock);
2450 }
Arve Hjønnevågec9d3d12009-06-16 14:48:21 -07002451}
2452
Arve Hjønnevågec9d3d12009-06-16 14:48:21 -07002453
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002454/**
2455 * Registers callback for SMSM state notifications when the specified
2456 * bits change.
2457 *
2458 * @smsm_entry Processor entry to deregister
2459 * @mask Bits to deregister (if result is 0, callback is removed)
2460 * @notify Notification function to deregister
2461 * @data Opaque data passed in to callback
2462 *
2463 * @returns Status code
2464 * <0 error code
2465 * 0 inserted new entry
2466 * 1 updated mask of existing entry
2467 */
2468int smsm_state_cb_register(uint32_t smsm_entry, uint32_t mask,
2469 void (*notify)(void *, uint32_t, uint32_t), void *data)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002470{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002471 struct smsm_state_cb_info *cb_info;
2472 struct smsm_state_cb_info *cb_found = 0;
2473 int ret = 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002474
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002475 if (smsm_entry >= SMSM_NUM_ENTRIES)
2476 return -EINVAL;
2477
Eric Holmbergc8002902011-09-16 13:55:57 -06002478 mutex_lock(&smsm_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002479
2480 if (!smsm_states) {
2481 /* smsm not yet initialized */
2482 ret = -ENODEV;
2483 goto cleanup;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002484 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002485
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002486 list_for_each_entry(cb_info,
2487 &smsm_states[smsm_entry].callbacks, cb_list) {
2488 if ((cb_info->notify == notify) &&
2489 (cb_info->data == data)) {
2490 cb_info->mask |= mask;
2491 cb_found = cb_info;
2492 ret = 1;
Brian Swetland5b0f5a32009-04-26 18:38:49 -07002493 break;
2494 }
2495 }
2496
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002497 if (!cb_found) {
2498 cb_info = kmalloc(sizeof(struct smsm_state_cb_info),
2499 GFP_ATOMIC);
2500 if (!cb_info) {
2501 ret = -ENOMEM;
2502 goto cleanup;
2503 }
2504
2505 cb_info->mask = mask;
2506 cb_info->notify = notify;
2507 cb_info->data = data;
2508 INIT_LIST_HEAD(&cb_info->cb_list);
2509 list_add_tail(&cb_info->cb_list,
2510 &smsm_states[smsm_entry].callbacks);
2511 }
2512
2513cleanup:
Eric Holmbergc8002902011-09-16 13:55:57 -06002514 mutex_unlock(&smsm_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002515 return ret;
2516}
2517EXPORT_SYMBOL(smsm_state_cb_register);
2518
2519
2520/**
2521 * Deregisters for SMSM state notifications for the specified bits.
2522 *
2523 * @smsm_entry Processor entry to deregister
2524 * @mask Bits to deregister (if result is 0, callback is removed)
2525 * @notify Notification function to deregister
2526 * @data Opaque data passed in to callback
2527 *
2528 * @returns Status code
2529 * <0 error code
2530 * 0 not found
2531 * 1 updated mask
2532 * 2 removed callback
2533 */
2534int smsm_state_cb_deregister(uint32_t smsm_entry, uint32_t mask,
2535 void (*notify)(void *, uint32_t, uint32_t), void *data)
2536{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002537 struct smsm_state_cb_info *cb_info;
2538 int ret = 0;
2539
2540 if (smsm_entry >= SMSM_NUM_ENTRIES)
2541 return -EINVAL;
2542
Eric Holmbergc8002902011-09-16 13:55:57 -06002543 mutex_lock(&smsm_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002544
2545 if (!smsm_states) {
2546 /* smsm not yet initialized */
Eric Holmbergc8002902011-09-16 13:55:57 -06002547 mutex_unlock(&smsm_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002548 return -ENODEV;
2549 }
2550
2551 list_for_each_entry(cb_info,
2552 &smsm_states[smsm_entry].callbacks, cb_list) {
2553 if ((cb_info->notify == notify) &&
2554 (cb_info->data == data)) {
2555 cb_info->mask &= ~mask;
2556 ret = 1;
2557 if (!cb_info->mask) {
2558 /* no mask bits set, remove callback */
2559 list_del(&cb_info->cb_list);
2560 kfree(cb_info);
2561 ret = 2;
2562 }
2563 break;
2564 }
2565 }
2566
Eric Holmbergc8002902011-09-16 13:55:57 -06002567 mutex_unlock(&smsm_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002568 return ret;
2569}
2570EXPORT_SYMBOL(smsm_state_cb_deregister);
2571
2572
2573int smd_core_init(void)
2574{
2575 int r;
2576 unsigned long flags = IRQF_TRIGGER_RISING;
2577 SMD_INFO("smd_core_init()\n");
Brian Swetland5b0f5a32009-04-26 18:38:49 -07002578
Brian Swetland37521a32009-07-01 18:30:47 -07002579 r = request_irq(INT_A9_M2A_0, smd_modem_irq_handler,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002580 flags, "smd_dev", 0);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002581 if (r < 0)
2582 return r;
2583 r = enable_irq_wake(INT_A9_M2A_0);
2584 if (r < 0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002585 pr_err("smd_core_init: "
2586 "enable_irq_wake failed for INT_A9_M2A_0\n");
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002587
2588 r = request_irq(INT_A9_M2A_5, smsm_irq_handler,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002589 flags, "smsm_dev", 0);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002590 if (r < 0) {
2591 free_irq(INT_A9_M2A_0, 0);
2592 return r;
2593 }
2594 r = enable_irq_wake(INT_A9_M2A_5);
2595 if (r < 0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002596 pr_err("smd_core_init: "
2597 "enable_irq_wake failed for INT_A9_M2A_5\n");
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002598
Brian Swetland37521a32009-07-01 18:30:47 -07002599#if defined(CONFIG_QDSP6)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002600#if (INT_ADSP_A11 == INT_ADSP_A11_SMSM)
2601 flags |= IRQF_SHARED;
2602#endif
Brian Swetland37521a32009-07-01 18:30:47 -07002603 r = request_irq(INT_ADSP_A11, smd_dsp_irq_handler,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002604 flags, "smd_dev", smd_dsp_irq_handler);
Brian Swetland37521a32009-07-01 18:30:47 -07002605 if (r < 0) {
2606 free_irq(INT_A9_M2A_0, 0);
2607 free_irq(INT_A9_M2A_5, 0);
2608 return r;
2609 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002610
2611 r = request_irq(INT_ADSP_A11_SMSM, smsm_irq_handler,
2612 flags, "smsm_dev", smsm_irq_handler);
2613 if (r < 0) {
2614 free_irq(INT_A9_M2A_0, 0);
2615 free_irq(INT_A9_M2A_5, 0);
2616 free_irq(INT_ADSP_A11, smd_dsp_irq_handler);
2617 return r;
2618 }
2619
2620 r = enable_irq_wake(INT_ADSP_A11);
2621 if (r < 0)
2622 pr_err("smd_core_init: "
2623 "enable_irq_wake failed for INT_ADSP_A11\n");
2624
2625#if (INT_ADSP_A11 != INT_ADSP_A11_SMSM)
2626 r = enable_irq_wake(INT_ADSP_A11_SMSM);
2627 if (r < 0)
2628 pr_err("smd_core_init: enable_irq_wake "
2629 "failed for INT_ADSP_A11_SMSM\n");
2630#endif
2631 flags &= ~IRQF_SHARED;
Brian Swetland37521a32009-07-01 18:30:47 -07002632#endif
2633
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002634#if defined(CONFIG_DSPS)
2635 r = request_irq(INT_DSPS_A11, smd_dsps_irq_handler,
2636 flags, "smd_dev", smd_dsps_irq_handler);
2637 if (r < 0) {
2638 free_irq(INT_A9_M2A_0, 0);
2639 free_irq(INT_A9_M2A_5, 0);
2640 free_irq(INT_ADSP_A11, smd_dsp_irq_handler);
2641 free_irq(INT_ADSP_A11_SMSM, smsm_irq_handler);
2642 return r;
2643 }
Brian Swetland5b0f5a32009-04-26 18:38:49 -07002644
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002645 r = enable_irq_wake(INT_DSPS_A11);
2646 if (r < 0)
2647 pr_err("smd_core_init: "
2648 "enable_irq_wake failed for INT_ADSP_A11\n");
Arve Hjønnevågec9d3d12009-06-16 14:48:21 -07002649#endif
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002650
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002651#if defined(CONFIG_WCNSS)
2652 r = request_irq(INT_WCNSS_A11, smd_wcnss_irq_handler,
2653 flags, "smd_dev", smd_wcnss_irq_handler);
2654 if (r < 0) {
2655 free_irq(INT_A9_M2A_0, 0);
2656 free_irq(INT_A9_M2A_5, 0);
2657 free_irq(INT_ADSP_A11, smd_dsp_irq_handler);
2658 free_irq(INT_ADSP_A11_SMSM, smsm_irq_handler);
2659 free_irq(INT_DSPS_A11, smd_dsps_irq_handler);
2660 return r;
2661 }
2662
2663 r = enable_irq_wake(INT_WCNSS_A11);
2664 if (r < 0)
2665 pr_err("smd_core_init: "
2666 "enable_irq_wake failed for INT_WCNSS_A11\n");
2667
2668 r = request_irq(INT_WCNSS_A11_SMSM, smsm_irq_handler,
2669 flags, "smsm_dev", smsm_irq_handler);
2670 if (r < 0) {
2671 free_irq(INT_A9_M2A_0, 0);
2672 free_irq(INT_A9_M2A_5, 0);
2673 free_irq(INT_ADSP_A11, smd_dsp_irq_handler);
2674 free_irq(INT_ADSP_A11_SMSM, smsm_irq_handler);
2675 free_irq(INT_DSPS_A11, smd_dsps_irq_handler);
2676 free_irq(INT_WCNSS_A11, smd_wcnss_irq_handler);
2677 return r;
2678 }
2679
2680 r = enable_irq_wake(INT_WCNSS_A11_SMSM);
2681 if (r < 0)
2682 pr_err("smd_core_init: "
2683 "enable_irq_wake failed for INT_WCNSS_A11_SMSM\n");
2684#endif
2685
Jeff Hugo6a8057c2011-08-16 13:47:12 -06002686#if defined(CONFIG_DSPS_SMSM)
2687 r = request_irq(INT_DSPS_A11_SMSM, smsm_irq_handler,
2688 flags, "smsm_dev", smsm_irq_handler);
2689 if (r < 0) {
2690 free_irq(INT_A9_M2A_0, 0);
2691 free_irq(INT_A9_M2A_5, 0);
2692 free_irq(INT_ADSP_A11, smd_dsp_irq_handler);
2693 free_irq(INT_ADSP_A11_SMSM, smsm_irq_handler);
2694 free_irq(INT_DSPS_A11, smd_dsps_irq_handler);
2695 free_irq(INT_WCNSS_A11, smd_wcnss_irq_handler);
2696 free_irq(INT_WCNSS_A11_SMSM, smsm_irq_handler);
2697 return r;
2698 }
2699
2700 r = enable_irq_wake(INT_DSPS_A11_SMSM);
2701 if (r < 0)
2702 pr_err("smd_core_init: "
2703 "enable_irq_wake failed for INT_DSPS_A11_SMSM\n");
2704#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002705 SMD_INFO("smd_core_init() done\n");
2706
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002707 return 0;
2708}
2709
Angshuman Sarkarbad32df2012-02-01 19:52:52 +05302710static int intr_init(struct interrupt_config_item *private_irq,
2711 struct smd_irq_config *platform_irq,
2712 struct platform_device *pdev
2713 )
2714{
2715 int irq_id;
2716 int ret;
2717 int ret_wake;
2718
2719 private_irq->out_bit_pos = platform_irq->out_bit_pos;
2720 private_irq->out_offset = platform_irq->out_offset;
2721 private_irq->out_base = platform_irq->out_base;
2722
2723 irq_id = platform_get_irq_byname(
2724 pdev,
2725 platform_irq->irq_name
2726 );
2727 SMD_DBG("smd: %s: register irq: %s id: %d\n", __func__,
2728 platform_irq->irq_name, irq_id);
2729 ret = request_irq(irq_id,
2730 private_irq->irq_handler,
2731 platform_irq->flags,
2732 platform_irq->device_name,
2733 (void *)platform_irq->dev_id
2734 );
2735 if (ret < 0) {
2736 platform_irq->irq_id = ret;
2737 } else {
2738 platform_irq->irq_id = irq_id;
2739 ret_wake = enable_irq_wake(irq_id);
2740 if (ret_wake < 0) {
2741 pr_err("smd: enable_irq_wake failed on %s",
2742 platform_irq->irq_name);
2743 }
2744 }
2745
2746 return ret;
2747}
2748
2749int smd_core_platform_init(struct platform_device *pdev)
2750{
2751 int i;
2752 int ret;
2753 uint32_t num_ss;
2754 struct smd_platform *smd_platform_data;
2755 struct smd_subsystem_config *smd_ss_config_list;
2756 struct smd_subsystem_config *cfg;
2757 int err_ret = 0;
2758
2759 smd_platform_data = pdev->dev.platform_data;
2760 num_ss = smd_platform_data->num_ss_configs;
2761 smd_ss_config_list = smd_platform_data->smd_ss_configs;
2762
2763 for (i = 0; i < num_ss; i++) {
2764 cfg = &smd_ss_config_list[i];
2765
2766 ret = intr_init(
2767 &private_intr_config[cfg->irq_config_id].smd,
2768 &cfg->smd_int,
2769 pdev
2770 );
2771
2772 if (ret < 0) {
2773 err_ret = ret;
2774 pr_err("smd: register irq failed on %s\n",
2775 cfg->smd_int.irq_name);
2776 break;
2777 }
2778
2779 ret = intr_init(
2780 &private_intr_config[cfg->irq_config_id].smsm,
2781 &cfg->smsm_int,
2782 pdev
2783 );
2784
2785 if (ret < 0) {
2786 err_ret = ret;
2787 pr_err("smd: register irq failed on %s\n",
2788 cfg->smsm_int.irq_name);
2789 break;
2790 }
2791 }
2792
2793 if (err_ret < 0) {
2794 pr_err("smd: deregistering IRQs\n");
2795 for (i = 0; i < num_ss; ++i) {
2796 cfg = &smd_ss_config_list[i];
2797
2798 if (cfg->smd_int.irq_id >= 0)
2799 free_irq(cfg->smd_int.irq_id,
2800 (void *)cfg->smd_int.dev_id
2801 );
2802 if (cfg->smsm_int.irq_id >= 0)
2803 free_irq(cfg->smsm_int.irq_id,
2804 (void *)cfg->smsm_int.dev_id
2805 );
2806 }
2807 return err_ret;
2808 }
2809
2810 SMD_INFO("smd_core_platform_init() done\n");
2811 return 0;
2812
2813}
2814
Gregory Bean4416e9e2010-07-28 10:22:12 -07002815static int __devinit msm_smd_probe(struct platform_device *pdev)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002816{
Angshuman Sarkarbad32df2012-02-01 19:52:52 +05302817 int ret;
Daniel Walker0aec66d2010-03-18 12:31:08 -07002818
Angshuman Sarkarbad32df2012-02-01 19:52:52 +05302819 SMD_INFO("smd probe\n");
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002820 INIT_WORK(&probe_work, smd_channel_probe_worker);
2821
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002822 channel_close_wq = create_singlethread_workqueue("smd_channel_close");
2823 if (IS_ERR(channel_close_wq)) {
2824 pr_err("%s: create_singlethread_workqueue ENOMEM\n", __func__);
2825 return -ENOMEM;
2826 }
2827
2828 if (smsm_init()) {
2829 pr_err("smsm_init() failed\n");
2830 return -1;
2831 }
2832
Angshuman Sarkarbad32df2012-02-01 19:52:52 +05302833 if (pdev) {
2834 if (pdev->dev.of_node) {
2835 pr_err("SMD: Device tree not currently supported\n");
2836 return -ENODEV;
2837 } else if (pdev->dev.platform_data) {
2838 ret = smd_core_platform_init(pdev);
2839 if (ret) {
2840 pr_err(
2841 "SMD: smd_core_platform_init() failed\n");
2842 return -ENODEV;
2843 }
2844 } else {
2845 ret = smd_core_init();
2846 if (ret) {
2847 pr_err("smd_core_init() failed\n");
2848 return -ENODEV;
2849 }
2850 }
2851 } else {
2852 pr_err("SMD: PDEV not found\n");
2853 return -ENODEV;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002854 }
2855
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002856 smd_initialized = 1;
2857
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002858 smd_alloc_loopback_channel();
Eric Holmbergc33d4ab2011-10-24 10:28:25 -06002859 smsm_irq_handler(0, 0);
2860 tasklet_schedule(&smd_fake_irq_tasklet);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002861
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002862 return 0;
2863}
2864
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002865static int restart_notifier_cb(struct notifier_block *this,
2866 unsigned long code,
2867 void *data);
2868
2869static struct restart_notifier_block restart_notifiers[] = {
Eric Holmbergca7ead22011-12-01 17:21:15 -07002870 {SMD_MODEM, "modem", .nb.notifier_call = restart_notifier_cb},
2871 {SMD_Q6, "lpass", .nb.notifier_call = restart_notifier_cb},
2872 {SMD_WCNSS, "riva", .nb.notifier_call = restart_notifier_cb},
2873 {SMD_DSPS, "dsps", .nb.notifier_call = restart_notifier_cb},
Eric Holmberg8b0e74f2012-02-08 09:56:17 -07002874 {SMD_MODEM, "gss", .nb.notifier_call = restart_notifier_cb},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002875};
2876
2877static int restart_notifier_cb(struct notifier_block *this,
2878 unsigned long code,
2879 void *data)
2880{
2881 if (code == SUBSYS_AFTER_SHUTDOWN) {
2882 struct restart_notifier_block *notifier;
2883
2884 notifier = container_of(this,
2885 struct restart_notifier_block, nb);
2886 SMD_INFO("%s: ssrestart for processor %d ('%s')\n",
2887 __func__, notifier->processor,
2888 notifier->name);
2889
2890 smd_channel_reset(notifier->processor);
2891 }
2892
2893 return NOTIFY_DONE;
2894}
2895
2896static __init int modem_restart_late_init(void)
2897{
2898 int i;
2899 void *handle;
2900 struct restart_notifier_block *nb;
2901
2902 for (i = 0; i < ARRAY_SIZE(restart_notifiers); i++) {
2903 nb = &restart_notifiers[i];
2904 handle = subsys_notif_register_notifier(nb->name, &nb->nb);
2905 SMD_DBG("%s: registering notif for '%s', handle=%p\n",
2906 __func__, nb->name, handle);
2907 }
2908 return 0;
2909}
2910late_initcall(modem_restart_late_init);
2911
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002912static struct platform_driver msm_smd_driver = {
2913 .probe = msm_smd_probe,
2914 .driver = {
2915 .name = MODULE_NAME,
2916 .owner = THIS_MODULE,
2917 },
2918};
2919
2920static int __init msm_smd_init(void)
2921{
2922 return platform_driver_register(&msm_smd_driver);
2923}
2924
2925module_init(msm_smd_init);
2926
2927MODULE_DESCRIPTION("MSM Shared Memory Core");
2928MODULE_AUTHOR("Brian Swetland <swetland@google.com>");
2929MODULE_LICENSE("GPL");