| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 1 | /* | 
 | 2 |  *  Shared Transport Line discipline driver Core | 
 | 3 |  *	Init Manager module responsible for GPIO control | 
 | 4 |  *	and firmware download | 
| Pavan Savoy | a0cc2f3 | 2010-10-06 12:18:14 -0400 | [diff] [blame] | 5 |  *  Copyright (C) 2009-2010 Texas Instruments | 
 | 6 |  *  Author: Pavan Savoy <pavan_savoy@ti.com> | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 7 |  * | 
 | 8 |  *  This program is free software; you can redistribute it and/or modify | 
 | 9 |  *  it under the terms of the GNU General Public License version 2 as | 
 | 10 |  *  published by the Free Software Foundation. | 
 | 11 |  * | 
 | 12 |  *  This program is distributed in the hope that it will be useful, | 
 | 13 |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 14 |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 | 15 |  *  GNU General Public License for more details. | 
 | 16 |  * | 
 | 17 |  *  You should have received a copy of the GNU General Public License | 
 | 18 |  *  along with this program; if not, write to the Free Software | 
 | 19 |  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
 | 20 |  * | 
 | 21 |  */ | 
 | 22 |  | 
 | 23 | #define pr_fmt(fmt) "(stk) :" fmt | 
 | 24 | #include <linux/platform_device.h> | 
 | 25 | #include <linux/jiffies.h> | 
 | 26 | #include <linux/firmware.h> | 
 | 27 | #include <linux/delay.h> | 
 | 28 | #include <linux/wait.h> | 
 | 29 | #include <linux/gpio.h> | 
| Pavan Savoy | c1afac1 | 2010-07-28 02:25:59 -0500 | [diff] [blame] | 30 | #include <linux/debugfs.h> | 
 | 31 | #include <linux/seq_file.h> | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 32 | #include <linux/sched.h> | 
| Randy Dunlap | 773d679 | 2011-04-26 09:18:51 -0700 | [diff] [blame] | 33 | #include <linux/sysfs.h> | 
| Pavan Savoy | ec60d0a | 2011-02-04 02:23:10 -0600 | [diff] [blame] | 34 | #include <linux/tty.h> | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 35 |  | 
| Pavan Savoy | 5c88b02 | 2011-02-04 02:23:09 -0600 | [diff] [blame] | 36 | #include <linux/skbuff.h> | 
| Pavan Savoy | e555867 | 2010-09-30 16:13:30 -0400 | [diff] [blame] | 37 | #include <linux/ti_wilink_st.h> | 
| Paul Gortmaker | eb12a67 | 2011-07-03 15:14:56 -0400 | [diff] [blame] | 38 | #include <linux/module.h> | 
| Pavan Savoy | 83ef41f | 2010-09-17 12:06:11 -0400 | [diff] [blame] | 39 |  | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 40 |  | 
| Pavan Savoy | dbd3a87 | 2010-08-19 14:08:51 -0400 | [diff] [blame] | 41 | #define MAX_ST_DEVICES	3	/* Imagine 1 on each UART for now */ | 
| Pavan Savoy | 73f12e8 | 2010-10-12 16:27:38 -0400 | [diff] [blame] | 42 | static struct platform_device *st_kim_devices[MAX_ST_DEVICES]; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 43 |  | 
 | 44 | /**********************************************************************/ | 
 | 45 | /* internal functions */ | 
 | 46 |  | 
| Pavan Savoy | 36b5aee4 | 2010-07-22 05:32:06 -0500 | [diff] [blame] | 47 | /** | 
| Pavan Savoy | dbd3a87 | 2010-08-19 14:08:51 -0400 | [diff] [blame] | 48 |  * st_get_plat_device - | 
 | 49 |  *	function which returns the reference to the platform device | 
 | 50 |  *	requested by id. As of now only 1 such device exists (id=0) | 
 | 51 |  *	the context requesting for reference can get the id to be | 
 | 52 |  *	requested by a. The protocol driver which is registering or | 
 | 53 |  *	b. the tty device which is opened. | 
 | 54 |  */ | 
 | 55 | static struct platform_device *st_get_plat_device(int id) | 
 | 56 | { | 
 | 57 | 	return st_kim_devices[id]; | 
 | 58 | } | 
 | 59 |  | 
 | 60 | /** | 
| Pavan Savoy | 36b5aee4 | 2010-07-22 05:32:06 -0500 | [diff] [blame] | 61 |  * validate_firmware_response - | 
 | 62 |  *	function to return whether the firmware response was proper | 
 | 63 |  *	in case of error don't complete so that waiting for proper | 
 | 64 |  *	response times out | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 65 |  */ | 
| Pavan Savoy | 27712b3 | 2012-08-03 14:49:40 -0500 | [diff] [blame] | 66 | static void validate_firmware_response(struct kim_data_s *kim_gdata) | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 67 | { | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 68 | 	struct sk_buff *skb = kim_gdata->rx_skb; | 
| Pavan Savoy | 8565adb | 2012-08-03 14:49:43 -0500 | [diff] [blame] | 69 | 	if (!skb) | 
 | 70 | 		return; | 
 | 71 |  | 
 | 72 | 	/* these magic numbers are the position in the response buffer which | 
 | 73 | 	 * allows us to distinguish whether the response is for the read | 
 | 74 | 	 * version info. command | 
 | 75 | 	 */ | 
 | 76 | 	if (skb->data[2] == 0x01 && skb->data[3] == 0x01 && | 
 | 77 | 			skb->data[4] == 0x10 && skb->data[5] == 0x00) { | 
 | 78 | 		/* fw version response */ | 
 | 79 | 		memcpy(kim_gdata->resp_buffer, | 
 | 80 | 				kim_gdata->rx_skb->data, | 
 | 81 | 				kim_gdata->rx_skb->len); | 
 | 82 | 		complete_all(&kim_gdata->kim_rcvd); | 
 | 83 | 		kim_gdata->rx_state = ST_W4_PACKET_TYPE; | 
 | 84 | 		kim_gdata->rx_skb = NULL; | 
 | 85 | 		kim_gdata->rx_count = 0; | 
 | 86 | 	} else if (unlikely(skb->data[5] != 0)) { | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 87 | 		pr_err("no proper response during fw download"); | 
 | 88 | 		pr_err("data6 %x", skb->data[5]); | 
| Pavan Savoy | 76ff0e6 | 2011-08-10 10:18:36 -0500 | [diff] [blame] | 89 | 		kfree_skb(skb); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 90 | 		return;		/* keep waiting for the proper response */ | 
 | 91 | 	} | 
 | 92 | 	/* becos of all the script being downloaded */ | 
 | 93 | 	complete_all(&kim_gdata->kim_rcvd); | 
 | 94 | 	kfree_skb(skb); | 
 | 95 | } | 
 | 96 |  | 
 | 97 | /* check for data len received inside kim_int_recv | 
 | 98 |  * most often hit the last case to update state to waiting for data | 
 | 99 |  */ | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 100 | static inline int kim_check_data_len(struct kim_data_s *kim_gdata, int len) | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 101 | { | 
 | 102 | 	register int room = skb_tailroom(kim_gdata->rx_skb); | 
 | 103 |  | 
| Pavan Savoy | e6d9e64 | 2010-07-22 05:32:05 -0500 | [diff] [blame] | 104 | 	pr_debug("len %d room %d", len, room); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 105 |  | 
 | 106 | 	if (!len) { | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 107 | 		validate_firmware_response(kim_gdata); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 108 | 	} else if (len > room) { | 
 | 109 | 		/* Received packet's payload length is larger. | 
 | 110 | 		 * We can't accommodate it in created skb. | 
 | 111 | 		 */ | 
 | 112 | 		pr_err("Data length is too large len %d room %d", len, | 
 | 113 | 			   room); | 
 | 114 | 		kfree_skb(kim_gdata->rx_skb); | 
 | 115 | 	} else { | 
 | 116 | 		/* Packet header has non-zero payload length and | 
 | 117 | 		 * we have enough space in created skb. Lets read | 
 | 118 | 		 * payload data */ | 
| Pavan Savoy | 5c88b02 | 2011-02-04 02:23:09 -0600 | [diff] [blame] | 119 | 		kim_gdata->rx_state = ST_W4_DATA; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 120 | 		kim_gdata->rx_count = len; | 
 | 121 | 		return len; | 
 | 122 | 	} | 
 | 123 |  | 
 | 124 | 	/* Change ST LL state to continue to process next | 
 | 125 | 	 * packet */ | 
 | 126 | 	kim_gdata->rx_state = ST_W4_PACKET_TYPE; | 
 | 127 | 	kim_gdata->rx_skb = NULL; | 
 | 128 | 	kim_gdata->rx_count = 0; | 
 | 129 |  | 
 | 130 | 	return 0; | 
 | 131 | } | 
 | 132 |  | 
