xref: /linux/drivers/misc/ti-st/st_ll.c (revision c0e297dc61f8d4453e07afbea1fa8d0e67cd4a34)
1 /*
2  *  Shared Transport driver
3  *	HCI-LL module responsible for TI proprietary HCI_LL protocol
4  *  Copyright (C) 2009-2010 Texas Instruments
5  *  Author: Pavan Savoy <pavan_savoy@ti.com>
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21 
22 #define pr_fmt(fmt) "(stll) :" fmt
23 #include <linux/skbuff.h>
24 #include <linux/module.h>
25 #include <linux/platform_device.h>
26 #include <linux/ti_wilink_st.h>
27 
28 /**********************************************************************/
29 
30 /* internal functions */
31 static void send_ll_cmd(struct st_data_s *st_data,
32 	unsigned char cmd)
33 {
34 
35 	pr_debug("%s: writing %x", __func__, cmd);
36 	st_int_write(st_data, &cmd, 1);
37 	return;
38 }
39 
40 static void ll_device_want_to_sleep(struct st_data_s *st_data)
41 {
42 	struct kim_data_s	*kim_data;
43 	struct ti_st_plat_data	*pdata;
44 
45 	pr_debug("%s", __func__);
46 	/* sanity check */
47 	if (st_data->ll_state != ST_LL_AWAKE)
48 		pr_err("ERR hcill: ST_LL_GO_TO_SLEEP_IND"
49 			  "in state %ld", st_data->ll_state);
50 
51 	send_ll_cmd(st_data, LL_SLEEP_ACK);
52 	/* update state */
53 	st_data->ll_state = ST_LL_ASLEEP;
54 
55 	/* communicate to platform about chip asleep */
56 	kim_data = st_data->kim_data;
57 	if (kim_data->kim_pdev->dev.of_node) {
58 		pr_debug("use device tree data");
59 		pdata = dt_pdata;
60 	} else {
61 		pdata = kim_data->kim_pdev->dev.platform_data;
62 	}
63 
64 	if (pdata->chip_asleep)
65 		pdata->chip_asleep(NULL);
66 }
67 
68 static void ll_device_want_to_wakeup(struct st_data_s *st_data)
69 {
70 	struct kim_data_s	*kim_data;
71 	struct ti_st_plat_data	*pdata;
72 
73 	/* diff actions in diff states */
74 	switch (st_data->ll_state) {
75 	case ST_LL_ASLEEP:
76 		send_ll_cmd(st_data, LL_WAKE_UP_ACK);	/* send wake_ack */
77 		break;
78 	case ST_LL_ASLEEP_TO_AWAKE:
79 		/* duplicate wake_ind */
80 		pr_err("duplicate wake_ind while waiting for Wake ack");
81 		break;
82 	case ST_LL_AWAKE:
83 		/* duplicate wake_ind */
84 		pr_err("duplicate wake_ind already AWAKE");
85 		break;
86 	case ST_LL_AWAKE_TO_ASLEEP:
87 		/* duplicate wake_ind */
88 		pr_err("duplicate wake_ind");
89 		break;
90 	}
91 	/* update state */
92 	st_data->ll_state = ST_LL_AWAKE;
93 
94 	/* communicate to platform about chip wakeup */
95 	kim_data = st_data->kim_data;
96 	if (kim_data->kim_pdev->dev.of_node) {
97 		pr_debug("use device tree data");
98 		pdata = dt_pdata;
99 	} else {
100 		pdata = kim_data->kim_pdev->dev.platform_data;
101 	}
102 
103 	if (pdata->chip_awake)
104 		pdata->chip_awake(NULL);
105 }
106 
107 /**********************************************************************/
108 /* functions invoked by ST Core */
109 
110 /* called when ST Core wants to
111  * enable ST LL */
112 void st_ll_enable(struct st_data_s *ll)
113 {
114 	ll->ll_state = ST_LL_AWAKE;
115 }
116 
117 /* called when ST Core /local module wants to
118  * disable ST LL */
119 void st_ll_disable(struct st_data_s *ll)
120 {
121 	ll->ll_state = ST_LL_INVALID;
122 }
123 
124 /* called when ST Core wants to update the state */
125 void st_ll_wakeup(struct st_data_s *ll)
126 {
127 	if (likely(ll->ll_state != ST_LL_AWAKE)) {
128 		send_ll_cmd(ll, LL_WAKE_UP_IND);	/* WAKE_IND */
129 		ll->ll_state = ST_LL_ASLEEP_TO_AWAKE;
130 	} else {
131 		/* don't send the duplicate wake_indication */
132 		pr_err(" Chip already AWAKE ");
133 	}
134 }
135 
136 /* called when ST Core wants the state */
137 unsigned long st_ll_getstate(struct st_data_s *ll)
138 {
139 	pr_debug(" returning state %ld", ll->ll_state);
140 	return ll->ll_state;
141 }
142 
143 /* called from ST Core, when a PM related packet arrives */
144 unsigned long st_ll_sleep_state(struct st_data_s *st_data,
145 	unsigned char cmd)
146 {
147 	switch (cmd) {
148 	case LL_SLEEP_IND:	/* sleep ind */
149 		pr_debug("sleep indication recvd");
150 		ll_device_want_to_sleep(st_data);
151 		break;
152 	case LL_SLEEP_ACK:	/* sleep ack */
153 		pr_err("sleep ack rcvd: host shouldn't");
154 		break;
155 	case LL_WAKE_UP_IND:	/* wake ind */
156 		pr_debug("wake indication recvd");
157 		ll_device_want_to_wakeup(st_data);
158 		break;
159 	case LL_WAKE_UP_ACK:	/* wake ack */
160 		pr_debug("wake ack rcvd");
161 		st_data->ll_state = ST_LL_AWAKE;
162 		break;
163 	default:
164 		pr_err(" unknown input/state ");
165 		return -EINVAL;
166 	}
167 	return 0;
168 }
169 
170 /* Called from ST CORE to initialize ST LL */
171 long st_ll_init(struct st_data_s *ll)
172 {
173 	/* set state to invalid */
174 	ll->ll_state = ST_LL_INVALID;
175 	return 0;
176 }
177 
178 /* Called from ST CORE to de-initialize ST LL */
179 long st_ll_deinit(struct st_data_s *ll)
180 {
181 	return 0;
182 }
183