4 * MediaTek <www.MediaTek.com>
5 * Hongcheng <hongcheng.xia@MediaTek.com>
7 * MT6620 FM Radio Driver -- setup data link
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include <linux/slab.h>
25 #include <linux/interrupt.h>
30 #include "fm_typedef.h"
33 #include "fm_stdlib.h"
35 #include "mt6620_fm.h"
36 #include "mt6620_fm_link.h"
37 static struct fm_link_event *link_event;
39 static struct fm_trace_fifo_t *cmd_fifo;
41 static struct fm_trace_fifo_t *evt_fifo;
43 static fm_s32 (*reset)(fm_s32 sta) = NULL;
45 static void mt6620_fm_wholechip_rst_cb(ENUM_WMTDRV_TYPE_T src,
46 ENUM_WMTDRV_TYPE_T dst,
47 ENUM_WMTMSG_TYPE_T type,
51 //To handle reset procedure please
52 ENUM_WMTRSTMSG_TYPE_T rst_msg;
54 if (sz <= sizeof(ENUM_WMTRSTMSG_TYPE_T)) {
55 memcpy((char *)&rst_msg, (char *)buf, sz);
56 WCN_DBG(FM_WAR | LINK, "[src=%d], [dst=%d], [type=%d], [buf=0x%x], [sz=%d], [max=%d]\n", src, dst, type, rst_msg, sz, WMTRSTMSG_RESET_MAX);
58 if ((src == WMTDRV_TYPE_WMT) && (dst == WMTDRV_TYPE_FM) && (type == WMTMSG_TYPE_RESET)) {
59 if (rst_msg == WMTRSTMSG_RESET_START) {
60 WCN_DBG(FM_WAR | LINK, "FM restart start!\n");
65 } else if (rst_msg == WMTRSTMSG_RESET_END) {
66 WCN_DBG(FM_WAR | LINK, "FM restart end!\n");
73 /*message format invalid*/
74 WCN_DBG(FM_WAR | LINK, "message format invalid!\n");
79 fm_s32 fm_link_setup(void* data)
83 if (!(link_event = fm_zalloc(sizeof(struct fm_link_event)))) {
84 WCN_DBG(FM_ALT | LINK, "fm_zalloc(fm_link_event) -ENOMEM\n");
88 link_event->ln_event = fm_flag_event_create("ln_evt");
90 if (!link_event->ln_event) {
91 WCN_DBG(FM_ALT | LINK, "create mt6620_ln_event failed\n");
96 fm_flag_event_get(link_event->ln_event);
99 WCN_DBG(FM_NTC | LINK, "fm link setup\n");
101 cmd_fifo = fm_trace_fifo_create("cmd_fifo");
103 WCN_DBG(FM_ALT | LINK, "create cmd_fifo failed\n");
108 evt_fifo = fm_trace_fifo_create("evt_fifo");
110 WCN_DBG(FM_ALT | LINK, "create evt_fifo failed\n");
115 reset = data; // get whole chip reset cb
116 mtk_wcn_wmt_msgcb_reg(WMTDRV_TYPE_FM, mt6620_fm_wholechip_rst_cb);
120 fm_trace_fifo_release(evt_fifo);
121 fm_trace_fifo_release(cmd_fifo);
122 fm_flag_event_put(link_event->ln_event);
130 fm_s32 fm_link_release(void)
133 fm_trace_fifo_release(evt_fifo);
134 fm_trace_fifo_release(cmd_fifo);
135 fm_flag_event_put(link_event->ln_event);
140 WCN_DBG(FM_NTC | LINK, "fm link release\n");
146 * the low level func to read a rigister
147 * @addr - rigister address
148 * @val - the pointer of target buf
149 * If success, return 0; else error code
151 fm_s32 fm_ctrl_rx(fm_u8 addr, fm_u16 *val)
158 * the low level func to write a rigister
159 * @addr - rigister address
160 * @val - value will be writed in the rigister
161 * If success, return 0; else error code
163 fm_s32 fm_ctrl_tx(fm_u8 addr, fm_u16 val)
169 * fm_cmd_tx() - send cmd to FM firmware and wait event
171 * @len - the length of cmd
172 * @mask - the event flag mask
173 * @ cnt - the retry conter
174 * @timeout - timeout per cmd
175 * Return 0, if success; error code, if failed
177 fm_s32 fm_cmd_tx(fm_u8* buf, fm_u16 len, fm_s32 mask, fm_s32 cnt, fm_s32 timeout, fm_s32(*callback)(struct fm_res_ctx* result))
180 struct task_struct *task = current;
181 struct fm_trace_t trace;
183 if ((NULL == buf) || (len < 0) || (0 == mask)
184 || (cnt > SW_RETRY_CNT_MAX) || (timeout > SW_WAIT_TIMEOUT_MAX)) {
185 WCN_DBG(FM_ERR | LINK, "cmd tx, invalid para\n");
189 FM_EVENT_CLR(link_event->ln_event, mask);
191 #ifdef FM_TRACE_ENABLE
193 trace.opcode = buf[1];
195 trace.tid = (fm_s32)task->pid;
196 fm_memset(trace.pkt, 0, FM_TRACE_PKT_SIZE);
197 fm_memcpy(trace.pkt, &buf[4], (trace.len > FM_TRACE_PKT_SIZE) ? FM_TRACE_PKT_SIZE : trace.len);
202 #ifdef FM_TRACE_ENABLE
203 if (fm_true == FM_TRACE_FULL(cmd_fifo)) {
204 FM_TRACE_OUT(cmd_fifo, NULL);
206 FM_TRACE_IN(cmd_fifo, &trace);
209 //send cmd to FM firmware
210 if (mtk_wcn_stp_send_data(buf, len, FM_TASK_INDX) <= 0) {
211 WCN_DBG(FM_EMG | LINK, "send data over stp failed\n");
215 //wait the response form FM firmware
216 ret_time = FM_EVENT_WAIT_TIMEOUT(link_event->ln_event, mask, timeout);
220 WCN_DBG(FM_WAR | LINK, "wait even timeout, [retry_cnt=%d], pid=%d\n", cnt, task->pid);
224 goto sw_retry; //retry if timeout and retry cnt > 0
226 WCN_DBG(FM_ALT | LINK, "fatal error, SW retry failed, reset HW\n");
231 FM_EVENT_CLR(link_event->ln_event, mask);
234 callback(&link_event->result);
240 fm_s32 fm_event_parser(fm_s32(*rds_parser)(struct rds_rx_t*, fm_s32))
247 fm_u8 rx_buf[RX_BUF_SIZE + 10] = {0}; //the 10 bytes are protect gaps
248 static volatile fm_task_parser_state state = FM_TASK_RX_PARSER_PKT_TYPE;
249 struct fm_trace_t trace;
250 struct task_struct *task = current;
252 len = mtk_wcn_stp_receive_data(rx_buf, RX_BUF_SIZE, FM_TASK_INDX);
253 WCN_DBG(FM_DBG | LINK, "[len=%d],[CMD=0x%02x 0x%02x 0x%02x 0x%02x]\n", len, rx_buf[0], rx_buf[1], rx_buf[2], rx_buf[3]);
259 case FM_TASK_RX_PARSER_PKT_TYPE:
261 if (ch == FM_TASK_EVENT_PKT_TYPE) {
262 if ((i + 5) < RX_BUF_SIZE) {
263 WCN_DBG(FM_DBG | LINK, "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x \n", rx_buf[i], rx_buf[i+1], rx_buf[i+2], rx_buf[i+3], rx_buf[i+4], rx_buf[i+5]);
265 WCN_DBG(FM_DBG | LINK, "0x%02x 0x%02x\n", rx_buf[i], rx_buf[i+1]);
268 state = FM_TASK_RX_PARSER_OPCODE;
270 WCN_DBG(FM_ALT | LINK, "event pkt type error (rx_buf[%d] = 0x%02x)\n", i, ch);
276 case FM_TASK_RX_PARSER_OPCODE:
279 state = FM_TASK_RX_PARSER_PKT_LEN_1;
282 case FM_TASK_RX_PARSER_PKT_LEN_1:
285 state = FM_TASK_RX_PARSER_PKT_LEN_2;
288 case FM_TASK_RX_PARSER_PKT_LEN_2:
290 length |= (fm_u16)(ch << 0x8);
292 #ifdef FM_TRACE_ENABLE
293 trace.type = FM_TASK_EVENT_PKT_TYPE;
294 trace.opcode = opcode;
296 trace.tid = (fm_s32)task->pid;
297 fm_memset(trace.pkt, 0, FM_TRACE_PKT_SIZE);
298 fm_memcpy(trace.pkt, &rx_buf[i], (length > FM_TRACE_PKT_SIZE) ? FM_TRACE_PKT_SIZE : length);
300 if (fm_true == FM_TRACE_FULL(cmd_fifo)) {
301 FM_TRACE_OUT(cmd_fifo, NULL);
303 FM_TRACE_IN(cmd_fifo, &trace);
306 state = FM_TASK_RX_PARSER_PKT_PAYLOAD;
308 state = FM_TASK_RX_PARSER_PKT_TYPE;
309 FM_EVENT_SEND(link_event->ln_event, (1 << opcode));
314 case FM_TASK_RX_PARSER_PKT_PAYLOAD:
319 if ((length == 1) && (rx_buf[i] == 1)) {
320 FM_EVENT_SEND(link_event->ln_event, FLAG_TUNE_DONE);
325 case FM_SOFT_MUTE_TUNE_OPCODE:
328 fm_memcpy(link_event->result.cqi, &rx_buf[i], (length > FM_CQI_BUF_SIZE) ? FM_CQI_BUF_SIZE : length);
329 FM_EVENT_SEND(link_event->ln_event, FLAG_SM_TUNE);
335 if ((i + 1) < RX_BUF_SIZE) {
336 link_event->result.seek_result = rx_buf[i] + (rx_buf[i+1] << 8); // 8760 means 87.60Mhz
339 FM_EVENT_SEND(link_event->ln_event, FLAG_SEEK_DONE);
344 //check if the result data is long enough
345 if ((RX_BUF_SIZE - i) < (sizeof(fm_u16) * FM_SCANTBL_SIZE)) {
346 WCN_DBG(FM_ALT | LINK, "FM_SCAN_OPCODE err, [tblsize=%d],[bufsize=%d]\n", (sizeof(fm_u16) * FM_SCANTBL_SIZE), (RX_BUF_SIZE - i));
347 FM_EVENT_SEND(link_event->ln_event, FLAG_SCAN_DONE);
349 } else if ((length >= FM_CQI_BUF_SIZE) && ((RX_BUF_SIZE - i) >= FM_CQI_BUF_SIZE)) {
350 fm_memcpy(link_event->result.cqi, &rx_buf[i], FM_CQI_BUF_SIZE);
351 FM_EVENT_SEND(link_event->ln_event, FLAG_CQI_DONE);
353 fm_memcpy(link_event->result.scan_result, &rx_buf[i], sizeof(fm_u16) * FM_SCANTBL_SIZE);
354 FM_EVENT_SEND(link_event->ln_event, FLAG_SCAN_DONE);
359 case FSPI_READ_OPCODE:
361 if ((i + 1) < RX_BUF_SIZE) {
362 link_event->result.fspi_rd = (rx_buf[i] + (rx_buf[i+1] << 8));
365 FM_EVENT_SEND(link_event->ln_event, (1 << opcode));
368 case RDS_RX_DATA_OPCODE:
370 //check if the rds data is long enough
371 if ((RX_BUF_SIZE - i) < length) {
372 WCN_DBG(FM_ALT | LINK, "RDS RX err, [rxlen=%d],[bufsize=%d]\n", (fm_s32)length, (RX_BUF_SIZE - i));
373 FM_EVENT_SEND(link_event->ln_event, (1 << opcode));
377 //copy rds data to rds buf
378 fm_memcpy(&link_event->result.rds_rx_result, &rx_buf[i], length);
380 /*Handle the RDS data that we get*/
382 rds_parser(&link_event->result.rds_rx_result, length);
384 WCN_DBG(FM_WAR | LINK, "no method to parse RDS data\n");
387 FM_EVENT_SEND(link_event->ln_event, (1 << opcode));
391 FM_EVENT_SEND(link_event->ln_event, (1 << opcode));
395 state = FM_TASK_RX_PARSER_PKT_TYPE;
407 fm_bool fm_wait_stc_done(fm_u32 sec)
412 fm_s32 fm_force_active_event(fm_u32 mask)
416 flag = FM_EVENT_GET(link_event->ln_event);
417 WCN_DBG(FM_WAR | LINK, "before force active event, [flag=0x%08x]\n", flag);
418 flag = FM_EVENT_SEND(link_event->ln_event, mask);
419 WCN_DBG(FM_WAR | LINK, "after force active event, [flag=0x%08x]\n", flag);
425 extern fm_s32 fm_print_cmd_fifo(void)
427 #ifdef FM_TRACE_ENABLE
428 struct fm_trace_t trace;
431 while (fm_false == FM_TRACE_EMPTY(cmd_fifo)) {
432 fm_memset(trace.pkt, 0, FM_TRACE_PKT_SIZE);
433 FM_TRACE_OUT(cmd_fifo, &trace);
434 WCN_DBG(FM_ALT | LINK, "trace, type %d, op %d, len %d, tid %d, time %d\n", trace.type, trace.opcode, trace.len, trace.tid, jiffies_to_msecs(abs(trace.time)));
436 while ((trace.len > 0) && (i < trace.len) && (i < (FM_TRACE_PKT_SIZE-8))) {
437 WCN_DBG(FM_ALT | LINK, "trace, %02x %02x %02x %02x %02x %02x %02x %02x\n", \
438 trace.pkt[i], trace.pkt[i+1], trace.pkt[i+2], trace.pkt[i+3], trace.pkt[i+4], trace.pkt[i+5], trace.pkt[i+6], trace.pkt[i+7]);
441 WCN_DBG(FM_ALT | LINK, "trace\n");
448 extern fm_s32 fm_print_evt_fifo(void)
450 #ifdef FM_TRACE_ENABLE
451 struct fm_trace_t trace;
454 while (fm_false == FM_TRACE_EMPTY(evt_fifo)) {
455 fm_memset(trace.pkt, 0, FM_TRACE_PKT_SIZE);
456 FM_TRACE_OUT(evt_fifo, &trace);
457 WCN_DBG(FM_ALT | LINK, "%s: op %d, len %d, %d\n", evt_fifo->name, trace.opcode, trace.len, jiffies_to_msecs(abs(trace.time)));
459 while ((trace.len > 0) && (i < trace.len) && (i < (FM_TRACE_PKT_SIZE-8))) {
460 WCN_DBG(FM_ALT | LINK, "%s: %02x %02x %02x %02x %02x %02x %02x %02x\n", \
461 evt_fifo->name, trace.pkt[i], trace.pkt[i+1], trace.pkt[i+2], trace.pkt[i+3], trace.pkt[i+4], trace.pkt[i+5], trace.pkt[i+6], trace.pkt[i+7]);
464 WCN_DBG(FM_ALT | LINK, "%s\n", evt_fifo->name);