| Pavan Savoy | 36b5aee4 | 2010-07-22 05:32:06 -0500 | [diff] [blame] | 133 | /** | 
 | 134 |  * kim_int_recv - receive function called during firmware download | 
 | 135 |  *	firmware download responses on different UART drivers | 
 | 136 |  *	have been observed to come in bursts of different | 
 | 137 |  *	tty_receive and hence the logic | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 138 |  */ | 
| Pavan Savoy | 27712b3 | 2012-08-03 14:49:40 -0500 | [diff] [blame] | 139 | static void kim_int_recv(struct kim_data_s *kim_gdata, | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 140 | 	const unsigned char *data, long count) | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 141 | { | 
| Pavan Savoy | 73f12e8 | 2010-10-12 16:27:38 -0400 | [diff] [blame] | 142 | 	const unsigned char *ptr; | 
| Pavan Savoy | 73f12e8 | 2010-10-12 16:27:38 -0400 | [diff] [blame] | 143 | 	int len = 0, type = 0; | 
| Pavan Savoy | 5c88b02 | 2011-02-04 02:23:09 -0600 | [diff] [blame] | 144 | 	unsigned char *plen; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 145 |  | 
| Pavan Savoy | e6d9e64 | 2010-07-22 05:32:05 -0500 | [diff] [blame] | 146 | 	pr_debug("%s", __func__); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 147 | 	/* Decode received bytes here */ | 
| Pavan Savoy | 73f12e8 | 2010-10-12 16:27:38 -0400 | [diff] [blame] | 148 | 	ptr = data; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 149 | 	if (unlikely(ptr == NULL)) { | 
 | 150 | 		pr_err(" received null from TTY "); | 
 | 151 | 		return; | 
 | 152 | 	} | 
| Pavan Savoy | 73f12e8 | 2010-10-12 16:27:38 -0400 | [diff] [blame] | 153 |  | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 154 | 	while (count) { | 
 | 155 | 		if (kim_gdata->rx_count) { | 
 | 156 | 			len = min_t(unsigned int, kim_gdata->rx_count, count); | 
 | 157 | 			memcpy(skb_put(kim_gdata->rx_skb, len), ptr, len); | 
 | 158 | 			kim_gdata->rx_count -= len; | 
 | 159 | 			count -= len; | 
 | 160 | 			ptr += len; | 
 | 161 |  | 
 | 162 | 			if (kim_gdata->rx_count) | 
 | 163 | 				continue; | 
 | 164 |  | 
 | 165 | 			/* Check ST RX state machine , where are we? */ | 
 | 166 | 			switch (kim_gdata->rx_state) { | 
 | 167 | 				/* Waiting for complete packet ? */ | 
| Pavan Savoy | 5c88b02 | 2011-02-04 02:23:09 -0600 | [diff] [blame] | 168 | 			case ST_W4_DATA: | 
| Pavan Savoy | e6d9e64 | 2010-07-22 05:32:05 -0500 | [diff] [blame] | 169 | 				pr_debug("Complete pkt received"); | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 170 | 				validate_firmware_response(kim_gdata); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 171 | 				kim_gdata->rx_state = ST_W4_PACKET_TYPE; | 
 | 172 | 				kim_gdata->rx_skb = NULL; | 
 | 173 | 				continue; | 
 | 174 | 				/* Waiting for Bluetooth event header ? */ | 
| Pavan Savoy | 5c88b02 | 2011-02-04 02:23:09 -0600 | [diff] [blame] | 175 | 			case ST_W4_HEADER: | 
 | 176 | 				plen = | 
 | 177 | 				(unsigned char *)&kim_gdata->rx_skb->data[1]; | 
 | 178 | 				pr_debug("event hdr: plen 0x%02x\n", *plen); | 
 | 179 | 				kim_check_data_len(kim_gdata, *plen); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 180 | 				continue; | 
 | 181 | 			}	/* end of switch */ | 
 | 182 | 		}		/* end of if rx_state */ | 
 | 183 | 		switch (*ptr) { | 
 | 184 | 			/* Bluetooth event packet? */ | 
| Pavan Savoy | 5c88b02 | 2011-02-04 02:23:09 -0600 | [diff] [blame] | 185 | 		case 0x04: | 
 | 186 | 			kim_gdata->rx_state = ST_W4_HEADER; | 
 | 187 | 			kim_gdata->rx_count = 2; | 
 | 188 | 			type = *ptr; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 189 | 			break; | 
 | 190 | 		default: | 
 | 191 | 			pr_info("unknown packet"); | 
 | 192 | 			ptr++; | 
 | 193 | 			count--; | 
 | 194 | 			continue; | 
| Pavan Savoy | 36b5aee4 | 2010-07-22 05:32:06 -0500 | [diff] [blame] | 195 | 		} | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 196 | 		ptr++; | 
 | 197 | 		count--; | 
 | 198 | 		kim_gdata->rx_skb = | 
| Pavan Savoy | 5c88b02 | 2011-02-04 02:23:09 -0600 | [diff] [blame] | 199 | 			alloc_skb(1024+8, GFP_ATOMIC); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 200 | 		if (!kim_gdata->rx_skb) { | 
 | 201 | 			pr_err("can't allocate mem for new packet"); | 
 | 202 | 			kim_gdata->rx_state = ST_W4_PACKET_TYPE; | 
 | 203 | 			kim_gdata->rx_count = 0; | 
 | 204 | 			return; | 
| Pavan Savoy | 36b5aee4 | 2010-07-22 05:32:06 -0500 | [diff] [blame] | 205 | 		} | 
| Pavan Savoy | 5c88b02 | 2011-02-04 02:23:09 -0600 | [diff] [blame] | 206 | 		skb_reserve(kim_gdata->rx_skb, 8); | 
 | 207 | 		kim_gdata->rx_skb->cb[0] = 4; | 
 | 208 | 		kim_gdata->rx_skb->cb[1] = 0; | 
 | 209 |  | 
| Pavan Savoy | 36b5aee4 | 2010-07-22 05:32:06 -0500 | [diff] [blame] | 210 | 	} | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 211 | 	return; | 
 | 212 | } | 
 | 213 |  | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 214 | static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name) | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 215 | { | 
 | 216 | 	unsigned short version = 0, chip = 0, min_ver = 0, maj_ver = 0; | 
| Pavan Savoy | 73f12e8 | 2010-10-12 16:27:38 -0400 | [diff] [blame] | 217 | 	const char read_ver_cmd[] = { 0x01, 0x01, 0x10, 0x00 }; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 218 |  | 
| Pavan Savoy | e6d9e64 | 2010-07-22 05:32:05 -0500 | [diff] [blame] | 219 | 	pr_debug("%s", __func__); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 220 |  | 
 | 221 | 	INIT_COMPLETION(kim_gdata->kim_rcvd); | 
 | 222 | 	if (4 != st_int_write(kim_gdata->core_data, read_ver_cmd, 4)) { | 
 | 223 | 		pr_err("kim: couldn't write 4 bytes"); | 
| Pavan Savoy | 7044266 | 2011-02-04 02:23:11 -0600 | [diff] [blame] | 224 | 		return -EIO; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 225 | 	} | 
 | 226 |  | 
| Pavan Savoy | 5370235 | 2012-08-03 14:49:42 -0500 | [diff] [blame] | 227 | 	if (!wait_for_completion_interruptible_timeout( | 
 | 228 | 		&kim_gdata->kim_rcvd, msecs_to_jiffies(CMD_RESP_TIME))) { | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 229 | 		pr_err(" waiting for ver info- timed out "); | 
| Pavan Savoy | 7044266 | 2011-02-04 02:23:11 -0600 | [diff] [blame] | 230 | 		return -ETIMEDOUT; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 231 | 	} | 
| Pavan Savoy | 74a4fcf | 2011-08-10 10:18:32 -0500 | [diff] [blame] | 232 | 	INIT_COMPLETION(kim_gdata->kim_rcvd); | 
| Pavan Savoy | 8565adb | 2012-08-03 14:49:43 -0500 | [diff] [blame] | 233 | 	/* the positions 12 & 13 in the response buffer provide with the | 
 | 234 | 	 * chip, major & minor numbers | 
 | 235 | 	 */ | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 236 |  | 
 | 237 | 	version = | 
| Pavan Savoy | 8565adb | 2012-08-03 14:49:43 -0500 | [diff] [blame] | 238 | 		MAKEWORD(kim_gdata->resp_buffer[12], | 
 | 239 | 				kim_gdata->resp_buffer[13]); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 240 | 	chip = (version & 0x7C00) >> 10; | 
 | 241 | 	min_ver = (version & 0x007F); | 
 | 242 | 	maj_ver = (version & 0x0380) >> 7; | 
 | 243 |  | 
 | 244 | 	if (version & 0x8000) | 
 | 245 | 		maj_ver |= 0x0008; | 
 | 246 |  | 
 | 247 | 	sprintf(bts_scr_name, "TIInit_%d.%d.%d.bts", chip, maj_ver, min_ver); | 
| Naveen Jain | e2a5328 | 2010-06-09 03:45:33 -0500 | [diff] [blame] | 248 |  | 
 | 249 | 	/* to be accessed later via sysfs entry */ | 
 | 250 | 	kim_gdata->version.full = version; | 
 | 251 | 	kim_gdata->version.chip = chip; | 
 | 252 | 	kim_gdata->version.maj_ver = maj_ver; | 
 | 253 | 	kim_gdata->version.min_ver = min_ver; | 
 | 254 |  | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 255 | 	pr_info("%s", bts_scr_name); | 
| Pavan Savoy | 320920c | 2010-07-14 08:21:12 -0500 | [diff] [blame] | 256 | 	return 0; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 257 | } | 
 | 258 |  | 
