blob: 1b68560c09a7b9b19d821348a1c024b23832fedc [file] [log] [blame]
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001/* arch/arm/mach-msm/smd.c
2 *
3 * Copyright (C) 2007 Google, Inc.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07004 * Copyright (c) 2008-2011, 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>
Brian Swetland2eb44eb2008-09-29 16:00:48 -070034#include <mach/msm_smd.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070035#include <mach/msm_iomap.h>
Brian Swetland2eb44eb2008-09-29 16:00:48 -070036#include <mach/system.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037#include <mach/subsystem_notif.h>
Angshuman Sarkaread67bd2011-09-21 20:13:12 +053038#include <mach/socinfo.h>
Brian Swetland2eb44eb2008-09-29 16:00:48 -070039
40#include "smd_private.h"
41#include "proc_comm.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070042#include "modem_notifier.h"
Brian Swetland2eb44eb2008-09-29 16:00:48 -070043
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070044#if defined(CONFIG_ARCH_QSD8X50) || defined(CONFIG_ARCH_MSM8X60) \
45 || defined(CONFIG_ARCH_MSM8960) || defined(CONFIG_ARCH_FSM9XXX)
Brian Swetland37521a32009-07-01 18:30:47 -070046#define CONFIG_QDSP6 1
47#endif
48
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070049#if defined(CONFIG_ARCH_MSM8X60) || defined(CONFIG_ARCH_MSM8960)
50#define CONFIG_DSPS 1
51#endif
52
53#if defined(CONFIG_ARCH_MSM8960)
54#define CONFIG_WCNSS 1
Jeff Hugo6a8057c2011-08-16 13:47:12 -060055#define CONFIG_DSPS_SMSM 1
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070056#endif
Brian Swetland2eb44eb2008-09-29 16:00:48 -070057
58#define MODULE_NAME "msm_smd"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070059#define SMEM_VERSION 0x000B
60#define SMD_VERSION 0x00020000
61
62uint32_t SMSM_NUM_ENTRIES = 8;
63uint32_t SMSM_NUM_HOSTS = 3;
Brian Swetland2eb44eb2008-09-29 16:00:48 -070064
65enum {
66 MSM_SMD_DEBUG = 1U << 0,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070067 MSM_SMSM_DEBUG = 1U << 1,
68 MSM_SMD_INFO = 1U << 2,
69 MSM_SMSM_INFO = 1U << 3,
70};
71
72struct smsm_shared_info {
73 uint32_t *state;
74 uint32_t *intr_mask;
75 uint32_t *intr_mux;
76};
77
78static struct smsm_shared_info smsm_info;
79
80struct smsm_size_info_type {
81 uint32_t num_hosts;
82 uint32_t num_entries;
83 uint32_t reserved0;
84 uint32_t reserved1;
85};
86
87struct smsm_state_cb_info {
88 struct list_head cb_list;
89 uint32_t mask;
90 void *data;
91 void (*notify)(void *data, uint32_t old_state, uint32_t new_state);
92};
93
94struct smsm_state_info {
95 struct list_head callbacks;
96 uint32_t last_value;
97};
98
99#define SMSM_STATE_ADDR(entry) (smsm_info.state + entry)
100#define SMSM_INTR_MASK_ADDR(entry, host) (smsm_info.intr_mask + \
101 entry * SMSM_NUM_HOSTS + host)
102#define SMSM_INTR_MUX_ADDR(entry) (smsm_info.intr_mux + entry)
103
104/* Internal definitions which are not exported in some targets */
105enum {
106 SMSM_APPS_DEM_I = 3,
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700107};
108
109static int msm_smd_debug_mask;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700110module_param_named(debug_mask, msm_smd_debug_mask,
111 int, S_IRUGO | S_IWUSR | S_IWGRP);
112
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700113#if defined(CONFIG_MSM_SMD_DEBUG)
114#define SMD_DBG(x...) do { \
115 if (msm_smd_debug_mask & MSM_SMD_DEBUG) \
116 printk(KERN_DEBUG x); \
117 } while (0)
118
119#define SMSM_DBG(x...) do { \
120 if (msm_smd_debug_mask & MSM_SMSM_DEBUG) \
121 printk(KERN_DEBUG x); \
122 } while (0)
123
124#define SMD_INFO(x...) do { \
125 if (msm_smd_debug_mask & MSM_SMD_INFO) \
126 printk(KERN_INFO x); \
127 } while (0)
128
129#define SMSM_INFO(x...) do { \
130 if (msm_smd_debug_mask & MSM_SMSM_INFO) \
131 printk(KERN_INFO x); \
132 } while (0)
133#else
134#define SMD_DBG(x...) do { } while (0)
135#define SMSM_DBG(x...) do { } while (0)
136#define SMD_INFO(x...) do { } while (0)
137#define SMSM_INFO(x...) do { } while (0)
138#endif
139
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700140static unsigned last_heap_free = 0xffffffff;
141
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700142static inline void smd_write_intr(unsigned int val,
143 const void __iomem *addr);
144
145#if defined(CONFIG_ARCH_MSM7X30)
146#define MSM_TRIG_A2M_SMD_INT \
147 (smd_write_intr(1 << 0, MSM_GCC_BASE + 0x8))
148#define MSM_TRIG_A2Q6_SMD_INT \
149 (smd_write_intr(1 << 8, MSM_GCC_BASE + 0x8))
150#define MSM_TRIG_A2M_SMSM_INT \
151 (smd_write_intr(1 << 5, MSM_GCC_BASE + 0x8))
152#define MSM_TRIG_A2Q6_SMSM_INT \
153 (smd_write_intr(1 << 8, MSM_GCC_BASE + 0x8))
154#define MSM_TRIG_A2DSPS_SMD_INT
Jeff Hugo6a8057c2011-08-16 13:47:12 -0600155#define MSM_TRIG_A2DSPS_SMSM_INT
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700156#define MSM_TRIG_A2WCNSS_SMD_INT
157#define MSM_TRIG_A2WCNSS_SMSM_INT
158#elif defined(CONFIG_ARCH_MSM8X60)
159#define MSM_TRIG_A2M_SMD_INT \
160 (smd_write_intr(1 << 3, MSM_GCC_BASE + 0x8))
161#define MSM_TRIG_A2Q6_SMD_INT \
162 (smd_write_intr(1 << 15, MSM_GCC_BASE + 0x8))
163#define MSM_TRIG_A2M_SMSM_INT \
164 (smd_write_intr(1 << 4, MSM_GCC_BASE + 0x8))
165#define MSM_TRIG_A2Q6_SMSM_INT \
166 (smd_write_intr(1 << 14, MSM_GCC_BASE + 0x8))
167#define MSM_TRIG_A2DSPS_SMD_INT \
168 (smd_write_intr(1, MSM_SIC_NON_SECURE_BASE + 0x4080))
Jeff Hugo6a8057c2011-08-16 13:47:12 -0600169#define MSM_TRIG_A2DSPS_SMSM_INT
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700170#define MSM_TRIG_A2WCNSS_SMD_INT
171#define MSM_TRIG_A2WCNSS_SMSM_INT
172#elif defined(CONFIG_ARCH_MSM8960)
173#define MSM_TRIG_A2M_SMD_INT \
174 (smd_write_intr(1 << 3, MSM_APCS_GCC_BASE + 0x8))
175#define MSM_TRIG_A2Q6_SMD_INT \
176 (smd_write_intr(1 << 15, MSM_APCS_GCC_BASE + 0x8))
177#define MSM_TRIG_A2M_SMSM_INT \
178 (smd_write_intr(1 << 4, MSM_APCS_GCC_BASE + 0x8))
179#define MSM_TRIG_A2Q6_SMSM_INT \
180 (smd_write_intr(1 << 14, MSM_APCS_GCC_BASE + 0x8))
181#define MSM_TRIG_A2DSPS_SMD_INT \
182 (smd_write_intr(1, MSM_SIC_NON_SECURE_BASE + 0x4080))
Jeff Hugo6a8057c2011-08-16 13:47:12 -0600183#define MSM_TRIG_A2DSPS_SMSM_INT \
184 (smd_write_intr(1, MSM_SIC_NON_SECURE_BASE + 0x4094))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700185#define MSM_TRIG_A2WCNSS_SMD_INT \
186 (smd_write_intr(1 << 25, MSM_APCS_GCC_BASE + 0x8))
187#define MSM_TRIG_A2WCNSS_SMSM_INT \
188 (smd_write_intr(1 << 23, MSM_APCS_GCC_BASE + 0x8))
189#elif defined(CONFIG_ARCH_FSM9XXX)
190#define MSM_TRIG_A2Q6_SMD_INT \
191 (smd_write_intr(1 << 10, MSM_GCC_BASE + 0x8))
192#define MSM_TRIG_A2Q6_SMSM_INT \
193 (smd_write_intr(1 << 10, MSM_GCC_BASE + 0x8))
194#define MSM_TRIG_A2M_SMD_INT \
195 (smd_write_intr(1 << 0, MSM_GCC_BASE + 0x8))
196#define MSM_TRIG_A2M_SMSM_INT \
197 (smd_write_intr(1 << 5, MSM_GCC_BASE + 0x8))
198#define MSM_TRIG_A2DSPS_SMD_INT
Jeff Hugo6a8057c2011-08-16 13:47:12 -0600199#define MSM_TRIG_A2DSPS_SMSM_INT
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700200#define MSM_TRIG_A2WCNSS_SMD_INT
201#define MSM_TRIG_A2WCNSS_SMSM_INT
202#else
203#define MSM_TRIG_A2M_SMD_INT \
204 (smd_write_intr(1, MSM_CSR_BASE + 0x400 + (0) * 4))
205#define MSM_TRIG_A2Q6_SMD_INT \
206 (smd_write_intr(1, MSM_CSR_BASE + 0x400 + (8) * 4))
207#define MSM_TRIG_A2M_SMSM_INT \
208 (smd_write_intr(1, MSM_CSR_BASE + 0x400 + (5) * 4))
209#define MSM_TRIG_A2Q6_SMSM_INT \
210 (smd_write_intr(1, MSM_CSR_BASE + 0x400 + (8) * 4))
211#define MSM_TRIG_A2DSPS_SMD_INT
Jeff Hugo6a8057c2011-08-16 13:47:12 -0600212#define MSM_TRIG_A2DSPS_SMSM_INT
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700213#define MSM_TRIG_A2WCNSS_SMD_INT
214#define MSM_TRIG_A2WCNSS_SMSM_INT
Brian Swetland37521a32009-07-01 18:30:47 -0700215#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700216
217#define SMD_LOOPBACK_CID 100
218
Eric Holmbergf6d7d1a2011-09-23 18:31:04 -0600219#define SMEM_SPINLOCK_SMEM_ALLOC "S:3"
220static remote_spinlock_t remote_spinlock;
221
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700222static LIST_HEAD(smd_ch_list_loopback);
223static irqreturn_t smsm_irq_handler(int irq, void *data);
224static void smd_fake_irq_handler(unsigned long arg);
225
226static void notify_smsm_cb_clients_worker(struct work_struct *work);
227static DECLARE_WORK(smsm_cb_work, notify_smsm_cb_clients_worker);
Eric Holmbergc8002902011-09-16 13:55:57 -0600228static DEFINE_MUTEX(smsm_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700229static struct smsm_state_info *smsm_states;
Angshuman Sarkar7ee0dca2011-08-22 21:37:34 +0530230static int spinlocks_initialized;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700231
232static inline void smd_write_intr(unsigned int val,
233 const void __iomem *addr)
234{
235 wmb();
236 __raw_writel(val, addr);
237}
238
239#ifdef CONFIG_WCNSS
240static inline void wakeup_v1_riva(void)
241{
242 /*
243 * workaround hack for RIVA v1 hardware bug
244 * trigger GPIO 40 to wake up RIVA from power collaspe
245 * not to be sent to customers
246 */
247 __raw_writel(0x0, MSM_TLMM_BASE + 0x1284);
248 __raw_writel(0x2, MSM_TLMM_BASE + 0x1284);
249 /* end workaround */
250}
251#else
252static inline void wakeup_v1_riva(void) {}
253#endif
254
255static void notify_other_smsm(uint32_t smsm_entry, uint32_t notify_mask)
256{
257 /* older protocol don't use smsm_intr_mask,
258 but still communicates with modem */
259 if (!smsm_info.intr_mask ||
260 (__raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_MODEM))
261 & notify_mask))
262 MSM_TRIG_A2M_SMSM_INT;
263
264 if (smsm_info.intr_mask &&
265 (__raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_Q6))
266 & notify_mask)) {
267#if !defined(CONFIG_ARCH_MSM8X60) && !defined(MCONFIG_ARCH_MSM8960)
268 uint32_t mux_val;
269
270 if (smsm_info.intr_mux) {
271 mux_val = __raw_readl(
272 SMSM_INTR_MUX_ADDR(SMEM_APPS_Q6_SMSM));
273 mux_val++;
274 __raw_writel(mux_val,
275 SMSM_INTR_MUX_ADDR(SMEM_APPS_Q6_SMSM));
276 }
277#endif
278 MSM_TRIG_A2Q6_SMSM_INT;
279 }
280
281 if (smsm_info.intr_mask &&
282 (__raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_WCNSS))
283 & notify_mask)) {
284 wakeup_v1_riva();
285 MSM_TRIG_A2WCNSS_SMSM_INT;
286 }
287
Jeff Hugo6a8057c2011-08-16 13:47:12 -0600288 if (smsm_info.intr_mask &&
289 (__raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_DSPS))
290 & notify_mask)) {
291 MSM_TRIG_A2DSPS_SMSM_INT;
292 }
293
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700294 schedule_work(&smsm_cb_work);
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700295}
296
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700297static inline void notify_modem_smd(void)
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700298{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700299 MSM_TRIG_A2M_SMD_INT;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700300}
301
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700302static inline void notify_dsp_smd(void)
303{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700304 MSM_TRIG_A2Q6_SMD_INT;
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700305}
306
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700307static inline void notify_dsps_smd(void)
308{
309 MSM_TRIG_A2DSPS_SMD_INT;
310}
311
312static inline void notify_wcnss_smd(void)
313{
314 wakeup_v1_riva();
315 MSM_TRIG_A2WCNSS_SMD_INT;
316}
317
318void smd_diag(void)
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700319{
320 char *x;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700321 int size;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700322
323 x = smem_find(ID_DIAG_ERR_MSG, SZ_DIAG_ERR_MSG);
324 if (x != 0) {
325 x[SZ_DIAG_ERR_MSG - 1] = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700326 SMD_INFO("smem: DIAG '%s'\n", x);
327 }
328
329 x = smem_get_entry(SMEM_ERR_CRASH_LOG, &size);
330 if (x != 0) {
331 x[size - 1] = 0;
332 pr_err("smem: CRASH LOG\n'%s'\n", x);
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700333 }
334}
335
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700336
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700337static void handle_modem_crash(void)
338{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700339 pr_err("MODEM/AMSS has CRASHED\n");
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700340 smd_diag();
341
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700342 /* hard reboot if possible FIXME
343 if (msm_reset_hook)
344 msm_reset_hook();
345 */
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700346
347 /* in this case the modem or watchdog should reboot us */
348 for (;;)
349 ;
350}
351
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700352int smsm_check_for_modem_crash(void)
Arve Hjønnevåg28379412009-05-20 16:52:36 -0700353{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700354 /* if the modem's not ready yet, we have to hope for the best */
355 if (!smsm_info.state)
356 return 0;
Arve Hjønnevåg28379412009-05-20 16:52:36 -0700357
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700358 if (__raw_readl(SMSM_STATE_ADDR(SMSM_MODEM_STATE)) & SMSM_RESET) {
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700359 handle_modem_crash();
360 return -1;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700361 }
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700362 return 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700363}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700364EXPORT_SYMBOL(smsm_check_for_modem_crash);
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700365
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700366/* the spinlock is used to synchronize between the
Brian Swetland03e00cd2009-07-01 17:58:37 -0700367 * irq handler and code that mutates the channel
368 * list or fiddles with channel state
369 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700370static DEFINE_SPINLOCK(smd_lock);
Brian Swetland03e00cd2009-07-01 17:58:37 -0700371DEFINE_SPINLOCK(smem_lock);
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700372
373/* the mutex is used during open() and close()
Brian Swetland03e00cd2009-07-01 17:58:37 -0700374 * operations to avoid races while creating or
375 * destroying smd_channel structures
376 */
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700377static DEFINE_MUTEX(smd_creation_mutex);
378
379static int smd_initialized;
380
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700381struct smd_shared_v1 {
382 struct smd_half_channel ch0;
383 unsigned char data0[SMD_BUF_SIZE];
384 struct smd_half_channel ch1;
385 unsigned char data1[SMD_BUF_SIZE];
386};
387
388struct smd_shared_v2 {
389 struct smd_half_channel ch0;
390 struct smd_half_channel ch1;
391};
392
393struct smd_channel {
394 volatile struct smd_half_channel *send;
395 volatile struct smd_half_channel *recv;
396 unsigned char *send_data;
397 unsigned char *recv_data;
398 unsigned fifo_size;
399 unsigned fifo_mask;
400 struct list_head ch_list;
401
402 unsigned current_packet;
403 unsigned n;
404 void *priv;
405 void (*notify)(void *priv, unsigned flags);
406
407 int (*read)(smd_channel_t *ch, void *data, int len, int user_buf);
408 int (*write)(smd_channel_t *ch, const void *data, int len,
409 int user_buf);
410 int (*read_avail)(smd_channel_t *ch);
411 int (*write_avail)(smd_channel_t *ch);
412 int (*read_from_cb)(smd_channel_t *ch, void *data, int len,
413 int user_buf);
414
415 void (*update_state)(smd_channel_t *ch);
416 unsigned last_state;
417 void (*notify_other_cpu)(void);
418
419 char name[20];
420 struct platform_device pdev;
421 unsigned type;
422
423 int pending_pkt_sz;
424
425 char is_pkt_ch;
426};
427
428struct edge_to_pid {
429 uint32_t local_pid;
430 uint32_t remote_pid;
431};
432
Eric Holmberg7dfd1972011-09-09 16:07:57 -0600433/*
434 * SMD Processor ID's.
435 *
436 * For all processors that have both SMSM and SMD clients,
437 * the SMSM Processor ID and the SMD Processor ID will
438 * be the same. In cases where a processor only supports
439 * SMD, the entry will only exist in this enum.
440 */
441enum {
442 SMD_APPS = SMSM_APPS,
443 SMD_MODEM = SMSM_MODEM,
444 SMD_Q6 = SMSM_Q6,
445 SMD_WCNSS = SMSM_WCNSS,
446 SMD_DSPS = SMSM_DSPS,
447 SMD_MODEM_Q6_FW,
448};
449
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700450/**
451 * Maps edge type to local and remote processor ID's.
452 */
453static struct edge_to_pid edge_to_pids[] = {
454 [SMD_APPS_MODEM] = {SMSM_APPS, SMSM_MODEM},
455 [SMD_APPS_QDSP] = {SMSM_APPS, SMSM_Q6},
456 [SMD_MODEM_QDSP] = {SMSM_MODEM, SMSM_Q6},
457 [SMD_APPS_DSPS] = {SMSM_APPS, SMSM_DSPS},
458 [SMD_MODEM_DSPS] = {SMSM_MODEM, SMSM_DSPS},
459 [SMD_QDSP_DSPS] = {SMSM_Q6, SMSM_DSPS},
460 [SMD_APPS_WCNSS] = {SMSM_APPS, SMSM_WCNSS},
461 [SMD_MODEM_WCNSS] = {SMSM_MODEM, SMSM_WCNSS},
462 [SMD_QDSP_WCNSS] = {SMSM_Q6, SMSM_WCNSS},
463 [SMD_DSPS_WCNSS] = {SMSM_DSPS, SMSM_WCNSS},
Eric Holmberg7dfd1972011-09-09 16:07:57 -0600464 [SMD_APPS_Q6FW] = {SMSM_APPS, SMD_MODEM_Q6_FW},
465 [SMD_MODEM_Q6FW] = {SMSM_MODEM, SMD_MODEM_Q6_FW},
466 [SMD_QDSP_Q6FW] = {SMSM_Q6, SMD_MODEM_Q6_FW},
467 [SMD_DSPS_Q6FW] = {SMSM_DSPS, SMD_MODEM_Q6_FW},
468 [SMD_WCNSS_Q6FW] = {SMSM_WCNSS, SMD_MODEM_Q6_FW},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700469};
470
471struct restart_notifier_block {
472 unsigned processor;
473 char *name;
474 struct notifier_block nb;
475};
476
477static struct platform_device loopback_tty_pdev = {.name = "LOOPBACK_TTY"};
478
479static LIST_HEAD(smd_ch_closed_list);
480static LIST_HEAD(smd_ch_closing_list);
481static LIST_HEAD(smd_ch_to_close_list);
482static LIST_HEAD(smd_ch_list_modem);
483static LIST_HEAD(smd_ch_list_dsp);
484static LIST_HEAD(smd_ch_list_dsps);
485static LIST_HEAD(smd_ch_list_wcnss);
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700486
487static unsigned char smd_ch_allocated[64];
488static struct work_struct probe_work;
489
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700490static void finalize_channel_close_fn(struct work_struct *work);
491static DECLARE_WORK(finalize_channel_close_work, finalize_channel_close_fn);
492static struct workqueue_struct *channel_close_wq;
493
494static int smd_alloc_channel(struct smd_alloc_elm *alloc_elm);
495
496/* on smp systems, the probe might get called from multiple cores,
497 hence use a lock */
498static DEFINE_MUTEX(smd_probe_lock);
499
500static void smd_channel_probe_worker(struct work_struct *work)
501{
502 struct smd_alloc_elm *shared;
503 unsigned n;
504 uint32_t type;
505
506 shared = smem_find(ID_CH_ALLOC_TBL, sizeof(*shared) * 64);
507
508 if (!shared) {
509 pr_err("%s: allocation table not initialized\n", __func__);
510 return;
511 }
512
513 mutex_lock(&smd_probe_lock);
514 for (n = 0; n < 64; n++) {
515 if (smd_ch_allocated[n])
516 continue;
517
518 /* channel should be allocated only if APPS
519 processor is involved */
520 type = SMD_CHANNEL_TYPE(shared[n].type);
521 if ((type != SMD_APPS_MODEM) && (type != SMD_APPS_QDSP) &&
522 (type != SMD_APPS_DSPS) && (type != SMD_APPS_WCNSS))
523 continue;
524 if (!shared[n].ref_count)
525 continue;
526 if (!shared[n].name[0])
527 continue;
528
529 if (!smd_alloc_channel(&shared[n]))
530 smd_ch_allocated[n] = 1;
531 else
532 SMD_INFO("Probe skipping ch %d, not allocated\n", n);
533 }
534 mutex_unlock(&smd_probe_lock);
535}
536
537/**
538 * Lookup processor ID and determine if it belongs to the proved edge
539 * type.
540 *
541 * @shared2: Pointer to v2 shared channel structure
542 * @type: Edge type
543 * @pid: Processor ID of processor on edge
544 * @local_ch: Channel that belongs to processor @pid
545 * @remote_ch: Other side of edge contained @pid
546 *
547 * Returns 0 for not on edge, 1 for found on edge
548 */
549static int pid_is_on_edge(struct smd_shared_v2 *shared2,
550 uint32_t type, uint32_t pid,
551 struct smd_half_channel **local_ch,
552 struct smd_half_channel **remote_ch
553 )
554{
555 int ret = 0;
556 struct edge_to_pid *edge;
557
558 *local_ch = 0;
559 *remote_ch = 0;
560
561 if (!shared2 || (type >= ARRAY_SIZE(edge_to_pids)))
562 return 0;
563
564 edge = &edge_to_pids[type];
565 if (edge->local_pid != edge->remote_pid) {
566 if (pid == edge->local_pid) {
567 *local_ch = &shared2->ch0;
568 *remote_ch = &shared2->ch1;
569 ret = 1;
570 } else if (pid == edge->remote_pid) {
571 *local_ch = &shared2->ch1;
572 *remote_ch = &shared2->ch0;
573 ret = 1;
574 }
575 }
576
577 return ret;
578}
579
580
581static void smd_channel_reset_state(struct smd_alloc_elm *shared,
582 unsigned new_state, unsigned pid)
583{
584 unsigned n;
585 struct smd_shared_v2 *shared2;
586 uint32_t type;
587 struct smd_half_channel *local_ch;
588 struct smd_half_channel *remote_ch;
589
590 for (n = 0; n < SMD_CHANNELS; n++) {
591 if (!shared[n].ref_count)
592 continue;
593 if (!shared[n].name[0])
594 continue;
595
596 type = SMD_CHANNEL_TYPE(shared[n].type);
597 shared2 = smem_alloc(SMEM_SMD_BASE_ID + n, sizeof(*shared2));
598 if (!shared2)
599 continue;
600
Eric Holmberg7dfd1972011-09-09 16:07:57 -0600601 if (pid_is_on_edge(shared2, type, pid, &local_ch, &remote_ch) ||
602 (pid == SMSM_MODEM &&
603 pid_is_on_edge(shared2, type, SMD_MODEM_Q6_FW,
604 &local_ch, &remote_ch))) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700605
606 /* force remote state for processor being restarted */
607 if (local_ch->state != SMD_SS_CLOSED) {
608 local_ch->state = new_state;
609 local_ch->fDSR = 0;
610 local_ch->fCTS = 0;
611 local_ch->fCD = 0;
612 local_ch->fSTATE = 1;
613 }
614 }
615 }
616}
617
618
619void smd_channel_reset(uint32_t restart_pid)
620{
621 struct smd_alloc_elm *shared;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700622 unsigned long flags;
623
624 SMD_DBG("%s: starting reset\n", __func__);
625 shared = smem_find(ID_CH_ALLOC_TBL, sizeof(*shared) * 64);
626 if (!shared) {
627 pr_err("%s: allocation table not initialized\n", __func__);
628 return;
629 }
630
Eric Holmbergf6d7d1a2011-09-23 18:31:04 -0600631 /* release any held spinlocks */
632 remote_spin_release(&remote_spinlock, restart_pid);
633 remote_spin_release_all(restart_pid);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700634
635 /* reset SMSM entry */
636 if (smsm_info.state) {
637 writel_relaxed(0, SMSM_STATE_ADDR(restart_pid));
638
639 /* clear apps SMSM to restart SMSM init handshake */
640 if (restart_pid == SMSM_MODEM)
641 writel_relaxed(0, SMSM_STATE_ADDR(SMSM_APPS));
642
643 /* notify SMSM processors */
644 smsm_irq_handler(0, 0);
645 MSM_TRIG_A2M_SMSM_INT;
646 MSM_TRIG_A2Q6_SMSM_INT;
Jeff Hugo6a8057c2011-08-16 13:47:12 -0600647 MSM_TRIG_A2DSPS_SMSM_INT;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700648 }
649
650 /* change all remote states to CLOSING */
651 mutex_lock(&smd_probe_lock);
652 spin_lock_irqsave(&smd_lock, flags);
653 smd_channel_reset_state(shared, SMD_SS_CLOSING, restart_pid);
654 spin_unlock_irqrestore(&smd_lock, flags);
655 mutex_unlock(&smd_probe_lock);
656
657 /* notify SMD processors */
658 mb();
659 smd_fake_irq_handler(0);
660 notify_modem_smd();
661 notify_dsp_smd();
662 notify_dsps_smd();
663 notify_wcnss_smd();
664
665 /* change all remote states to CLOSED */
666 mutex_lock(&smd_probe_lock);
667 spin_lock_irqsave(&smd_lock, flags);
668 smd_channel_reset_state(shared, SMD_SS_CLOSED, restart_pid);
669 spin_unlock_irqrestore(&smd_lock, flags);
670 mutex_unlock(&smd_probe_lock);
671
672 /* notify SMD processors */
673 mb();
674 smd_fake_irq_handler(0);
675 notify_modem_smd();
676 notify_dsp_smd();
677 notify_dsps_smd();
678 notify_wcnss_smd();
679
680 SMD_DBG("%s: finished reset\n", __func__);
681}
682
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700683/* how many bytes are available for reading */
684static int smd_stream_read_avail(struct smd_channel *ch)
685{
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700686 return (ch->recv->head - ch->recv->tail) & ch->fifo_mask;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700687}
688
689/* how many bytes we are free to write */
690static int smd_stream_write_avail(struct smd_channel *ch)
691{
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700692 return ch->fifo_mask -
693 ((ch->send->head - ch->send->tail) & ch->fifo_mask);
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700694}
695
696static int smd_packet_read_avail(struct smd_channel *ch)
697{
698 if (ch->current_packet) {
699 int n = smd_stream_read_avail(ch);
700 if (n > ch->current_packet)
701 n = ch->current_packet;
702 return n;
703 } else {
704 return 0;
705 }
706}
707
708static int smd_packet_write_avail(struct smd_channel *ch)
709{
710 int n = smd_stream_write_avail(ch);
711 return n > SMD_HEADER_SIZE ? n - SMD_HEADER_SIZE : 0;
712}
713
714static int ch_is_open(struct smd_channel *ch)
715{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700716 return (ch->recv->state == SMD_SS_OPENED ||
717 ch->recv->state == SMD_SS_FLUSHING)
718 && (ch->send->state == SMD_SS_OPENED);
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700719}
720
721/* provide a pointer and length to readable data in the fifo */
722static unsigned ch_read_buffer(struct smd_channel *ch, void **ptr)
723{
724 unsigned head = ch->recv->head;
725 unsigned tail = ch->recv->tail;
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700726 *ptr = (void *) (ch->recv_data + tail);
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700727
728 if (tail <= head)
729 return head - tail;
730 else
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700731 return ch->fifo_size - tail;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700732}
733
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700734static int read_intr_blocked(struct smd_channel *ch)
735{
736 return ch->recv->fBLOCKREADINTR;
737}
738
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700739/* advance the fifo read pointer after data from ch_read_buffer is consumed */
740static void ch_read_done(struct smd_channel *ch, unsigned count)
741{
742 BUG_ON(count > smd_stream_read_avail(ch));
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700743 ch->recv->tail = (ch->recv->tail + count) & ch->fifo_mask;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700744 wmb();
Haley Teng7632fba2009-10-12 10:38:10 -0700745 ch->send->fTAIL = 1;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700746}
747
748/* basic read interface to ch_read_{buffer,done} used
Brian Swetland03e00cd2009-07-01 17:58:37 -0700749 * by smd_*_read() and update_packet_state()
750 * will read-and-discard if the _data pointer is null
751 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700752static int ch_read(struct smd_channel *ch, void *_data, int len, int user_buf)
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700753{
754 void *ptr;
755 unsigned n;
756 unsigned char *data = _data;
757 int orig_len = len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700758 int r = 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700759
760 while (len > 0) {
761 n = ch_read_buffer(ch, &ptr);
762 if (n == 0)
763 break;
764
765 if (n > len)
766 n = len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700767 if (_data) {
768 if (user_buf) {
769 r = copy_to_user(data, ptr, n);
770 if (r > 0) {
771 pr_err("%s: "
772 "copy_to_user could not copy "
773 "%i bytes.\n",
774 __func__,
775 r);
776 }
777 } else
778 memcpy(data, ptr, n);
779 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700780
781 data += n;
782 len -= n;
783 ch_read_done(ch, n);
784 }
785
786 return orig_len - len;
787}
788
789static void update_stream_state(struct smd_channel *ch)
790{
791 /* streams have no special state requiring updating */
792}
793
794static void update_packet_state(struct smd_channel *ch)
795{
796 unsigned hdr[5];
797 int r;
798
799 /* can't do anything if we're in the middle of a packet */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700800 while (ch->current_packet == 0) {
801 /* discard 0 length packets if any */
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700802
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700803 /* don't bother unless we can get the full header */
804 if (smd_stream_read_avail(ch) < SMD_HEADER_SIZE)
805 return;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700806
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700807 r = ch_read(ch, hdr, SMD_HEADER_SIZE, 0);
808 BUG_ON(r != SMD_HEADER_SIZE);
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700809
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700810 ch->current_packet = hdr[0];
811 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700812}
813
814/* provide a pointer and length to next free space in the fifo */
815static unsigned ch_write_buffer(struct smd_channel *ch, void **ptr)
816{
817 unsigned head = ch->send->head;
818 unsigned tail = ch->send->tail;
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700819 *ptr = (void *) (ch->send_data + head);
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700820
821 if (head < tail) {
822 return tail - head - 1;
823 } else {
824 if (tail == 0)
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700825 return ch->fifo_size - head - 1;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700826 else
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700827 return ch->fifo_size - head;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700828 }
829}
830
831/* advace the fifo write pointer after freespace
832 * from ch_write_buffer is filled
833 */
834static void ch_write_done(struct smd_channel *ch, unsigned count)
835{
836 BUG_ON(count > smd_stream_write_avail(ch));
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700837 ch->send->head = (ch->send->head + count) & ch->fifo_mask;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700838 wmb();
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700839 ch->send->fHEAD = 1;
840}
841
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700842static void ch_set_state(struct smd_channel *ch, unsigned n)
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700843{
844 if (n == SMD_SS_OPENED) {
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700845 ch->send->fDSR = 1;
846 ch->send->fCTS = 1;
847 ch->send->fCD = 1;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700848 } else {
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700849 ch->send->fDSR = 0;
850 ch->send->fCTS = 0;
851 ch->send->fCD = 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700852 }
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700853 ch->send->state = n;
854 ch->send->fSTATE = 1;
855 ch->notify_other_cpu();
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700856}
857
858static void do_smd_probe(void)
859{
860 struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE;
861 if (shared->heap_info.free_offset != last_heap_free) {
862 last_heap_free = shared->heap_info.free_offset;
863 schedule_work(&probe_work);
864 }
865}
866
867static void smd_state_change(struct smd_channel *ch,
868 unsigned last, unsigned next)
869{
870 ch->last_state = next;
871
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700872 SMD_INFO("SMD: ch %d %d -> %d\n", ch->n, last, next);
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700873
874 switch (next) {
875 case SMD_SS_OPENING:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700876 if (ch->send->state == SMD_SS_CLOSING ||
877 ch->send->state == SMD_SS_CLOSED) {
878 ch->recv->tail = 0;
879 ch->send->head = 0;
880 ch->send->fBLOCKREADINTR = 0;
881 ch_set_state(ch, SMD_SS_OPENING);
882 }
883 break;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700884 case SMD_SS_OPENED:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700885 if (ch->send->state == SMD_SS_OPENING) {
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700886 ch_set_state(ch, SMD_SS_OPENED);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700887 ch->notify(ch->priv, SMD_EVENT_OPEN);
888 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700889 break;
890 case SMD_SS_FLUSHING:
891 case SMD_SS_RESET:
892 /* we should force them to close? */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700893 break;
894 case SMD_SS_CLOSED:
895 if (ch->send->state == SMD_SS_OPENED) {
896 ch_set_state(ch, SMD_SS_CLOSING);
897 ch->current_packet = 0;
898 ch->notify(ch->priv, SMD_EVENT_CLOSE);
899 }
900 break;
901 case SMD_SS_CLOSING:
902 if (ch->send->state == SMD_SS_CLOSED) {
903 list_move(&ch->ch_list,
904 &smd_ch_to_close_list);
905 queue_work(channel_close_wq,
906 &finalize_channel_close_work);
907 }
908 break;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700909 }
910}
911
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700912static void handle_smd_irq_closing_list(void)
913{
914 unsigned long flags;
915 struct smd_channel *ch;
916 struct smd_channel *index;
917 unsigned tmp;
918
919 spin_lock_irqsave(&smd_lock, flags);
920 list_for_each_entry_safe(ch, index, &smd_ch_closing_list, ch_list) {
921 if (ch->recv->fSTATE)
922 ch->recv->fSTATE = 0;
923 tmp = ch->recv->state;
924 if (tmp != ch->last_state)
925 smd_state_change(ch, ch->last_state, tmp);
926 }
927 spin_unlock_irqrestore(&smd_lock, flags);
928}
929
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700930static void handle_smd_irq(struct list_head *list, void (*notify)(void))
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700931{
932 unsigned long flags;
933 struct smd_channel *ch;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700934 unsigned ch_flags;
935 unsigned tmp;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700936 unsigned char state_change;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700937
938 spin_lock_irqsave(&smd_lock, flags);
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700939 list_for_each_entry(ch, list, ch_list) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700940 state_change = 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700941 ch_flags = 0;
942 if (ch_is_open(ch)) {
943 if (ch->recv->fHEAD) {
944 ch->recv->fHEAD = 0;
945 ch_flags |= 1;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700946 }
947 if (ch->recv->fTAIL) {
948 ch->recv->fTAIL = 0;
949 ch_flags |= 2;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700950 }
951 if (ch->recv->fSTATE) {
952 ch->recv->fSTATE = 0;
953 ch_flags |= 4;
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700954 }
955 }
956 tmp = ch->recv->state;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700957 if (tmp != ch->last_state) {
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700958 smd_state_change(ch, ch->last_state, tmp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700959 state_change = 1;
960 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700961 if (ch_flags) {
962 ch->update_state(ch);
963 ch->notify(ch->priv, SMD_EVENT_DATA);
964 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700965 if (ch_flags & 0x4 && !state_change)
966 ch->notify(ch->priv, SMD_EVENT_STATUS);
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700967 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -0700968 spin_unlock_irqrestore(&smd_lock, flags);
969 do_smd_probe();
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700970}
971
Brian Swetland37521a32009-07-01 18:30:47 -0700972static irqreturn_t smd_modem_irq_handler(int irq, void *data)
Brian Swetland5b0f5a32009-04-26 18:38:49 -0700973{
Brian Swetland37521a32009-07-01 18:30:47 -0700974 handle_smd_irq(&smd_ch_list_modem, notify_modem_smd);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700975 handle_smd_irq_closing_list();
Brian Swetland37521a32009-07-01 18:30:47 -0700976 return IRQ_HANDLED;
977}
978
Daniel Walkerb13525c2010-03-18 10:10:30 -0700979#if defined(CONFIG_QDSP6)
Brian Swetland37521a32009-07-01 18:30:47 -0700980static irqreturn_t smd_dsp_irq_handler(int irq, void *data)
981{
982 handle_smd_irq(&smd_ch_list_dsp, notify_dsp_smd);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700983 handle_smd_irq_closing_list();
984 return IRQ_HANDLED;
985}
986#endif
987
988#if defined(CONFIG_DSPS)
989static irqreturn_t smd_dsps_irq_handler(int irq, void *data)
990{
991 handle_smd_irq(&smd_ch_list_dsps, notify_dsps_smd);
992 handle_smd_irq_closing_list();
993 return IRQ_HANDLED;
994}
995#endif
996
997#if defined(CONFIG_WCNSS)
998static irqreturn_t smd_wcnss_irq_handler(int irq, void *data)
999{
1000 handle_smd_irq(&smd_ch_list_wcnss, notify_wcnss_smd);
1001 handle_smd_irq_closing_list();
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001002 return IRQ_HANDLED;
1003}
Daniel Walkerb13525c2010-03-18 10:10:30 -07001004#endif
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001005
1006static void smd_fake_irq_handler(unsigned long arg)
1007{
Brian Swetland37521a32009-07-01 18:30:47 -07001008 handle_smd_irq(&smd_ch_list_modem, notify_modem_smd);
1009 handle_smd_irq(&smd_ch_list_dsp, notify_dsp_smd);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001010 handle_smd_irq(&smd_ch_list_dsps, notify_dsps_smd);
1011 handle_smd_irq(&smd_ch_list_wcnss, notify_wcnss_smd);
1012 handle_smd_irq_closing_list();
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001013}
1014
1015static DECLARE_TASKLET(smd_fake_irq_tasklet, smd_fake_irq_handler, 0);
1016
Brian Swetland37521a32009-07-01 18:30:47 -07001017static inline int smd_need_int(struct smd_channel *ch)
1018{
1019 if (ch_is_open(ch)) {
1020 if (ch->recv->fHEAD || ch->recv->fTAIL || ch->recv->fSTATE)
1021 return 1;
1022 if (ch->recv->state != ch->last_state)
1023 return 1;
1024 }
1025 return 0;
1026}
1027
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001028void smd_sleep_exit(void)
1029{
1030 unsigned long flags;
1031 struct smd_channel *ch;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001032 int need_int = 0;
1033
1034 spin_lock_irqsave(&smd_lock, flags);
Brian Swetland37521a32009-07-01 18:30:47 -07001035 list_for_each_entry(ch, &smd_ch_list_modem, ch_list) {
1036 if (smd_need_int(ch)) {
1037 need_int = 1;
1038 break;
1039 }
1040 }
1041 list_for_each_entry(ch, &smd_ch_list_dsp, ch_list) {
1042 if (smd_need_int(ch)) {
1043 need_int = 1;
1044 break;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001045 }
1046 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001047 list_for_each_entry(ch, &smd_ch_list_dsps, ch_list) {
1048 if (smd_need_int(ch)) {
1049 need_int = 1;
1050 break;
1051 }
1052 }
1053 list_for_each_entry(ch, &smd_ch_list_wcnss, ch_list) {
1054 if (smd_need_int(ch)) {
1055 need_int = 1;
1056 break;
1057 }
1058 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001059 spin_unlock_irqrestore(&smd_lock, flags);
1060 do_smd_probe();
Brian Swetland37521a32009-07-01 18:30:47 -07001061
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001062 if (need_int) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001063 SMD_DBG("smd_sleep_exit need interrupt\n");
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001064 tasklet_schedule(&smd_fake_irq_tasklet);
1065 }
1066}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001067EXPORT_SYMBOL(smd_sleep_exit);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001068
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001069static int smd_is_packet(struct smd_alloc_elm *alloc_elm)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001070{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001071 if (SMD_XFER_TYPE(alloc_elm->type) == 1)
1072 return 0;
1073 else if (SMD_XFER_TYPE(alloc_elm->type) == 2)
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001074 return 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001075
1076 /* for cases where xfer type is 0 */
1077 if (!strncmp(alloc_elm->name, "DAL", 3))
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001078 return 0;
1079
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001080 /* for cases where xfer type is 0 */
1081 if (!strncmp(alloc_elm->name, "RPCCALL_QDSP", 12))
1082 return 0;
1083
1084 if (alloc_elm->cid > 4 || alloc_elm->cid == 1)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001085 return 1;
1086 else
1087 return 0;
1088}
1089
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001090static int smd_stream_write(smd_channel_t *ch, const void *_data, int len,
1091 int user_buf)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001092{
1093 void *ptr;
1094 const unsigned char *buf = _data;
1095 unsigned xfer;
1096 int orig_len = len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001097 int r = 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001098
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001099 SMD_DBG("smd_stream_write() %d -> ch%d\n", len, ch->n);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001100 if (len < 0)
1101 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001102 else if (len == 0)
1103 return 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001104
1105 while ((xfer = ch_write_buffer(ch, &ptr)) != 0) {
1106 if (!ch_is_open(ch))
1107 break;
1108 if (xfer > len)
1109 xfer = len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001110 if (user_buf) {
1111 r = copy_from_user(ptr, buf, xfer);
1112 if (r > 0) {
1113 pr_err("%s: "
1114 "copy_from_user could not copy %i "
1115 "bytes.\n",
1116 __func__,
1117 r);
1118 }
1119 } else
1120 memcpy(ptr, buf, xfer);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001121 ch_write_done(ch, xfer);
1122 len -= xfer;
1123 buf += xfer;
1124 if (len == 0)
1125 break;
1126 }
1127
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001128 if (orig_len - len)
1129 ch->notify_other_cpu();
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001130
1131 return orig_len - len;
1132}
1133
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001134static int smd_packet_write(smd_channel_t *ch, const void *_data, int len,
1135 int user_buf)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001136{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001137 int ret;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001138 unsigned hdr[5];
1139
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001140 SMD_DBG("smd_packet_write() %d -> ch%d\n", len, ch->n);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001141 if (len < 0)
1142 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001143 else if (len == 0)
1144 return 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001145
1146 if (smd_stream_write_avail(ch) < (len + SMD_HEADER_SIZE))
1147 return -ENOMEM;
1148
1149 hdr[0] = len;
1150 hdr[1] = hdr[2] = hdr[3] = hdr[4] = 0;
1151
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001152
1153 ret = smd_stream_write(ch, hdr, sizeof(hdr), 0);
1154 if (ret < 0 || ret != sizeof(hdr)) {
1155 SMD_DBG("%s failed to write pkt header: "
1156 "%d returned\n", __func__, ret);
1157 return -1;
1158 }
1159
1160
1161 ret = smd_stream_write(ch, _data, len, user_buf);
1162 if (ret < 0 || ret != len) {
1163 SMD_DBG("%s failed to write pkt data: "
1164 "%d returned\n", __func__, ret);
1165 return ret;
1166 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001167
1168 return len;
1169}
1170
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001171static int smd_stream_read(smd_channel_t *ch, void *data, int len, int user_buf)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001172{
1173 int r;
1174
1175 if (len < 0)
1176 return -EINVAL;
1177
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001178 r = ch_read(ch, data, len, user_buf);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001179 if (r > 0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001180 if (!read_intr_blocked(ch))
1181 ch->notify_other_cpu();
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001182
1183 return r;
1184}
1185
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001186static int smd_packet_read(smd_channel_t *ch, void *data, int len, int user_buf)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001187{
1188 unsigned long flags;
1189 int r;
1190
1191 if (len < 0)
1192 return -EINVAL;
1193
1194 if (len > ch->current_packet)
1195 len = ch->current_packet;
1196
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001197 r = ch_read(ch, data, len, user_buf);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001198 if (r > 0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001199 if (!read_intr_blocked(ch))
1200 ch->notify_other_cpu();
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001201
1202 spin_lock_irqsave(&smd_lock, flags);
1203 ch->current_packet -= r;
1204 update_packet_state(ch);
1205 spin_unlock_irqrestore(&smd_lock, flags);
1206
1207 return r;
1208}
1209
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001210static int smd_packet_read_from_cb(smd_channel_t *ch, void *data, int len,
1211 int user_buf)
1212{
1213 int r;
1214
1215 if (len < 0)
1216 return -EINVAL;
1217
1218 if (len > ch->current_packet)
1219 len = ch->current_packet;
1220
1221 r = ch_read(ch, data, len, user_buf);
1222 if (r > 0)
1223 if (!read_intr_blocked(ch))
1224 ch->notify_other_cpu();
1225
1226 ch->current_packet -= r;
1227 update_packet_state(ch);
1228
1229 return r;
1230}
1231
1232static int smd_alloc_v2(struct smd_channel *ch)
1233{
1234 struct smd_shared_v2 *shared2;
1235 void *buffer;
1236 unsigned buffer_sz;
1237
1238 shared2 = smem_alloc(SMEM_SMD_BASE_ID + ch->n, sizeof(*shared2));
1239 if (!shared2) {
1240 SMD_INFO("smem_alloc failed ch=%d\n", ch->n);
1241 return -1;
1242 }
1243 buffer = smem_get_entry(SMEM_SMD_FIFO_BASE_ID + ch->n, &buffer_sz);
1244 if (!buffer) {
1245 SMD_INFO("smem_get_entry failed \n");
1246 return -1;
1247 }
1248
1249 /* buffer must be a power-of-two size */
1250 if (buffer_sz & (buffer_sz - 1))
1251 return -1;
1252
1253 buffer_sz /= 2;
1254 ch->send = &shared2->ch0;
1255 ch->recv = &shared2->ch1;
1256 ch->send_data = buffer;
1257 ch->recv_data = buffer + buffer_sz;
1258 ch->fifo_size = buffer_sz;
1259 return 0;
1260}
1261
1262static int smd_alloc_v1(struct smd_channel *ch)
1263{
1264 struct smd_shared_v1 *shared1;
1265 shared1 = smem_alloc(ID_SMD_CHANNELS + ch->n, sizeof(*shared1));
1266 if (!shared1) {
1267 pr_err("smd_alloc_channel() cid %d does not exist\n", ch->n);
1268 return -1;
1269 }
1270 ch->send = &shared1->ch0;
1271 ch->recv = &shared1->ch1;
1272 ch->send_data = shared1->data0;
1273 ch->recv_data = shared1->data1;
1274 ch->fifo_size = SMD_BUF_SIZE;
1275 return 0;
1276}
1277
1278static int smd_alloc_channel(struct smd_alloc_elm *alloc_elm)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001279{
1280 struct smd_channel *ch;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001281
1282 ch = kzalloc(sizeof(struct smd_channel), GFP_KERNEL);
1283 if (ch == 0) {
1284 pr_err("smd_alloc_channel() out of memory\n");
Brian Swetland34f719b2009-10-30 16:22:05 -07001285 return -1;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001286 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001287 ch->n = alloc_elm->cid;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001288
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001289 if (smd_alloc_v2(ch) && smd_alloc_v1(ch)) {
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001290 kfree(ch);
Brian Swetland34f719b2009-10-30 16:22:05 -07001291 return -1;
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001292 }
1293
1294 ch->fifo_mask = ch->fifo_size - 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001295 ch->type = SMD_CHANNEL_TYPE(alloc_elm->type);
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001296
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001297 if (ch->type == SMD_APPS_MODEM)
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001298 ch->notify_other_cpu = notify_modem_smd;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001299 else if (ch->type == SMD_APPS_QDSP)
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001300 ch->notify_other_cpu = notify_dsp_smd;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001301 else if (ch->type == SMD_APPS_DSPS)
1302 ch->notify_other_cpu = notify_dsps_smd;
1303 else
1304 ch->notify_other_cpu = notify_wcnss_smd;
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001305
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001306 if (smd_is_packet(alloc_elm)) {
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001307 ch->read = smd_packet_read;
1308 ch->write = smd_packet_write;
1309 ch->read_avail = smd_packet_read_avail;
1310 ch->write_avail = smd_packet_write_avail;
1311 ch->update_state = update_packet_state;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001312 ch->read_from_cb = smd_packet_read_from_cb;
1313 ch->is_pkt_ch = 1;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001314 } else {
1315 ch->read = smd_stream_read;
1316 ch->write = smd_stream_write;
1317 ch->read_avail = smd_stream_read_avail;
1318 ch->write_avail = smd_stream_write_avail;
1319 ch->update_state = update_stream_state;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001320 ch->read_from_cb = smd_stream_read;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001321 }
1322
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001323 memcpy(ch->name, alloc_elm->name, SMD_MAX_CH_NAME_LEN);
1324 ch->name[SMD_MAX_CH_NAME_LEN-1] = 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001325
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001326 ch->pdev.name = ch->name;
1327 ch->pdev.id = ch->type;
1328
1329 SMD_INFO("smd_alloc_channel() '%s' cid=%d\n",
1330 ch->name, ch->n);
1331
1332 mutex_lock(&smd_creation_mutex);
1333 list_add(&ch->ch_list, &smd_ch_closed_list);
1334 mutex_unlock(&smd_creation_mutex);
1335
1336 platform_device_register(&ch->pdev);
1337 if (!strncmp(ch->name, "LOOPBACK", 8) && ch->type == SMD_APPS_MODEM) {
1338 /* create a platform driver to be used by smd_tty driver
1339 * so that it can access the loopback port
1340 */
1341 loopback_tty_pdev.id = ch->type;
1342 platform_device_register(&loopback_tty_pdev);
1343 }
1344 return 0;
1345}
1346
1347static inline void notify_loopback_smd(void)
1348{
1349 unsigned long flags;
1350 struct smd_channel *ch;
1351
1352 spin_lock_irqsave(&smd_lock, flags);
1353 list_for_each_entry(ch, &smd_ch_list_loopback, ch_list) {
1354 ch->notify(ch->priv, SMD_EVENT_DATA);
1355 }
1356 spin_unlock_irqrestore(&smd_lock, flags);
1357}
1358
1359static int smd_alloc_loopback_channel(void)
1360{
1361 static struct smd_half_channel smd_loopback_ctl;
1362 static char smd_loopback_data[SMD_BUF_SIZE];
1363 struct smd_channel *ch;
1364
1365 ch = kzalloc(sizeof(struct smd_channel), GFP_KERNEL);
1366 if (ch == 0) {
1367 pr_err("%s: out of memory\n", __func__);
1368 return -1;
1369 }
1370 ch->n = SMD_LOOPBACK_CID;
1371
1372 ch->send = &smd_loopback_ctl;
1373 ch->recv = &smd_loopback_ctl;
1374 ch->send_data = smd_loopback_data;
1375 ch->recv_data = smd_loopback_data;
1376 ch->fifo_size = SMD_BUF_SIZE;
1377
1378 ch->fifo_mask = ch->fifo_size - 1;
1379 ch->type = SMD_LOOPBACK_TYPE;
1380 ch->notify_other_cpu = notify_loopback_smd;
1381
1382 ch->read = smd_stream_read;
1383 ch->write = smd_stream_write;
1384 ch->read_avail = smd_stream_read_avail;
1385 ch->write_avail = smd_stream_write_avail;
1386 ch->update_state = update_stream_state;
1387 ch->read_from_cb = smd_stream_read;
1388
1389 memset(ch->name, 0, 20);
1390 memcpy(ch->name, "local_loopback", 14);
1391
1392 ch->pdev.name = ch->name;
1393 ch->pdev.id = ch->type;
1394
1395 SMD_INFO("%s: '%s' cid=%d\n", __func__, ch->name, ch->n);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001396
1397 mutex_lock(&smd_creation_mutex);
1398 list_add(&ch->ch_list, &smd_ch_closed_list);
1399 mutex_unlock(&smd_creation_mutex);
1400
1401 platform_device_register(&ch->pdev);
Brian Swetland34f719b2009-10-30 16:22:05 -07001402 return 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001403}
1404
1405static void do_nothing_notify(void *priv, unsigned flags)
1406{
1407}
1408
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001409static void finalize_channel_close_fn(struct work_struct *work)
1410{
1411 unsigned long flags;
1412 struct smd_channel *ch;
1413 struct smd_channel *index;
1414
1415 spin_lock_irqsave(&smd_lock, flags);
1416 list_for_each_entry_safe(ch, index, &smd_ch_to_close_list, ch_list) {
1417 list_del(&ch->ch_list);
1418 spin_unlock_irqrestore(&smd_lock, flags);
1419 mutex_lock(&smd_creation_mutex);
1420 list_add(&ch->ch_list, &smd_ch_closed_list);
1421 mutex_unlock(&smd_creation_mutex);
1422 ch->notify(ch->priv, SMD_EVENT_REOPEN_READY);
1423 ch->notify = do_nothing_notify;
1424 spin_lock_irqsave(&smd_lock, flags);
1425 }
1426 spin_unlock_irqrestore(&smd_lock, flags);
1427}
1428
1429struct smd_channel *smd_get_channel(const char *name, uint32_t type)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001430{
1431 struct smd_channel *ch;
1432
1433 mutex_lock(&smd_creation_mutex);
1434 list_for_each_entry(ch, &smd_ch_closed_list, ch_list) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001435 if (!strcmp(name, ch->name) &&
1436 (type == ch->type)) {
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001437 list_del(&ch->ch_list);
1438 mutex_unlock(&smd_creation_mutex);
1439 return ch;
1440 }
1441 }
1442 mutex_unlock(&smd_creation_mutex);
1443
1444 return NULL;
1445}
1446
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001447int smd_named_open_on_edge(const char *name, uint32_t edge,
1448 smd_channel_t **_ch,
1449 void *priv, void (*notify)(void *, unsigned))
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001450{
1451 struct smd_channel *ch;
1452 unsigned long flags;
1453
1454 if (smd_initialized == 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001455 SMD_INFO("smd_open() before smd_init()\n");
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001456 return -ENODEV;
1457 }
1458
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001459 SMD_DBG("smd_open('%s', %p, %p)\n", name, priv, notify);
1460
1461 ch = smd_get_channel(name, edge);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001462 if (!ch)
1463 return -ENODEV;
1464
1465 if (notify == 0)
1466 notify = do_nothing_notify;
1467
1468 ch->notify = notify;
1469 ch->current_packet = 0;
1470 ch->last_state = SMD_SS_CLOSED;
1471 ch->priv = priv;
1472
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001473 if (edge == SMD_LOOPBACK_TYPE) {
1474 ch->last_state = SMD_SS_OPENED;
1475 ch->send->state = SMD_SS_OPENED;
1476 ch->send->fDSR = 1;
1477 ch->send->fCTS = 1;
1478 ch->send->fCD = 1;
1479 }
1480
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001481 *_ch = ch;
1482
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001483 SMD_DBG("smd_open: opening '%s'\n", ch->name);
1484
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001485 spin_lock_irqsave(&smd_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001486 if (SMD_CHANNEL_TYPE(ch->type) == SMD_APPS_MODEM)
Brian Swetland37521a32009-07-01 18:30:47 -07001487 list_add(&ch->ch_list, &smd_ch_list_modem);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001488 else if (SMD_CHANNEL_TYPE(ch->type) == SMD_APPS_QDSP)
Brian Swetland37521a32009-07-01 18:30:47 -07001489 list_add(&ch->ch_list, &smd_ch_list_dsp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001490 else if (SMD_CHANNEL_TYPE(ch->type) == SMD_APPS_DSPS)
1491 list_add(&ch->ch_list, &smd_ch_list_dsps);
1492 else if (SMD_CHANNEL_TYPE(ch->type) == SMD_APPS_WCNSS)
1493 list_add(&ch->ch_list, &smd_ch_list_wcnss);
1494 else
1495 list_add(&ch->ch_list, &smd_ch_list_loopback);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001496
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001497 SMD_DBG("%s: opening ch %d\n", __func__, ch->n);
1498
1499 if (edge != SMD_LOOPBACK_TYPE)
1500 smd_state_change(ch, ch->last_state, SMD_SS_OPENING);
1501
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001502 spin_unlock_irqrestore(&smd_lock, flags);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001503
1504 return 0;
1505}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001506EXPORT_SYMBOL(smd_named_open_on_edge);
1507
1508
1509int smd_open(const char *name, smd_channel_t **_ch,
1510 void *priv, void (*notify)(void *, unsigned))
1511{
1512 return smd_named_open_on_edge(name, SMD_APPS_MODEM, _ch, priv,
1513 notify);
1514}
1515EXPORT_SYMBOL(smd_open);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001516
1517int smd_close(smd_channel_t *ch)
1518{
1519 unsigned long flags;
1520
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001521 if (ch == 0)
1522 return -1;
1523
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001524 SMD_INFO("smd_close(%s)\n", ch->name);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001525
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001526 spin_lock_irqsave(&smd_lock, flags);
1527 list_del(&ch->ch_list);
1528 if (ch->n == SMD_LOOPBACK_CID) {
1529 ch->send->fDSR = 0;
1530 ch->send->fCTS = 0;
1531 ch->send->fCD = 0;
1532 ch->send->state = SMD_SS_CLOSED;
1533 } else
1534 ch_set_state(ch, SMD_SS_CLOSED);
1535
1536 if (ch->recv->state == SMD_SS_OPENED) {
1537 list_add(&ch->ch_list, &smd_ch_closing_list);
1538 spin_unlock_irqrestore(&smd_lock, flags);
1539 } else {
1540 spin_unlock_irqrestore(&smd_lock, flags);
1541 ch->notify = do_nothing_notify;
1542 mutex_lock(&smd_creation_mutex);
1543 list_add(&ch->ch_list, &smd_ch_closed_list);
1544 mutex_unlock(&smd_creation_mutex);
1545 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001546
1547 return 0;
1548}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001549EXPORT_SYMBOL(smd_close);
1550
1551int smd_write_start(smd_channel_t *ch, int len)
1552{
1553 int ret;
1554 unsigned hdr[5];
1555
1556 if (!ch) {
1557 pr_err("%s: Invalid channel specified\n", __func__);
1558 return -ENODEV;
1559 }
1560 if (!ch->is_pkt_ch) {
1561 pr_err("%s: non-packet channel specified\n", __func__);
1562 return -EACCES;
1563 }
1564 if (len < 1) {
1565 pr_err("%s: invalid length: %d\n", __func__, len);
1566 return -EINVAL;
1567 }
1568
1569 if (ch->pending_pkt_sz) {
1570 pr_err("%s: packet of size: %d in progress\n", __func__,
1571 ch->pending_pkt_sz);
1572 return -EBUSY;
1573 }
1574 ch->pending_pkt_sz = len;
1575
1576 if (smd_stream_write_avail(ch) < (SMD_HEADER_SIZE)) {
1577 ch->pending_pkt_sz = 0;
1578 SMD_DBG("%s: no space to write packet header\n", __func__);
1579 return -EAGAIN;
1580 }
1581
1582 hdr[0] = len;
1583 hdr[1] = hdr[2] = hdr[3] = hdr[4] = 0;
1584
1585
1586 ret = smd_stream_write(ch, hdr, sizeof(hdr), 0);
1587 if (ret < 0 || ret != sizeof(hdr)) {
1588 ch->pending_pkt_sz = 0;
1589 pr_err("%s: packet header failed to write\n", __func__);
1590 return -EPERM;
1591 }
1592 return 0;
1593}
1594EXPORT_SYMBOL(smd_write_start);
1595
1596int smd_write_segment(smd_channel_t *ch, void *data, int len, int user_buf)
1597{
1598 int bytes_written;
1599
1600 if (!ch) {
1601 pr_err("%s: Invalid channel specified\n", __func__);
1602 return -ENODEV;
1603 }
1604 if (len < 1) {
1605 pr_err("%s: invalid length: %d\n", __func__, len);
1606 return -EINVAL;
1607 }
1608
1609 if (!ch->pending_pkt_sz) {
1610 pr_err("%s: no transaction in progress\n", __func__);
1611 return -ENOEXEC;
1612 }
1613 if (ch->pending_pkt_sz - len < 0) {
1614 pr_err("%s: segment of size: %d will make packet go over "
1615 "length\n", __func__, len);
1616 return -EINVAL;
1617 }
1618
1619 bytes_written = smd_stream_write(ch, data, len, user_buf);
1620
1621 ch->pending_pkt_sz -= bytes_written;
1622
1623 return bytes_written;
1624}
1625EXPORT_SYMBOL(smd_write_segment);
1626
1627int smd_write_end(smd_channel_t *ch)
1628{
1629
1630 if (!ch) {
1631 pr_err("%s: Invalid channel specified\n", __func__);
1632 return -ENODEV;
1633 }
1634 if (ch->pending_pkt_sz) {
1635 pr_err("%s: current packet not completely written\n", __func__);
1636 return -E2BIG;
1637 }
1638
1639 return 0;
1640}
1641EXPORT_SYMBOL(smd_write_end);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001642
1643int smd_read(smd_channel_t *ch, void *data, int len)
1644{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001645 return ch->read(ch, data, len, 0);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001646}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001647EXPORT_SYMBOL(smd_read);
1648
1649int smd_read_user_buffer(smd_channel_t *ch, void *data, int len)
1650{
1651 return ch->read(ch, data, len, 1);
1652}
1653EXPORT_SYMBOL(smd_read_user_buffer);
1654
1655int smd_read_from_cb(smd_channel_t *ch, void *data, int len)
1656{
1657 return ch->read_from_cb(ch, data, len, 0);
1658}
1659EXPORT_SYMBOL(smd_read_from_cb);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001660
1661int smd_write(smd_channel_t *ch, const void *data, int len)
1662{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001663 return ch->pending_pkt_sz ? -EBUSY : ch->write(ch, data, len, 0);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001664}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001665EXPORT_SYMBOL(smd_write);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001666
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001667int smd_write_user_buffer(smd_channel_t *ch, const void *data, int len)
Brian Swetland636eb9c2009-12-07 15:28:08 -08001668{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001669 return ch->pending_pkt_sz ? -EBUSY : ch->write(ch, data, len, 1);
Brian Swetland636eb9c2009-12-07 15:28:08 -08001670}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001671EXPORT_SYMBOL(smd_write_user_buffer);
Brian Swetland636eb9c2009-12-07 15:28:08 -08001672
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001673int smd_read_avail(smd_channel_t *ch)
1674{
1675 return ch->read_avail(ch);
1676}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001677EXPORT_SYMBOL(smd_read_avail);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001678
1679int smd_write_avail(smd_channel_t *ch)
1680{
1681 return ch->write_avail(ch);
1682}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001683EXPORT_SYMBOL(smd_write_avail);
1684
1685void smd_enable_read_intr(smd_channel_t *ch)
1686{
1687 if (ch)
1688 ch->send->fBLOCKREADINTR = 0;
1689}
1690EXPORT_SYMBOL(smd_enable_read_intr);
1691
1692void smd_disable_read_intr(smd_channel_t *ch)
1693{
1694 if (ch)
1695 ch->send->fBLOCKREADINTR = 1;
1696}
1697EXPORT_SYMBOL(smd_disable_read_intr);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001698
1699int smd_wait_until_readable(smd_channel_t *ch, int bytes)
1700{
1701 return -1;
1702}
1703
1704int smd_wait_until_writable(smd_channel_t *ch, int bytes)
1705{
1706 return -1;
1707}
1708
1709int smd_cur_packet_size(smd_channel_t *ch)
1710{
1711 return ch->current_packet;
1712}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001713EXPORT_SYMBOL(smd_cur_packet_size);
1714
1715int smd_tiocmget(smd_channel_t *ch)
1716{
1717 return (ch->recv->fDSR ? TIOCM_DSR : 0) |
1718 (ch->recv->fCTS ? TIOCM_CTS : 0) |
1719 (ch->recv->fCD ? TIOCM_CD : 0) |
1720 (ch->recv->fRI ? TIOCM_RI : 0) |
1721 (ch->send->fCTS ? TIOCM_RTS : 0) |
1722 (ch->send->fDSR ? TIOCM_DTR : 0);
1723}
1724EXPORT_SYMBOL(smd_tiocmget);
1725
Vamsi Krishnacb12a102011-08-17 15:18:26 -07001726/* this api will be called while holding smd_lock */
1727int
1728smd_tiocmset_from_cb(smd_channel_t *ch, unsigned int set, unsigned int clear)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001729{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001730 if (set & TIOCM_DTR)
1731 ch->send->fDSR = 1;
1732
1733 if (set & TIOCM_RTS)
1734 ch->send->fCTS = 1;
1735
1736 if (clear & TIOCM_DTR)
1737 ch->send->fDSR = 0;
1738
1739 if (clear & TIOCM_RTS)
1740 ch->send->fCTS = 0;
1741
1742 ch->send->fSTATE = 1;
1743 barrier();
1744 ch->notify_other_cpu();
Vamsi Krishnacb12a102011-08-17 15:18:26 -07001745
1746 return 0;
1747}
1748EXPORT_SYMBOL(smd_tiocmset_from_cb);
1749
1750int smd_tiocmset(smd_channel_t *ch, unsigned int set, unsigned int clear)
1751{
1752 unsigned long flags;
1753
1754 spin_lock_irqsave(&smd_lock, flags);
1755 smd_tiocmset_from_cb(ch, set, clear);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001756 spin_unlock_irqrestore(&smd_lock, flags);
1757
1758 return 0;
1759}
1760EXPORT_SYMBOL(smd_tiocmset);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001761
1762
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001763/* -------------------------------------------------------------------------- */
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001764
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001765/* smem_alloc returns the pointer to smem item if it is already allocated.
1766 * Otherwise, it returns NULL.
1767 */
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001768void *smem_alloc(unsigned id, unsigned size)
1769{
1770 return smem_find(id, size);
1771}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001772EXPORT_SYMBOL(smem_alloc);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001773
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001774/* smem_alloc2 returns the pointer to smem item. If it is not allocated,
1775 * it allocates it and then returns the pointer to it.
1776 */
Angshuman Sarkar4eade0d2011-08-17 14:06:23 +05301777void *smem_alloc2(unsigned id, unsigned size_in)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001778{
1779 struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE;
1780 struct smem_heap_entry *toc = shared->heap_toc;
1781 unsigned long flags;
1782 void *ret = NULL;
1783
1784 if (!shared->heap_info.initialized) {
1785 pr_err("%s: smem heap info not initialized\n", __func__);
1786 return NULL;
1787 }
1788
1789 if (id >= SMEM_NUM_ITEMS)
1790 return NULL;
1791
1792 size_in = ALIGN(size_in, 8);
1793 remote_spin_lock_irqsave(&remote_spinlock, flags);
1794 if (toc[id].allocated) {
1795 SMD_DBG("%s: %u already allocated\n", __func__, id);
1796 if (size_in != toc[id].size)
1797 pr_err("%s: wrong size %u (expected %u)\n",
1798 __func__, toc[id].size, size_in);
1799 else
1800 ret = (void *)(MSM_SHARED_RAM_BASE + toc[id].offset);
1801 } else if (id > SMEM_FIXED_ITEM_LAST) {
1802 SMD_DBG("%s: allocating %u\n", __func__, id);
1803 if (shared->heap_info.heap_remaining >= size_in) {
1804 toc[id].offset = shared->heap_info.free_offset;
1805 toc[id].size = size_in;
1806 wmb();
1807 toc[id].allocated = 1;
1808
1809 shared->heap_info.free_offset += size_in;
1810 shared->heap_info.heap_remaining -= size_in;
1811 ret = (void *)(MSM_SHARED_RAM_BASE + toc[id].offset);
1812 } else
1813 pr_err("%s: not enough memory %u (required %u)\n",
1814 __func__, shared->heap_info.heap_remaining,
1815 size_in);
1816 }
1817 wmb();
1818 remote_spin_unlock_irqrestore(&remote_spinlock, flags);
1819 return ret;
1820}
Angshuman Sarkar4eade0d2011-08-17 14:06:23 +05301821EXPORT_SYMBOL(smem_alloc2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001822
1823void *smem_get_entry(unsigned id, unsigned *size)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001824{
1825 struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE;
1826 struct smem_heap_entry *toc = shared->heap_toc;
Angshuman Sarkar7ee0dca2011-08-22 21:37:34 +05301827 int use_spinlocks = spinlocks_initialized;
1828 void *ret = 0;
1829 unsigned long flags = 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001830
1831 if (id >= SMEM_NUM_ITEMS)
Angshuman Sarkar7ee0dca2011-08-22 21:37:34 +05301832 return ret;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001833
Angshuman Sarkar7ee0dca2011-08-22 21:37:34 +05301834 if (use_spinlocks)
1835 remote_spin_lock_irqsave(&remote_spinlock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001836 /* toc is in device memory and cannot be speculatively accessed */
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001837 if (toc[id].allocated) {
1838 *size = toc[id].size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001839 barrier();
Angshuman Sarkar7ee0dca2011-08-22 21:37:34 +05301840 ret = (void *) (MSM_SHARED_RAM_BASE + toc[id].offset);
Brian Swetland5b0f5a32009-04-26 18:38:49 -07001841 } else {
1842 *size = 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001843 }
Angshuman Sarkar7ee0dca2011-08-22 21:37:34 +05301844 if (use_spinlocks)
1845 remote_spin_unlock_irqrestore(&remote_spinlock, flags);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001846
Angshuman Sarkar7ee0dca2011-08-22 21:37:34 +05301847 return ret;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001848}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001849EXPORT_SYMBOL(smem_get_entry);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001850
1851void *smem_find(unsigned id, unsigned size_in)
1852{
1853 unsigned size;
1854 void *ptr;
1855
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001856 ptr = smem_get_entry(id, &size);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001857 if (!ptr)
1858 return 0;
1859
1860 size_in = ALIGN(size_in, 8);
1861 if (size_in != size) {
1862 pr_err("smem_find(%d, %d): wrong size %d\n",
1863 id, size_in, size);
1864 return 0;
1865 }
1866
1867 return ptr;
1868}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001869EXPORT_SYMBOL(smem_find);
1870
1871static int smsm_cb_init(void)
1872{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001873 struct smsm_state_info *state_info;
1874 int n;
1875 int ret = 0;
1876
1877 smsm_states = kmalloc(sizeof(struct smsm_state_info)*SMSM_NUM_ENTRIES,
1878 GFP_KERNEL);
1879
1880 if (!smsm_states) {
1881 pr_err("%s: SMSM init failed\n", __func__);
1882 return -ENOMEM;
1883 }
1884
Eric Holmbergc8002902011-09-16 13:55:57 -06001885 mutex_lock(&smsm_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001886 for (n = 0; n < SMSM_NUM_ENTRIES; n++) {
1887 state_info = &smsm_states[n];
1888 state_info->last_value = __raw_readl(SMSM_STATE_ADDR(n));
1889 INIT_LIST_HEAD(&state_info->callbacks);
1890 }
Eric Holmbergc8002902011-09-16 13:55:57 -06001891 mutex_unlock(&smsm_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001892
1893 return ret;
1894}
1895
1896static int smsm_init(void)
1897{
1898 struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE;
1899 int i;
1900 struct smsm_size_info_type *smsm_size_info;
1901
1902 i = remote_spin_lock_init(&remote_spinlock, SMEM_SPINLOCK_SMEM_ALLOC);
1903 if (i) {
1904 pr_err("%s: remote spinlock init failed %d\n", __func__, i);
1905 return i;
1906 }
Angshuman Sarkar7ee0dca2011-08-22 21:37:34 +05301907 spinlocks_initialized = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001908
1909 smsm_size_info = smem_alloc(SMEM_SMSM_SIZE_INFO,
1910 sizeof(struct smsm_size_info_type));
1911 if (smsm_size_info) {
1912 SMSM_NUM_ENTRIES = smsm_size_info->num_entries;
1913 SMSM_NUM_HOSTS = smsm_size_info->num_hosts;
1914 }
1915
1916 if (!smsm_info.state) {
1917 smsm_info.state = smem_alloc2(ID_SHARED_STATE,
1918 SMSM_NUM_ENTRIES *
1919 sizeof(uint32_t));
1920
1921 if (smsm_info.state) {
1922 __raw_writel(0, SMSM_STATE_ADDR(SMSM_APPS_STATE));
1923 if ((shared->version[VERSION_MODEM] >> 16) >= 0xB)
1924 __raw_writel(0, \
1925 SMSM_STATE_ADDR(SMSM_APPS_DEM_I));
1926 }
1927 }
1928
1929 if (!smsm_info.intr_mask) {
1930 smsm_info.intr_mask = smem_alloc2(SMEM_SMSM_CPU_INTR_MASK,
1931 SMSM_NUM_ENTRIES *
1932 SMSM_NUM_HOSTS *
1933 sizeof(uint32_t));
1934
1935 if (smsm_info.intr_mask)
1936 for (i = 0; i < SMSM_NUM_ENTRIES; i++)
1937 __raw_writel(0xffffffff,
1938 SMSM_INTR_MASK_ADDR(i, SMSM_APPS));
1939 }
1940
1941 if (!smsm_info.intr_mux)
1942 smsm_info.intr_mux = smem_alloc2(SMEM_SMD_SMSM_INTR_MUX,
1943 SMSM_NUM_INTR_MUX *
1944 sizeof(uint32_t));
1945
1946 i = smsm_cb_init();
1947 if (i)
1948 return i;
1949
1950 wmb();
1951 return 0;
1952}
1953
1954void smsm_reset_modem(unsigned mode)
1955{
1956 if (mode == SMSM_SYSTEM_DOWNLOAD) {
1957 mode = SMSM_RESET | SMSM_SYSTEM_DOWNLOAD;
1958 } else if (mode == SMSM_MODEM_WAIT) {
1959 mode = SMSM_RESET | SMSM_MODEM_WAIT;
1960 } else { /* reset_mode is SMSM_RESET or default */
1961 mode = SMSM_RESET;
1962 }
1963
1964 smsm_change_state(SMSM_APPS_STATE, mode, mode);
1965}
1966EXPORT_SYMBOL(smsm_reset_modem);
1967
1968void smsm_reset_modem_cont(void)
1969{
1970 unsigned long flags;
1971 uint32_t state;
1972
1973 if (!smsm_info.state)
1974 return;
1975
1976 spin_lock_irqsave(&smem_lock, flags);
1977 state = __raw_readl(SMSM_STATE_ADDR(SMSM_APPS_STATE)) \
1978 & ~SMSM_MODEM_WAIT;
1979 __raw_writel(state, SMSM_STATE_ADDR(SMSM_APPS_STATE));
1980 wmb();
1981 spin_unlock_irqrestore(&smem_lock, flags);
1982}
1983EXPORT_SYMBOL(smsm_reset_modem_cont);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07001984
1985static irqreturn_t smsm_irq_handler(int irq, void *data)
1986{
1987 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001988
1989#if !defined(CONFIG_ARCH_MSM8X60)
1990 uint32_t mux_val;
1991 static uint32_t prev_smem_q6_apps_smsm;
1992
1993 if (irq == INT_ADSP_A11_SMSM) {
1994 if (!smsm_info.intr_mux)
1995 return IRQ_HANDLED;
1996 mux_val = __raw_readl(SMSM_INTR_MUX_ADDR(SMEM_Q6_APPS_SMSM));
1997 if (mux_val != prev_smem_q6_apps_smsm)
1998 prev_smem_q6_apps_smsm = mux_val;
1999 return IRQ_HANDLED;
2000 }
2001#else
2002 if (irq == INT_ADSP_A11_SMSM)
2003 return IRQ_HANDLED;
2004#endif
2005
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002006
2007 spin_lock_irqsave(&smem_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002008 if (!smsm_info.state) {
2009 SMSM_INFO("<SM NO STATE>\n");
2010 } else {
2011 unsigned old_apps, apps;
2012 unsigned modm = __raw_readl(SMSM_STATE_ADDR(SMSM_MODEM_STATE));
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002013
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002014 old_apps = apps = __raw_readl(SMSM_STATE_ADDR(SMSM_APPS_STATE));
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002015
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002016 SMSM_DBG("<SM %08x %08x>\n", apps, modm);
2017 if (apps & SMSM_RESET) {
2018 /* If we get an interrupt and the apps SMSM_RESET
2019 bit is already set, the modem is acking the
2020 app's reset ack. */
Angshuman Sarkaread67bd2011-09-21 20:13:12 +05302021 if (!cpu_is_msm8960())
2022 apps &= ~SMSM_RESET;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002023 /* Issue a fake irq to handle any
2024 * smd state changes during reset
2025 */
2026 smd_fake_irq_handler(0);
Brian Swetland5b0f5a32009-04-26 18:38:49 -07002027
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002028 /* queue modem restart notify chain */
2029 modem_queue_start_reset_notify();
2030
2031 } else if (modm & SMSM_RESET) {
Angshuman Sarkaread67bd2011-09-21 20:13:12 +05302032 if (!cpu_is_msm8960())
2033 apps |= SMSM_RESET;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002034
2035 pr_err("\nSMSM: Modem SMSM state changed to SMSM_RESET.");
2036 modem_queue_start_reset_notify();
2037
2038 } else if (modm & SMSM_INIT) {
2039 if (!(apps & SMSM_INIT)) {
2040 apps |= SMSM_INIT;
2041 modem_queue_smsm_init_notify();
2042 }
2043
2044 if (modm & SMSM_SMDINIT)
2045 apps |= SMSM_SMDINIT;
2046 if ((apps & (SMSM_INIT | SMSM_SMDINIT | SMSM_RPCINIT)) ==
2047 (SMSM_INIT | SMSM_SMDINIT | SMSM_RPCINIT))
2048 apps |= SMSM_RUN;
2049 } else if (modm & SMSM_SYSTEM_DOWNLOAD) {
2050 pr_err("\nSMSM: Modem SMSM state changed to SMSM_SYSTEM_DOWNLOAD.");
2051 modem_queue_start_reset_notify();
2052 }
2053
2054 if (old_apps != apps) {
2055 SMSM_DBG("<SM %08x NOTIFY>\n", apps);
2056 __raw_writel(apps, SMSM_STATE_ADDR(SMSM_APPS_STATE));
2057 do_smd_probe();
2058 notify_other_smsm(SMSM_APPS_STATE, (old_apps ^ apps));
2059 }
2060
2061 schedule_work(&smsm_cb_work);
2062 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002063 spin_unlock_irqrestore(&smem_lock, flags);
2064 return IRQ_HANDLED;
2065}
2066
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002067int smsm_change_intr_mask(uint32_t smsm_entry,
2068 uint32_t clear_mask, uint32_t set_mask)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002069{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002070 uint32_t old_mask, new_mask;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002071 unsigned long flags;
Brian Swetland5b0f5a32009-04-26 18:38:49 -07002072
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002073 if (smsm_entry >= SMSM_NUM_ENTRIES) {
2074 pr_err("smsm_change_state: Invalid entry %d\n",
2075 smsm_entry);
2076 return -EINVAL;
2077 }
2078
2079 if (!smsm_info.intr_mask) {
2080 pr_err("smsm_change_intr_mask <SM NO STATE>\n");
Brian Swetland5b0f5a32009-04-26 18:38:49 -07002081 return -EIO;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002082 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002083
2084 spin_lock_irqsave(&smem_lock, flags);
2085
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002086 old_mask = __raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_APPS));
2087 new_mask = (old_mask & ~clear_mask) | set_mask;
2088 __raw_writel(new_mask, SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_APPS));
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002089
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002090 wmb();
2091 spin_unlock_irqrestore(&smem_lock, flags);
Brian Swetland5b0f5a32009-04-26 18:38:49 -07002092
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002093 return 0;
2094}
2095EXPORT_SYMBOL(smsm_change_intr_mask);
2096
2097int smsm_get_intr_mask(uint32_t smsm_entry, uint32_t *intr_mask)
2098{
2099 if (smsm_entry >= SMSM_NUM_ENTRIES) {
2100 pr_err("smsm_change_state: Invalid entry %d\n",
2101 smsm_entry);
2102 return -EINVAL;
2103 }
2104
2105 if (!smsm_info.intr_mask) {
2106 pr_err("smsm_change_intr_mask <SM NO STATE>\n");
2107 return -EIO;
2108 }
2109
2110 *intr_mask = __raw_readl(SMSM_INTR_MASK_ADDR(smsm_entry, SMSM_APPS));
2111 return 0;
2112}
2113EXPORT_SYMBOL(smsm_get_intr_mask);
2114
2115int smsm_change_state(uint32_t smsm_entry,
2116 uint32_t clear_mask, uint32_t set_mask)
2117{
2118 unsigned long flags;
2119 uint32_t old_state, new_state;
2120
2121 if (smsm_entry >= SMSM_NUM_ENTRIES) {
2122 pr_err("smsm_change_state: Invalid entry %d",
2123 smsm_entry);
2124 return -EINVAL;
2125 }
2126
2127 if (!smsm_info.state) {
2128 pr_err("smsm_change_state <SM NO STATE>\n");
2129 return -EIO;
2130 }
2131 spin_lock_irqsave(&smem_lock, flags);
2132
2133 old_state = __raw_readl(SMSM_STATE_ADDR(smsm_entry));
2134 new_state = (old_state & ~clear_mask) | set_mask;
2135 __raw_writel(new_state, SMSM_STATE_ADDR(smsm_entry));
2136 SMSM_DBG("smsm_change_state %x\n", new_state);
2137 notify_other_smsm(SMSM_APPS_STATE, (old_state ^ new_state));
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002138
2139 spin_unlock_irqrestore(&smem_lock, flags);
2140
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002141 return 0;
2142}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002143EXPORT_SYMBOL(smsm_change_state);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002144
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002145uint32_t smsm_get_state(uint32_t smsm_entry)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002146{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002147 uint32_t rv = 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002148
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002149 /* needs interface change to return error code */
2150 if (smsm_entry >= SMSM_NUM_ENTRIES) {
2151 pr_err("smsm_change_state: Invalid entry %d",
2152 smsm_entry);
2153 return 0;
2154 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002155
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002156 if (!smsm_info.state) {
2157 pr_err("smsm_get_state <SM NO STATE>\n");
2158 } else {
2159 rv = __raw_readl(SMSM_STATE_ADDR(smsm_entry));
2160 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002161
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002162 return rv;
2163}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002164EXPORT_SYMBOL(smsm_get_state);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002165
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002166/**
2167 * Performs SMSM callback client notifiction.
2168 */
2169void notify_smsm_cb_clients_worker(struct work_struct *work)
Arve Hjønnevågec9d3d12009-06-16 14:48:21 -07002170{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002171 struct smsm_state_cb_info *cb_info;
2172 struct smsm_state_info *state_info;
2173 int n;
2174 uint32_t new_state;
2175 uint32_t state_changes;
Brian Swetland03e00cd2009-07-01 17:58:37 -07002176
Eric Holmbergc8002902011-09-16 13:55:57 -06002177 mutex_lock(&smsm_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002178
2179 if (!smsm_states) {
2180 /* smsm not yet initialized */
Eric Holmbergc8002902011-09-16 13:55:57 -06002181 mutex_unlock(&smsm_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002182 return;
Arve Hjønnevågec9d3d12009-06-16 14:48:21 -07002183 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002184
2185 for (n = 0; n < SMSM_NUM_ENTRIES; n++) {
2186 state_info = &smsm_states[n];
2187 new_state = __raw_readl(SMSM_STATE_ADDR(n));
2188
2189 if (new_state != state_info->last_value) {
2190 state_changes = state_info->last_value ^ new_state;
2191
2192 list_for_each_entry(cb_info,
2193 &state_info->callbacks, cb_list) {
2194
2195 if (cb_info->mask & state_changes)
2196 cb_info->notify(cb_info->data,
2197 state_info->last_value,
2198 new_state);
2199 }
2200 state_info->last_value = new_state;
2201 }
2202 }
2203
Eric Holmbergc8002902011-09-16 13:55:57 -06002204 mutex_unlock(&smsm_lock);
Arve Hjønnevågec9d3d12009-06-16 14:48:21 -07002205}
2206
Arve Hjønnevågec9d3d12009-06-16 14:48:21 -07002207
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002208/**
2209 * Registers callback for SMSM state notifications when the specified
2210 * bits change.
2211 *
2212 * @smsm_entry Processor entry to deregister
2213 * @mask Bits to deregister (if result is 0, callback is removed)
2214 * @notify Notification function to deregister
2215 * @data Opaque data passed in to callback
2216 *
2217 * @returns Status code
2218 * <0 error code
2219 * 0 inserted new entry
2220 * 1 updated mask of existing entry
2221 */
2222int smsm_state_cb_register(uint32_t smsm_entry, uint32_t mask,
2223 void (*notify)(void *, uint32_t, uint32_t), void *data)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002224{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002225 struct smsm_state_cb_info *cb_info;
2226 struct smsm_state_cb_info *cb_found = 0;
2227 int ret = 0;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002228
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002229 if (smsm_entry >= SMSM_NUM_ENTRIES)
2230 return -EINVAL;
2231
Eric Holmbergc8002902011-09-16 13:55:57 -06002232 mutex_lock(&smsm_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002233
2234 if (!smsm_states) {
2235 /* smsm not yet initialized */
2236 ret = -ENODEV;
2237 goto cleanup;
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002238 }
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002239
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002240 list_for_each_entry(cb_info,
2241 &smsm_states[smsm_entry].callbacks, cb_list) {
2242 if ((cb_info->notify == notify) &&
2243 (cb_info->data == data)) {
2244 cb_info->mask |= mask;
2245 cb_found = cb_info;
2246 ret = 1;
Brian Swetland5b0f5a32009-04-26 18:38:49 -07002247 break;
2248 }
2249 }
2250
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002251 if (!cb_found) {
2252 cb_info = kmalloc(sizeof(struct smsm_state_cb_info),
2253 GFP_ATOMIC);
2254 if (!cb_info) {
2255 ret = -ENOMEM;
2256 goto cleanup;
2257 }
2258
2259 cb_info->mask = mask;
2260 cb_info->notify = notify;
2261 cb_info->data = data;
2262 INIT_LIST_HEAD(&cb_info->cb_list);
2263 list_add_tail(&cb_info->cb_list,
2264 &smsm_states[smsm_entry].callbacks);
2265 }
2266
2267cleanup:
Eric Holmbergc8002902011-09-16 13:55:57 -06002268 mutex_unlock(&smsm_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002269 return ret;
2270}
2271EXPORT_SYMBOL(smsm_state_cb_register);
2272
2273
2274/**
2275 * Deregisters for SMSM state notifications for the specified bits.
2276 *
2277 * @smsm_entry Processor entry to deregister
2278 * @mask Bits to deregister (if result is 0, callback is removed)
2279 * @notify Notification function to deregister
2280 * @data Opaque data passed in to callback
2281 *
2282 * @returns Status code
2283 * <0 error code
2284 * 0 not found
2285 * 1 updated mask
2286 * 2 removed callback
2287 */
2288int smsm_state_cb_deregister(uint32_t smsm_entry, uint32_t mask,
2289 void (*notify)(void *, uint32_t, uint32_t), void *data)
2290{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002291 struct smsm_state_cb_info *cb_info;
2292 int ret = 0;
2293
2294 if (smsm_entry >= SMSM_NUM_ENTRIES)
2295 return -EINVAL;
2296
Eric Holmbergc8002902011-09-16 13:55:57 -06002297 mutex_lock(&smsm_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002298
2299 if (!smsm_states) {
2300 /* smsm not yet initialized */
Eric Holmbergc8002902011-09-16 13:55:57 -06002301 mutex_unlock(&smsm_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002302 return -ENODEV;
2303 }
2304
2305 list_for_each_entry(cb_info,
2306 &smsm_states[smsm_entry].callbacks, cb_list) {
2307 if ((cb_info->notify == notify) &&
2308 (cb_info->data == data)) {
2309 cb_info->mask &= ~mask;
2310 ret = 1;
2311 if (!cb_info->mask) {
2312 /* no mask bits set, remove callback */
2313 list_del(&cb_info->cb_list);
2314 kfree(cb_info);
2315 ret = 2;
2316 }
2317 break;
2318 }
2319 }
2320
Eric Holmbergc8002902011-09-16 13:55:57 -06002321 mutex_unlock(&smsm_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002322 return ret;
2323}
2324EXPORT_SYMBOL(smsm_state_cb_deregister);
2325
2326
2327int smd_core_init(void)
2328{
2329 int r;
2330 unsigned long flags = IRQF_TRIGGER_RISING;
2331 SMD_INFO("smd_core_init()\n");
Brian Swetland5b0f5a32009-04-26 18:38:49 -07002332
Brian Swetland37521a32009-07-01 18:30:47 -07002333 r = request_irq(INT_A9_M2A_0, smd_modem_irq_handler,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002334 flags, "smd_dev", 0);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002335 if (r < 0)
2336 return r;
2337 r = enable_irq_wake(INT_A9_M2A_0);
2338 if (r < 0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002339 pr_err("smd_core_init: "
2340 "enable_irq_wake failed for INT_A9_M2A_0\n");
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002341
2342 r = request_irq(INT_A9_M2A_5, smsm_irq_handler,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002343 flags, "smsm_dev", 0);
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002344 if (r < 0) {
2345 free_irq(INT_A9_M2A_0, 0);
2346 return r;
2347 }
2348 r = enable_irq_wake(INT_A9_M2A_5);
2349 if (r < 0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002350 pr_err("smd_core_init: "
2351 "enable_irq_wake failed for INT_A9_M2A_5\n");
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002352
Brian Swetland37521a32009-07-01 18:30:47 -07002353#if defined(CONFIG_QDSP6)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002354#if (INT_ADSP_A11 == INT_ADSP_A11_SMSM)
2355 flags |= IRQF_SHARED;
2356#endif
Brian Swetland37521a32009-07-01 18:30:47 -07002357 r = request_irq(INT_ADSP_A11, smd_dsp_irq_handler,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002358 flags, "smd_dev", smd_dsp_irq_handler);
Brian Swetland37521a32009-07-01 18:30:47 -07002359 if (r < 0) {
2360 free_irq(INT_A9_M2A_0, 0);
2361 free_irq(INT_A9_M2A_5, 0);
2362 return r;
2363 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002364
2365 r = request_irq(INT_ADSP_A11_SMSM, smsm_irq_handler,
2366 flags, "smsm_dev", smsm_irq_handler);
2367 if (r < 0) {
2368 free_irq(INT_A9_M2A_0, 0);
2369 free_irq(INT_A9_M2A_5, 0);
2370 free_irq(INT_ADSP_A11, smd_dsp_irq_handler);
2371 return r;
2372 }
2373
2374 r = enable_irq_wake(INT_ADSP_A11);
2375 if (r < 0)
2376 pr_err("smd_core_init: "
2377 "enable_irq_wake failed for INT_ADSP_A11\n");
2378
2379#if (INT_ADSP_A11 != INT_ADSP_A11_SMSM)
2380 r = enable_irq_wake(INT_ADSP_A11_SMSM);
2381 if (r < 0)
2382 pr_err("smd_core_init: enable_irq_wake "
2383 "failed for INT_ADSP_A11_SMSM\n");
2384#endif
2385 flags &= ~IRQF_SHARED;
Brian Swetland37521a32009-07-01 18:30:47 -07002386#endif
2387
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002388#if defined(CONFIG_DSPS)
2389 r = request_irq(INT_DSPS_A11, smd_dsps_irq_handler,
2390 flags, "smd_dev", smd_dsps_irq_handler);
2391 if (r < 0) {
2392 free_irq(INT_A9_M2A_0, 0);
2393 free_irq(INT_A9_M2A_5, 0);
2394 free_irq(INT_ADSP_A11, smd_dsp_irq_handler);
2395 free_irq(INT_ADSP_A11_SMSM, smsm_irq_handler);
2396 return r;
2397 }
Brian Swetland5b0f5a32009-04-26 18:38:49 -07002398
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002399 r = enable_irq_wake(INT_DSPS_A11);
2400 if (r < 0)
2401 pr_err("smd_core_init: "
2402 "enable_irq_wake failed for INT_ADSP_A11\n");
Arve Hjønnevågec9d3d12009-06-16 14:48:21 -07002403#endif
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002404
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002405#if defined(CONFIG_WCNSS)
2406 r = request_irq(INT_WCNSS_A11, smd_wcnss_irq_handler,
2407 flags, "smd_dev", smd_wcnss_irq_handler);
2408 if (r < 0) {
2409 free_irq(INT_A9_M2A_0, 0);
2410 free_irq(INT_A9_M2A_5, 0);
2411 free_irq(INT_ADSP_A11, smd_dsp_irq_handler);
2412 free_irq(INT_ADSP_A11_SMSM, smsm_irq_handler);
2413 free_irq(INT_DSPS_A11, smd_dsps_irq_handler);
2414 return r;
2415 }
2416
2417 r = enable_irq_wake(INT_WCNSS_A11);
2418 if (r < 0)
2419 pr_err("smd_core_init: "
2420 "enable_irq_wake failed for INT_WCNSS_A11\n");
2421
2422 r = request_irq(INT_WCNSS_A11_SMSM, smsm_irq_handler,
2423 flags, "smsm_dev", smsm_irq_handler);
2424 if (r < 0) {
2425 free_irq(INT_A9_M2A_0, 0);
2426 free_irq(INT_A9_M2A_5, 0);
2427 free_irq(INT_ADSP_A11, smd_dsp_irq_handler);
2428 free_irq(INT_ADSP_A11_SMSM, smsm_irq_handler);
2429 free_irq(INT_DSPS_A11, smd_dsps_irq_handler);
2430 free_irq(INT_WCNSS_A11, smd_wcnss_irq_handler);
2431 return r;
2432 }
2433
2434 r = enable_irq_wake(INT_WCNSS_A11_SMSM);
2435 if (r < 0)
2436 pr_err("smd_core_init: "
2437 "enable_irq_wake failed for INT_WCNSS_A11_SMSM\n");
2438#endif
2439
Jeff Hugo6a8057c2011-08-16 13:47:12 -06002440#if defined(CONFIG_DSPS_SMSM)
2441 r = request_irq(INT_DSPS_A11_SMSM, smsm_irq_handler,
2442 flags, "smsm_dev", smsm_irq_handler);
2443 if (r < 0) {
2444 free_irq(INT_A9_M2A_0, 0);
2445 free_irq(INT_A9_M2A_5, 0);
2446 free_irq(INT_ADSP_A11, smd_dsp_irq_handler);
2447 free_irq(INT_ADSP_A11_SMSM, smsm_irq_handler);
2448 free_irq(INT_DSPS_A11, smd_dsps_irq_handler);
2449 free_irq(INT_WCNSS_A11, smd_wcnss_irq_handler);
2450 free_irq(INT_WCNSS_A11_SMSM, smsm_irq_handler);
2451 return r;
2452 }
2453
2454 r = enable_irq_wake(INT_DSPS_A11_SMSM);
2455 if (r < 0)
2456 pr_err("smd_core_init: "
2457 "enable_irq_wake failed for INT_DSPS_A11_SMSM\n");
2458#endif
2459
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002460 /* we may have missed a signal while booting -- fake
2461 * an interrupt to make sure we process any existing
2462 * state
2463 */
2464 smsm_irq_handler(0, 0);
2465
2466 SMD_INFO("smd_core_init() done\n");
2467
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002468 return 0;
2469}
2470
Gregory Bean4416e9e2010-07-28 10:22:12 -07002471static int __devinit msm_smd_probe(struct platform_device *pdev)
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002472{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002473 SMD_INFO("smd probe\n");
Daniel Walker0aec66d2010-03-18 12:31:08 -07002474
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002475 INIT_WORK(&probe_work, smd_channel_probe_worker);
2476
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002477 channel_close_wq = create_singlethread_workqueue("smd_channel_close");
2478 if (IS_ERR(channel_close_wq)) {
2479 pr_err("%s: create_singlethread_workqueue ENOMEM\n", __func__);
2480 return -ENOMEM;
2481 }
2482
2483 if (smsm_init()) {
2484 pr_err("smsm_init() failed\n");
2485 return -1;
2486 }
2487
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002488 if (smd_core_init()) {
2489 pr_err("smd_core_init() failed\n");
2490 return -1;
2491 }
2492
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002493 smd_initialized = 1;
2494
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002495 smd_alloc_loopback_channel();
2496
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002497 return 0;
2498}
2499
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002500static int restart_notifier_cb(struct notifier_block *this,
2501 unsigned long code,
2502 void *data);
2503
2504static struct restart_notifier_block restart_notifiers[] = {
2505 {SMSM_MODEM, "modem", .nb.notifier_call = restart_notifier_cb},
2506 {SMSM_Q6, "lpass", .nb.notifier_call = restart_notifier_cb},
2507};
2508
2509static int restart_notifier_cb(struct notifier_block *this,
2510 unsigned long code,
2511 void *data)
2512{
2513 if (code == SUBSYS_AFTER_SHUTDOWN) {
2514 struct restart_notifier_block *notifier;
2515
2516 notifier = container_of(this,
2517 struct restart_notifier_block, nb);
2518 SMD_INFO("%s: ssrestart for processor %d ('%s')\n",
2519 __func__, notifier->processor,
2520 notifier->name);
2521
2522 smd_channel_reset(notifier->processor);
2523 }
2524
2525 return NOTIFY_DONE;
2526}
2527
2528static __init int modem_restart_late_init(void)
2529{
2530 int i;
2531 void *handle;
2532 struct restart_notifier_block *nb;
2533
2534 for (i = 0; i < ARRAY_SIZE(restart_notifiers); i++) {
2535 nb = &restart_notifiers[i];
2536 handle = subsys_notif_register_notifier(nb->name, &nb->nb);
2537 SMD_DBG("%s: registering notif for '%s', handle=%p\n",
2538 __func__, nb->name, handle);
2539 }
2540 return 0;
2541}
2542late_initcall(modem_restart_late_init);
2543
Brian Swetland2eb44eb2008-09-29 16:00:48 -07002544static struct platform_driver msm_smd_driver = {
2545 .probe = msm_smd_probe,
2546 .driver = {
2547 .name = MODULE_NAME,
2548 .owner = THIS_MODULE,
2549 },
2550};
2551
2552static int __init msm_smd_init(void)
2553{
2554 return platform_driver_register(&msm_smd_driver);
2555}
2556
2557module_init(msm_smd_init);
2558
2559MODULE_DESCRIPTION("MSM Shared Memory Core");
2560MODULE_AUTHOR("Brian Swetland <swetland@google.com>");
2561MODULE_LICENSE("GPL");