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