| Pavan Savoy | 27712b3 | 2012-08-03 14:49:40 -0500 | [diff] [blame] | 259 | static void skip_change_remote_baud(unsigned char **ptr, long *len) | 
| Pavan Savoy | ef04d12 | 2011-02-04 02:23:13 -0600 | [diff] [blame] | 260 | { | 
 | 261 | 	unsigned char *nxt_action, *cur_action; | 
 | 262 | 	cur_action = *ptr; | 
 | 263 |  | 
 | 264 | 	nxt_action = cur_action + sizeof(struct bts_action) + | 
 | 265 | 		((struct bts_action *) cur_action)->size; | 
 | 266 |  | 
 | 267 | 	if (((struct bts_action *) nxt_action)->type != ACTION_WAIT_EVENT) { | 
 | 268 | 		pr_err("invalid action after change remote baud command"); | 
 | 269 | 	} else { | 
 | 270 | 		*ptr = *ptr + sizeof(struct bts_action) + | 
| Shahar Lev | 9d031d9 | 2011-05-23 11:36:11 +0300 | [diff] [blame] | 271 | 			((struct bts_action *)cur_action)->size; | 
| Pavan Savoy | ef04d12 | 2011-02-04 02:23:13 -0600 | [diff] [blame] | 272 | 		*len = *len - (sizeof(struct bts_action) + | 
| Shahar Lev | 9d031d9 | 2011-05-23 11:36:11 +0300 | [diff] [blame] | 273 | 				((struct bts_action *)cur_action)->size); | 
| Pavan Savoy | ef04d12 | 2011-02-04 02:23:13 -0600 | [diff] [blame] | 274 | 		/* warn user on not commenting these in firmware */ | 
 | 275 | 		pr_warn("skipping the wait event of change remote baud"); | 
 | 276 | 	} | 
 | 277 | } | 
 | 278 |  | 
