12eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland/* arch/arm/mach-msm/smd.c
22eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland *
32eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland * Copyright (C) 2007 Google, Inc.
42eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland * Author: Brian Swetland <swetland@google.com>
52eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland *
62eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland * This software is licensed under the terms of the GNU General Public
72eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland * License version 2, as published by the Free Software Foundation, and
82eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland * may be copied, distributed, and modified under those terms.
92eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland *
102eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland * This program is distributed in the hope that it will be useful,
112eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland * but WITHOUT ANY WARRANTY; without even the implied warranty of
122eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
132eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland * GNU General Public License for more details.
142eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland *
152eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland */
162eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
179be58f317d79278afc23186d861ada774cf386eaDavid Brown#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
189be58f317d79278afc23186d861ada774cf386eaDavid Brown
192eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland#include <linux/platform_device.h>
202eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland#include <linux/module.h>
212eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland#include <linux/fs.h>
222eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland#include <linux/cdev.h>
232eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland#include <linux/device.h>
242eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland#include <linux/wait.h>
252eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland#include <linux/interrupt.h>
262eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland#include <linux/irq.h>
272eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland#include <linux/list.h>
282eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland#include <linux/slab.h>
292eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland#include <linux/debugfs.h>
302eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland#include <linux/delay.h>
312eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
322eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland#include <mach/msm_smd.h>
332eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
342eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland#include "smd_private.h"
352eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland#include "proc_comm.h"
362eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
3737521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland#if defined(CONFIG_ARCH_QSD8X50)
3837521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland#define CONFIG_QDSP6 1
3937521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland#endif
4037521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland
412eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland#define MODULE_NAME "msm_smd"
422eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
432eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandenum {
442eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	MSM_SMD_DEBUG = 1U << 0,
452eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	MSM_SMSM_DEBUG = 1U << 0,
462eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland};
472eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
482eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic int msm_smd_debug_mask;
492eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
5003e00cd350c6636b5f2a9854609fea93a5c7b677Brian Swetlandstruct shared_info {
515b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	int ready;
52a1b478e829cb3e80021b5e2839d90275310d9eaeArnd Bergmann	void __iomem *state;
535b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland};
545b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland
55283794100d2b1c1645b2949273aa4be29929812dArve Hjønnevågstatic unsigned dummy_state[SMSM_STATE_COUNT];
565b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland
575b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetlandstatic struct shared_info smd_info = {
58a1b478e829cb3e80021b5e2839d90275310d9eaeArnd Bergmann	/* FIXME: not a real __iomem pointer */
59a1b478e829cb3e80021b5e2839d90275310d9eaeArnd Bergmann	.state = &dummy_state,
605b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland};
615b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland
622eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandmodule_param_named(debug_mask, msm_smd_debug_mask,
632eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		   int, S_IRUGO | S_IWUSR | S_IWGRP);
642eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
652eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic unsigned last_heap_free = 0xffffffff;
662eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
672eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic inline void notify_other_smsm(void)
682eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
69b42dc44afca902d19f86d541cbb665f31b9149a0Dima Zavin	msm_a2m_int(5);
7037521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland#ifdef CONFIG_QDSP6
71b42dc44afca902d19f86d541cbb665f31b9149a0Dima Zavin	msm_a2m_int(8);
7237521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland#endif
732eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
742eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
755b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetlandstatic inline void notify_modem_smd(void)
762eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
77b42dc44afca902d19f86d541cbb665f31b9149a0Dima Zavin	msm_a2m_int(0);
782eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
792eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
805b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetlandstatic inline void notify_dsp_smd(void)
815b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland{
82b42dc44afca902d19f86d541cbb665f31b9149a0Dima Zavin	msm_a2m_int(8);
835b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland}
845b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland
852eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic void smd_diag(void)
862eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
872eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	char *x;
882eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
892eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	x = smem_find(ID_DIAG_ERR_MSG, SZ_DIAG_ERR_MSG);
902eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (x != 0) {
912eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		x[SZ_DIAG_ERR_MSG - 1] = 0;
929be58f317d79278afc23186d861ada774cf386eaDavid Brown		pr_debug("DIAG '%s'\n", x);
932eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
942eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
952eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
962eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland/* call when SMSM_RESET flag is set in the A9's smsm_state */
972eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic void handle_modem_crash(void)
982eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
992eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	pr_err("ARM9 has CRASHED\n");
1002eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	smd_diag();
1012eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
1022eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	/* in this case the modem or watchdog should reboot us */
1032eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	for (;;)
1042eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		;
1052eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
1062eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
107283794100d2b1c1645b2949273aa4be29929812dArve Hjønnevåguint32_t raw_smsm_get_state(enum smsm_state_item item)
108283794100d2b1c1645b2949273aa4be29929812dArve Hjønnevåg{
109283794100d2b1c1645b2949273aa4be29929812dArve Hjønnevåg	return readl(smd_info.state + item * 4);
110283794100d2b1c1645b2949273aa4be29929812dArve Hjønnevåg}
111283794100d2b1c1645b2949273aa4be29929812dArve Hjønnevåg
1122eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic int check_for_modem_crash(void)
1132eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
114283794100d2b1c1645b2949273aa4be29929812dArve Hjønnevåg	if (raw_smsm_get_state(SMSM_STATE_MODEM) & SMSM_RESET) {
1152eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		handle_modem_crash();
1162eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return -1;
1172eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
1185b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	return 0;
1192eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
1202eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
1212eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland/* the spinlock is used to synchronize between the
12203e00cd350c6636b5f2a9854609fea93a5c7b677Brian Swetland * irq handler and code that mutates the channel
12303e00cd350c6636b5f2a9854609fea93a5c7b677Brian Swetland * list or fiddles with channel state
12403e00cd350c6636b5f2a9854609fea93a5c7b677Brian Swetland */
12503e00cd350c6636b5f2a9854609fea93a5c7b677Brian SwetlandDEFINE_SPINLOCK(smd_lock);
12603e00cd350c6636b5f2a9854609fea93a5c7b677Brian SwetlandDEFINE_SPINLOCK(smem_lock);
1272eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
1282eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland/* the mutex is used during open() and close()
12903e00cd350c6636b5f2a9854609fea93a5c7b677Brian Swetland * operations to avoid races while creating or
13003e00cd350c6636b5f2a9854609fea93a5c7b677Brian Swetland * destroying smd_channel structures
13103e00cd350c6636b5f2a9854609fea93a5c7b677Brian Swetland */
1322eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic DEFINE_MUTEX(smd_creation_mutex);
1332eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
1342eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic int smd_initialized;
1352eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
13603e00cd350c6636b5f2a9854609fea93a5c7b677Brian SwetlandLIST_HEAD(smd_ch_closed_list);
13737521a3181123dc4a9584cc4b8572c08ea0a8274Brian SwetlandLIST_HEAD(smd_ch_list_modem);
13837521a3181123dc4a9584cc4b8572c08ea0a8274Brian SwetlandLIST_HEAD(smd_ch_list_dsp);
1392eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
1402eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic unsigned char smd_ch_allocated[64];
1412eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic struct work_struct probe_work;
1422eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
1432eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland/* how many bytes are available for reading */
1442eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic int smd_stream_read_avail(struct smd_channel *ch)
1452eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
1465b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	return (ch->recv->head - ch->recv->tail) & ch->fifo_mask;
1472eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
1482eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
1492eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland/* how many bytes we are free to write */
1502eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic int smd_stream_write_avail(struct smd_channel *ch)
1512eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
1525b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	return ch->fifo_mask -
1535b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		((ch->send->head - ch->send->tail) & ch->fifo_mask);
1542eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
1552eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
1562eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic int smd_packet_read_avail(struct smd_channel *ch)
1572eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
1582eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (ch->current_packet) {
1592eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		int n = smd_stream_read_avail(ch);
1602eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		if (n > ch->current_packet)
1612eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			n = ch->current_packet;
1622eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return n;
1632eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	} else {
1642eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return 0;
1652eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
1662eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
1672eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
1682eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic int smd_packet_write_avail(struct smd_channel *ch)
1692eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
1702eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	int n = smd_stream_write_avail(ch);
1712eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return n > SMD_HEADER_SIZE ? n - SMD_HEADER_SIZE : 0;
1722eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
1732eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
1742eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic int ch_is_open(struct smd_channel *ch)
1752eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
1762eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return (ch->recv->state == SMD_SS_OPENED) &&
1772eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		(ch->send->state == SMD_SS_OPENED);
1782eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
1792eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
1802eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland/* provide a pointer and length to readable data in the fifo */
1812eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic unsigned ch_read_buffer(struct smd_channel *ch, void **ptr)
1822eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
1832eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned head = ch->recv->head;
1842eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned tail = ch->recv->tail;
1855b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	*ptr = (void *) (ch->recv_data + tail);
1862eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
1872eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (tail <= head)
1882eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return head - tail;
1892eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	else
1905b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		return ch->fifo_size - tail;
1912eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
1922eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
1932eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland/* advance the fifo read pointer after data from ch_read_buffer is consumed */
1942eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic void ch_read_done(struct smd_channel *ch, unsigned count)
1952eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
1962eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	BUG_ON(count > smd_stream_read_avail(ch));
1975b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	ch->recv->tail = (ch->recv->tail + count) & ch->fifo_mask;
1987632fba05197999fb0d24776b567682ebd62f62aHaley Teng	ch->send->fTAIL = 1;
1992eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
2002eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
2012eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland/* basic read interface to ch_read_{buffer,done} used
20203e00cd350c6636b5f2a9854609fea93a5c7b677Brian Swetland * by smd_*_read() and update_packet_state()
20303e00cd350c6636b5f2a9854609fea93a5c7b677Brian Swetland * will read-and-discard if the _data pointer is null
20403e00cd350c6636b5f2a9854609fea93a5c7b677Brian Swetland */
2052eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic int ch_read(struct smd_channel *ch, void *_data, int len)
2062eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
2072eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	void *ptr;
2082eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned n;
2092eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned char *data = _data;
2102eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	int orig_len = len;
2112eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
2122eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	while (len > 0) {
2132eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		n = ch_read_buffer(ch, &ptr);
2142eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		if (n == 0)
2152eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			break;
2162eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
2172eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		if (n > len)
2182eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			n = len;
2192eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		if (_data)
2202eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			memcpy(data, ptr, n);
2212eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
2222eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		data += n;
2232eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		len -= n;
2242eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		ch_read_done(ch, n);
2252eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
2262eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
2272eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return orig_len - len;
2282eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
2292eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
2302eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic void update_stream_state(struct smd_channel *ch)
2312eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
2322eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	/* streams have no special state requiring updating */
2332eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
2342eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
2352eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic void update_packet_state(struct smd_channel *ch)
2362eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
2372eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned hdr[5];
2382eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	int r;
2392eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
2402eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	/* can't do anything if we're in the middle of a packet */
2412eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (ch->current_packet != 0)
2422eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return;
2432eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
2442eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	/* don't bother unless we can get the full header */
2452eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (smd_stream_read_avail(ch) < SMD_HEADER_SIZE)
2462eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return;
2472eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
2482eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	r = ch_read(ch, hdr, SMD_HEADER_SIZE);
2492eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	BUG_ON(r != SMD_HEADER_SIZE);
2502eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
2512eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	ch->current_packet = hdr[0];
2522eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
2532eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
2542eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland/* provide a pointer and length to next free space in the fifo */
2552eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic unsigned ch_write_buffer(struct smd_channel *ch, void **ptr)
2562eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
2572eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned head = ch->send->head;
2582eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned tail = ch->send->tail;
2595b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	*ptr = (void *) (ch->send_data + head);
2602eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
2612eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (head < tail) {
2622eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return tail - head - 1;
2632eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	} else {
2642eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		if (tail == 0)
2655b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland			return ch->fifo_size - head - 1;
2662eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		else
2675b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland			return ch->fifo_size - head;
2682eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
2692eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
2702eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
2712eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland/* advace the fifo write pointer after freespace
2722eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland * from ch_write_buffer is filled
2732eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland */
2742eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic void ch_write_done(struct smd_channel *ch, unsigned count)
2752eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
2762eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	BUG_ON(count > smd_stream_write_avail(ch));
2775b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	ch->send->head = (ch->send->head + count) & ch->fifo_mask;
2782eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	ch->send->fHEAD = 1;
2792eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
2802eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
2815b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetlandstatic void ch_set_state(struct smd_channel *ch, unsigned n)
2822eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
2832eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (n == SMD_SS_OPENED) {
2845b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		ch->send->fDSR = 1;
2855b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		ch->send->fCTS = 1;
2865b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		ch->send->fCD = 1;
2872eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	} else {
2885b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		ch->send->fDSR = 0;
2895b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		ch->send->fCTS = 0;
2905b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		ch->send->fCD = 0;
2912eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
2925b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	ch->send->state = n;
2935b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	ch->send->fSTATE = 1;
2945b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	ch->notify_other_cpu();
2952eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
2962eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
2972eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic void do_smd_probe(void)
2982eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
2992eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE;
3002eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (shared->heap_info.free_offset != last_heap_free) {
3012eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		last_heap_free = shared->heap_info.free_offset;
3022eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		schedule_work(&probe_work);
3032eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
3042eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
3052eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
3062eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic void smd_state_change(struct smd_channel *ch,
3072eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			     unsigned last, unsigned next)
3082eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
3092eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	ch->last_state = next;
3102eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
3119be58f317d79278afc23186d861ada774cf386eaDavid Brown	pr_debug("ch %d %d -> %d\n", ch->n, last, next);
3122eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
3132eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	switch (next) {
3142eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	case SMD_SS_OPENING:
3152eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		ch->recv->tail = 0;
3162eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	case SMD_SS_OPENED:
3172eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		if (ch->send->state != SMD_SS_OPENED)
3185b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland			ch_set_state(ch, SMD_SS_OPENED);
3192eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		ch->notify(ch->priv, SMD_EVENT_OPEN);
3202eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		break;
3212eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	case SMD_SS_FLUSHING:
3222eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	case SMD_SS_RESET:
3232eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		/* we should force them to close? */
3242eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	default:
3252eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		ch->notify(ch->priv, SMD_EVENT_CLOSE);
3262eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
3272eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
3282eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
3295b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetlandstatic void handle_smd_irq(struct list_head *list, void (*notify)(void))
3302eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
3312eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned long flags;
3322eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	struct smd_channel *ch;
3332eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	int do_notify = 0;
3342eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned ch_flags;
3352eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned tmp;
3362eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
3372eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	spin_lock_irqsave(&smd_lock, flags);
3385b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	list_for_each_entry(ch, list, ch_list) {
3392eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		ch_flags = 0;
3402eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		if (ch_is_open(ch)) {
3412eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			if (ch->recv->fHEAD) {
3422eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland				ch->recv->fHEAD = 0;
3432eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland				ch_flags |= 1;
3442eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland				do_notify |= 1;
3452eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			}
3462eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			if (ch->recv->fTAIL) {
3472eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland				ch->recv->fTAIL = 0;
3482eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland				ch_flags |= 2;
3492eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland				do_notify |= 1;
3502eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			}
3512eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			if (ch->recv->fSTATE) {
3522eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland				ch->recv->fSTATE = 0;
3532eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland				ch_flags |= 4;
3542eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland				do_notify |= 1;
3552eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			}
3562eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		}
3572eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		tmp = ch->recv->state;
3582eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		if (tmp != ch->last_state)
3592eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			smd_state_change(ch, ch->last_state, tmp);
3602eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		if (ch_flags) {
3612eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			ch->update_state(ch);
3622eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			ch->notify(ch->priv, SMD_EVENT_DATA);
3632eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		}
3642eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
3652eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (do_notify)
3665b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		notify();
3672eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	spin_unlock_irqrestore(&smd_lock, flags);
3682eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	do_smd_probe();
3695b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland}
3705b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland
37137521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetlandstatic irqreturn_t smd_modem_irq_handler(int irq, void *data)
37237521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland{
37337521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland	handle_smd_irq(&smd_ch_list_modem, notify_modem_smd);
37437521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland	return IRQ_HANDLED;
37537521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland}
37637521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland
377b13525c2637957a757709945fbc9bc8b1065d071Daniel Walker#if defined(CONFIG_QDSP6)
37837521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetlandstatic irqreturn_t smd_dsp_irq_handler(int irq, void *data)
3795b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland{
38037521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland	handle_smd_irq(&smd_ch_list_dsp, notify_dsp_smd);
3812eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return IRQ_HANDLED;
3822eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
383b13525c2637957a757709945fbc9bc8b1065d071Daniel Walker#endif
3842eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
3852eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic void smd_fake_irq_handler(unsigned long arg)
3862eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
38737521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland	handle_smd_irq(&smd_ch_list_modem, notify_modem_smd);
38837521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland	handle_smd_irq(&smd_ch_list_dsp, notify_dsp_smd);
3892eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
3902eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
3912eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic DECLARE_TASKLET(smd_fake_irq_tasklet, smd_fake_irq_handler, 0);
3922eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
39337521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetlandstatic inline int smd_need_int(struct smd_channel *ch)
39437521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland{
39537521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland	if (ch_is_open(ch)) {
39637521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland		if (ch->recv->fHEAD || ch->recv->fTAIL || ch->recv->fSTATE)
39737521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland			return 1;
39837521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland		if (ch->recv->state != ch->last_state)
39937521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland			return 1;
40037521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland	}
40137521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland	return 0;
40237521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland}
40337521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland
4042eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandvoid smd_sleep_exit(void)
4052eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
4062eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned long flags;
4072eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	struct smd_channel *ch;
4082eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	int need_int = 0;
4092eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
4102eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	spin_lock_irqsave(&smd_lock, flags);
41137521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland	list_for_each_entry(ch, &smd_ch_list_modem, ch_list) {
41237521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland		if (smd_need_int(ch)) {
41337521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland			need_int = 1;
41437521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland			break;
41537521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland		}
41637521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland	}
41737521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland	list_for_each_entry(ch, &smd_ch_list_dsp, ch_list) {
41837521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland		if (smd_need_int(ch)) {
41937521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland			need_int = 1;
42037521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland			break;
4212eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		}
4222eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
4232eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	spin_unlock_irqrestore(&smd_lock, flags);
4242eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	do_smd_probe();
42537521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland
4262eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (need_int) {
4272eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		if (msm_smd_debug_mask & MSM_SMD_DEBUG)
4282eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			pr_info("smd_sleep_exit need interrupt\n");
4292eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		tasklet_schedule(&smd_fake_irq_tasklet);
4302eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
4312eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
4322eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
4332eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
4342eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandvoid smd_kick(smd_channel_t *ch)
4352eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
4362eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned long flags;
4372eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned tmp;
4382eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
4392eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	spin_lock_irqsave(&smd_lock, flags);
4402eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	ch->update_state(ch);
4412eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	tmp = ch->recv->state;
4422eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (tmp != ch->last_state) {
4432eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		ch->last_state = tmp;
4442eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		if (tmp == SMD_SS_OPENED)
4452eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			ch->notify(ch->priv, SMD_EVENT_OPEN);
4462eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		else
4472eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			ch->notify(ch->priv, SMD_EVENT_CLOSE);
4482eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
4492eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	ch->notify(ch->priv, SMD_EVENT_DATA);
4505b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	ch->notify_other_cpu();
4512eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	spin_unlock_irqrestore(&smd_lock, flags);
4522eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
4532eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
4545b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetlandstatic int smd_is_packet(int chn, unsigned type)
4552eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
4565b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	type &= SMD_KIND_MASK;
4575b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	if (type == SMD_KIND_PACKET)
4585b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		return 1;
4595b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	if (type == SMD_KIND_STREAM)
4605b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		return 0;
4615b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland
4625b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	/* older AMSS reports SMD_KIND_UNKNOWN always */
4632eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if ((chn > 4) || (chn == 1))
4642eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return 1;
4652eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	else
4662eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return 0;
4672eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
4682eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
4692eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic int smd_stream_write(smd_channel_t *ch, const void *_data, int len)
4702eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
4712eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	void *ptr;
4722eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	const unsigned char *buf = _data;
4732eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned xfer;
4742eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	int orig_len = len;
4752eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
4762eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (len < 0)
4772eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return -EINVAL;
4782eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
4792eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	while ((xfer = ch_write_buffer(ch, &ptr)) != 0) {
4802eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		if (!ch_is_open(ch))
4812eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			break;
4822eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		if (xfer > len)
4832eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			xfer = len;
4842eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		memcpy(ptr, buf, xfer);
4852eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		ch_write_done(ch, xfer);
4862eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		len -= xfer;
4872eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		buf += xfer;
4882eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		if (len == 0)
4892eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			break;
4902eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
4912eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
4925b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	ch->notify_other_cpu();
4932eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
4942eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return orig_len - len;
4952eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
4962eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
4972eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic int smd_packet_write(smd_channel_t *ch, const void *_data, int len)
4982eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
4992eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned hdr[5];
5002eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
5012eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (len < 0)
5022eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return -EINVAL;
5032eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
5042eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (smd_stream_write_avail(ch) < (len + SMD_HEADER_SIZE))
5052eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return -ENOMEM;
5062eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
5072eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	hdr[0] = len;
5082eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	hdr[1] = hdr[2] = hdr[3] = hdr[4] = 0;
5092eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
5102eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	smd_stream_write(ch, hdr, sizeof(hdr));
5112eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	smd_stream_write(ch, _data, len);
5122eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
5132eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return len;
5142eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
5152eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
5162eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic int smd_stream_read(smd_channel_t *ch, void *data, int len)
5172eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
5182eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	int r;
5192eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
5202eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (len < 0)
5212eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return -EINVAL;
5222eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
5232eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	r = ch_read(ch, data, len);
5242eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (r > 0)
5255b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		ch->notify_other_cpu();
5262eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
5272eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return r;
5282eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
5292eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
5302eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic int smd_packet_read(smd_channel_t *ch, void *data, int len)
5312eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
5322eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned long flags;
5332eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	int r;
5342eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
5352eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (len < 0)
5362eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return -EINVAL;
5372eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
5382eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (len > ch->current_packet)
5392eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		len = ch->current_packet;
5402eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
5412eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	r = ch_read(ch, data, len);
5422eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (r > 0)
5435b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		ch->notify_other_cpu();
5442eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
5452eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	spin_lock_irqsave(&smd_lock, flags);
5462eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	ch->current_packet -= r;
5472eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	update_packet_state(ch);
5482eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	spin_unlock_irqrestore(&smd_lock, flags);
5492eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
5502eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return r;
5512eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
5522eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
55334f719b0c25cca6e11164f926fc798c25499aa96Brian Swetlandstatic int smd_alloc_channel(const char *name, uint32_t cid, uint32_t type)
5545b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland{
5555b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	struct smd_channel *ch;
5562eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
5572eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	ch = kzalloc(sizeof(struct smd_channel), GFP_KERNEL);
5582eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (ch == 0) {
5592eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		pr_err("smd_alloc_channel() out of memory\n");
56034f719b0c25cca6e11164f926fc798c25499aa96Brian Swetland		return -1;
5612eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
5622eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	ch->n = cid;
5632eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
564bf83de4037780b11b27f1e32e33c1e8e7e42602eDaniel Walker	if (_smd_alloc_channel(ch)) {
5655b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		kfree(ch);
56634f719b0c25cca6e11164f926fc798c25499aa96Brian Swetland		return -1;
5675b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	}
5685b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland
5695b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	ch->fifo_mask = ch->fifo_size - 1;
5705b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	ch->type = type;
5715b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland
5725b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	if ((type & SMD_TYPE_MASK) == SMD_TYPE_APPS_MODEM)
5735b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		ch->notify_other_cpu = notify_modem_smd;
5745b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	else
5755b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		ch->notify_other_cpu = notify_dsp_smd;
5765b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland
5775b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	if (smd_is_packet(cid, type)) {
5782eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		ch->read = smd_packet_read;
5792eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		ch->write = smd_packet_write;
5802eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		ch->read_avail = smd_packet_read_avail;
5812eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		ch->write_avail = smd_packet_write_avail;
5822eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		ch->update_state = update_packet_state;
5832eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	} else {
5842eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		ch->read = smd_stream_read;
5852eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		ch->write = smd_stream_write;
5862eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		ch->read_avail = smd_stream_read_avail;
5872eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		ch->write_avail = smd_stream_write_avail;
5882eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		ch->update_state = update_stream_state;
5892eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
5902eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
5915b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	if ((type & 0xff) == 0)
5925b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		memcpy(ch->name, "SMD_", 4);
5935b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	else
5945b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		memcpy(ch->name, "DSP_", 4);
5952eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	memcpy(ch->name + 4, name, 20);
5962eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	ch->name[23] = 0;
5972eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	ch->pdev.name = ch->name;
5982eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	ch->pdev.id = -1;
5992eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
6009be58f317d79278afc23186d861ada774cf386eaDavid Brown	pr_debug("smd_alloc_channel() cid=%02d size=%05d '%s'\n",
6015b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		ch->n, ch->fifo_size, ch->name);
6022eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
6032eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	mutex_lock(&smd_creation_mutex);
6042eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	list_add(&ch->ch_list, &smd_ch_closed_list);
6052eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	mutex_unlock(&smd_creation_mutex);
6062eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
6072eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	platform_device_register(&ch->pdev);
60834f719b0c25cca6e11164f926fc798c25499aa96Brian Swetland	return 0;
6092eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
6102eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
6113843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walkerstatic void smd_channel_probe_worker(struct work_struct *work)
6123843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker{
6133843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker	struct smd_alloc_elm *shared;
6143843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker	unsigned ctype;
6153843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker	unsigned type;
6163843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker	unsigned n;
6173843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker
6183843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker	shared = smem_find(ID_CH_ALLOC_TBL, sizeof(*shared) * 64);
6193843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker	if (!shared) {
6209be58f317d79278afc23186d861ada774cf386eaDavid Brown		pr_err("cannot find allocation table\n");
6213843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker		return;
6223843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker	}
6233843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker	for (n = 0; n < 64; n++) {
6243843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker		if (smd_ch_allocated[n])
6253843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker			continue;
6263843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker		if (!shared[n].ref_count)
6273843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker			continue;
6283843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker		if (!shared[n].name[0])
6293843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker			continue;
6303843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker		ctype = shared[n].ctype;
6313843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker		type = ctype & SMD_TYPE_MASK;
6323843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker
6333843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker		/* DAL channels are stream but neither the modem,
6343843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker		 * nor the DSP correctly indicate this.  Fixup manually.
6353843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker		 */
6363843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker		if (!memcmp(shared[n].name, "DAL", 3))
6373843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker			ctype = (ctype & (~SMD_KIND_MASK)) | SMD_KIND_STREAM;
6383843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker
6393843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker		type = shared[n].ctype & SMD_TYPE_MASK;
6403843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker		if ((type == SMD_TYPE_APPS_MODEM) ||
6413843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker		    (type == SMD_TYPE_APPS_DSP))
6423843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker			if (!smd_alloc_channel(shared[n].name, shared[n].cid, ctype))
6433843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker				smd_ch_allocated[n] = 1;
6443843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker	}
6453843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker}
6463843ac3a5cd01824b5801209808e73fb9df7ee22Daniel Walker
6472eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic void do_nothing_notify(void *priv, unsigned flags)
6482eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
6492eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
6502eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
6512eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstruct smd_channel *smd_get_channel(const char *name)
6522eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
6532eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	struct smd_channel *ch;
6542eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
6552eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	mutex_lock(&smd_creation_mutex);
6562eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	list_for_each_entry(ch, &smd_ch_closed_list, ch_list) {
6572eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		if (!strcmp(name, ch->name)) {
6582eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			list_del(&ch->ch_list);
6592eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			mutex_unlock(&smd_creation_mutex);
6602eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			return ch;
6612eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		}
6622eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
6632eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	mutex_unlock(&smd_creation_mutex);
6642eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
6652eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return NULL;
6662eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
6672eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
6682eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandint smd_open(const char *name, smd_channel_t **_ch,
6692eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	     void *priv, void (*notify)(void *, unsigned))
6702eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
6712eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	struct smd_channel *ch;
6722eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned long flags;
6732eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
6742eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (smd_initialized == 0) {
6752eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		pr_info("smd_open() before smd_init()\n");
6762eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return -ENODEV;
6772eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
6782eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
6792eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	ch = smd_get_channel(name);
6802eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (!ch)
6812eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return -ENODEV;
6822eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
6832eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (notify == 0)
6842eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		notify = do_nothing_notify;
6852eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
6862eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	ch->notify = notify;
6872eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	ch->current_packet = 0;
6882eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	ch->last_state = SMD_SS_CLOSED;
6892eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	ch->priv = priv;
6902eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
6912eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	*_ch = ch;
6922eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
6932eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	spin_lock_irqsave(&smd_lock, flags);
69437521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland
69537521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland	if ((ch->type & SMD_TYPE_MASK) == SMD_TYPE_APPS_MODEM)
69637521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland		list_add(&ch->ch_list, &smd_ch_list_modem);
69737521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland	else
69837521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland		list_add(&ch->ch_list, &smd_ch_list_dsp);
6992eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
7002eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	/* If the remote side is CLOSING, we need to get it to
7012eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	 * move to OPENING (which we'll do by moving from CLOSED to
7022eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	 * OPENING) and then get it to move from OPENING to
7032eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	 * OPENED (by doing the same state change ourselves).
7042eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	 *
7052eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	 * Otherwise, it should be OPENING and we can move directly
7062eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	 * to OPENED so that it will follow.
7072eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	 */
7082eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (ch->recv->state == SMD_SS_CLOSING) {
7092eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		ch->send->head = 0;
7105b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		ch_set_state(ch, SMD_SS_OPENING);
7112eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	} else {
7125b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		ch_set_state(ch, SMD_SS_OPENED);
7132eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
7142eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	spin_unlock_irqrestore(&smd_lock, flags);
7152eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	smd_kick(ch);
7162eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
7172eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return 0;
7182eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
7192eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
7202eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandint smd_close(smd_channel_t *ch)
7212eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
7222eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned long flags;
7232eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
7242eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (ch == 0)
7252eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return -1;
7262eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
7272eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	spin_lock_irqsave(&smd_lock, flags);
7282eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	ch->notify = do_nothing_notify;
7292eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	list_del(&ch->ch_list);
7305b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	ch_set_state(ch, SMD_SS_CLOSED);
7312eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	spin_unlock_irqrestore(&smd_lock, flags);
7322eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
7332eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	mutex_lock(&smd_creation_mutex);
7342eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	list_add(&ch->ch_list, &smd_ch_closed_list);
7352eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	mutex_unlock(&smd_creation_mutex);
7362eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
7372eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return 0;
7382eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
7392eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
7402eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandint smd_read(smd_channel_t *ch, void *data, int len)
7412eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
7422eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return ch->read(ch, data, len);
7432eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
7442eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
7452eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandint smd_write(smd_channel_t *ch, const void *data, int len)
7462eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
7472eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return ch->write(ch, data, len);
7482eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
7492eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
750636eb9cbaef7989ce7809a0d842bf78470a0a1f4Brian Swetlandint smd_write_atomic(smd_channel_t *ch, const void *data, int len)
751636eb9cbaef7989ce7809a0d842bf78470a0a1f4Brian Swetland{
752636eb9cbaef7989ce7809a0d842bf78470a0a1f4Brian Swetland	unsigned long flags;
753636eb9cbaef7989ce7809a0d842bf78470a0a1f4Brian Swetland	int res;
754636eb9cbaef7989ce7809a0d842bf78470a0a1f4Brian Swetland	spin_lock_irqsave(&smd_lock, flags);
755636eb9cbaef7989ce7809a0d842bf78470a0a1f4Brian Swetland	res = ch->write(ch, data, len);
756636eb9cbaef7989ce7809a0d842bf78470a0a1f4Brian Swetland	spin_unlock_irqrestore(&smd_lock, flags);
757636eb9cbaef7989ce7809a0d842bf78470a0a1f4Brian Swetland	return res;
758636eb9cbaef7989ce7809a0d842bf78470a0a1f4Brian Swetland}
759636eb9cbaef7989ce7809a0d842bf78470a0a1f4Brian Swetland
7602eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandint smd_read_avail(smd_channel_t *ch)
7612eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
7622eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return ch->read_avail(ch);
7632eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
7642eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
7652eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandint smd_write_avail(smd_channel_t *ch)
7662eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
7672eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return ch->write_avail(ch);
7682eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
7692eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
7702eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandint smd_wait_until_readable(smd_channel_t *ch, int bytes)
7712eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
7722eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return -1;
7732eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
7742eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
7752eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandint smd_wait_until_writable(smd_channel_t *ch, int bytes)
7762eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
7772eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return -1;
7782eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
7792eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
7802eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandint smd_cur_packet_size(smd_channel_t *ch)
7812eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
7822eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return ch->current_packet;
7832eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
7842eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
7852eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
7862eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland/* ------------------------------------------------------------------------- */
7872eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
7882eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandvoid *smem_alloc(unsigned id, unsigned size)
7892eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
7902eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return smem_find(id, size);
7912eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
7922eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
793a1b478e829cb3e80021b5e2839d90275310d9eaeArnd Bergmannvoid __iomem *smem_item(unsigned id, unsigned *size)
7942eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
7952eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE;
7962eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	struct smem_heap_entry *toc = shared->heap_toc;
7972eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
7982eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (id >= SMEM_NUM_ITEMS)
799a1b478e829cb3e80021b5e2839d90275310d9eaeArnd Bergmann		return NULL;
8002eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
8012eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (toc[id].allocated) {
8022eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		*size = toc[id].size;
803a1b478e829cb3e80021b5e2839d90275310d9eaeArnd Bergmann		return (MSM_SHARED_RAM_BASE + toc[id].offset);
8045b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	} else {
8055b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		*size = 0;
8062eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
8072eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
808a1b478e829cb3e80021b5e2839d90275310d9eaeArnd Bergmann	return NULL;
8092eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
8102eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
8112eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandvoid *smem_find(unsigned id, unsigned size_in)
8122eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
8132eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned size;
8142eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	void *ptr;
8152eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
8165b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	ptr = smem_item(id, &size);
8172eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (!ptr)
8182eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return 0;
8192eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
8202eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	size_in = ALIGN(size_in, 8);
8212eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (size_in != size) {
8222eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		pr_err("smem_find(%d, %d): wrong size %d\n",
8232eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		       id, size_in, size);
8242eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return 0;
8252eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
8262eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
8272eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return ptr;
8282eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
8292eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
8302eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic irqreturn_t smsm_irq_handler(int irq, void *data)
8312eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
8322eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned long flags;
8335b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	unsigned apps, modm;
8342eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
8352eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	spin_lock_irqsave(&smem_lock, flags);
8362eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
837283794100d2b1c1645b2949273aa4be29929812dArve Hjønnevåg	apps = raw_smsm_get_state(SMSM_STATE_APPS);
838283794100d2b1c1645b2949273aa4be29929812dArve Hjønnevåg	modm = raw_smsm_get_state(SMSM_STATE_MODEM);
8392eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
8405b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	if (msm_smd_debug_mask & MSM_SMSM_DEBUG)
8415b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		pr_info("<SM %08x %08x>\n", apps, modm);
84279848a2a7333eee6424b38c05b4ea4a0ce56eb47Daniel Walker	if (modm & SMSM_RESET)
8435b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		handle_modem_crash();
84479848a2a7333eee6424b38c05b4ea4a0ce56eb47Daniel Walker
8455b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	do_smd_probe();
8465b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland
8472eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	spin_unlock_irqrestore(&smem_lock, flags);
8482eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return IRQ_HANDLED;
8492eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
8502eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
851283794100d2b1c1645b2949273aa4be29929812dArve Hjønnevågint smsm_change_state(enum smsm_state_item item,
852283794100d2b1c1645b2949273aa4be29929812dArve Hjønnevåg		      uint32_t clear_mask, uint32_t set_mask)
8532eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
854a1b478e829cb3e80021b5e2839d90275310d9eaeArnd Bergmann	void __iomem *addr = smd_info.state + item * 4;
8552eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned long flags;
8565b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	unsigned state;
8575b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland
8585b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	if (!smd_info.ready)
8595b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		return -EIO;
8602eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
8612eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	spin_lock_irqsave(&smem_lock, flags);
8622eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
863283794100d2b1c1645b2949273aa4be29929812dArve Hjønnevåg	if (raw_smsm_get_state(SMSM_STATE_MODEM) & SMSM_RESET)
8645b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		handle_modem_crash();
8655b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland
866283794100d2b1c1645b2949273aa4be29929812dArve Hjønnevåg	state = (readl(addr) & ~clear_mask) | set_mask;
867283794100d2b1c1645b2949273aa4be29929812dArve Hjønnevåg	writel(state, addr);
8685b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland
8695b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	if (msm_smd_debug_mask & MSM_SMSM_DEBUG)
870283794100d2b1c1645b2949273aa4be29929812dArve Hjønnevåg		pr_info("smsm_change_state %d %x\n", item, state);
8715b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	notify_other_smsm();
8722eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
8732eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	spin_unlock_irqrestore(&smem_lock, flags);
8742eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
8752eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return 0;
8762eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
8772eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
878283794100d2b1c1645b2949273aa4be29929812dArve Hjønnevåguint32_t smsm_get_state(enum smsm_state_item item)
8792eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
8802eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	unsigned long flags;
8812eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	uint32_t rv;
8822eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
8832eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	spin_lock_irqsave(&smem_lock, flags);
8842eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
885283794100d2b1c1645b2949273aa4be29929812dArve Hjønnevåg	rv = readl(smd_info.state + item * 4);
8862eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
887283794100d2b1c1645b2949273aa4be29929812dArve Hjønnevåg	if (item == SMSM_STATE_MODEM && (rv & SMSM_RESET))
8882eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		handle_modem_crash();
8892eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
8902eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	spin_unlock_irqrestore(&smem_lock, flags);
8912eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
8922eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return rv;
8932eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
8942eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
895ec9d3d14ffa9454e6d51e5dd1889d6e9e0be5198Arve Hjønnevåg#ifdef CONFIG_ARCH_MSM_SCORPION
896ec9d3d14ffa9454e6d51e5dd1889d6e9e0be5198Arve Hjønnevåg
8972eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandint smsm_set_sleep_duration(uint32_t delay)
8982eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
89903e00cd350c6636b5f2a9854609fea93a5c7b677Brian Swetland	struct msm_dem_slave_data *ptr;
90003e00cd350c6636b5f2a9854609fea93a5c7b677Brian Swetland
90103e00cd350c6636b5f2a9854609fea93a5c7b677Brian Swetland	ptr = smem_find(SMEM_APPS_DEM_SLAVE_DATA, sizeof(*ptr));
9022eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (ptr == NULL) {
903ec9d3d14ffa9454e6d51e5dd1889d6e9e0be5198Arve Hjønnevåg		pr_err("smsm_set_sleep_duration <SM NO APPS_DEM_SLAVE_DATA>\n");
9042eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return -EIO;
9052eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
9062eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (msm_smd_debug_mask & MSM_SMSM_DEBUG)
9072eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		pr_info("smsm_set_sleep_duration %d -> %d\n",
908ec9d3d14ffa9454e6d51e5dd1889d6e9e0be5198Arve Hjønnevåg		       ptr->sleep_time, delay);
909ec9d3d14ffa9454e6d51e5dd1889d6e9e0be5198Arve Hjønnevåg	ptr->sleep_time = delay;
9102eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return 0;
9112eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
9122eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
913ec9d3d14ffa9454e6d51e5dd1889d6e9e0be5198Arve Hjønnevåg#else
914ec9d3d14ffa9454e6d51e5dd1889d6e9e0be5198Arve Hjønnevåg
915ec9d3d14ffa9454e6d51e5dd1889d6e9e0be5198Arve Hjønnevågint smsm_set_sleep_duration(uint32_t delay)
9162eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
917ec9d3d14ffa9454e6d51e5dd1889d6e9e0be5198Arve Hjønnevåg	uint32_t *ptr;
9182eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
91903e00cd350c6636b5f2a9854609fea93a5c7b677Brian Swetland	ptr = smem_find(SMEM_SMSM_SLEEP_DELAY, sizeof(*ptr));
9202eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (ptr == NULL) {
921ec9d3d14ffa9454e6d51e5dd1889d6e9e0be5198Arve Hjønnevåg		pr_err("smsm_set_sleep_duration <SM NO SLEEP_DELAY>\n");
9222eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return -EIO;
9232eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
9242eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (msm_smd_debug_mask & MSM_SMSM_DEBUG)
925ec9d3d14ffa9454e6d51e5dd1889d6e9e0be5198Arve Hjønnevåg		pr_info("smsm_set_sleep_duration %d -> %d\n",
926ec9d3d14ffa9454e6d51e5dd1889d6e9e0be5198Arve Hjønnevåg		       *ptr, delay);
927ec9d3d14ffa9454e6d51e5dd1889d6e9e0be5198Arve Hjønnevåg	*ptr = delay;
9282eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return 0;
9292eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
9302eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
931ec9d3d14ffa9454e6d51e5dd1889d6e9e0be5198Arve Hjønnevåg#endif
932ec9d3d14ffa9454e6d51e5dd1889d6e9e0be5198Arve Hjønnevåg
9332eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandint smd_core_init(void)
9342eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
9352eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	int r;
9362eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
9375b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	/* wait for essential items to be initialized */
9385b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	for (;;) {
9395b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		unsigned size;
940a1b478e829cb3e80021b5e2839d90275310d9eaeArnd Bergmann		void __iomem *state;
9415b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		state = smem_item(SMEM_SMSM_SHARED_STATE, &size);
942283794100d2b1c1645b2949273aa4be29929812dArve Hjønnevåg		if (size == SMSM_V1_SIZE || size == SMSM_V2_SIZE) {
943a1b478e829cb3e80021b5e2839d90275310d9eaeArnd Bergmann			smd_info.state = state;
9445b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland			break;
9455b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland		}
9465b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	}
9475b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland
9485b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	smd_info.ready = 1;
9495b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland
95037521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland	r = request_irq(INT_A9_M2A_0, smd_modem_irq_handler,
9512eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			IRQF_TRIGGER_RISING, "smd_dev", 0);
9522eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (r < 0)
9532eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return r;
9542eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	r = enable_irq_wake(INT_A9_M2A_0);
9552eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (r < 0)
9562eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		pr_err("smd_core_init: enable_irq_wake failed for A9_M2A_0\n");
9572eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
9582eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	r = request_irq(INT_A9_M2A_5, smsm_irq_handler,
9592eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland			IRQF_TRIGGER_RISING, "smsm_dev", 0);
9602eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (r < 0) {
9612eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		free_irq(INT_A9_M2A_0, 0);
9622eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return r;
9632eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
9642eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	r = enable_irq_wake(INT_A9_M2A_5);
9652eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (r < 0)
9662eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		pr_err("smd_core_init: enable_irq_wake failed for A9_M2A_5\n");
9672eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
96837521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland#if defined(CONFIG_QDSP6)
96937521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland	r = request_irq(INT_ADSP_A11, smd_dsp_irq_handler,
97037521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland			IRQF_TRIGGER_RISING, "smd_dsp", 0);
97137521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland	if (r < 0) {
97237521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland		free_irq(INT_A9_M2A_0, 0);
97337521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland		free_irq(INT_A9_M2A_5, 0);
97437521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland		return r;
97537521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland	}
97637521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland#endif
97737521a3181123dc4a9584cc4b8572c08ea0a8274Brian Swetland
9785b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	/* check for any SMD channels that may already exist */
9795b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	do_smd_probe();
9805b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland
9815b0f5a3f6084397194a8b556cdca572ad8e14f05Brian Swetland	/* indicate that we're up and running */
982283794100d2b1c1645b2949273aa4be29929812dArve Hjønnevåg	smsm_change_state(SMSM_STATE_APPS,
983ec9d3d14ffa9454e6d51e5dd1889d6e9e0be5198Arve Hjønnevåg			  ~0, SMSM_INIT | SMSM_SMDINIT | SMSM_RPCINIT | SMSM_RUN);
984ec9d3d14ffa9454e6d51e5dd1889d6e9e0be5198Arve Hjønnevåg#ifdef CONFIG_ARCH_MSM_SCORPION
985ec9d3d14ffa9454e6d51e5dd1889d6e9e0be5198Arve Hjønnevåg	smsm_change_state(SMSM_STATE_APPS_DEM, ~0, 0);
986ec9d3d14ffa9454e6d51e5dd1889d6e9e0be5198Arve Hjønnevåg#endif
9872eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
9882eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return 0;
9892eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
9902eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
991351a102dbf489d0e9c9b0883f76e2a94d895503dGreg Kroah-Hartmanstatic int msm_smd_probe(struct platform_device *pdev)
9922eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
9930aec66d493c1476daa88ec56b1076e51f1ceff0bDaniel Walker	/*
9940aec66d493c1476daa88ec56b1076e51f1ceff0bDaniel Walker	 * If we haven't waited for the ARM9 to boot up till now,
9950aec66d493c1476daa88ec56b1076e51f1ceff0bDaniel Walker	 * then we need to wait here. Otherwise this should just
9960aec66d493c1476daa88ec56b1076e51f1ceff0bDaniel Walker	 * return immediately.
9970aec66d493c1476daa88ec56b1076e51f1ceff0bDaniel Walker	 */
9980aec66d493c1476daa88ec56b1076e51f1ceff0bDaniel Walker	proc_comm_boot_wait();
9990aec66d493c1476daa88ec56b1076e51f1ceff0bDaniel Walker
10002eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	INIT_WORK(&probe_work, smd_channel_probe_worker);
10012eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
10022eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	if (smd_core_init()) {
10032eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		pr_err("smd_core_init() failed\n");
10042eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		return -1;
10052eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	}
10062eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
10072eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	do_smd_probe();
10082eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
10092eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	msm_check_for_modem_crash = check_for_modem_crash;
10102eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
10111207babdcdfe5501d1528c86b445a9d1045ecc01Iliyan Malchev	msm_init_last_radio_log(THIS_MODULE);
10121207babdcdfe5501d1528c86b445a9d1045ecc01Iliyan Malchev
10132eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	smd_initialized = 1;
10142eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
10152eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return 0;
10162eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
10172eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
10182eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic struct platform_driver msm_smd_driver = {
10192eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	.probe = msm_smd_probe,
10202eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	.driver = {
10212eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		.name = MODULE_NAME,
10222eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland		.owner = THIS_MODULE,
10232eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	},
10242eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland};
10252eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
10262eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandstatic int __init msm_smd_init(void)
10272eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland{
10282eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland	return platform_driver_register(&msm_smd_driver);
10292eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland}
10302eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
10312eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetlandmodule_init(msm_smd_init);
10322eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian Swetland
10332eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian SwetlandMODULE_DESCRIPTION("MSM Shared Memory Core");
10342eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian SwetlandMODULE_AUTHOR("Brian Swetland <swetland@google.com>");
10352eb44eb9c8026f3f548bfbc903156b6aea54ed24Brian SwetlandMODULE_LICENSE("GPL");
1036