blob: 3616e2b032a1ba951bdfe7c6e9771cd3adbfa98a [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>
Channagoud Kadabi54aafe42014-11-21 19:26:16 -080033#include <kernel/event.h>
Sundarajan Srinivasanae6ece02014-07-01 14:01:29 -070034#include <platform/irqs.h>
35#include <platform/iomap.h>
36#include <platform/interrupts.h>
37#include <reg.h>
38#include <malloc.h>
39#include <bits.h>
40
41smd_channel_alloc_entry_t *smd_channel_alloc_entry;
Channagoud Kadabi54aafe42014-11-21 19:26:16 -080042static event_t smd_closed;
Sundarajan Srinivasanae6ece02014-07-01 14:01:29 -070043
44static void smd_write_state(smd_channel_info_t *ch, uint32_t state)
45{
46 if(state == SMD_SS_OPENED)
47 {
48 ch->port_info->ch0.DTR_DSR = 1;
49 ch->port_info->ch0.CTS_RTS = 1;
50 ch->port_info->ch0.CD = 1;
51 }
52 else
53 {
54 ch->port_info->ch0.DTR_DSR = 0;
55 ch->port_info->ch0.CTS_RTS = 0;
56 ch->port_info->ch0.CD = 0;
57 }
58
59 ch->port_info->ch0.stream_state = state;
60}
61
62static void smd_state_update(smd_channel_info_t *ch, uint32_t flag)
63{
64 ch->port_info->ch0.state_updated = flag;
65}
66
67void smd_get_channel_entry(smd_channel_info_t *ch, uint32_t ch_type)
68{
69 int i = 0;
70
71 for(i = 0; i< SMEM_NUM_SMD_STREAM_CHANNELS; i++)
72 {
73 if((smd_channel_alloc_entry[i].ctype & 0xFF) == ch_type)
74 {
75 memcpy(&ch->alloc_entry, &smd_channel_alloc_entry[i], sizeof(smd_channel_alloc_entry_t));
76 break;
77 }
78 }
79
80 /* Channel not found */
81 if(i == SMEM_NUM_SMD_STREAM_CHANNELS)
82 {
83 dprintf(CRITICAL, "smd channel type %x not found\n", ch_type);
84 ASSERT(0);
85 }
86}
87
88int smd_get_channel_info(smd_channel_info_t *ch, uint32_t ch_type)
89{
90 int ret = 0;
91 uint8_t *fifo_buf = NULL;
92 uint32_t fifo_buf_size = 0;
93 uint32_t size = 0;
94
95 smd_get_channel_entry(ch, ch_type);
96
97
98 ch->port_info = smem_get_alloc_entry(SMEM_SMD_BASE_ID + ch->alloc_entry.cid,
99 &size);
100
101 fifo_buf = smem_get_alloc_entry(SMEM_SMD_FIFO_BASE_ID + ch->alloc_entry.cid,
102 &fifo_buf_size);
103
104 fifo_buf_size /= 2;
105 ch->send_buf = fifo_buf;
106 ch->recv_buf = fifo_buf + fifo_buf_size;
107 ch->fifo_size = fifo_buf_size;
108
109 return ret;
110}
111
112int smd_init(smd_channel_info_t *ch, uint32_t ch_type)
113{
114 unsigned ret = 0;
115
116 smd_channel_alloc_entry = (smd_channel_alloc_entry_t*)memalign(CACHE_LINE, SMD_CHANNEL_ALLOC_MAX);
117 ASSERT(smd_channel_alloc_entry);
118
119 ret = smem_read_alloc_entry(SMEM_CHANNEL_ALLOC_TBL,
120 (void*)smd_channel_alloc_entry,
121 SMD_CHANNEL_ALLOC_MAX);
122 if(ret)
123 {
124 dprintf(CRITICAL,"ERROR reading smem channel alloc tbl\n");
125 return -1;
126 }
127
128 smd_get_channel_info(ch, ch_type);
129
130 register_int_handler(SMD_IRQ, smd_irq_handler, ch);
Sundarajan Srinivasanae6ece02014-07-01 14:01:29 -0700131
132 smd_set_state(ch, SMD_SS_OPENING, 1);
133
134 smd_notify_rpm();
135
Unnati Gandhia7d9a0b2014-11-13 16:03:35 +0530136 unmask_interrupt(SMD_IRQ);
137
Sundarajan Srinivasanae6ece02014-07-01 14:01:29 -0700138 return 0;
139}
140
141void smd_uninit(smd_channel_info_t *ch)
142{
Channagoud Kadabi54aafe42014-11-21 19:26:16 -0800143 event_init(&smd_closed, false, EVENT_FLAG_AUTOUNSIGNAL);
Sundarajan Srinivasanae6ece02014-07-01 14:01:29 -0700144 smd_set_state(ch, SMD_SS_CLOSING, 1);
145
146 smd_notify_rpm();
Channagoud Kadabi54aafe42014-11-21 19:26:16 -0800147 /* Wait for the SMD-RPM channel to be closed */
148 event_wait(&smd_closed);
Sundarajan Srinivasanae6ece02014-07-01 14:01:29 -0700149}
150
151bool is_channel_open(smd_channel_info_t *ch)
152{
153 if(ch->port_info->ch0.stream_state == SMD_SS_OPENED &&
154 (ch->port_info->ch1.stream_state == SMD_SS_OPENED ||
155 ch->port_info->ch1.stream_state == SMD_SS_FLUSHING))
156 return true;
157 else
158 return false;
159}
160
Channagoud Kadabi4e3dc812014-10-14 12:32:56 -0700161/* Copy the local buffer to fifo buffer.
162 * Takes care of fifo overlap.
163 * Uses the fifo as circular buffer, if the request data
164 * exceeds the max size of the buffer start from the beginning.
165 */
166static void memcpy_to_fifo(smd_channel_info_t *ch_ptr, uint32_t *src, size_t len)
167{
168 uint32_t write_index = ch_ptr->port_info->ch0.write_index;
169 uint32_t *dest = (uint32_t *)(ch_ptr->send_buf + write_index);
170
171 while(len)
172 {
173 *dest++ = *src++;
174 write_index += 4;
175 len -= 4;
176
177 if (write_index >= ch_ptr->fifo_size)
178 {
179 write_index = 0;
180 dest = (uint32_t *)(ch_ptr->send_buf + write_index);
181 }
182 }
183 ch_ptr->port_info->ch0.write_index = write_index;
184}
185
186/* Copy the fifo buffer to a local destination.
187 * Takes care of fifo overlap.
188 * If the response data is split across with some part at
189 * end of fifo and some at the beginning of the fifo
190 */
191void memcpy_from_fifo(smd_channel_info_t *ch_ptr, uint32_t *dest, size_t len)
192{
193 uint32_t read_index = ch_ptr->port_info->ch1.read_index;
194 uint32_t *src = (uint32_t *)(ch_ptr->recv_buf + read_index);
195
196 while(len)
197 {
198 *dest++ = *src++;
199 read_index += 4;
200 len -= 4;
201
202 if (read_index >= ch_ptr->fifo_size)
203 {
204 read_index = 0;
205 src = (uint32_t *) (ch_ptr->recv_buf + read_index);
206 }
207 }
208
209 ch_ptr->port_info->ch1.read_index = read_index;
210}
211
Channagoud Kadabi637683d2014-11-26 14:51:14 -0800212void smd_read(smd_channel_info_t *ch, uint32_t *len, int ch_type, uint32_t *response)
Sundarajan Srinivasanae6ece02014-07-01 14:01:29 -0700213{
214 smd_pkt_hdr smd_hdr;
215 uint32_t size = 0;
216
217 /* Read the indices from smem */
218 ch->port_info = smem_get_alloc_entry(SMEM_SMD_BASE_ID + ch->alloc_entry.cid,
219 &size);
220 if(!ch->port_info->ch1.DTR_DSR)
221 {
222 dprintf(CRITICAL,"%s: DTR is off\n", __func__);
Channagoud Kadabi637683d2014-11-26 14:51:14 -0800223 return;
Sundarajan Srinivasanae6ece02014-07-01 14:01:29 -0700224 }
225
Channagoud Kadabi161d7cc2014-08-13 12:57:16 -0700226 /* Wait until the data updated in the smd buffer is equal to smd packet header*/
227 while ((ch->port_info->ch1.write_index - ch->port_info->ch1.read_index) < sizeof(smd_pkt_hdr))
228 {
229 /* Get the update info from memory */
230 arch_invalidate_cache_range((addr_t) ch->port_info, size);
Channagoud Kadabi161d7cc2014-08-13 12:57:16 -0700231 }
232
Sundarajan Srinivasanae6ece02014-07-01 14:01:29 -0700233 /* Copy the smd buffer to local buf */
Channagoud Kadabi4e3dc812014-10-14 12:32:56 -0700234 memcpy_from_fifo(ch, &smd_hdr, sizeof(smd_hdr));
235
236 arch_invalidate_cache_range((addr_t)&smd_hdr, sizeof(smd_hdr));
Sundarajan Srinivasanae6ece02014-07-01 14:01:29 -0700237
238 *len = smd_hdr.pkt_size;
239
Channagoud Kadabi161d7cc2014-08-13 12:57:16 -0700240 /* Wait on the data being updated in SMEM before returing the response */
241 while ((ch->port_info->ch1.write_index - ch->port_info->ch1.read_index) < smd_hdr.pkt_size)
242 {
243 /* Get the update info from memory */
244 arch_invalidate_cache_range((addr_t) ch->port_info, size);
Channagoud Kadabi161d7cc2014-08-13 12:57:16 -0700245 }
246
247 /* We are good to return the response now */
Channagoud Kadabi637683d2014-11-26 14:51:14 -0800248 memcpy_from_fifo(ch, response, smd_hdr.pkt_size);
Channagoud Kadabi4e3dc812014-10-14 12:32:56 -0700249
Channagoud Kadabi637683d2014-11-26 14:51:14 -0800250 arch_invalidate_cache_range((addr_t)response, smd_hdr.pkt_size);
Channagoud Kadabi4e3dc812014-10-14 12:32:56 -0700251
252 return response;
Sundarajan Srinivasanae6ece02014-07-01 14:01:29 -0700253}
254
255void smd_signal_read_complete(smd_channel_info_t *ch, uint32_t len)
256{
Channagoud Kadabi161d7cc2014-08-13 12:57:16 -0700257 /* Clear the data_written flag */
258 ch->port_info->ch1.data_written = 0;
259
Sundarajan Srinivasanae6ece02014-07-01 14:01:29 -0700260 /* Set the data_read flag */
Channagoud Kadabi161d7cc2014-08-13 12:57:16 -0700261 ch->port_info->ch0.data_read = 1;
Sundarajan Srinivasanae6ece02014-07-01 14:01:29 -0700262 ch->port_info->ch0.mask_recv_intr = 1;
Sundarajan Srinivasanae6ece02014-07-01 14:01:29 -0700263
264 dsb();
265
266 smd_notify_rpm();
267}
268
269int smd_write(smd_channel_info_t *ch, void *data, uint32_t len, int ch_type)
270{
271 smd_pkt_hdr smd_hdr;
272 uint32_t size = 0;
273
274 memset(&smd_hdr, 0, sizeof(smd_pkt_hdr));
275
276 if(len + sizeof(smd_hdr) > ch->fifo_size)
277 {
278 dprintf(CRITICAL,"%s: len is greater than fifo sz\n", __func__);
279 return -1;
280 }
281
282 /* Read the indices from smem */
283 ch->port_info = smem_get_alloc_entry(SMEM_SMD_BASE_ID + ch->alloc_entry.cid,
284 &size);
285
286 if(!is_channel_open(ch))
287 {
288 dprintf(CRITICAL,"%s: channel is not in OPEN state \n", __func__);
289 return -1;
290 }
291
292 if(!ch->port_info->ch0.DTR_DSR)
293 {
294 dprintf(CRITICAL,"%s: DTR is off\n", __func__);
295 return -1;
296 }
297
298 /* Clear the data_read flag */
299 ch->port_info->ch1.data_read = 0;
300
301 /*copy the local buf to smd buf */
302 smd_hdr.pkt_size = len;
303
Channagoud Kadabi4e3dc812014-10-14 12:32:56 -0700304 memcpy_to_fifo(ch, (uint32_t *)&smd_hdr, sizeof(smd_hdr));
Sundarajan Srinivasanae6ece02014-07-01 14:01:29 -0700305
Channagoud Kadabi4e3dc812014-10-14 12:32:56 -0700306 memcpy_to_fifo(ch, data, len);
Sundarajan Srinivasanae6ece02014-07-01 14:01:29 -0700307
308 dsb();
309
310 /* Set the necessary flags */
311
312 ch->port_info->ch0.data_written = 1;
313 ch->port_info->ch0.mask_recv_intr = 0;
Sundarajan Srinivasanae6ece02014-07-01 14:01:29 -0700314
315 dsb();
316
317 smd_notify_rpm();
318
319 return 0;
320}
321
322void smd_notify_rpm()
323{
324 /* Set BIT 0 to notify RPM via IPC interrupt*/
325 writel(BIT(0), APCS_ALIAS0_IPC_INTERRUPT);
326}
327
328void smd_set_state(smd_channel_info_t *ch, uint32_t state, uint32_t flag)
329{
330 uint32_t current_state;
331 uint32_t size = 0;
332
333 if(!ch->port_info)
334 {
335 ch->port_info = smem_get_alloc_entry(SMEM_SMD_BASE_ID + ch->alloc_entry.cid,
336 &size);
337 ASSERT(ch->port_info);
338 }
339
340 current_state = ch->port_info->ch0.stream_state;
341
342 switch(state)
343 {
344 case SMD_SS_CLOSED:
345 if(current_state == SMD_SS_OPENED)
346 {
347 smd_write_state(ch, SMD_SS_CLOSING);
348 }
349 else
350 {
351 smd_write_state(ch, SMD_SS_CLOSED);
352 }
353 break;
354 case SMD_SS_OPENING:
355 if(current_state == SMD_SS_CLOSING || current_state == SMD_SS_CLOSED)
356 {
357 smd_write_state(ch, SMD_SS_OPENING);
358 ch->port_info->ch1.read_index = 0;
359 ch->port_info->ch0.write_index = 0;
360 ch->port_info->ch0.mask_recv_intr = 0;
361 }
362 break;
363 case SMD_SS_OPENED:
364 if(current_state == SMD_SS_OPENING)
365 {
366 smd_write_state(ch, SMD_SS_OPENED);
367 }
368 break;
369 case SMD_SS_CLOSING:
370 if(current_state == SMD_SS_OPENED)
371 {
372 smd_write_state(ch, SMD_SS_CLOSING);
373 }
374 break;
375 case SMD_SS_FLUSHING:
376 case SMD_SS_RESET:
377 case SMD_SS_RESET_OPENING:
378 default:
379 break;
380 }
381
382 ch->current_state = state;
383
384 smd_state_update(ch, flag);
385}
386
387
388enum handler_return smd_irq_handler(void* data)
389{
390 smd_channel_info_t *ch = (smd_channel_info_t*)data;
391
392 if(ch->current_state == SMD_SS_CLOSED)
393 {
394 free(smd_channel_alloc_entry);
Channagoud Kadabi54aafe42014-11-21 19:26:16 -0800395 event_signal(&smd_closed, false);
Sundarajan Srinivasanae6ece02014-07-01 14:01:29 -0700396 return INT_NO_RESCHEDULE;
397 }
398
399 if(ch->port_info->ch1.state_updated)
400 ch->port_info->ch1.state_updated = 0;
401
402 /* Should we have to use a do while and change states until we complete */
403 if(ch->current_state != ch->port_info->ch1.stream_state)
404 {
405 smd_set_state(ch, ch->port_info->ch1.stream_state, 0);
406 }
407
408 if(ch->current_state == SMD_SS_CLOSING)
409 {
410 smd_set_state(ch, SMD_SS_CLOSED, 1);
411 smd_notify_rpm();
412 dprintf(CRITICAL,"Channel alloc freed\n");
413 }
414
415 return INT_NO_RESCHEDULE;
416}