| Pavan Savoy | 36b5aee4 | 2010-07-22 05:32:06 -0500 | [diff] [blame] | 279 | /** | 
 | 280 |  * download_firmware - | 
 | 281 |  *	internal function which parses through the .bts firmware | 
 | 282 |  *	script file intreprets SEND, DELAY actions only as of now | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 283 |  */ | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 284 | static long download_firmware(struct kim_data_s *kim_gdata) | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 285 | { | 
| Pavan Savoy | 320920c | 2010-07-14 08:21:12 -0500 | [diff] [blame] | 286 | 	long err = 0; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 287 | 	long len = 0; | 
| Pavan Savoy | 73f12e8 | 2010-10-12 16:27:38 -0400 | [diff] [blame] | 288 | 	unsigned char *ptr = NULL; | 
 | 289 | 	unsigned char *action_ptr = NULL; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 290 | 	unsigned char bts_scr_name[30] = { 0 };	/* 30 char long bts scr name? */ | 
| Pavan Savoy | ef04d12 | 2011-02-04 02:23:13 -0600 | [diff] [blame] | 291 | 	int wr_room_space; | 
 | 292 | 	int cmd_size; | 
 | 293 | 	unsigned long timeout; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 294 |  | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 295 | 	err = read_local_version(kim_gdata, bts_scr_name); | 
| Pavan Savoy | 320920c | 2010-07-14 08:21:12 -0500 | [diff] [blame] | 296 | 	if (err != 0) { | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 297 | 		pr_err("kim: failed to read local ver"); | 
 | 298 | 		return err; | 
 | 299 | 	} | 
 | 300 | 	err = | 
 | 301 | 	    request_firmware(&kim_gdata->fw_entry, bts_scr_name, | 
 | 302 | 			     &kim_gdata->kim_pdev->dev); | 
 | 303 | 	if (unlikely((err != 0) || (kim_gdata->fw_entry->data == NULL) || | 
 | 304 | 		     (kim_gdata->fw_entry->size == 0))) { | 
 | 305 | 		pr_err(" request_firmware failed(errno %ld) for %s", err, | 
 | 306 | 			   bts_scr_name); | 
| Pavan Savoy | 7044266 | 2011-02-04 02:23:11 -0600 | [diff] [blame] | 307 | 		return -EINVAL; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 308 | 	} | 
 | 309 | 	ptr = (void *)kim_gdata->fw_entry->data; | 
 | 310 | 	len = kim_gdata->fw_entry->size; | 
 | 311 | 	/* bts_header to remove out magic number and | 
 | 312 | 	 * version | 
 | 313 | 	 */ | 
 | 314 | 	ptr += sizeof(struct bts_header); | 
 | 315 | 	len -= sizeof(struct bts_header); | 
 | 316 |  | 
 | 317 | 	while (len > 0 && ptr) { | 
| Pavan Savoy | e6d9e64 | 2010-07-22 05:32:05 -0500 | [diff] [blame] | 318 | 		pr_debug(" action size %d, type %d ", | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 319 | 			   ((struct bts_action *)ptr)->size, | 
 | 320 | 			   ((struct bts_action *)ptr)->type); | 
 | 321 |  | 
 | 322 | 		switch (((struct bts_action *)ptr)->type) { | 
 | 323 | 		case ACTION_SEND_COMMAND:	/* action send */ | 
| Pavan Savoy | 2f81a02 | 2011-08-10 10:18:34 -0500 | [diff] [blame] | 324 | 			pr_debug("S"); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 325 | 			action_ptr = &(((struct bts_action *)ptr)->data[0]); | 
 | 326 | 			if (unlikely | 
 | 327 | 			    (((struct hci_command *)action_ptr)->opcode == | 
 | 328 | 			     0xFF36)) { | 
 | 329 | 				/* ignore remote change | 
 | 330 | 				 * baud rate HCI VS command */ | 
| Pavan Savoy | ef04d12 | 2011-02-04 02:23:13 -0600 | [diff] [blame] | 331 | 				pr_warn("change remote baud" | 
| Pavan Savoy | e6d9e64 | 2010-07-22 05:32:05 -0500 | [diff] [blame] | 332 | 				    " rate command in firmware"); | 
| Pavan Savoy | ef04d12 | 2011-02-04 02:23:13 -0600 | [diff] [blame] | 333 | 				skip_change_remote_baud(&ptr, &len); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 334 | 				break; | 
 | 335 | 			} | 
| Pavan Savoy | ef04d12 | 2011-02-04 02:23:13 -0600 | [diff] [blame] | 336 | 			/* | 
 | 337 | 			 * Make sure we have enough free space in uart | 
 | 338 | 			 * tx buffer to write current firmware command | 
 | 339 | 			 */ | 
 | 340 | 			cmd_size = ((struct bts_action *)ptr)->size; | 
 | 341 | 			timeout = jiffies + msecs_to_jiffies(CMD_WR_TIME); | 
 | 342 | 			do { | 
 | 343 | 				wr_room_space = | 
 | 344 | 					st_get_uart_wr_room(kim_gdata->core_data); | 
 | 345 | 				if (wr_room_space < 0) { | 
 | 346 | 					pr_err("Unable to get free " | 
 | 347 | 							"space info from uart tx buffer"); | 
 | 348 | 					release_firmware(kim_gdata->fw_entry); | 
 | 349 | 					return wr_room_space; | 
 | 350 | 				} | 
 | 351 | 				mdelay(1); /* wait 1ms before checking room */ | 
 | 352 | 			} while ((wr_room_space < cmd_size) && | 
 | 353 | 					time_before(jiffies, timeout)); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 354 |  | 
| Pavan Savoy | ef04d12 | 2011-02-04 02:23:13 -0600 | [diff] [blame] | 355 | 			/* Timeout happened ? */ | 
 | 356 | 			if (time_after_eq(jiffies, timeout)) { | 
 | 357 | 				pr_err("Timeout while waiting for free " | 
 | 358 | 						"free space in uart tx buffer"); | 
 | 359 | 				release_firmware(kim_gdata->fw_entry); | 
 | 360 | 				return -ETIMEDOUT; | 
 | 361 | 			} | 
| Pavan Savoy | 2f81a02 | 2011-08-10 10:18:34 -0500 | [diff] [blame] | 362 | 			/* reinit completion before sending for the | 
 | 363 | 			 * relevant wait | 
 | 364 | 			 */ | 
 | 365 | 			INIT_COMPLETION(kim_gdata->kim_rcvd); | 
| Pavan Savoy | ef04d12 | 2011-02-04 02:23:13 -0600 | [diff] [blame] | 366 |  | 
 | 367 | 			/* | 
 | 368 | 			 * Free space found in uart buffer, call st_int_write | 
 | 369 | 			 * to send current firmware command to the uart tx | 
 | 370 | 			 * buffer. | 
 | 371 | 			 */ | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 372 | 			err = st_int_write(kim_gdata->core_data, | 
 | 373 | 			((struct bts_action_send *)action_ptr)->data, | 
 | 374 | 					   ((struct bts_action *)ptr)->size); | 
 | 375 | 			if (unlikely(err < 0)) { | 
 | 376 | 				release_firmware(kim_gdata->fw_entry); | 
| Pavan Savoy | 7044266 | 2011-02-04 02:23:11 -0600 | [diff] [blame] | 377 | 				return err; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 378 | 			} | 
| Pavan Savoy | ef04d12 | 2011-02-04 02:23:13 -0600 | [diff] [blame] | 379 | 			/* | 
 | 380 | 			 * Check number of bytes written to the uart tx buffer | 
 | 381 | 			 * and requested command write size | 
 | 382 | 			 */ | 
 | 383 | 			if (err != cmd_size) { | 
 | 384 | 				pr_err("Number of bytes written to uart " | 
 | 385 | 						"tx buffer are not matching with " | 
 | 386 | 						"requested cmd write size"); | 
 | 387 | 				release_firmware(kim_gdata->fw_entry); | 
 | 388 | 				return -EIO; | 
 | 389 | 			} | 
 | 390 | 			break; | 
 | 391 | 		case ACTION_WAIT_EVENT:  /* wait */ | 
| Pavan Savoy | 2f81a02 | 2011-08-10 10:18:34 -0500 | [diff] [blame] | 392 | 			pr_debug("W"); | 
| Pavan Savoy | 5370235 | 2012-08-03 14:49:42 -0500 | [diff] [blame] | 393 | 			if (!wait_for_completion_interruptible_timeout( | 
 | 394 | 					&kim_gdata->kim_rcvd, | 
 | 395 | 					msecs_to_jiffies(CMD_RESP_TIME))) { | 
| Pavan Savoy | ef04d12 | 2011-02-04 02:23:13 -0600 | [diff] [blame] | 396 | 				pr_err("response timeout during fw download "); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 397 | 				/* timed out */ | 
 | 398 | 				release_firmware(kim_gdata->fw_entry); | 
| Pavan Savoy | 7044266 | 2011-02-04 02:23:11 -0600 | [diff] [blame] | 399 | 				return -ETIMEDOUT; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 400 | 			} | 
| Pavan Savoy | ef04d12 | 2011-02-04 02:23:13 -0600 | [diff] [blame] | 401 | 			INIT_COMPLETION(kim_gdata->kim_rcvd); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 402 | 			break; | 
 | 403 | 		case ACTION_DELAY:	/* sleep */ | 
 | 404 | 			pr_info("sleep command in scr"); | 
 | 405 | 			action_ptr = &(((struct bts_action *)ptr)->data[0]); | 
 | 406 | 			mdelay(((struct bts_action_delay *)action_ptr)->msec); | 
 | 407 | 			break; | 
 | 408 | 		} | 
 | 409 | 		len = | 
 | 410 | 		    len - (sizeof(struct bts_action) + | 
 | 411 | 			   ((struct bts_action *)ptr)->size); | 
 | 412 | 		ptr = | 
 | 413 | 		    ptr + sizeof(struct bts_action) + | 
 | 414 | 		    ((struct bts_action *)ptr)->size; | 
 | 415 | 	} | 
 | 416 | 	/* fw download complete */ | 
 | 417 | 	release_firmware(kim_gdata->fw_entry); | 
| Pavan Savoy | 320920c | 2010-07-14 08:21:12 -0500 | [diff] [blame] | 418 | 	return 0; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 419 | } | 
 | 420 |  | 
 | 421 | /**********************************************************************/ | 
 | 422 | /* functions called from ST core */ | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 423 | /* called from ST Core, when REG_IN_PROGRESS (registration in progress) | 
 | 424 |  * can be because of | 
 | 425 |  * 1. response to read local version | 
 | 426 |  * 2. during send/recv's of firmware download | 
 | 427 |  */ | 
 | 428 | void st_kim_recv(void *disc_data, const unsigned char *data, long count) | 
 | 429 | { | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 430 | 	struct st_data_s	*st_gdata = (struct st_data_s *)disc_data; | 
 | 431 | 	struct kim_data_s	*kim_gdata = st_gdata->kim_data; | 
 | 432 |  | 
| Pavan Savoy | 8565adb | 2012-08-03 14:49:43 -0500 | [diff] [blame] | 433 | 	/* proceed to gather all data and distinguish read fw version response | 
 | 434 | 	 * from other fw responses when data gathering is complete | 
 | 435 | 	 */ | 
 | 436 | 	kim_int_recv(kim_gdata, data, count); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 437 | 	return; | 
 | 438 | } | 
 | 439 |  | 
 | 440 | /* to signal completion of line discipline installation | 
 | 441 |  * called from ST Core, upon tty_open | 
 | 442 |  */ | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 443 | void st_kim_complete(void *kim_data) | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 444 | { | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 445 | 	struct kim_data_s	*kim_gdata = (struct kim_data_s *)kim_data; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 446 | 	complete(&kim_gdata->ldisc_installed); | 
 | 447 | } | 
 | 448 |  | 
