blob: 6377e7ef7bc9f4e5b4a69583651d71014560f322 [file] [log] [blame]
Sundarajan Srinivasanae6ece02014-07-01 14:01:29 -07001/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are
5 * met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above
9 * copyright notice, this list of conditions and the following
10 * disclaimer in the documentation and/or other materials provided
11 * with the distribution.
12 * * Neither the name of The Linux Fundation, Inc. nor the names of its
13 * contributors may be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 */
29
30#include <smd.h>
31#include <smem.h>
32#include <debug.h>
33#include <platform/irqs.h>
34#include <platform/iomap.h>
35#include <platform/interrupts.h>
36#include <reg.h>
37#include <malloc.h>
38#include <bits.h>
39
40smd_channel_alloc_entry_t *smd_channel_alloc_entry;
41
42
43static void smd_write_state(smd_channel_info_t *ch, uint32_t state)
44{
45 if(state == SMD_SS_OPENED)
46 {
47 ch->port_info->ch0.DTR_DSR = 1;
48 ch->port_info->ch0.CTS_RTS = 1;
49 ch->port_info->ch0.CD = 1;
50 }
51 else
52 {
53 ch->port_info->ch0.DTR_DSR = 0;
54 ch->port_info->ch0.CTS_RTS = 0;
55 ch->port_info->ch0.CD = 0;
56 }
57
58 ch->port_info->ch0.stream_state = state;
59}
60
61static void smd_state_update(smd_channel_info_t *ch, uint32_t flag)
62{
63 ch->port_info->ch0.state_updated = flag;
64}
65
66void smd_get_channel_entry(smd_channel_info_t *ch, uint32_t ch_type)
67{
68 int i = 0;
69
70 for(i = 0; i< SMEM_NUM_SMD_STREAM_CHANNELS; i++)
71 {
72 if((smd_channel_alloc_entry[i].ctype & 0xFF) == ch_type)
73 {
74 memcpy(&ch->alloc_entry, &smd_channel_alloc_entry[i], sizeof(smd_channel_alloc_entry_t));
75 break;
76 }
77 }
78
79 /* Channel not found */
80 if(i == SMEM_NUM_SMD_STREAM_CHANNELS)
81 {
82 dprintf(CRITICAL, "smd channel type %x not found\n", ch_type);
83 ASSERT(0);
84 }
85}
86
87int smd_get_channel_info(smd_channel_info_t *ch, uint32_t ch_type)
88{
89 int ret = 0;
90 uint8_t *fifo_buf = NULL;
91 uint32_t fifo_buf_size = 0;
92 uint32_t size = 0;
93
94 smd_get_channel_entry(ch, ch_type);
95
96
97 ch->port_info = smem_get_alloc_entry(SMEM_SMD_BASE_ID + ch->alloc_entry.cid,
98 &size);
99
100 fifo_buf = smem_get_alloc_entry(SMEM_SMD_FIFO_BASE_ID + ch->alloc_entry.cid,
101 &fifo_buf_size);
102
103 fifo_buf_size /= 2;
104 ch->send_buf = fifo_buf;
105 ch->recv_buf = fifo_buf + fifo_buf_size;
106 ch->fifo_size = fifo_buf_size;
107
108 return ret;
109}
110
111int smd_init(smd_channel_info_t *ch, uint32_t ch_type)
112{
113 unsigned ret = 0;
114
115 smd_channel_alloc_entry = (smd_channel_alloc_entry_t*)memalign(CACHE_LINE, SMD_CHANNEL_ALLOC_MAX);
116 ASSERT(smd_channel_alloc_entry);
117
118 ret = smem_read_alloc_entry(SMEM_CHANNEL_ALLOC_TBL,
119 (void*)smd_channel_alloc_entry,
120 SMD_CHANNEL_ALLOC_MAX);
121 if(ret)
122 {
123 dprintf(CRITICAL,"ERROR reading smem channel alloc tbl\n");
124 return -1;
125 }
126
127 smd_get_channel_info(ch, ch_type);
128
129 register_int_handler(SMD_IRQ, smd_irq_handler, ch);
130 unmask_interrupt(SMD_IRQ);
131
132 smd_set_state(ch, SMD_SS_OPENING, 1);
133
134 smd_notify_rpm();
135
136 return 0;
137}
138
139void smd_uninit(smd_channel_info_t *ch)
140{
141 smd_set_state(ch, SMD_SS_CLOSING, 1);
142
143 smd_notify_rpm();
144}
145
146bool is_channel_open(smd_channel_info_t *ch)
147{
148 if(ch->port_info->ch0.stream_state == SMD_SS_OPENED &&
149 (ch->port_info->ch1.stream_state == SMD_SS_OPENED ||
150 ch->port_info->ch1.stream_state == SMD_SS_FLUSHING))
151 return true;
152 else
153 return false;
154}
155
156uint8_t* smd_read(smd_channel_info_t *ch, uint32_t *len, int ch_type)
157{
158 smd_pkt_hdr smd_hdr;
159 uint32_t size = 0;
160
161 /* Read the indices from smem */
162 ch->port_info = smem_get_alloc_entry(SMEM_SMD_BASE_ID + ch->alloc_entry.cid,
163 &size);
164 if(!ch->port_info->ch1.DTR_DSR)
165 {
166 dprintf(CRITICAL,"%s: DTR is off\n", __func__);
167 return -1;
168 }
169
170 /* Clear the data_written flag */
171 ch->port_info->ch1.data_written = 0;
172
173 arch_invalidate_cache_range((addr_t)(ch->recv_buf + ch->port_info->ch1.read_index), sizeof(smd_hdr));
174
175 /* Copy the smd buffer to local buf */
176 memcpy(&smd_hdr, (void*)(ch->recv_buf + ch->port_info->ch1.read_index), sizeof(smd_hdr));
177
178 *len = smd_hdr.pkt_size;
179
180 return (uint8_t*)(ch->recv_buf + ch->port_info->ch1.read_index + sizeof(smd_hdr));
181}
182
183void smd_signal_read_complete(smd_channel_info_t *ch, uint32_t len)
184{
185 ch->port_info->ch1.read_index += sizeof(smd_pkt_hdr) + len;
186
187 /* Set the data_read flag */
188 ch->port_info->ch1.data_read = 1;
189 ch->port_info->ch0.mask_recv_intr = 1;
190 ch->port_info->ch0.state_updated = 1;
191
192 dsb();
193
194 smd_notify_rpm();
195}
196
197int smd_write(smd_channel_info_t *ch, void *data, uint32_t len, int ch_type)
198{
199 smd_pkt_hdr smd_hdr;
200 uint32_t size = 0;
201
202 memset(&smd_hdr, 0, sizeof(smd_pkt_hdr));
203
204 if(len + sizeof(smd_hdr) > ch->fifo_size)
205 {
206 dprintf(CRITICAL,"%s: len is greater than fifo sz\n", __func__);
207 return -1;
208 }
209
210 /* Read the indices from smem */
211 ch->port_info = smem_get_alloc_entry(SMEM_SMD_BASE_ID + ch->alloc_entry.cid,
212 &size);
213
214 if(!is_channel_open(ch))
215 {
216 dprintf(CRITICAL,"%s: channel is not in OPEN state \n", __func__);
217 return -1;
218 }
219
220 if(!ch->port_info->ch0.DTR_DSR)
221 {
222 dprintf(CRITICAL,"%s: DTR is off\n", __func__);
223 return -1;
224 }
225
226 /* Clear the data_read flag */
227 ch->port_info->ch1.data_read = 0;
228
229 /*copy the local buf to smd buf */
230 smd_hdr.pkt_size = len;
231
232 memcpy(ch->send_buf + ch->port_info->ch0.write_index, &smd_hdr, sizeof(smd_hdr));
233
234 memcpy(ch->send_buf + ch->port_info->ch0.write_index + sizeof(smd_hdr), data, len);
235
236 arch_invalidate_cache_range((addr_t)ch->send_buf+ch->port_info->ch0.write_index, sizeof(smd_hdr) + len);
237
238 /* Update write index */
239 ch->port_info->ch0.write_index += sizeof(smd_hdr) + len;
240
241 dsb();
242
243 /* Set the necessary flags */
244
245 ch->port_info->ch0.data_written = 1;
246 ch->port_info->ch0.mask_recv_intr = 0;
247 ch->port_info->ch0.state_updated = 1;
248
249 dsb();
250
251 smd_notify_rpm();
252
253 return 0;
254}
255
256void smd_notify_rpm()
257{
258 /* Set BIT 0 to notify RPM via IPC interrupt*/
259 writel(BIT(0), APCS_ALIAS0_IPC_INTERRUPT);
260}
261
262void smd_set_state(smd_channel_info_t *ch, uint32_t state, uint32_t flag)
263{
264 uint32_t current_state;
265 uint32_t size = 0;
266
267 if(!ch->port_info)
268 {
269 ch->port_info = smem_get_alloc_entry(SMEM_SMD_BASE_ID + ch->alloc_entry.cid,
270 &size);
271 ASSERT(ch->port_info);
272 }
273
274 current_state = ch->port_info->ch0.stream_state;
275
276 switch(state)
277 {
278 case SMD_SS_CLOSED:
279 if(current_state == SMD_SS_OPENED)
280 {
281 smd_write_state(ch, SMD_SS_CLOSING);
282 }
283 else
284 {
285 smd_write_state(ch, SMD_SS_CLOSED);
286 }
287 break;
288 case SMD_SS_OPENING:
289 if(current_state == SMD_SS_CLOSING || current_state == SMD_SS_CLOSED)
290 {
291 smd_write_state(ch, SMD_SS_OPENING);
292 ch->port_info->ch1.read_index = 0;
293 ch->port_info->ch0.write_index = 0;
294 ch->port_info->ch0.mask_recv_intr = 0;
295 }
296 break;
297 case SMD_SS_OPENED:
298 if(current_state == SMD_SS_OPENING)
299 {
300 smd_write_state(ch, SMD_SS_OPENED);
301 }
302 break;
303 case SMD_SS_CLOSING:
304 if(current_state == SMD_SS_OPENED)
305 {
306 smd_write_state(ch, SMD_SS_CLOSING);
307 }
308 break;
309 case SMD_SS_FLUSHING:
310 case SMD_SS_RESET:
311 case SMD_SS_RESET_OPENING:
312 default:
313 break;
314 }
315
316 ch->current_state = state;
317
318 smd_state_update(ch, flag);
319}
320
321
322enum handler_return smd_irq_handler(void* data)
323{
324 smd_channel_info_t *ch = (smd_channel_info_t*)data;
325
326 if(ch->current_state == SMD_SS_CLOSED)
327 {
328 free(smd_channel_alloc_entry);
329 return INT_NO_RESCHEDULE;
330 }
331
332 if(ch->port_info->ch1.state_updated)
333 ch->port_info->ch1.state_updated = 0;
334
335 /* Should we have to use a do while and change states until we complete */
336 if(ch->current_state != ch->port_info->ch1.stream_state)
337 {
338 smd_set_state(ch, ch->port_info->ch1.stream_state, 0);
339 }
340
341 if(ch->current_state == SMD_SS_CLOSING)
342 {
343 smd_set_state(ch, SMD_SS_CLOSED, 1);
344 smd_notify_rpm();
345 dprintf(CRITICAL,"Channel alloc freed\n");
346 }
347
348 return INT_NO_RESCHEDULE;
349}