From 02d694f3b46da51fb4dec1c74690afdd82c62639 Mon Sep 17 00:00:00 2001 From: Chris Blair Date: Fri, 4 Nov 2011 14:19:57 +0000 Subject: modem: Add M6718 IPC SPI driver state machine Adds the implementation of the driver protocol state machine. ST-Ericsson ID: 369397 ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-12224 ST-Ericsson Linux next: NA Change-Id: Iffa99154ab2d3f0150c96c7bc535ffe26f6988db Signed-off-by: Chris Blair Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36501 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Jonas ABERG --- drivers/modem/m6718_spi/statemachine.c | 1402 ++++++++++++++++++++++++++++++++ 1 file changed, 1402 insertions(+) create mode 100644 drivers/modem/m6718_spi/statemachine.c (limited to 'drivers/modem/m6718_spi/statemachine.c') diff --git a/drivers/modem/m6718_spi/statemachine.c b/drivers/modem/m6718_spi/statemachine.c new file mode 100644 index 00000000000..a2092cd2f94 --- /dev/null +++ b/drivers/modem/m6718_spi/statemachine.c @@ -0,0 +1,1402 @@ +/* + * Copyright (C) ST-Ericsson SA 2010,2011 + * + * Author: Chris Blair for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * U9500 <-> M6718 IPC protocol implementation using SPI. + * state machine definition and functionality. + */ +#include +#include "modem_statemachine.h" +#include "modem_util.h" +#include "modem_netlink.h" +#include "modem_debug.h" +#include "modem_queue.h" +#include "modem_protocol.h" + +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE +#include "modem_state.h" +#endif + +#define CMD_BOOTREQ (1) +#define CMD_BOOTRESP (2) +#define CMD_WRITE (3) +#define CMD_READ (4) + +static u8 sm_init_enter(u8 event, struct ipc_link_context *context) +{ +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE + /* if modem is off un-configure the IPC GPIO pins for low-power */ + if (modem_state_get_state() == MODEM_STATE_OFF) { + dev_info(&context->sdev->dev, + "link %d: modem is off, un-configuring GPIO\n", + context->link->id); + ipc_util_link_gpio_unconfig(context); + } +#endif + /* nothing more to do until an event happens */ + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_init_exit(u8 event, + struct ipc_link_context *context) +{ + bool int_active = false; + + /* + * For reset event just re-enter init in case the modem has + * powered off - we need to reconfigure our GPIO pins + */ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_INIT); + + /* re-sample link INT pin */ + int_active = ipc_util_int_is_active(context); + atomic_set(&context->state_int, int_active); + + dev_info(&context->sdev->dev, + "link %d: link initialised; SS:INACTIVE(%d) INT:%s(%d)\n", + context->link->id, + ipc_util_ss_level_inactive(context), + int_active ? "ACTIVE" : "INACTIVE", + int_active ? ipc_util_int_level_active(context) : + ipc_util_int_level_inactive(context)); + + /* handshake is only on link 0 */ + if (context->link->id == 0) { + if (!int_active) { + dev_info(&context->sdev->dev, + "link %d: slave INT signal is inactive\n", + context->link->id); + /* start boot handshake */ + return ipc_sm_state(IPC_SM_SLW_TX_BOOTREQ); + } else { + /* wait for slave INT signal to stabilise inactive */ + return ipc_sm_state(IPC_SM_WAIT_SLAVE_STABLE); + } + } else { + dev_info(&context->sdev->dev, + "link %d: boot sync not needed, going idle\n", + context->link->id); + return ipc_sm_state(IPC_SM_IDL); + } +} + +static const struct ipc_sm_state *sm_init_aud_exit(u8 event, + struct ipc_link_context *context) +{ + bool int_active = false; + + /* + * For reset event just re-enter init in case the modem has + * powered off - we need to reconfigure our GPIO pins + */ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_INIT_AUD); + + /* re-sample link INT pin */ + int_active = ipc_util_int_is_active(context); + atomic_set(&context->state_int, int_active); + + dev_info(&context->sdev->dev, + "link %d: link initialised; SS:INACTIVE(%d) INT:%s(%d)\n", + context->link->id, + ipc_util_ss_level_inactive(context), + int_active ? "ACTIVE" : "INACTIVE", + int_active ? ipc_util_int_level_active(context) : + ipc_util_int_level_inactive(context)); + dev_info(&context->sdev->dev, + "link %d: boot sync not needed, going idle\n", + context->link->id); + return ipc_sm_state(IPC_SM_IDL_AUD); +} + +static u8 sm_wait_slave_stable_enter(u8 event, struct ipc_link_context *context) +{ + static unsigned long printk_warn_time; + if (printk_timed_ratelimit(&printk_warn_time, 60 * 1000)) + dev_info(&context->sdev->dev, + "link %d: waiting for stable inactive slave INT\n", + context->link->id); + ipc_util_start_slave_stable_timer(context); + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_wait_slave_stable_exit(u8 event, + struct ipc_link_context *context) +{ + if (!ipc_util_int_is_active(context)) { + dev_info(&context->sdev->dev, + "link %d: slave INT signal is stable inactive\n", + context->link->id); + return ipc_sm_state(IPC_SM_SLW_TX_BOOTREQ); + } else { + return ipc_sm_state(IPC_SM_WAIT_SLAVE_STABLE); + } +} + +static u8 sm_wait_handshake_inactive_enter(u8 event, + struct ipc_link_context *context) +{ + dev_info(&context->sdev->dev, + "link %d: waiting for stable inactive slave INT\n", + context->link->id); + ipc_util_start_slave_stable_timer(context); + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_wait_handshake_inactive_exit(u8 event, + struct ipc_link_context *context) +{ + int i; + + if (!ipc_util_int_is_active(context)) { + dev_info(&context->sdev->dev, + "link %d: slave INT signal is inactive, going idle\n", + context->link->id); + + /* modem sync is done */ + atomic_inc(&l1_context.boot_sync_done); + ipc_broadcast_modem_online(context); + + /* + * Kick the state machine for any initialised links - skip link0 + * since this link has just completed handshake + */ + for (i = 1; i < IPC_NBR_SUPPORTED_SPI_LINKS; i++) + if (l1_context.device_context[i].state != NULL) { + dev_dbg(&context->sdev->dev, + "link %d has already been probed, " + "kicking state machine\n", i); + ipc_sm_kick(IPC_SM_RUN_INIT, + &l1_context.device_context[i]); + } + return ipc_sm_state(IPC_SM_IDL); + } else { + return ipc_sm_state(IPC_SM_WAIT_HANDSHAKE_INACTIVE); + } +} + +static u8 sm_idl_enter(u8 event, struct ipc_link_context *context) +{ + ipc_util_deactivate_ss(context); + ipc_dbg_enter_idle(context); + + /* check if tx queue contains items */ + if (atomic_read(&context->tx_q_count) > 0) { + dev_dbg(&context->sdev->dev, + "link %d: tx queue contains items\n", + context->link->id); + return IPC_SM_RUN_TX_REQ; + } + + /* check if modem has already requested transaction start */ + if (atomic_read(&context->state_int)) { + dev_dbg(&context->sdev->dev, + "link %d: slave has already signalled ready\n", + context->link->id); + return IPC_SM_RUN_SLAVE_IRQ; + } + + dev_dbg(&context->sdev->dev, + "link %d: going idle\n", context->link->id); + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_idl_exit(u8 event, + struct ipc_link_context *context) +{ + ipc_dbg_exit_idle(context); + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET); + else if (event == IPC_SM_RUN_TX_REQ) + return ipc_sm_state(IPC_SM_SLW_TX_WR_CMD); + else if (event == IPC_SM_RUN_SLAVE_IRQ) + return ipc_sm_state(IPC_SM_SLW_TX_RD_CMD); + else + return ipc_sm_state(IPC_SM_HALT); +} + +static const struct ipc_sm_state *sm_idl_aud_exit(u8 event, + struct ipc_link_context *context) +{ + ipc_dbg_exit_idle(context); + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET_AUD); + + /* always transmit data first */ + return ipc_sm_state(IPC_SM_SLW_TX_WR_DAT_AUD); +} + +static u8 sm_slw_tx_wr_cmd_enter(u8 event, struct ipc_link_context *context) +{ + struct ipc_tx_queue *frame; + + /* get the frame from the head of the tx queue */ + if (ipc_queue_is_empty(context)) { + dev_err(&context->sdev->dev, + "link %d error: tx queue is empty!\n", + context->link->id); + return IPC_SM_RUN_ABORT; + } + frame = ipc_queue_get_frame(context); + ipc_dbg_dump_frame(&context->sdev->dev, context->link->id, frame, true); + + context->cmd = ipc_util_make_l1_header(CMD_WRITE, frame->counter, + frame->len); + + dev_dbg(&context->sdev->dev, + "link %d: TX FRAME cmd %08x (type %d counter %d len %d)\n", + context->link->id, + context->cmd, + ipc_util_get_l1_cmd(context->cmd), + ipc_util_get_l1_counter(context->cmd), + ipc_util_get_l1_length(context->cmd)); + + ipc_util_spi_message_prepare(context, &context->cmd, + NULL, IPC_L1_HDR_SIZE); + context->frame = frame; + + /* slave might already have signalled ready to transmit */ + if (atomic_read(&context->state_int)) { + dev_dbg(&context->sdev->dev, + "link %d: slave has already signalled ready\n", + context->link->id); + ipc_util_activate_ss(context); + return IPC_SM_RUN_SLAVE_IRQ; + } else { + ipc_util_activate_ss_with_tmo(context); + return IPC_SM_RUN_NONE; + } +} + +static const struct ipc_sm_state *sm_slw_tx_wr_cmd_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET); + else if (event == IPC_SM_RUN_COMMS_TMO) + return ipc_sm_state(IPC_SM_HALT); + else + return ipc_sm_state(IPC_SM_ACT_TX_WR_CMD); +} + +static u8 sm_act_tx_wr_cmd_enter(u8 event, struct ipc_link_context *context) +{ + int err; + + /* slave is ready - start the spi transfer */ + dev_dbg(&context->sdev->dev, + "link %d: starting spi tfr\n", context->link->id); + err = spi_async(context->sdev, &context->spi_message); + if (err < 0) { + dev_err(&context->sdev->dev, + "link %d error: spi tfr start failed, error %d\n", + context->link->id, err); + return IPC_SM_RUN_ABORT; + } + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_act_tx_wr_cmd_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET); + else + return ipc_sm_state(IPC_SM_SLW_TX_WR_DAT); +} + +static u8 sm_slw_tx_wr_dat_enter(u8 event, struct ipc_link_context *context) +{ + /* prepare to transfer the frame tx data */ + ipc_util_spi_message_prepare(context, context->frame->data, + NULL, context->frame->len); + + /* slave might already have signalled ready to transmit */ + if (atomic_read(&context->state_int)) { + dev_dbg(&context->sdev->dev, + "link %d: slave has already signalled ready\n", + context->link->id); + ipc_util_activate_ss(context); + return IPC_SM_RUN_SLAVE_IRQ; + } else { + ipc_util_activate_ss_with_tmo(context); + return IPC_SM_RUN_NONE; + } +} + +static u8 sm_slw_tx_wr_dat_aud_enter(u8 event, struct ipc_link_context *context) +{ + struct ipc_tx_queue *frame = NULL; + + /* check if there is a frame to be sent */ + if (!ipc_queue_is_empty(context)) { + frame = ipc_queue_get_frame(context); + } else { + /* no frame to send, create an empty one */ + dev_dbg(&context->sdev->dev, + "link %d: no frame to send, allocating dummy\n", + context->link->id); + frame = ipc_queue_new_frame(context, 0); + if (frame == NULL) + return IPC_SM_RUN_ABORT; + } + + ipc_dbg_dump_frame(&context->sdev->dev, context->link->id, frame, true); + + /* prepare to transfer the frame tx data */ + context->frame = frame; + ipc_util_spi_message_prepare(context, context->frame->data, + NULL, context->frame->len); + + /* slave might already have signalled ready to transmit */ + if (event == IPC_SM_RUN_SLAVE_IRQ || atomic_read(&context->state_int)) { + dev_dbg(&context->sdev->dev, + "link %d: slave has already signalled ready\n", + context->link->id); + ipc_util_activate_ss(context); + return IPC_SM_RUN_SLAVE_IRQ; + } else { + ipc_util_activate_ss_with_tmo(context); + return IPC_SM_RUN_NONE; + } +} + +static const struct ipc_sm_state *sm_slw_tx_wr_dat_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET); + else if (event == IPC_SM_RUN_COMMS_TMO) + return ipc_sm_state(IPC_SM_HALT); + else + return ipc_sm_state(IPC_SM_ACT_TX_WR_DAT); +} + +static const struct ipc_sm_state *sm_slw_tx_wr_dat_aud_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET_AUD); + else if (event == IPC_SM_RUN_COMMS_TMO) + return ipc_sm_state(IPC_SM_HALT_AUD); + else + return ipc_sm_state(IPC_SM_ACT_TX_WR_DAT_AUD); +} + +static u8 sm_act_tx_wr_dat_enter(u8 event, struct ipc_link_context *context) +{ + int err; + + /* slave is ready - start the spi transfer */ + dev_dbg(&context->sdev->dev, + "link %d: starting spi tfr\n", context->link->id); + err = spi_async(context->sdev, &context->spi_message); + if (err < 0) { + dev_err(&context->sdev->dev, + "link %d error: spi tfr start failed, error %d\n", + context->link->id, err); + return IPC_SM_RUN_ABORT; + } + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_act_tx_wr_dat_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET); + +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT + /* frame is sent, increment link tx counter */ + context->tx_bytes += context->frame->actual_len; +#endif +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_VERIFY_FRAMES + { + u8 channel; + + channel = ipc_util_get_l2_channel(*(u32 *)context->frame->data); + if (ipc_util_channel_is_loopback(channel)) { + context->last_frame = context->frame; + } else { + ipc_queue_delete_frame(context->frame); + context->frame = NULL; + } + } +#else + /* free the sent frame */ + ipc_queue_delete_frame(context->frame); + context->frame = NULL; +#endif + return ipc_sm_state(IPC_SM_SLW_TX_RD_CMD); +} + +static const struct ipc_sm_state *sm_act_tx_wr_dat_aud_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET_AUD); + +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT + /* frame is sent, increment link tx counter */ + context->tx_bytes += context->frame->actual_len; +#endif +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_VERIFY_FRAMES + { + u8 channel; + + channel = ipc_util_get_l2_channel(*(u32 *)context->frame->data); + if (ipc_util_channel_is_loopback(channel)) { + /* create a copy of the frame */ + context->last_frame = ipc_queue_new_frame(context, + context->frame->actual_len); + memcpy(context->last_frame->data, + context->frame->data, + context->frame->actual_len); + } + } +#endif + return ipc_sm_state(IPC_SM_SLW_RX_WR_DAT_AUD); +} + +static u8 sm_slw_tx_rd_cmd_enter(u8 event, struct ipc_link_context *context) +{ + context->cmd = ipc_util_make_l1_header(CMD_READ, 0, 0); + dev_dbg(&context->sdev->dev, + "link %d: cmd %08x (type %d)\n", + context->link->id, + context->cmd, + ipc_util_get_l1_cmd(context->cmd)); + + /* prepare the spi message to transfer */ + ipc_util_spi_message_prepare(context, &context->cmd, + NULL, IPC_L1_HDR_SIZE); + + /* check if the slave requested this transaction */ + if (event == IPC_SM_RUN_SLAVE_IRQ) { + dev_dbg(&context->sdev->dev, + "link %d: slave initiated transaction, continue\n", + context->link->id); + ipc_util_activate_ss(context); + return IPC_SM_RUN_SLAVE_IRQ; + } else { + /* slave might already have signalled ready to transmit */ + if (atomic_read(&context->state_int)) { + dev_dbg(&context->sdev->dev, + "link %d: slave has already signalled ready\n", + context->link->id); + ipc_util_activate_ss(context); + return IPC_SM_RUN_SLAVE_IRQ; + } else { + ipc_util_activate_ss_with_tmo(context); + return IPC_SM_RUN_NONE; + } + } +} + +static const struct ipc_sm_state *sm_slw_tx_rd_cmd_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET); + else if (event == IPC_SM_RUN_COMMS_TMO) + return ipc_sm_state(IPC_SM_HALT); + else + return ipc_sm_state(IPC_SM_ACT_TX_RD_CMD); +} + +static u8 sm_act_tx_rd_cmd_enter(u8 event, struct ipc_link_context *context) +{ + int err; + + /* slave is ready - start the spi transfer */ + dev_dbg(&context->sdev->dev, + "link %d: starting spi tfr\n", context->link->id); + err = spi_async(context->sdev, &context->spi_message); + if (err < 0) { + dev_err(&context->sdev->dev, + "link %d error: spi tfr start failed, error %d\n", + context->link->id, err); + return IPC_SM_RUN_ABORT; + } + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_act_tx_rd_cmd_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET); + else + return ipc_sm_state(IPC_SM_SLW_RX_WR_CMD); +} + +static u8 sm_slw_rx_wr_cmd_enter(u8 event, struct ipc_link_context *context) +{ + /* prepare to receive MESSAGE WRITE frame header */ + ipc_util_spi_message_prepare(context, NULL, + &context->cmd, IPC_L1_HDR_SIZE); + + /* slave might already have signalled ready to transmit */ + if (atomic_read(&context->state_int)) { + dev_dbg(&context->sdev->dev, + "link %d: slave has already signalled ready\n", + context->link->id); + ipc_util_activate_ss(context); + return IPC_SM_RUN_SLAVE_IRQ; + } else { + ipc_util_activate_ss_with_tmo(context); + return IPC_SM_RUN_NONE; + } +} + +static const struct ipc_sm_state *sm_slw_rx_wr_cmd_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET); + else if (event == IPC_SM_RUN_COMMS_TMO) + return ipc_sm_state(IPC_SM_HALT); + else + return ipc_sm_state(IPC_SM_ACT_RX_WR_CMD); +} + +static u8 sm_act_rx_wr_cmd_enter(u8 event, struct ipc_link_context *context) +{ + int err; + + /* slave is ready - start the spi transfer */ + dev_dbg(&context->sdev->dev, + "link %d: starting spi tfr\n", context->link->id); + err = spi_async(context->sdev, &context->spi_message); + if (err < 0) { + dev_err(&context->sdev->dev, + "link %d error: spi tfr start failed, error %d\n", + context->link->id, err); + return IPC_SM_RUN_ABORT; + } + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_act_rx_wr_cmd_exit(u8 event, + struct ipc_link_context *context) +{ + u8 cmd_type = ipc_util_get_l1_cmd(context->cmd); + int counter = ipc_util_get_l1_counter(context->cmd); + int length = ipc_util_get_l1_length(context->cmd); + + dev_dbg(&context->sdev->dev, + "link %d: RX HEADER %08x (type %d counter %d length %d)\n", + context->link->id, + context->cmd, + cmd_type, + counter, + length); + + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET); + + if (cmd_type == CMD_WRITE) { + /* slave has data to send - allocate a frame to hold it */ + context->frame = ipc_queue_new_frame(context, length); + if (context->frame == NULL) + return ipc_sm_state(IPC_SM_IDL); + + context->frame->counter = counter; + ipc_util_spi_message_prepare(context, NULL, + context->frame->data, context->frame->len); + return ipc_sm_state(IPC_SM_ACT_RX_WR_DAT); + } else { + if (cmd_type != 0) + dev_err(&context->sdev->dev, + "link %d error: received invalid frame type %x " + "(%08x)! assuming TRANSACTION_END...\n", + context->link->id, + cmd_type, + context->cmd); + + /* slave has no data to send */ + dev_dbg(&context->sdev->dev, + "link %d: slave has no data to send\n", + context->link->id); + return ipc_sm_state(IPC_SM_IDL); + } +} + +static u8 sm_slw_rx_wr_dat_aud_enter(u8 event, struct ipc_link_context *context) +{ + /* + * We're using the same frame buffer we just sent, so no need for a + * new allocation here, just prepare the spi message + */ + ipc_util_spi_message_prepare(context, NULL, + context->frame->data, context->frame->len); + + /* slave might already have signalled ready to transmit */ + if (atomic_read(&context->state_int)) { + dev_dbg(&context->sdev->dev, + "link %d: slave has already signalled ready\n", + context->link->id); + ipc_util_activate_ss(context); + return IPC_SM_RUN_SLAVE_IRQ; + } else { + ipc_util_activate_ss_with_tmo(context); + return IPC_SM_RUN_NONE; + } +} + +static const struct ipc_sm_state *sm_slw_rx_wr_dat_aud_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET_AUD); + else if (event == IPC_SM_RUN_COMMS_TMO) + return ipc_sm_state(IPC_SM_HALT_AUD); + else + return ipc_sm_state(IPC_SM_ACT_RX_WR_DAT_AUD); +} + +static u8 sm_act_rx_wr_dat_enter(u8 event, struct ipc_link_context *context) +{ + int err; + + /* assume slave is still ready - prepare and start the spi transfer */ + ipc_util_spi_message_prepare(context, NULL, + context->frame->data, context->frame->len); + + dev_dbg(&context->sdev->dev, + "link %d: starting spi tfr\n", context->link->id); + err = spi_async(context->sdev, &context->spi_message); + if (err < 0) { + dev_err(&context->sdev->dev, + "link %d error: spi tfr start failed, error %d\n", + context->link->id, err); + return IPC_SM_RUN_ABORT; + } + return IPC_SM_RUN_NONE; +} + +static u8 sm_act_rx_wr_dat_aud_enter(u8 event, struct ipc_link_context *context) +{ + int err; + + dev_dbg(&context->sdev->dev, + "link %d: starting spi tfr\n", context->link->id); + err = spi_async(context->sdev, &context->spi_message); + if (err < 0) { + dev_err(&context->sdev->dev, + "link %d error: spi tfr start failed, error %d\n", + context->link->id, err); + return IPC_SM_RUN_ABORT; + } + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_act_rx_wr_dat_exit(u8 event, + struct ipc_link_context *context) +{ + u32 frame_hdr; + unsigned char l2_header; + unsigned int l2_length; + u8 *l2_data; + + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET); + + dev_dbg(&context->sdev->dev, + "link %d: RX PAYLOAD %d bytes\n", + context->link->id, context->frame->len); + +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT + /* frame is received, increment link rx counter */ + context->rx_bytes += context->frame->len; +#endif + /* decode L2 header */ + frame_hdr = *(u32 *)context->frame->data; + l2_header = ipc_util_get_l2_channel(frame_hdr); + l2_length = ipc_util_get_l2_length(frame_hdr); + l2_data = (u8 *)context->frame->data + IPC_L2_HDR_SIZE; + + context->frame->actual_len = l2_length + IPC_L2_HDR_SIZE; + ipc_dbg_dump_frame(&context->sdev->dev, context->link->id, + context->frame, false); + + if (l2_length > (context->frame->len - 4)) { + dev_err(&context->sdev->dev, + "link %d: suspicious frame: L1 len %d L2 len %d\n", + context->link->id, context->frame->len, l2_length); + } + + dev_dbg(&context->sdev->dev, + "link %d: L2 PDU decode: header 0x%08x channel %d length %d " + "data[%02x%02x%02x...]\n", + context->link->id, frame_hdr, l2_header, l2_length, + l2_data[0], l2_data[1], l2_data[2]); + + if (ipc_util_channel_is_loopback(l2_header)) + ipc_dbg_verify_rx_frame(context); + + /* pass received frame up to L2mux layer */ + if (!modem_protocol_channel_is_open(l2_header)) { + dev_err(&context->sdev->dev, + "link %d error: received frame on invalid channel %d, " + "frame discarded\n", + context->link->id, l2_header); + } else { +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT + /* + * Discard loopback frames if we are taking throughput + * measurements - we'll be loading the links and so will likely + * overload the buffers. + */ + if (!ipc_util_channel_is_loopback(l2_header)) +#endif + modem_m6718_spi_receive(context->sdev, + l2_header, l2_length, l2_data); + } + + /* data is copied by L2mux so free the frame here */ + ipc_queue_delete_frame(context->frame); + context->frame = NULL; + + /* check tx queue for content */ + if (!ipc_queue_is_empty(context)) { + dev_dbg(&context->sdev->dev, + "link %d: tx queue not empty\n", context->link->id); + return ipc_sm_state(IPC_SM_SLW_TX_WR_CMD); + } else { + dev_dbg(&context->sdev->dev, + "link %d: tx queue empty\n", context->link->id); + return ipc_sm_state(IPC_SM_SLW_TX_RD_CMD); + } +} + +static const struct ipc_sm_state *sm_act_rx_wr_dat_aud_exit(u8 event, + struct ipc_link_context *context) +{ + u32 frame_hdr; + unsigned char l2_header; + unsigned int l2_length; + u8 *l2_data; + + if (event == IPC_SM_RUN_RESET) + return ipc_sm_state(IPC_SM_RESET_AUD); + + dev_dbg(&context->sdev->dev, + "link %d: RX PAYLOAD %d bytes\n", + context->link->id, context->frame->len); + + /* decode L2 header */ + frame_hdr = *(u32 *)context->frame->data; + l2_header = ipc_util_get_l2_channel(frame_hdr); + l2_length = ipc_util_get_l2_length(frame_hdr); + l2_data = (u8 *)context->frame->data + IPC_L2_HDR_SIZE; + +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT + /* frame is received, increment link rx counter */ + context->rx_bytes += l2_length; +#endif + if (frame_hdr != 0) + context->frame->actual_len = l2_length + IPC_L2_HDR_SIZE; + else + context->frame->actual_len = 0; + ipc_dbg_dump_frame(&context->sdev->dev, context->link->id, + context->frame, false); + + if (l2_length > (context->frame->len - 4)) + dev_err(&context->sdev->dev, + "link %d: suspicious frame: L1 len %d L2 len %d\n", + context->link->id, context->frame->len, l2_length); + + dev_dbg(&context->sdev->dev, + "link %d: L2 PDU decode: header 0x%08x channel %d length %d " + "data[%02x%02x%02x...]\n", + context->link->id, frame_hdr, l2_header, l2_length, + l2_data[0], l2_data[1], l2_data[2]); + + if (ipc_util_channel_is_loopback(l2_header)) + ipc_dbg_verify_rx_frame(context); + + /* did the slave actually have anything to send? */ + if (frame_hdr != 0) { + /* pass received frame up to L2mux layer */ + if (!modem_protocol_channel_is_open(l2_header)) { + dev_err(&context->sdev->dev, + "link %d error: received frame on invalid " + "channel %d, frame discarded\n", + context->link->id, l2_header); + } else { +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT + /* + * Discard loopback frames if we are taking throughput + * measurements - we'll be loading the links and so will + * likely overload the buffers. + */ + if (!ipc_util_channel_is_loopback(l2_header)) +#endif + modem_m6718_spi_receive(context->sdev, + l2_header, l2_length, l2_data); + } + } else { + dev_dbg(&context->sdev->dev, + "link %d: received dummy frame, discarding\n", + context->link->id); + } + + /* data is copied by L2mux so free the frame here */ + ipc_queue_delete_frame(context->frame); + context->frame = NULL; + + /* audio link goes idle ready for next transaction */ + return ipc_sm_state(IPC_SM_IDL_AUD); +} + +static u8 sm_halt_enter(u8 event, struct ipc_link_context *context) +{ + dev_err(&context->sdev->dev, + "link %d error: HALTED\n", context->link->id); + +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_MODEM_STATE + /* + * Force modem reset, this will cause a reset event from the modemstate + * driver which will reset the links. If debugfs is enabled then there + * is a userspace file which controls whether MSR is enabled or not. + */ +#ifdef CONFIG_DEBUG_FS + if (l1_context.msr_disable) { + dev_info(&context->sdev->dev, + "link %d: MSR is disabled by user, " + "not requesting modem reset\n", context->link->id); + return IPC_SM_RUN_RESET; + } +#endif + modem_state_force_reset(); +#endif + return IPC_SM_RUN_RESET; +} + +static const struct ipc_sm_state *sm_halt_exit(u8 event, + struct ipc_link_context *context) +{ + return ipc_sm_state(IPC_SM_RESET); +} + +static const struct ipc_sm_state *sm_halt_aud_exit(u8 event, + struct ipc_link_context *context) +{ + return ipc_sm_state(IPC_SM_RESET_AUD); +} + +static u8 sm_reset_enter(u8 event, struct ipc_link_context *context) +{ + dev_err(&context->sdev->dev, + "link %d resetting\n", context->link->id); + + if (context->link->id == 0) + ipc_broadcast_modem_reset(context); + + ipc_util_deactivate_ss(context); + ipc_queue_reset(context); + if (context->frame != NULL) { + ipc_queue_delete_frame(context->frame); + context->frame = NULL; + } +#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_VERIFY_FRAMES + if (context->last_frame != NULL) { + ipc_queue_delete_frame(context->last_frame); + context->last_frame = NULL; + } +#endif + dev_dbg(&context->sdev->dev, + "link %d reset completed\n", context->link->id); + + return IPC_SM_RUN_RESET; +} + +static const struct ipc_sm_state *sm_reset_exit(u8 event, + struct ipc_link_context *context) +{ + return ipc_sm_state(IPC_SM_INIT); +} + +static const struct ipc_sm_state *sm_reset_aud_exit(u8 event, + struct ipc_link_context *context) +{ + return ipc_sm_state(IPC_SM_INIT_AUD); +} + +static u8 sm_slw_tx_bootreq_enter(u8 event, struct ipc_link_context *context) +{ + dev_info(&context->sdev->dev, + "link %d: waiting for boot sync\n", context->link->id); + + ipc_util_activate_ss(context); + context->cmd = ipc_util_make_l1_header(CMD_BOOTREQ, 0, + IPC_DRIVER_VERSION); + dev_dbg(&context->sdev->dev, + "link %d: TX HEADER cmd %08x (type %x)\n", + context->link->id, + context->cmd, + ipc_util_get_l1_cmd(context->cmd)); + ipc_util_spi_message_prepare(context, &context->cmd, + NULL, IPC_L1_HDR_SIZE); + + /* wait now for the slave to indicate ready... */ + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_slw_tx_bootreq_exit(u8 event, + struct ipc_link_context *context) +{ + return ipc_sm_state(IPC_SM_ACT_TX_BOOTREQ); +} + +static u8 sm_act_tx_bootreq_enter(u8 event, struct ipc_link_context *context) +{ + int err; + + /* slave is ready - start the spi transfer */ + dev_dbg(&context->sdev->dev, + "link %d: starting spi tfr\n", context->link->id); + err = spi_async(context->sdev, &context->spi_message); + if (err < 0) { + dev_err(&context->sdev->dev, + "link %d error: spi tfr start failed, error %d\n", + context->link->id, err); + return IPC_SM_RUN_ABORT; + } + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_act_tx_bootreq_exit(u8 event, + struct ipc_link_context *context) +{ + return ipc_sm_state(IPC_SM_SLW_RX_BOOTRESP); +} + +static u8 sm_slw_rx_bootresp_enter(u8 event, struct ipc_link_context *context) +{ + /* prepare to receive BOOTRESP frame header */ + ipc_util_spi_message_prepare(context, NULL, + &context->cmd, IPC_L1_HDR_SIZE); + + /* slave might already have signalled ready to transmit */ + if (atomic_read(&context->state_int)) { + dev_dbg(&context->sdev->dev, + "link %d: slave has already signalled ready\n", + context->link->id); + ipc_util_activate_ss(context); + return IPC_SM_RUN_SLAVE_IRQ; + } else { + ipc_util_activate_ss_with_tmo(context); + return IPC_SM_RUN_NONE; + } +} + +static const struct ipc_sm_state *sm_slw_rx_bootresp_exit(u8 event, + struct ipc_link_context *context) +{ + if (event == IPC_SM_RUN_COMMS_TMO) { + /* + * Modem timeout: was it really ready or just noise? + * Revert to waiting for handshake to start. + */ + ipc_util_deactivate_ss(context); + return ipc_sm_state(IPC_SM_SLW_TX_BOOTREQ); + } else { + return ipc_sm_state(IPC_SM_ACT_RX_BOOTRESP); + } +} + +static u8 sm_act_rx_bootresp_enter(u8 event, struct ipc_link_context *context) +{ + int err; + + /* slave is ready - start the spi transfer */ + dev_dbg(&context->sdev->dev, + "link %d: starting spi tfr\n", context->link->id); + err = spi_async(context->sdev, &context->spi_message); + if (err < 0) { + dev_err(&context->sdev->dev, + "link %d error: spi tfr start failed, error %d\n", + context->link->id, err); + return IPC_SM_RUN_ABORT; + } + return IPC_SM_RUN_NONE; +} + +static const struct ipc_sm_state *sm_act_rx_bootresp_exit(u8 event, + struct ipc_link_context *context) +{ + u8 cmd_type = ipc_util_get_l1_cmd(context->cmd); + u8 modem_ver; + + dev_dbg(&context->sdev->dev, + "link %d: RX HEADER %08x (type %d)\n", + context->link->id, context->cmd, cmd_type); + + if (cmd_type == CMD_BOOTRESP) { + modem_ver = ipc_util_get_l1_bootresp_ver(context->cmd); + + dev_info(&context->sdev->dev, + "link %d: boot sync done; " + "APE version %02x, MODEM version %02x\n", + context->link->id, IPC_DRIVER_VERSION, modem_ver); + + /* check for minimum required modem version */ + if (modem_ver < IPC_DRIVER_MODEM_MIN_VER) { + dev_warn(&context->sdev->dev, + "link %d warning: modem version mismatch! " + "minimum required version is %02x\n", + context->link->id, + IPC_DRIVER_MODEM_MIN_VER); + } + + return ipc_sm_state(IPC_SM_WAIT_HANDSHAKE_INACTIVE); + } else { + /* invalid response... this is not our slave */ + dev_err(&context->sdev->dev, + "link %d error: expected %x (BOOTRESP), received %x.\n", + context->link->id, + CMD_BOOTRESP, + cmd_type); + return ipc_sm_state(IPC_SM_HALT); + } +} + +/* the driver protocol state machine */ +static const struct ipc_sm_state state_machine[IPC_SM_STATE_ID_NBR] = { + [IPC_SM_INIT] = { + .id = IPC_SM_INIT, + .enter = sm_init_enter, + .exit = sm_init_exit, + .events = IPC_SM_RUN_INIT | IPC_SM_RUN_RESET + }, + [IPC_SM_HALT] = { + .id = IPC_SM_HALT, + .enter = sm_halt_enter, + .exit = sm_halt_exit, + .events = IPC_SM_RUN_RESET + }, + [IPC_SM_RESET] = { + .id = IPC_SM_RESET, + .enter = sm_reset_enter, + .exit = sm_reset_exit, + .events = IPC_SM_RUN_RESET + }, + [IPC_SM_WAIT_SLAVE_STABLE] = { + .id = IPC_SM_WAIT_SLAVE_STABLE, + .enter = sm_wait_slave_stable_enter, + .exit = sm_wait_slave_stable_exit, + .events = IPC_SM_RUN_STABLE_TMO + }, + [IPC_SM_WAIT_HANDSHAKE_INACTIVE] = { + .id = IPC_SM_WAIT_HANDSHAKE_INACTIVE, + .enter = sm_wait_handshake_inactive_enter, + .exit = sm_wait_handshake_inactive_exit, + .events = IPC_SM_RUN_STABLE_TMO + }, + [IPC_SM_SLW_TX_BOOTREQ] = { + .id = IPC_SM_SLW_TX_BOOTREQ, + .enter = sm_slw_tx_bootreq_enter, + .exit = sm_slw_tx_bootreq_exit, + .events = IPC_SM_RUN_SLAVE_IRQ + }, + [IPC_SM_ACT_TX_BOOTREQ] = { + .id = IPC_SM_ACT_TX_BOOTREQ, + .enter = sm_act_tx_bootreq_enter, + .exit = sm_act_tx_bootreq_exit, + .events = IPC_SM_RUN_TFR_COMPLETE + }, + [IPC_SM_SLW_RX_BOOTRESP] = { + .id = IPC_SM_SLW_RX_BOOTRESP, + .enter = sm_slw_rx_bootresp_enter, + .exit = sm_slw_rx_bootresp_exit, + .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_COMMS_TMO + }, + [IPC_SM_ACT_RX_BOOTRESP] = { + .id = IPC_SM_ACT_RX_BOOTRESP, + .enter = sm_act_rx_bootresp_enter, + .exit = sm_act_rx_bootresp_exit, + .events = IPC_SM_RUN_TFR_COMPLETE + }, + [IPC_SM_IDL] = { + .id = IPC_SM_IDL, + .enter = sm_idl_enter, + .exit = sm_idl_exit, + .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_TX_REQ | + IPC_SM_RUN_RESET + }, + [IPC_SM_SLW_TX_WR_CMD] = { + .id = IPC_SM_SLW_TX_WR_CMD, + .enter = sm_slw_tx_wr_cmd_enter, + .exit = sm_slw_tx_wr_cmd_exit, + .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_COMMS_TMO | + IPC_SM_RUN_RESET + }, + [IPC_SM_ACT_TX_WR_CMD] = { + .id = IPC_SM_ACT_TX_WR_CMD, + .enter = sm_act_tx_wr_cmd_enter, + .exit = sm_act_tx_wr_cmd_exit, + .events = IPC_SM_RUN_TFR_COMPLETE | IPC_SM_RUN_RESET + }, + [IPC_SM_SLW_TX_WR_DAT] = { + .id = IPC_SM_SLW_TX_WR_DAT, + .enter = sm_slw_tx_wr_dat_enter, + .exit = sm_slw_tx_wr_dat_exit, + .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_COMMS_TMO | + IPC_SM_RUN_RESET + }, + [IPC_SM_ACT_TX_WR_DAT] = { + .id = IPC_SM_ACT_TX_WR_DAT, + .enter = sm_act_tx_wr_dat_enter, + .exit = sm_act_tx_wr_dat_exit, + .events = IPC_SM_RUN_TFR_COMPLETE | IPC_SM_RUN_RESET + }, + [IPC_SM_SLW_TX_RD_CMD] = { + .id = IPC_SM_SLW_TX_RD_CMD, + .enter = sm_slw_tx_rd_cmd_enter, + .exit = sm_slw_tx_rd_cmd_exit, + .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_COMMS_TMO | + IPC_SM_RUN_RESET + }, + [IPC_SM_ACT_TX_RD_CMD] = { + .id = IPC_SM_ACT_TX_RD_CMD, + .enter = sm_act_tx_rd_cmd_enter, + .exit = sm_act_tx_rd_cmd_exit, + .events = IPC_SM_RUN_TFR_COMPLETE | IPC_SM_RUN_RESET + }, + [IPC_SM_SLW_RX_WR_CMD] = { + .id = IPC_SM_SLW_RX_WR_CMD, + .enter = sm_slw_rx_wr_cmd_enter, + .exit = sm_slw_rx_wr_cmd_exit, + .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_COMMS_TMO | + IPC_SM_RUN_RESET + }, + [IPC_SM_ACT_RX_WR_CMD] = { + .id = IPC_SM_ACT_RX_WR_CMD, + .enter = sm_act_rx_wr_cmd_enter, + .exit = sm_act_rx_wr_cmd_exit, + .events = IPC_SM_RUN_TFR_COMPLETE | IPC_SM_RUN_RESET + }, + [IPC_SM_ACT_RX_WR_DAT] = { + .id = IPC_SM_ACT_RX_WR_DAT, + .enter = sm_act_rx_wr_dat_enter, + .exit = sm_act_rx_wr_dat_exit, + .events = IPC_SM_RUN_TFR_COMPLETE | IPC_SM_RUN_RESET + }, + /* audio link states below */ + [IPC_SM_INIT_AUD] = { + .id = IPC_SM_INIT_AUD, + .enter = sm_init_enter, + .exit = sm_init_aud_exit, + .events = IPC_SM_RUN_INIT | IPC_SM_RUN_RESET + }, + [IPC_SM_HALT_AUD] = { + .id = IPC_SM_HALT_AUD, + .enter = sm_halt_enter, + .exit = sm_halt_aud_exit, + .events = IPC_SM_RUN_RESET + }, + [IPC_SM_RESET_AUD] = { + .id = IPC_SM_RESET_AUD, + .enter = sm_reset_enter, + .exit = sm_reset_aud_exit, + .events = IPC_SM_RUN_RESET + }, + [IPC_SM_IDL_AUD] = { + .id = IPC_SM_IDL_AUD, + .enter = sm_idl_enter, + .exit = sm_idl_aud_exit, + .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_TX_REQ | + IPC_SM_RUN_RESET + }, + [IPC_SM_SLW_TX_WR_DAT_AUD] = { + .id = IPC_SM_SLW_TX_WR_DAT_AUD, + .enter = sm_slw_tx_wr_dat_aud_enter, + .exit = sm_slw_tx_wr_dat_aud_exit, + .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_COMMS_TMO | + IPC_SM_RUN_RESET + }, + [IPC_SM_ACT_TX_WR_DAT_AUD] = { + .id = IPC_SM_ACT_TX_WR_DAT_AUD, + .enter = sm_act_tx_wr_dat_enter, + .exit = sm_act_tx_wr_dat_aud_exit, + .events = IPC_SM_RUN_TFR_COMPLETE | IPC_SM_RUN_RESET + }, + [IPC_SM_SLW_RX_WR_DAT_AUD] = { + .id = IPC_SM_SLW_RX_WR_DAT_AUD, + .enter = sm_slw_rx_wr_dat_aud_enter, + .exit = sm_slw_rx_wr_dat_aud_exit, + .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_COMMS_TMO | + IPC_SM_RUN_RESET + }, + [IPC_SM_ACT_RX_WR_DAT_AUD] = { + .id = IPC_SM_ACT_RX_WR_DAT_AUD, + .enter = sm_act_rx_wr_dat_aud_enter, + .exit = sm_act_rx_wr_dat_aud_exit, + .events = IPC_SM_RUN_TFR_COMPLETE | IPC_SM_RUN_RESET + } +}; + + +const struct ipc_sm_state *ipc_sm_idle_state(struct ipc_link_context *context) +{ + if (context->link->id == IPC_LINK_AUDIO) + return ipc_sm_state(IPC_SM_IDL_AUD); + else + return ipc_sm_state(IPC_SM_IDL); +} + +const struct ipc_sm_state *ipc_sm_init_state(struct ipc_link_context *context) +{ + if (context->link->id == IPC_LINK_AUDIO) + return ipc_sm_state(IPC_SM_INIT_AUD); + else + return ipc_sm_state(IPC_SM_INIT); +} + +const struct ipc_sm_state *ipc_sm_state(u8 id) +{ + BUG_ON(id >= IPC_SM_STATE_ID_NBR); + return &state_machine[id]; +} + +bool ipc_sm_valid_for_state(u8 event, const struct ipc_sm_state *state) +{ + return (state->events & event) == event; +} + +static void state_machine_run(struct ipc_link_context *context, u8 event) +{ + struct modem_m6718_spi_link_platform_data *link = context->link; + struct spi_device *sdev = context->sdev; + const struct ipc_sm_state *cur_state = context->state; + + /* some sanity checking */ + if (context == NULL || link == NULL || cur_state == NULL) { + pr_err("M6718 IPC protocol error: " + "inconsistent driver state, ignoring event\n"); + return; + } + + dev_dbg(&sdev->dev, "link %d: RUNNING in %s (%s)\n", link->id, + ipc_dbg_state_id(cur_state), ipc_dbg_event(event)); + + /* valid trigger event for current state? */ + if (!ipc_sm_valid_for_state(event, cur_state)) { + dev_dbg(&sdev->dev, + "link %d: ignoring invalid event\n", link->id); + ipc_dbg_ignoring_event(context, event); + return; + } + ipc_dbg_handling_event(context, event); + + /* run machine while state entry functions trigger new changes */ + do { + if (event == IPC_SM_RUN_SLAVE_IRQ && + !ipc_util_int_is_active(context)) { + dev_err(&sdev->dev, + "link %d error: slave is not ready! (%s)", + link->id, + ipc_dbg_state_id(cur_state)); + } + + if (event == IPC_SM_RUN_ABORT) { + dev_err(&sdev->dev, + "link %d error: abort event\n", link->id); + /* reset state to idle */ + context->state = ipc_sm_idle_state(context); + break; + } else { + /* exit current state */ + dev_dbg(&sdev->dev, "link %d: exit %s (%s)\n", + link->id, ipc_dbg_state_id(cur_state), + ipc_dbg_event(event)); + cur_state = cur_state->exit(event, context); + context->state = cur_state; + } + + /* reset state of slave irq to prepare for next event */ + if (event == IPC_SM_RUN_SLAVE_IRQ) + atomic_set(&context->state_int, 0); + + /* enter new state */ + dev_dbg(&sdev->dev, "link %d: enter %s (%s)\n", link->id, + ipc_dbg_state_id(cur_state), ipc_dbg_event(event)); + event = context->state->enter(event, context); + ipc_dbg_entering_state(context); + } while (event != IPC_SM_RUN_NONE); + + dev_dbg(&sdev->dev, "link %d: STOPPED in %s\n", link->id, + ipc_dbg_state_id(cur_state)); +} + +void ipc_sm_kick(u8 event, struct ipc_link_context *context) +{ + unsigned long flags; + struct modem_m6718_spi_link_platform_data *link = context->link; + struct spi_device *sdev = context->sdev; + struct spi_message *msg = &context->spi_message; + u8 i; + + spin_lock_irqsave(&context->sm_lock, flags); + switch (event) { + case IPC_SM_RUN_SLAVE_IRQ: + dev_dbg(&sdev->dev, + "link %d EVENT: slave-ready irq\n", link->id); + del_timer(&context->comms_timer); + atomic_set(&context->state_int, + ipc_util_int_is_active(context)); + break; + + case IPC_SM_RUN_TFR_COMPLETE: + dev_dbg(&sdev->dev, + "link %d EVENT: spi tfr complete (status %d len %d)\n", + link->id, msg->status, msg->actual_length); + ipc_dbg_dump_spi_tfr(context); + break; + + case IPC_SM_RUN_COMMS_TMO: + { + char *statestr; + struct ipc_link_context *contexts = l1_context.device_context; + + statestr = ipc_dbg_link_state_str(context); + dev_err(&sdev->dev, + "link %d EVENT: modem comms timeout (%s)!\n", + link->id, ipc_dbg_state_id(context->state)); + if (statestr != NULL) { + dev_err(&sdev->dev, "%s", statestr); + kfree(statestr); + } + + /* cancel all link timeout timers except this one */ + for (i = 0; i < IPC_NBR_SUPPORTED_SPI_LINKS; i++) + if (contexts[i].link->id != link->id) + del_timer(&contexts[i].comms_timer); + break; + } + + case IPC_SM_RUN_STABLE_TMO: + dev_dbg(&sdev->dev, + "link %d EVENT: slave-stable timeout\n", link->id); + break; + + case IPC_SM_RUN_RESET: + dev_dbg(&sdev->dev, + "link %d EVENT: reset\n", link->id); + del_timer(&context->comms_timer); + break; + + default: + break; + } + + state_machine_run(context, event); + spin_unlock_irqrestore(&context->sm_lock, flags); +} + -- cgit v1.2.3 From 4cf60a4301367bec5962ffb905c51a62c85bd1a6 Mon Sep 17 00:00:00 2001 From: Chris Blair Date: Wed, 4 Jan 2012 16:19:01 +0000 Subject: modem m6718: Handle modem irq before resume Handles the scenario where the modem wakes up the APE and the modem irq is serviced before the IPC driver is resumed. The event is regsitered and serviced later when the driver resume hook is called. ST-Ericsson ID: 405458 ST-Ericsson FOSS-OUT ID: Trivial ST-Ericsson Linux next: NA Change-Id: I441decea1cbc657e8dc13ca63a3116f50581c6a7 Signed-off-by: Chris Blair Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/44410 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Rickard EVERTSSON Reviewed-by: Linus WALLEIJ --- drivers/modem/m6718_spi/modem_driver.c | 34 +++++++++++++--- drivers/modem/m6718_spi/modem_private.h | 1 + drivers/modem/m6718_spi/modem_protocol.h | 2 + drivers/modem/m6718_spi/modem_util.h | 4 ++ drivers/modem/m6718_spi/protocol.c | 66 ++++++++++++++++++++++++++++++++ drivers/modem/m6718_spi/statemachine.c | 6 ++- drivers/modem/m6718_spi/util.c | 14 +++++++ 7 files changed, 121 insertions(+), 6 deletions(-) (limited to 'drivers/modem/m6718_spi/statemachine.c') diff --git a/drivers/modem/m6718_spi/modem_driver.c b/drivers/modem/m6718_spi/modem_driver.c index 623a9191d27..8086e97aa7c 100644 --- a/drivers/modem/m6718_spi/modem_driver.c +++ b/drivers/modem/m6718_spi/modem_driver.c @@ -214,13 +214,25 @@ static int __exit spi_remove(struct spi_device *sdev) static int spi_suspend(struct spi_device *sdev, pm_message_t mesg) { bool busy; + int ret = -EBUSY; + dev_dbg(&sdev->dev, "suspend called\n"); busy = modem_protocol_is_busy(sdev); - dev_dbg(&sdev->dev, "suspend called, protocol busy:%d\n", busy); - if (!busy) - return modem_net_suspend(modem_driver_data.ndev); - else + if (busy) { + dev_warn(&sdev->dev, "suspend failed (protocol busy)\n"); return -EBUSY; + } + ret = modem_protocol_suspend(sdev); + if (ret) { + dev_warn(&sdev->dev, "suspend failed, (protocol suspend))\n"); + return ret; + } + ret = modem_net_suspend(modem_driver_data.ndev); + if (ret) { + dev_warn(&sdev->dev, "suspend failed, (netdev suspend)\n"); + return ret; + } + return 0; } /** @@ -229,8 +241,20 @@ static int spi_suspend(struct spi_device *sdev, pm_message_t mesg) */ static int spi_resume(struct spi_device *sdev) { + int ret; + dev_dbg(&sdev->dev, "resume called\n"); - return modem_net_resume(modem_driver_data.ndev); + ret = modem_protocol_resume(sdev); + if (ret) { + dev_warn(&sdev->dev, "resume failed, (protocol resume))\n"); + return ret; + } + ret = modem_net_resume(modem_driver_data.ndev); + if (ret) { + dev_warn(&sdev->dev, "resume failed, (netdev resume))\n"); + return ret; + } + return 0; } #endif /* CONFIG_PM */ diff --git a/drivers/modem/m6718_spi/modem_private.h b/drivers/modem/m6718_spi/modem_private.h index 929cb62d672..10e651c01ea 100644 --- a/drivers/modem/m6718_spi/modem_private.h +++ b/drivers/modem/m6718_spi/modem_private.h @@ -49,6 +49,7 @@ struct ipc_tx_queue { struct ipc_link_context { struct modem_m6718_spi_link_platform_data *link; struct spi_device *sdev; + atomic_t suspended; atomic_t gpio_configured; atomic_t state_int; spinlock_t sm_lock; diff --git a/drivers/modem/m6718_spi/modem_protocol.h b/drivers/modem/m6718_spi/modem_protocol.h index 69511945cb5..751dcba1087 100644 --- a/drivers/modem/m6718_spi/modem_protocol.h +++ b/drivers/modem/m6718_spi/modem_protocol.h @@ -18,5 +18,7 @@ int modem_protocol_probe(struct spi_device *sdev); void modem_protocol_exit(void); bool modem_protocol_is_busy(struct spi_device *sdev); bool modem_protocol_channel_is_open(u8 channel); +int modem_protocol_suspend(struct spi_device *sdev); +int modem_protocol_resume(struct spi_device *sdev); #endif /* _MODEM_PROTOCOL_H_ */ diff --git a/drivers/modem/m6718_spi/modem_util.h b/drivers/modem/m6718_spi/modem_util.h index 3219900a3c8..2d9e2e39abc 100644 --- a/drivers/modem/m6718_spi/modem_util.h +++ b/drivers/modem/m6718_spi/modem_util.h @@ -50,4 +50,8 @@ bool ipc_util_link_gpio_request(struct ipc_link_context *context, bool ipc_util_link_gpio_config(struct ipc_link_context *context); bool ipc_util_link_gpio_unconfig(struct ipc_link_context *context); +bool ipc_util_link_is_suspended(struct ipc_link_context *context); +void ipc_util_suspend_link(struct ipc_link_context *context); +void ipc_util_resume_link(struct ipc_link_context *context); + #endif /* _MODEM_UTIL_H_ */ diff --git a/drivers/modem/m6718_spi/protocol.c b/drivers/modem/m6718_spi/protocol.c index aa21d4dbcde..fa6b2528dd4 100644 --- a/drivers/modem/m6718_spi/protocol.c +++ b/drivers/modem/m6718_spi/protocol.c @@ -196,6 +196,71 @@ bool modem_protocol_is_busy(struct spi_device *sdev) return false; } +int modem_protocol_suspend(struct spi_device *sdev) +{ + struct modem_m6718_spi_link_platform_data *link = + sdev->dev.platform_data; + struct ipc_link_context *context; + int link_id; + + if (link == NULL) { + /* platform data missing in board config? */ + dev_err(&sdev->dev, "error: no platform data for link!\n"); + return -ENODEV; + } + + link_id = link->id; + context = &l1_context.device_context[link_id]; + + if (link_id >= IPC_NBR_SUPPORTED_SPI_LINKS) { + dev_err(&sdev->dev, + "link %d error: too many links! (max %d)\n", + link->id, IPC_NBR_SUPPORTED_SPI_LINKS); + return -ENODEV; + } + + ipc_util_suspend_link(context); + return 0; +} + +int modem_protocol_resume(struct spi_device *sdev) +{ + struct modem_m6718_spi_link_platform_data *link = + sdev->dev.platform_data; + struct ipc_link_context *context; + int link_id; + + if (link == NULL) { + /* platform data missing in board config? */ + dev_err(&sdev->dev, "error: no platform data for link!\n"); + return -ENODEV; + } + + link_id = link->id; + context = &l1_context.device_context[link_id]; + + if (link_id >= IPC_NBR_SUPPORTED_SPI_LINKS) { + dev_err(&sdev->dev, + "link %d error: too many links! (max %d)\n", + link->id, IPC_NBR_SUPPORTED_SPI_LINKS); + return -ENODEV; + } + + ipc_util_resume_link(context); + + /* + * If the resume event was an interrupt from the slave then the event + * is pending and we need to service it now. + */ + if (ipc_util_int_is_active(context)) { + dev_dbg(&sdev->dev, + "link %d: slave-ready is pending after resume\n", + link_id); + ipc_sm_kick(IPC_SM_RUN_SLAVE_IRQ, context); + } + return 0; +} + static void spi_tfr_complete(void *context) { ipc_sm_kick(IPC_SM_RUN_TFR_COMPLETE, @@ -306,6 +371,7 @@ int modem_protocol_probe(struct spi_device *sdev) /* init link context */ context->link = link; context->sdev = sdev; + ipc_util_resume_link(context); atomic_set(&context->gpio_configured, 0); atomic_set(&context->state_int, ipc_util_int_level_inactive(context)); diff --git a/drivers/modem/m6718_spi/statemachine.c b/drivers/modem/m6718_spi/statemachine.c index a2092cd2f94..a956661c3bf 100644 --- a/drivers/modem/m6718_spi/statemachine.c +++ b/drivers/modem/m6718_spi/statemachine.c @@ -1396,7 +1396,11 @@ void ipc_sm_kick(u8 event, struct ipc_link_context *context) break; } - state_machine_run(context, event); + if (!ipc_util_link_is_suspended(context)) + state_machine_run(context, event); + else + dev_dbg(&sdev->dev, + "link %d is suspended, waiting for resume\n", link->id); spin_unlock_irqrestore(&context->sm_lock, flags); } diff --git a/drivers/modem/m6718_spi/util.c b/drivers/modem/m6718_spi/util.c index 43862baff99..9c89eb9b34a 100644 --- a/drivers/modem/m6718_spi/util.c +++ b/drivers/modem/m6718_spi/util.c @@ -266,3 +266,17 @@ bool ipc_util_link_gpio_unconfig(struct ipc_link_context *context) return true; } +bool ipc_util_link_is_suspended(struct ipc_link_context *context) +{ + return atomic_read(&context->suspended) == 1; +} + +void ipc_util_suspend_link(struct ipc_link_context *context) +{ + atomic_set(&context->suspended, 1); +} + +void ipc_util_resume_link(struct ipc_link_context *context) +{ + atomic_set(&context->suspended, 0); +} -- cgit v1.2.3 From 67cbe0b8b9090fa4c8b91040714add0e3b02c3fd Mon Sep 17 00:00:00 2001 From: Chris Blair Date: Tue, 10 Jan 2012 09:53:46 +0000 Subject: modem m6718: Revert protocol to version 2 Changes the IPC protocol to version 0x02 which used the same protocol for both common and audio links. Since this protocol is not suitable for DMA transfers, the link must be configured in interrupt transfer mode. ST-Ericsson ID: 409301 ST-Ericsson FOSS-OUT ID: Trivial ST-Ericsson Linux next: NA Change-Id: Ifb7fc0ad008983e59bcfe97f59d9aea128613888 Signed-off-by: Chris Blair Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/44936 Reviewed-by: Srinidhi KASAGAR --- drivers/modem/m6718_spi/debug.c | 8 - drivers/modem/m6718_spi/modem_private.h | 4 +- drivers/modem/m6718_spi/modem_statemachine.h | 10 - drivers/modem/m6718_spi/protocol.c | 4 +- drivers/modem/m6718_spi/queue.c | 25 +-- drivers/modem/m6718_spi/statemachine.c | 325 +-------------------------- drivers/modem/m6718_spi/util.c | 1 - 7 files changed, 11 insertions(+), 366 deletions(-) (limited to 'drivers/modem/m6718_spi/statemachine.c') diff --git a/drivers/modem/m6718_spi/debug.c b/drivers/modem/m6718_spi/debug.c index 522a37163c1..06ee4c34a5a 100644 --- a/drivers/modem/m6718_spi/debug.c +++ b/drivers/modem/m6718_spi/debug.c @@ -36,14 +36,6 @@ static const char * const sm_state_id_str[] = { "IPC_SLW_RX_WR_CMD", "IPC_ACT_RX_WR_CMD", "IPC_ACT_RX_WR_DAT", - "IPC_INIT_AUD", - "IPC_HALT_AUD", - "IPC_RESET_AUD", - "IPC_IDL_AUD", - "IPC_SLW_TX_WR_DAT_AUD", - "IPC_ACT_TX_WR_DAT_AUD", - "IPC_SLW_RX_WR_DAT_AUD", - "IPC_ACT_RX_WR_DAT_AUD", }; /* name of each state machine run cause */ diff --git a/drivers/modem/m6718_spi/modem_private.h b/drivers/modem/m6718_spi/modem_private.h index 10e651c01ea..a4de4ba9e93 100644 --- a/drivers/modem/m6718_spi/modem_private.h +++ b/drivers/modem/m6718_spi/modem_private.h @@ -24,8 +24,8 @@ #include "modem_protocol.h" #include "modem_statemachine.h" -#define IPC_DRIVER_VERSION (0x03) /* APE protocol version */ -#define IPC_DRIVER_MODEM_MIN_VER (0x03) /* version required from modem */ +#define IPC_DRIVER_VERSION (0x02) /* APE protocol version */ +#define IPC_DRIVER_MODEM_MIN_VER (0x02) /* version required from modem */ #define IPC_NBR_SUPPORTED_SPI_LINKS (2) #define IPC_LINK_COMMON (0) diff --git a/drivers/modem/m6718_spi/modem_statemachine.h b/drivers/modem/m6718_spi/modem_statemachine.h index 55e4a520d3d..6a2a32cad3a 100644 --- a/drivers/modem/m6718_spi/modem_statemachine.h +++ b/drivers/modem/m6718_spi/modem_statemachine.h @@ -15,7 +15,6 @@ /* valid states for the driver state machine */ enum ipc_sm_state_id { - /* common link and shared states below */ IPC_SM_INIT, IPC_SM_HALT, IPC_SM_RESET, @@ -35,15 +34,6 @@ enum ipc_sm_state_id { IPC_SM_SLW_RX_WR_CMD, IPC_SM_ACT_RX_WR_CMD, IPC_SM_ACT_RX_WR_DAT, - /* audio link states below */ - IPC_SM_INIT_AUD, - IPC_SM_HALT_AUD, - IPC_SM_RESET_AUD, - IPC_SM_IDL_AUD, - IPC_SM_SLW_TX_WR_DAT_AUD, - IPC_SM_ACT_TX_WR_DAT_AUD, - IPC_SM_SLW_RX_WR_DAT_AUD, - IPC_SM_ACT_RX_WR_DAT_AUD, IPC_SM_STATE_ID_NBR }; diff --git a/drivers/modem/m6718_spi/protocol.c b/drivers/modem/m6718_spi/protocol.c index fa6b2528dd4..38e9190e397 100644 --- a/drivers/modem/m6718_spi/protocol.c +++ b/drivers/modem/m6718_spi/protocol.c @@ -183,9 +183,7 @@ bool modem_protocol_is_busy(struct spi_device *sdev) for (i = 0; i < IPC_NBR_SUPPORTED_SPI_LINKS; i++) switch (l1_context.device_context[i].state->id) { case IPC_SM_IDL: - case IPC_SM_IDL_AUD: case IPC_SM_INIT: - case IPC_SM_INIT_AUD: case IPC_SM_WAIT_SLAVE_STABLE: /* not busy; continue checking */ break; @@ -280,7 +278,7 @@ static irqreturn_t slave_ready_irq(int irq, void *dev) } #ifdef WORKAROUND_DUPLICATED_IRQ - if (link->id != IPC_LINK_AUDIO && pl022_tfr_in_progress(sdev)) { + if (pl022_tfr_in_progress(sdev)) { dev_warn(&sdev->dev, "link %d warning: slave irq while transfer " "is active! discarding event\n", link->id); diff --git a/drivers/modem/m6718_spi/queue.c b/drivers/modem/m6718_spi/queue.c index 911d538ee82..fe23ac36736 100644 --- a/drivers/modem/m6718_spi/queue.c +++ b/drivers/modem/m6718_spi/queue.c @@ -14,9 +14,6 @@ #define FRAME_LENGTH_ALIGN (4) #define MAX_FRAME_COUNTER (256) -/* fixed L1 frame size for audio link: 4 byte L2 header + 664 byte L2 payload */ -#define FRAME_SIZE_AUDIO (668) - void ipc_queue_init(struct ipc_link_context *context) { spin_lock_init(&context->tx_q_update_lock); @@ -37,24 +34,10 @@ struct ipc_tx_queue *ipc_queue_new_frame(struct ipc_link_context *link_context, struct ipc_tx_queue *frame; u32 padded_len = l2_length; - /* audio link frames are always a fixed size */ - if (link_context->link->id == IPC_LINK_AUDIO) { - if (l2_length > FRAME_SIZE_AUDIO) { - dev_err(&link_context->sdev->dev, - "link %d error: invalid frame size %d " - "requested, max is %d\n", - link_context->link->id, - l2_length, - FRAME_SIZE_AUDIO); - return NULL; - } - padded_len = FRAME_SIZE_AUDIO; - } else { - /* frame length padded to alignment boundary */ - if (padded_len % FRAME_LENGTH_ALIGN) - padded_len += (FRAME_LENGTH_ALIGN - - (padded_len % FRAME_LENGTH_ALIGN)); - } + /* frame length padded to alignment boundary */ + if (padded_len % FRAME_LENGTH_ALIGN) + padded_len += (FRAME_LENGTH_ALIGN - + (padded_len % FRAME_LENGTH_ALIGN)); dev_dbg(&link_context->sdev->dev, "link %d: new frame: length %d, padded to %d\n", diff --git a/drivers/modem/m6718_spi/statemachine.c b/drivers/modem/m6718_spi/statemachine.c index a956661c3bf..bb047fcee18 100644 --- a/drivers/modem/m6718_spi/statemachine.c +++ b/drivers/modem/m6718_spi/statemachine.c @@ -84,35 +84,6 @@ static const struct ipc_sm_state *sm_init_exit(u8 event, } } -static const struct ipc_sm_state *sm_init_aud_exit(u8 event, - struct ipc_link_context *context) -{ - bool int_active = false; - - /* - * For reset event just re-enter init in case the modem has - * powered off - we need to reconfigure our GPIO pins - */ - if (event == IPC_SM_RUN_RESET) - return ipc_sm_state(IPC_SM_INIT_AUD); - - /* re-sample link INT pin */ - int_active = ipc_util_int_is_active(context); - atomic_set(&context->state_int, int_active); - - dev_info(&context->sdev->dev, - "link %d: link initialised; SS:INACTIVE(%d) INT:%s(%d)\n", - context->link->id, - ipc_util_ss_level_inactive(context), - int_active ? "ACTIVE" : "INACTIVE", - int_active ? ipc_util_int_level_active(context) : - ipc_util_int_level_inactive(context)); - dev_info(&context->sdev->dev, - "link %d: boot sync not needed, going idle\n", - context->link->id); - return ipc_sm_state(IPC_SM_IDL_AUD); -} - static u8 sm_wait_slave_stable_enter(u8 event, struct ipc_link_context *context) { static unsigned long printk_warn_time; @@ -219,17 +190,6 @@ static const struct ipc_sm_state *sm_idl_exit(u8 event, return ipc_sm_state(IPC_SM_HALT); } -static const struct ipc_sm_state *sm_idl_aud_exit(u8 event, - struct ipc_link_context *context) -{ - ipc_dbg_exit_idle(context); - if (event == IPC_SM_RUN_RESET) - return ipc_sm_state(IPC_SM_RESET_AUD); - - /* always transmit data first */ - return ipc_sm_state(IPC_SM_SLW_TX_WR_DAT_AUD); -} - static u8 sm_slw_tx_wr_cmd_enter(u8 event, struct ipc_link_context *context) { struct ipc_tx_queue *frame; @@ -328,43 +288,6 @@ static u8 sm_slw_tx_wr_dat_enter(u8 event, struct ipc_link_context *context) } } -static u8 sm_slw_tx_wr_dat_aud_enter(u8 event, struct ipc_link_context *context) -{ - struct ipc_tx_queue *frame = NULL; - - /* check if there is a frame to be sent */ - if (!ipc_queue_is_empty(context)) { - frame = ipc_queue_get_frame(context); - } else { - /* no frame to send, create an empty one */ - dev_dbg(&context->sdev->dev, - "link %d: no frame to send, allocating dummy\n", - context->link->id); - frame = ipc_queue_new_frame(context, 0); - if (frame == NULL) - return IPC_SM_RUN_ABORT; - } - - ipc_dbg_dump_frame(&context->sdev->dev, context->link->id, frame, true); - - /* prepare to transfer the frame tx data */ - context->frame = frame; - ipc_util_spi_message_prepare(context, context->frame->data, - NULL, context->frame->len); - - /* slave might already have signalled ready to transmit */ - if (event == IPC_SM_RUN_SLAVE_IRQ || atomic_read(&context->state_int)) { - dev_dbg(&context->sdev->dev, - "link %d: slave has already signalled ready\n", - context->link->id); - ipc_util_activate_ss(context); - return IPC_SM_RUN_SLAVE_IRQ; - } else { - ipc_util_activate_ss_with_tmo(context); - return IPC_SM_RUN_NONE; - } -} - static const struct ipc_sm_state *sm_slw_tx_wr_dat_exit(u8 event, struct ipc_link_context *context) { @@ -376,17 +299,6 @@ static const struct ipc_sm_state *sm_slw_tx_wr_dat_exit(u8 event, return ipc_sm_state(IPC_SM_ACT_TX_WR_DAT); } -static const struct ipc_sm_state *sm_slw_tx_wr_dat_aud_exit(u8 event, - struct ipc_link_context *context) -{ - if (event == IPC_SM_RUN_RESET) - return ipc_sm_state(IPC_SM_RESET_AUD); - else if (event == IPC_SM_RUN_COMMS_TMO) - return ipc_sm_state(IPC_SM_HALT_AUD); - else - return ipc_sm_state(IPC_SM_ACT_TX_WR_DAT_AUD); -} - static u8 sm_act_tx_wr_dat_enter(u8 event, struct ipc_link_context *context) { int err; @@ -434,34 +346,6 @@ static const struct ipc_sm_state *sm_act_tx_wr_dat_exit(u8 event, return ipc_sm_state(IPC_SM_SLW_TX_RD_CMD); } -static const struct ipc_sm_state *sm_act_tx_wr_dat_aud_exit(u8 event, - struct ipc_link_context *context) -{ - if (event == IPC_SM_RUN_RESET) - return ipc_sm_state(IPC_SM_RESET_AUD); - -#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT - /* frame is sent, increment link tx counter */ - context->tx_bytes += context->frame->actual_len; -#endif -#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_VERIFY_FRAMES - { - u8 channel; - - channel = ipc_util_get_l2_channel(*(u32 *)context->frame->data); - if (ipc_util_channel_is_loopback(channel)) { - /* create a copy of the frame */ - context->last_frame = ipc_queue_new_frame(context, - context->frame->actual_len); - memcpy(context->last_frame->data, - context->frame->data, - context->frame->actual_len); - } - } -#endif - return ipc_sm_state(IPC_SM_SLW_RX_WR_DAT_AUD); -} - static u8 sm_slw_tx_rd_cmd_enter(u8 event, struct ipc_link_context *context) { context->cmd = ipc_util_make_l1_header(CMD_READ, 0, 0); @@ -626,39 +510,6 @@ static const struct ipc_sm_state *sm_act_rx_wr_cmd_exit(u8 event, } } -static u8 sm_slw_rx_wr_dat_aud_enter(u8 event, struct ipc_link_context *context) -{ - /* - * We're using the same frame buffer we just sent, so no need for a - * new allocation here, just prepare the spi message - */ - ipc_util_spi_message_prepare(context, NULL, - context->frame->data, context->frame->len); - - /* slave might already have signalled ready to transmit */ - if (atomic_read(&context->state_int)) { - dev_dbg(&context->sdev->dev, - "link %d: slave has already signalled ready\n", - context->link->id); - ipc_util_activate_ss(context); - return IPC_SM_RUN_SLAVE_IRQ; - } else { - ipc_util_activate_ss_with_tmo(context); - return IPC_SM_RUN_NONE; - } -} - -static const struct ipc_sm_state *sm_slw_rx_wr_dat_aud_exit(u8 event, - struct ipc_link_context *context) -{ - if (event == IPC_SM_RUN_RESET) - return ipc_sm_state(IPC_SM_RESET_AUD); - else if (event == IPC_SM_RUN_COMMS_TMO) - return ipc_sm_state(IPC_SM_HALT_AUD); - else - return ipc_sm_state(IPC_SM_ACT_RX_WR_DAT_AUD); -} - static u8 sm_act_rx_wr_dat_enter(u8 event, struct ipc_link_context *context) { int err; @@ -679,22 +530,6 @@ static u8 sm_act_rx_wr_dat_enter(u8 event, struct ipc_link_context *context) return IPC_SM_RUN_NONE; } -static u8 sm_act_rx_wr_dat_aud_enter(u8 event, struct ipc_link_context *context) -{ - int err; - - dev_dbg(&context->sdev->dev, - "link %d: starting spi tfr\n", context->link->id); - err = spi_async(context->sdev, &context->spi_message); - if (err < 0) { - dev_err(&context->sdev->dev, - "link %d error: spi tfr start failed, error %d\n", - context->link->id, err); - return IPC_SM_RUN_ABORT; - } - return IPC_SM_RUN_NONE; -} - static const struct ipc_sm_state *sm_act_rx_wr_dat_exit(u8 event, struct ipc_link_context *context) { @@ -774,86 +609,6 @@ static const struct ipc_sm_state *sm_act_rx_wr_dat_exit(u8 event, } } -static const struct ipc_sm_state *sm_act_rx_wr_dat_aud_exit(u8 event, - struct ipc_link_context *context) -{ - u32 frame_hdr; - unsigned char l2_header; - unsigned int l2_length; - u8 *l2_data; - - if (event == IPC_SM_RUN_RESET) - return ipc_sm_state(IPC_SM_RESET_AUD); - - dev_dbg(&context->sdev->dev, - "link %d: RX PAYLOAD %d bytes\n", - context->link->id, context->frame->len); - - /* decode L2 header */ - frame_hdr = *(u32 *)context->frame->data; - l2_header = ipc_util_get_l2_channel(frame_hdr); - l2_length = ipc_util_get_l2_length(frame_hdr); - l2_data = (u8 *)context->frame->data + IPC_L2_HDR_SIZE; - -#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT - /* frame is received, increment link rx counter */ - context->rx_bytes += l2_length; -#endif - if (frame_hdr != 0) - context->frame->actual_len = l2_length + IPC_L2_HDR_SIZE; - else - context->frame->actual_len = 0; - ipc_dbg_dump_frame(&context->sdev->dev, context->link->id, - context->frame, false); - - if (l2_length > (context->frame->len - 4)) - dev_err(&context->sdev->dev, - "link %d: suspicious frame: L1 len %d L2 len %d\n", - context->link->id, context->frame->len, l2_length); - - dev_dbg(&context->sdev->dev, - "link %d: L2 PDU decode: header 0x%08x channel %d length %d " - "data[%02x%02x%02x...]\n", - context->link->id, frame_hdr, l2_header, l2_length, - l2_data[0], l2_data[1], l2_data[2]); - - if (ipc_util_channel_is_loopback(l2_header)) - ipc_dbg_verify_rx_frame(context); - - /* did the slave actually have anything to send? */ - if (frame_hdr != 0) { - /* pass received frame up to L2mux layer */ - if (!modem_protocol_channel_is_open(l2_header)) { - dev_err(&context->sdev->dev, - "link %d error: received frame on invalid " - "channel %d, frame discarded\n", - context->link->id, l2_header); - } else { -#ifdef CONFIG_MODEM_M6718_SPI_ENABLE_FEATURE_THROUGHPUT_MEASUREMENT - /* - * Discard loopback frames if we are taking throughput - * measurements - we'll be loading the links and so will - * likely overload the buffers. - */ - if (!ipc_util_channel_is_loopback(l2_header)) -#endif - modem_m6718_spi_receive(context->sdev, - l2_header, l2_length, l2_data); - } - } else { - dev_dbg(&context->sdev->dev, - "link %d: received dummy frame, discarding\n", - context->link->id); - } - - /* data is copied by L2mux so free the frame here */ - ipc_queue_delete_frame(context->frame); - context->frame = NULL; - - /* audio link goes idle ready for next transaction */ - return ipc_sm_state(IPC_SM_IDL_AUD); -} - static u8 sm_halt_enter(u8 event, struct ipc_link_context *context) { dev_err(&context->sdev->dev, @@ -884,12 +639,6 @@ static const struct ipc_sm_state *sm_halt_exit(u8 event, return ipc_sm_state(IPC_SM_RESET); } -static const struct ipc_sm_state *sm_halt_aud_exit(u8 event, - struct ipc_link_context *context) -{ - return ipc_sm_state(IPC_SM_RESET_AUD); -} - static u8 sm_reset_enter(u8 event, struct ipc_link_context *context) { dev_err(&context->sdev->dev, @@ -922,12 +671,6 @@ static const struct ipc_sm_state *sm_reset_exit(u8 event, return ipc_sm_state(IPC_SM_INIT); } -static const struct ipc_sm_state *sm_reset_aud_exit(u8 event, - struct ipc_link_context *context) -{ - return ipc_sm_state(IPC_SM_INIT_AUD); -} - static u8 sm_slw_tx_bootreq_enter(u8 event, struct ipc_link_context *context) { dev_info(&context->sdev->dev, @@ -1047,10 +790,10 @@ static const struct ipc_sm_state *sm_act_rx_bootresp_exit(u8 event, context->link->id, IPC_DRIVER_VERSION, modem_ver); /* check for minimum required modem version */ - if (modem_ver < IPC_DRIVER_MODEM_MIN_VER) { + if (modem_ver != IPC_DRIVER_MODEM_MIN_VER) { dev_warn(&context->sdev->dev, "link %d warning: modem version mismatch! " - "minimum required version is %02x\n", + "required version is %02x\n", context->link->id, IPC_DRIVER_MODEM_MIN_VER); } @@ -1188,75 +931,16 @@ static const struct ipc_sm_state state_machine[IPC_SM_STATE_ID_NBR] = { .exit = sm_act_rx_wr_dat_exit, .events = IPC_SM_RUN_TFR_COMPLETE | IPC_SM_RUN_RESET }, - /* audio link states below */ - [IPC_SM_INIT_AUD] = { - .id = IPC_SM_INIT_AUD, - .enter = sm_init_enter, - .exit = sm_init_aud_exit, - .events = IPC_SM_RUN_INIT | IPC_SM_RUN_RESET - }, - [IPC_SM_HALT_AUD] = { - .id = IPC_SM_HALT_AUD, - .enter = sm_halt_enter, - .exit = sm_halt_aud_exit, - .events = IPC_SM_RUN_RESET - }, - [IPC_SM_RESET_AUD] = { - .id = IPC_SM_RESET_AUD, - .enter = sm_reset_enter, - .exit = sm_reset_aud_exit, - .events = IPC_SM_RUN_RESET - }, - [IPC_SM_IDL_AUD] = { - .id = IPC_SM_IDL_AUD, - .enter = sm_idl_enter, - .exit = sm_idl_aud_exit, - .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_TX_REQ | - IPC_SM_RUN_RESET - }, - [IPC_SM_SLW_TX_WR_DAT_AUD] = { - .id = IPC_SM_SLW_TX_WR_DAT_AUD, - .enter = sm_slw_tx_wr_dat_aud_enter, - .exit = sm_slw_tx_wr_dat_aud_exit, - .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_COMMS_TMO | - IPC_SM_RUN_RESET - }, - [IPC_SM_ACT_TX_WR_DAT_AUD] = { - .id = IPC_SM_ACT_TX_WR_DAT_AUD, - .enter = sm_act_tx_wr_dat_enter, - .exit = sm_act_tx_wr_dat_aud_exit, - .events = IPC_SM_RUN_TFR_COMPLETE | IPC_SM_RUN_RESET - }, - [IPC_SM_SLW_RX_WR_DAT_AUD] = { - .id = IPC_SM_SLW_RX_WR_DAT_AUD, - .enter = sm_slw_rx_wr_dat_aud_enter, - .exit = sm_slw_rx_wr_dat_aud_exit, - .events = IPC_SM_RUN_SLAVE_IRQ | IPC_SM_RUN_COMMS_TMO | - IPC_SM_RUN_RESET - }, - [IPC_SM_ACT_RX_WR_DAT_AUD] = { - .id = IPC_SM_ACT_RX_WR_DAT_AUD, - .enter = sm_act_rx_wr_dat_aud_enter, - .exit = sm_act_rx_wr_dat_aud_exit, - .events = IPC_SM_RUN_TFR_COMPLETE | IPC_SM_RUN_RESET - } }; - const struct ipc_sm_state *ipc_sm_idle_state(struct ipc_link_context *context) { - if (context->link->id == IPC_LINK_AUDIO) - return ipc_sm_state(IPC_SM_IDL_AUD); - else - return ipc_sm_state(IPC_SM_IDL); + return ipc_sm_state(IPC_SM_IDL); } const struct ipc_sm_state *ipc_sm_init_state(struct ipc_link_context *context) { - if (context->link->id == IPC_LINK_AUDIO) - return ipc_sm_state(IPC_SM_INIT_AUD); - else - return ipc_sm_state(IPC_SM_INIT); + return ipc_sm_state(IPC_SM_INIT); } const struct ipc_sm_state *ipc_sm_state(u8 id) @@ -1403,4 +1087,3 @@ void ipc_sm_kick(u8 event, struct ipc_link_context *context) "link %d is suspended, waiting for resume\n", link->id); spin_unlock_irqrestore(&context->sm_lock, flags); } - diff --git a/drivers/modem/m6718_spi/util.c b/drivers/modem/m6718_spi/util.c index 9c89eb9b34a..9026f4427dd 100644 --- a/drivers/modem/m6718_spi/util.c +++ b/drivers/modem/m6718_spi/util.c @@ -136,7 +136,6 @@ bool ipc_util_link_is_idle(struct ipc_link_context *context) switch (context->state->id) { case IPC_SM_IDL: - case IPC_SM_IDL_AUD: return true; default: return false; -- cgit v1.2.3