| Pavan Savoy | 36b5aee4 | 2010-07-22 05:32:06 -0500 | [diff] [blame] | 449 | /** | 
 | 450 |  * st_kim_start - called from ST Core upon 1st registration | 
 | 451 |  *	This involves toggling the chip enable gpio, reading | 
 | 452 |  *	the firmware version from chip, forming the fw file name | 
 | 453 |  *	based on the chip version, requesting the fw, parsing it | 
 | 454 |  *	and perform download(send/recv). | 
 | 455 |  */ | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 456 | long st_kim_start(void *kim_data) | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 457 | { | 
| Pavan Savoy | 320920c | 2010-07-14 08:21:12 -0500 | [diff] [blame] | 458 | 	long err = 0; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 459 | 	long retry = POR_RETRY_COUNT; | 
| Pavan Savoy | 0d7c5f2 | 2011-08-10 10:18:31 -0500 | [diff] [blame] | 460 | 	struct ti_st_plat_data	*pdata; | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 461 | 	struct kim_data_s	*kim_gdata = (struct kim_data_s *)kim_data; | 
 | 462 |  | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 463 | 	pr_info(" %s", __func__); | 
| Pavan Savoy | 0d7c5f2 | 2011-08-10 10:18:31 -0500 | [diff] [blame] | 464 | 	pdata = kim_gdata->kim_pdev->dev.platform_data; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 465 |  | 
 | 466 | 	do { | 
| Pavan Savoy | 0d7c5f2 | 2011-08-10 10:18:31 -0500 | [diff] [blame] | 467 | 		/* platform specific enabling code here */ | 
 | 468 | 		if (pdata->chip_enable) | 
 | 469 | 			pdata->chip_enable(kim_gdata); | 
 | 470 |  | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 471 | 		/* re-initialize the completion */ | 
 | 472 | 		INIT_COMPLETION(kim_gdata->ldisc_installed); | 
| Pavan Savoy | ec60d0a | 2011-02-04 02:23:10 -0600 | [diff] [blame] | 473 | 		/* send notification to UIM */ | 
 | 474 | 		kim_gdata->ldisc_install = 1; | 
 | 475 | 		pr_info("ldisc_install = 1"); | 
 | 476 | 		sysfs_notify(&kim_gdata->kim_pdev->dev.kobj, | 
 | 477 | 				NULL, "install"); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 478 | 		/* wait for ldisc to be installed */ | 
| Pavan Savoy | 5370235 | 2012-08-03 14:49:42 -0500 | [diff] [blame] | 479 | 		err = wait_for_completion_interruptible_timeout( | 
 | 480 | 			&kim_gdata->ldisc_installed, msecs_to_jiffies(LDISC_TIME)); | 
| Pavan Savoy | 18ccecf | 2011-12-15 10:38:21 -0600 | [diff] [blame] | 481 | 		if (!err) { | 
 | 482 | 			/* ldisc installation timeout, | 
 | 483 | 			 * flush uart, power cycle BT_EN */ | 
 | 484 | 			pr_err("ldisc installation timeout"); | 
 | 485 | 			err = st_kim_stop(kim_gdata); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 486 | 			continue; | 
 | 487 | 		} else { | 
 | 488 | 			/* ldisc installed now */ | 
| Pavan Savoy | 18ccecf | 2011-12-15 10:38:21 -0600 | [diff] [blame] | 489 | 			pr_info("line discipline installed"); | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 490 | 			err = download_firmware(kim_gdata); | 
| Pavan Savoy | 320920c | 2010-07-14 08:21:12 -0500 | [diff] [blame] | 491 | 			if (err != 0) { | 
| Pavan Savoy | 18ccecf | 2011-12-15 10:38:21 -0600 | [diff] [blame] | 492 | 				/* ldisc installed but fw download failed, | 
 | 493 | 				 * flush uart & power cycle BT_EN */ | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 494 | 				pr_err("download firmware failed"); | 
| Pavan Savoy | 18ccecf | 2011-12-15 10:38:21 -0600 | [diff] [blame] | 495 | 				err = st_kim_stop(kim_gdata); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 496 | 				continue; | 
 | 497 | 			} else {	/* on success don't retry */ | 
 | 498 | 				break; | 
 | 499 | 			} | 
 | 500 | 		} | 
 | 501 | 	} while (retry--); | 
 | 502 | 	return err; | 
 | 503 | } | 
 | 504 |  | 
