| /* arch/arm/mach-msm/proc_comm.c | 
 |  * | 
 |  * Copyright (C) 2007-2008 Google, Inc. | 
 |  * Author: Brian Swetland <swetland@google.com> | 
 |  * | 
 |  * This software is licensed under the terms of the GNU General Public | 
 |  * License version 2, as published by the Free Software Foundation, and | 
 |  * may be copied, distributed, and modified under those terms. | 
 |  * | 
 |  * This program is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |  * GNU General Public License for more details. | 
 |  * | 
 |  */ | 
 |  | 
 | #include <linux/delay.h> | 
 | #include <linux/errno.h> | 
 | #include <linux/io.h> | 
 | #include <linux/spinlock.h> | 
 | #include <mach/msm_iomap.h> | 
 | #include <mach/system.h> | 
 |  | 
 | #include "proc_comm.h" | 
 |  | 
 | #define MSM_A2M_INT(n) (MSM_CSR_BASE + 0x400 + (n) * 4) | 
 |  | 
 | static inline void notify_other_proc_comm(void) | 
 | { | 
 | 	writel(1, MSM_A2M_INT(6)); | 
 | } | 
 |  | 
 | #define APP_COMMAND 0x00 | 
 | #define APP_STATUS  0x04 | 
 | #define APP_DATA1   0x08 | 
 | #define APP_DATA2   0x0C | 
 |  | 
 | #define MDM_COMMAND 0x10 | 
 | #define MDM_STATUS  0x14 | 
 | #define MDM_DATA1   0x18 | 
 | #define MDM_DATA2   0x1C | 
 |  | 
 | static DEFINE_SPINLOCK(proc_comm_lock); | 
 |  | 
 | /* The higher level SMD support will install this to | 
 |  * provide a way to check for and handle modem restart. | 
 |  */ | 
 | int (*msm_check_for_modem_crash)(void); | 
 |  | 
 | /* Poll for a state change, checking for possible | 
 |  * modem crashes along the way (so we don't wait | 
 |  * forever while the ARM9 is blowing up). | 
 |  * | 
 |  * Return an error in the event of a modem crash and | 
 |  * restart so the msm_proc_comm() routine can restart | 
 |  * the operation from the beginning. | 
 |  */ | 
 | static int proc_comm_wait_for(void __iomem *addr, unsigned value) | 
 | { | 
 | 	for (;;) { | 
 | 		if (readl(addr) == value) | 
 | 			return 0; | 
 |  | 
 | 		if (msm_check_for_modem_crash) | 
 | 			if (msm_check_for_modem_crash()) | 
 | 				return -EAGAIN; | 
 | 	} | 
 | } | 
 |  | 
 | int msm_proc_comm(unsigned cmd, unsigned *data1, unsigned *data2) | 
 | { | 
 | 	void __iomem *base = MSM_SHARED_RAM_BASE; | 
 | 	unsigned long flags; | 
 | 	int ret; | 
 |  | 
 | 	spin_lock_irqsave(&proc_comm_lock, flags); | 
 |  | 
 | 	for (;;) { | 
 | 		if (proc_comm_wait_for(base + MDM_STATUS, PCOM_READY)) | 
 | 			continue; | 
 |  | 
 | 		writel(cmd, base + APP_COMMAND); | 
 | 		writel(data1 ? *data1 : 0, base + APP_DATA1); | 
 | 		writel(data2 ? *data2 : 0, base + APP_DATA2); | 
 |  | 
 | 		notify_other_proc_comm(); | 
 |  | 
 | 		if (proc_comm_wait_for(base + APP_COMMAND, PCOM_CMD_DONE)) | 
 | 			continue; | 
 |  | 
 | 		if (readl(base + APP_STATUS) != PCOM_CMD_FAIL) { | 
 | 			if (data1) | 
 | 				*data1 = readl(base + APP_DATA1); | 
 | 			if (data2) | 
 | 				*data2 = readl(base + APP_DATA2); | 
 | 			ret = 0; | 
 | 		} else { | 
 | 			ret = -EIO; | 
 | 		} | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	writel(PCOM_CMD_IDLE, base + APP_COMMAND); | 
 |  | 
 | 	spin_unlock_irqrestore(&proc_comm_lock, flags); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 |  |