| Pavan Savoy | 36b5aee4 | 2010-07-22 05:32:06 -0500 | [diff] [blame] | 505 | /** | 
| Pavan Savoy | 18ccecf | 2011-12-15 10:38:21 -0600 | [diff] [blame] | 506 |  * st_kim_stop - stop communication with chip. | 
 | 507 |  *	This can be called from ST Core/KIM, on the- | 
 | 508 |  *	(a) last un-register when chip need not be powered there-after, | 
 | 509 |  *	(b) upon failure to either install ldisc or download firmware. | 
 | 510 |  *	The function is responsible to (a) notify UIM about un-installation, | 
 | 511 |  *	(b) flush UART if the ldisc was installed. | 
| Pavan Savoy | eccf297 | 2012-08-03 14:49:39 -0500 | [diff] [blame] | 512 |  *	(c) invoke platform's chip disabling routine. | 
| Pavan Savoy | 36b5aee4 | 2010-07-22 05:32:06 -0500 | [diff] [blame] | 513 |  */ | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 514 | long st_kim_stop(void *kim_data) | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 515 | { | 
| Pavan Savoy | 320920c | 2010-07-14 08:21:12 -0500 | [diff] [blame] | 516 | 	long err = 0; | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 517 | 	struct kim_data_s	*kim_gdata = (struct kim_data_s *)kim_data; | 
| Pavan Savoy | 0d7c5f2 | 2011-08-10 10:18:31 -0500 | [diff] [blame] | 518 | 	struct ti_st_plat_data	*pdata = | 
 | 519 | 		kim_gdata->kim_pdev->dev.platform_data; | 
| Pavan Savoy | 18ccecf | 2011-12-15 10:38:21 -0600 | [diff] [blame] | 520 | 	struct tty_struct	*tty = kim_gdata->core_data->tty; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 521 |  | 
 | 522 | 	INIT_COMPLETION(kim_gdata->ldisc_installed); | 
| Pavan Savoy | ec60d0a | 2011-02-04 02:23:10 -0600 | [diff] [blame] | 523 |  | 
| Pavan Savoy | 18ccecf | 2011-12-15 10:38:21 -0600 | [diff] [blame] | 524 | 	if (tty) {	/* can be called before ldisc is installed */ | 
 | 525 | 		/* Flush any pending characters in the driver and discipline. */ | 
 | 526 | 		tty_ldisc_flush(tty); | 
 | 527 | 		tty_driver_flush_buffer(tty); | 
 | 528 | 		tty->ops->flush_buffer(tty); | 
 | 529 | 	} | 
| Pavan Savoy | ec60d0a | 2011-02-04 02:23:10 -0600 | [diff] [blame] | 530 |  | 
 | 531 | 	/* send uninstall notification to UIM */ | 
 | 532 | 	pr_info("ldisc_install = 0"); | 
 | 533 | 	kim_gdata->ldisc_install = 0; | 
 | 534 | 	sysfs_notify(&kim_gdata->kim_pdev->dev.kobj, NULL, "install"); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 535 |  | 
 | 536 | 	/* wait for ldisc to be un-installed */ | 
| Pavan Savoy | 5370235 | 2012-08-03 14:49:42 -0500 | [diff] [blame] | 537 | 	err = wait_for_completion_interruptible_timeout( | 
 | 538 | 		&kim_gdata->ldisc_installed, msecs_to_jiffies(LDISC_TIME)); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 539 | 	if (!err) {		/* timeout */ | 
 | 540 | 		pr_err(" timed out waiting for ldisc to be un-installed"); | 
| Pavan Savoy | b64365a | 2012-08-03 14:49:41 -0500 | [diff] [blame] | 541 | 		err = -ETIMEDOUT; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 542 | 	} | 
 | 543 |  | 
| Pavan Savoy | 0d7c5f2 | 2011-08-10 10:18:31 -0500 | [diff] [blame] | 544 | 	/* platform specific disable */ | 
 | 545 | 	if (pdata->chip_disable) | 
 | 546 | 		pdata->chip_disable(kim_gdata); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 547 | 	return err; | 
 | 548 | } | 
 | 549 |  | 
 | 550 | /**********************************************************************/ | 
 | 551 | /* functions called from subsystems */ | 
| Pavan Savoy | c1afac1 | 2010-07-28 02:25:59 -0500 | [diff] [blame] | 552 | /* called when debugfs entry is read from */ | 
| Naveen Jain | e2a5328 | 2010-06-09 03:45:33 -0500 | [diff] [blame] | 553 |  | 
| Pavan Savoy | c1afac1 | 2010-07-28 02:25:59 -0500 | [diff] [blame] | 554 | static int show_version(struct seq_file *s, void *unused) | 
| Naveen Jain | e2a5328 | 2010-06-09 03:45:33 -0500 | [diff] [blame] | 555 | { | 
| Pavan Savoy | c1afac1 | 2010-07-28 02:25:59 -0500 | [diff] [blame] | 556 | 	struct kim_data_s *kim_gdata = (struct kim_data_s *)s->private; | 
 | 557 | 	seq_printf(s, "%04X %d.%d.%d\n", kim_gdata->version.full, | 
| Naveen Jain | e2a5328 | 2010-06-09 03:45:33 -0500 | [diff] [blame] | 558 | 			kim_gdata->version.chip, kim_gdata->version.maj_ver, | 
 | 559 | 			kim_gdata->version.min_ver); | 
| Pavan Savoy | c1afac1 | 2010-07-28 02:25:59 -0500 | [diff] [blame] | 560 | 	return 0; | 
| Naveen Jain | e2a5328 | 2010-06-09 03:45:33 -0500 | [diff] [blame] | 561 | } | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 562 |  | 
| Pavan Savoy | c1afac1 | 2010-07-28 02:25:59 -0500 | [diff] [blame] | 563 | static int show_list(struct seq_file *s, void *unused) | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 564 | { | 
| Pavan Savoy | c1afac1 | 2010-07-28 02:25:59 -0500 | [diff] [blame] | 565 | 	struct kim_data_s *kim_gdata = (struct kim_data_s *)s->private; | 
 | 566 | 	kim_st_list_protocols(kim_gdata->core_data, s); | 
 | 567 | 	return 0; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 568 | } | 
 | 569 |  | 
| Pavan Savoy | ec60d0a | 2011-02-04 02:23:10 -0600 | [diff] [blame] | 570 | static ssize_t show_install(struct device *dev, | 
 | 571 | 		struct device_attribute *attr, char *buf) | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 572 | { | 
| Pavan Savoy | ec60d0a | 2011-02-04 02:23:10 -0600 | [diff] [blame] | 573 | 	struct kim_data_s *kim_data = dev_get_drvdata(dev); | 
 | 574 | 	return sprintf(buf, "%d\n", kim_data->ldisc_install); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 575 | } | 
 | 576 |  | 
| Pavan Savoy | 933aae5 | 2011-12-15 10:38:22 -0600 | [diff] [blame] | 577 | #ifdef DEBUG | 
 | 578 | static ssize_t store_dev_name(struct device *dev, | 
 | 579 | 		struct device_attribute *attr, const char *buf, size_t count) | 
 | 580 | { | 
 | 581 | 	struct kim_data_s *kim_data = dev_get_drvdata(dev); | 
 | 582 | 	pr_debug("storing dev name >%s<", buf); | 
 | 583 | 	strncpy(kim_data->dev_name, buf, count); | 
 | 584 | 	pr_debug("stored dev name >%s<", kim_data->dev_name); | 
 | 585 | 	return count; | 
 | 586 | } | 
 | 587 |  | 
 | 588 | static ssize_t store_baud_rate(struct device *dev, | 
 | 589 | 		struct device_attribute *attr, const char *buf, size_t count) | 
 | 590 | { | 
 | 591 | 	struct kim_data_s *kim_data = dev_get_drvdata(dev); | 
 | 592 | 	pr_debug("storing baud rate >%s<", buf); | 
 | 593 | 	sscanf(buf, "%ld", &kim_data->baud_rate); | 
 | 594 | 	pr_debug("stored baud rate >%ld<", kim_data->baud_rate); | 
 | 595 | 	return count; | 
 | 596 | } | 
 | 597 | #endif	/* if DEBUG */ | 
 | 598 |  | 
| Pavan Savoy | ec60d0a | 2011-02-04 02:23:10 -0600 | [diff] [blame] | 599 | static ssize_t show_dev_name(struct device *dev, | 
 | 600 | 		struct device_attribute *attr, char *buf) | 
 | 601 | { | 
 | 602 | 	struct kim_data_s *kim_data = dev_get_drvdata(dev); | 
 | 603 | 	return sprintf(buf, "%s\n", kim_data->dev_name); | 
 | 604 | } | 
 | 605 |  | 
 | 606 | static ssize_t show_baud_rate(struct device *dev, | 
 | 607 | 		struct device_attribute *attr, char *buf) | 
 | 608 | { | 
 | 609 | 	struct kim_data_s *kim_data = dev_get_drvdata(dev); | 
 | 610 | 	return sprintf(buf, "%ld\n", kim_data->baud_rate); | 
 | 611 | } | 
 | 612 |  | 
 | 613 | static ssize_t show_flow_cntrl(struct device *dev, | 
 | 614 | 		struct device_attribute *attr, char *buf) | 
 | 615 | { | 
 | 616 | 	struct kim_data_s *kim_data = dev_get_drvdata(dev); | 
 | 617 | 	return sprintf(buf, "%d\n", kim_data->flow_cntrl); | 
 | 618 | } | 
 | 619 |  | 
 | 620 | /* structures specific for sysfs entries */ | 
 | 621 | static struct kobj_attribute ldisc_install = | 
 | 622 | __ATTR(install, 0444, (void *)show_install, NULL); | 
 | 623 |  | 
 | 624 | static struct kobj_attribute uart_dev_name = | 
| Pavan Savoy | 933aae5 | 2011-12-15 10:38:22 -0600 | [diff] [blame] | 625 | #ifdef DEBUG	/* TODO: move this to debug-fs if possible */ | 
 | 626 | __ATTR(dev_name, 0644, (void *)show_dev_name, (void *)store_dev_name); | 
 | 627 | #else | 
| Pavan Savoy | ec60d0a | 2011-02-04 02:23:10 -0600 | [diff] [blame] | 628 | __ATTR(dev_name, 0444, (void *)show_dev_name, NULL); | 
| Pavan Savoy | 933aae5 | 2011-12-15 10:38:22 -0600 | [diff] [blame] | 629 | #endif | 
| Pavan Savoy | ec60d0a | 2011-02-04 02:23:10 -0600 | [diff] [blame] | 630 |  | 
 | 631 | static struct kobj_attribute uart_baud_rate = | 
| Pavan Savoy | 933aae5 | 2011-12-15 10:38:22 -0600 | [diff] [blame] | 632 | #ifdef DEBUG	/* TODO: move to debugfs */ | 
 | 633 | __ATTR(baud_rate, 0644, (void *)show_baud_rate, (void *)store_baud_rate); | 
 | 634 | #else | 
| Pavan Savoy | ec60d0a | 2011-02-04 02:23:10 -0600 | [diff] [blame] | 635 | __ATTR(baud_rate, 0444, (void *)show_baud_rate, NULL); | 
| Pavan Savoy | 933aae5 | 2011-12-15 10:38:22 -0600 | [diff] [blame] | 636 | #endif | 
| Pavan Savoy | ec60d0a | 2011-02-04 02:23:10 -0600 | [diff] [blame] | 637 |  | 
 | 638 | static struct kobj_attribute uart_flow_cntrl = | 
 | 639 | __ATTR(flow_cntrl, 0444, (void *)show_flow_cntrl, NULL); | 
 | 640 |  | 
 | 641 | static struct attribute *uim_attrs[] = { | 
 | 642 | 	&ldisc_install.attr, | 
 | 643 | 	&uart_dev_name.attr, | 
 | 644 | 	&uart_baud_rate.attr, | 
 | 645 | 	&uart_flow_cntrl.attr, | 
 | 646 | 	NULL, | 
 | 647 | }; | 
 | 648 |  | 
 | 649 | static struct attribute_group uim_attr_grp = { | 
 | 650 | 	.attrs = uim_attrs, | 
 | 651 | }; | 
 | 652 |  | 
| Pavan Savoy | 36b5aee4 | 2010-07-22 05:32:06 -0500 | [diff] [blame] | 653 | /** | 
 | 654 |  * st_kim_ref - reference the core's data | 
 | 655 |  *	This references the per-ST platform device in the arch/xx/ | 
 | 656 |  *	board-xx.c file. | 
 | 657 |  *	This would enable multiple such platform devices to exist | 
 | 658 |  *	on a given platform | 
 | 659 |  */ | 
| Pavan Savoy | dbd3a87 | 2010-08-19 14:08:51 -0400 | [diff] [blame] | 660 | void st_kim_ref(struct st_data_s **core_data, int id) | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 661 | { | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 662 | 	struct platform_device	*pdev; | 
 | 663 | 	struct kim_data_s	*kim_gdata; | 
 | 664 | 	/* get kim_gdata reference from platform device */ | 
| Pavan Savoy | dbd3a87 | 2010-08-19 14:08:51 -0400 | [diff] [blame] | 665 | 	pdev = st_get_plat_device(id); | 
| Steven Rostedt | 7316a9f | 2011-05-23 16:45:32 -0400 | [diff] [blame] | 666 | 	if (!pdev) { | 
 | 667 | 		*core_data = NULL; | 
 | 668 | 		return; | 
 | 669 | 	} | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 670 | 	kim_gdata = dev_get_drvdata(&pdev->dev); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 671 | 	*core_data = kim_gdata->core_data; | 
 | 672 | } | 
 | 673 |  | 
| Pavan Savoy | c1afac1 | 2010-07-28 02:25:59 -0500 | [diff] [blame] | 674 | static int kim_version_open(struct inode *i, struct file *f) | 
 | 675 | { | 
 | 676 | 	return single_open(f, show_version, i->i_private); | 
 | 677 | } | 
 | 678 |  | 
 | 679 | static int kim_list_open(struct inode *i, struct file *f) | 
 | 680 | { | 
 | 681 | 	return single_open(f, show_list, i->i_private); | 
 | 682 | } | 
 | 683 |  | 
 | 684 | static const struct file_operations version_debugfs_fops = { | 
 | 685 | 	/* version info */ | 
 | 686 | 	.open = kim_version_open, | 
 | 687 | 	.read = seq_read, | 
 | 688 | 	.llseek = seq_lseek, | 
 | 689 | 	.release = single_release, | 
 | 690 | }; | 
 | 691 | static const struct file_operations list_debugfs_fops = { | 
 | 692 | 	/* protocols info */ | 
 | 693 | 	.open = kim_list_open, | 
 | 694 | 	.read = seq_read, | 
 | 695 | 	.llseek = seq_lseek, | 
 | 696 | 	.release = single_release, | 
 | 697 | }; | 
 | 698 |  | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 699 | /**********************************************************************/ | 
 | 700 | /* functions called from platform device driver subsystem | 
 | 701 |  * need to have a relevant platform device entry in the platform's | 
 | 702 |  * board-*.c file | 
 | 703 |  */ | 
 | 704 |  | 
| Pavan Savoy | 27712b3 | 2012-08-03 14:49:40 -0500 | [diff] [blame] | 705 | static struct dentry *kim_debugfs_dir; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 706 | static int kim_probe(struct platform_device *pdev) | 
 | 707 | { | 
 | 708 | 	long status; | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 709 | 	struct kim_data_s	*kim_gdata; | 
| Pavan Savoy | ec60d0a | 2011-02-04 02:23:10 -0600 | [diff] [blame] | 710 | 	struct ti_st_plat_data	*pdata = pdev->dev.platform_data; | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 711 |  | 
| Pavan Savoy | dfb7ef7 | 2010-09-10 15:58:55 -0400 | [diff] [blame] | 712 | 	if ((pdev->id != -1) && (pdev->id < MAX_ST_DEVICES)) { | 
 | 713 | 		/* multiple devices could exist */ | 
 | 714 | 		st_kim_devices[pdev->id] = pdev; | 
 | 715 | 	} else { | 
| Lucas De Marchi | 25985ed | 2011-03-30 22:57:33 -0300 | [diff] [blame] | 716 | 		/* platform's sure about existence of 1 device */ | 
| Pavan Savoy | dfb7ef7 | 2010-09-10 15:58:55 -0400 | [diff] [blame] | 717 | 		st_kim_devices[0] = pdev; | 
 | 718 | 	} | 
 | 719 |  | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 720 | 	kim_gdata = kzalloc(sizeof(struct kim_data_s), GFP_ATOMIC); | 
 | 721 | 	if (!kim_gdata) { | 
 | 722 | 		pr_err("no mem to allocate"); | 
 | 723 | 		return -ENOMEM; | 
 | 724 | 	} | 
 | 725 | 	dev_set_drvdata(&pdev->dev, kim_gdata); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 726 |  | 
 | 727 | 	status = st_core_init(&kim_gdata->core_data); | 
 | 728 | 	if (status != 0) { | 
 | 729 | 		pr_err(" ST core init failed"); | 
| Pavan Savoy | 7044266 | 2011-02-04 02:23:11 -0600 | [diff] [blame] | 730 | 		return -EIO; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 731 | 	} | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 732 | 	/* refer to itself */ | 
 | 733 | 	kim_gdata->core_data->kim_data = kim_gdata; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 734 |  | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 735 | 	/* get reference of pdev for request_firmware | 
 | 736 | 	 */ | 
 | 737 | 	kim_gdata->kim_pdev = pdev; | 
 | 738 | 	init_completion(&kim_gdata->kim_rcvd); | 
 | 739 | 	init_completion(&kim_gdata->ldisc_installed); | 
| Naveen Jain | b38fc2d | 2010-06-09 03:45:32 -0500 | [diff] [blame] | 740 |  | 
| Pavan Savoy | ec60d0a | 2011-02-04 02:23:10 -0600 | [diff] [blame] | 741 | 	status = sysfs_create_group(&pdev->dev.kobj, &uim_attr_grp); | 
 | 742 | 	if (status) { | 
 | 743 | 		pr_err("failed to create sysfs entries"); | 
 | 744 | 		return status; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 745 | 	} | 
| Naveen Jain | e2a5328 | 2010-06-09 03:45:33 -0500 | [diff] [blame] | 746 |  | 
| Pavan Savoy | ec60d0a | 2011-02-04 02:23:10 -0600 | [diff] [blame] | 747 | 	/* copying platform data */ | 
 | 748 | 	strncpy(kim_gdata->dev_name, pdata->dev_name, UART_DEV_NAME_LEN); | 
 | 749 | 	kim_gdata->flow_cntrl = pdata->flow_cntrl; | 
 | 750 | 	kim_gdata->baud_rate = pdata->baud_rate; | 
 | 751 | 	pr_info("sysfs entries created\n"); | 
 | 752 |  | 
| Pavan Savoy | c1afac1 | 2010-07-28 02:25:59 -0500 | [diff] [blame] | 753 | 	kim_debugfs_dir = debugfs_create_dir("ti-st", NULL); | 
 | 754 | 	if (IS_ERR(kim_debugfs_dir)) { | 
 | 755 | 		pr_err(" debugfs entries creation failed "); | 
 | 756 | 		kim_debugfs_dir = NULL; | 
| Pavan Savoy | 7044266 | 2011-02-04 02:23:11 -0600 | [diff] [blame] | 757 | 		return -EIO; | 
| Naveen Jain | e2a5328 | 2010-06-09 03:45:33 -0500 | [diff] [blame] | 758 | 	} | 
| Pavan Savoy | c1afac1 | 2010-07-28 02:25:59 -0500 | [diff] [blame] | 759 |  | 
 | 760 | 	debugfs_create_file("version", S_IRUGO, kim_debugfs_dir, | 
 | 761 | 				kim_gdata, &version_debugfs_fops); | 
 | 762 | 	debugfs_create_file("protocols", S_IRUGO, kim_debugfs_dir, | 
 | 763 | 				kim_gdata, &list_debugfs_fops); | 
 | 764 | 	pr_info(" debugfs entries created "); | 
| Pavan Savoy | 320920c | 2010-07-14 08:21:12 -0500 | [diff] [blame] | 765 | 	return 0; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 766 | } | 
 | 767 |  | 
 | 768 | static int kim_remove(struct platform_device *pdev) | 
 | 769 | { | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 770 | 	struct kim_data_s	*kim_gdata; | 
 | 771 |  | 
 | 772 | 	kim_gdata = dev_get_drvdata(&pdev->dev); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 773 |  | 
| Pavan Savoy | 781a739 | 2011-02-04 02:23:15 -0600 | [diff] [blame] | 774 | 	debugfs_remove_recursive(kim_debugfs_dir); | 
| Pavan Savoy | ec60d0a | 2011-02-04 02:23:10 -0600 | [diff] [blame] | 775 | 	sysfs_remove_group(&pdev->dev.kobj, &uim_attr_grp); | 
| Pavan Savoy | 781a739 | 2011-02-04 02:23:15 -0600 | [diff] [blame] | 776 | 	pr_info("sysfs entries removed"); | 
 | 777 |  | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 778 | 	kim_gdata->kim_pdev = NULL; | 
 | 779 | 	st_core_exit(kim_gdata->core_data); | 
| Pavan Savoy | 38d9df4 | 2010-07-08 08:55:17 -0500 | [diff] [blame] | 780 |  | 
 | 781 | 	kfree(kim_gdata); | 
 | 782 | 	kim_gdata = NULL; | 
| Pavan Savoy | 320920c | 2010-07-14 08:21:12 -0500 | [diff] [blame] | 783 | 	return 0; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 784 | } | 
 | 785 |  | 
| Pavan Savoy | 27712b3 | 2012-08-03 14:49:40 -0500 | [diff] [blame] | 786 | static int kim_suspend(struct platform_device *pdev, pm_message_t state) | 
| Pavan Savoy | ec60d0a | 2011-02-04 02:23:10 -0600 | [diff] [blame] | 787 | { | 
 | 788 | 	struct ti_st_plat_data	*pdata = pdev->dev.platform_data; | 
 | 789 |  | 
 | 790 | 	if (pdata->suspend) | 
 | 791 | 		return pdata->suspend(pdev, state); | 
 | 792 |  | 
 | 793 | 	return -EOPNOTSUPP; | 
 | 794 | } | 
 | 795 |  | 
| Pavan Savoy | 27712b3 | 2012-08-03 14:49:40 -0500 | [diff] [blame] | 796 | static int kim_resume(struct platform_device *pdev) | 
| Pavan Savoy | ec60d0a | 2011-02-04 02:23:10 -0600 | [diff] [blame] | 797 | { | 
 | 798 | 	struct ti_st_plat_data	*pdata = pdev->dev.platform_data; | 
 | 799 |  | 
 | 800 | 	if (pdata->resume) | 
 | 801 | 		return pdata->resume(pdev); | 
 | 802 |  | 
 | 803 | 	return -EOPNOTSUPP; | 
 | 804 | } | 
 | 805 |  | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 806 | /**********************************************************************/ | 
 | 807 | /* entry point for ST KIM module, called in from ST Core */ | 
| Pavan Savoy | ec60d0a | 2011-02-04 02:23:10 -0600 | [diff] [blame] | 808 | static struct platform_driver kim_platform_driver = { | 
 | 809 | 	.probe = kim_probe, | 
 | 810 | 	.remove = kim_remove, | 
 | 811 | 	.suspend = kim_suspend, | 
 | 812 | 	.resume = kim_resume, | 
 | 813 | 	.driver = { | 
 | 814 | 		.name = "kim", | 
 | 815 | 		.owner = THIS_MODULE, | 
 | 816 | 	}, | 
 | 817 | }; | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 818 |  | 
| Axel Lin | b00e126 | 2012-01-22 15:33:49 +0800 | [diff] [blame] | 819 | module_platform_driver(kim_platform_driver); | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 820 |  | 
| Pavan Savoy | d0088ce | 2010-04-08 13:16:54 -0500 | [diff] [blame] | 821 | MODULE_AUTHOR("Pavan Savoy <pavan_savoy@ti.com>"); | 
 | 822 | MODULE_DESCRIPTION("Shared Transport Driver for TI BT/FM/GPS combo chips "); | 
 | 823 | MODULE_LICENSE("GPL"); |