if HSI_DLP
-config HSI_DLP_TTY_NAME
- string "Base name for the TTY"
+config HSI_DLP_IPC_TTY_NAME
+ string "Base name for the IPC TTY"
default "IFX"
help
- Sets the base name for the TTY associated to this IMC modem protocol
+ Sets the base name for the TTY associated to this eDLP modem protocol
for LTE.
config HSI_DLP_TTY_STATS
#include <linux/wait.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <asm/intel_scu_ipc.h>
#include "dlp_main.h"
DLP_DIR_TRANSMIT_AND_RECEIVE
};
+/* Modem cold boot management */
+#define V1P35CNT_W 0x0E0 /* PMIC reg to power off the modem */
+#define V1P35_OFF 4
+#define V1P35_ON 6
+#define COLD_BOOT_DELAY_OFF 20000 /* 20 ms (use of usleep_range) */
+#define COLD_BOOT_DELAY_ON 10000 /* 10 ms (use of usleep_range) */
+
+/* Delays for powering up/resetting the modem */
+#define DLP_DURATION_ON1 60 /* ON1 pulse delay: 60 us */
+#define DLP_DURATION_RST 1 /* RST_BBN pulse delay: 1 ms */
+#define DLP_POST_DELAY 200 /* Post seq. sleep duration: 200 ms */
+
+
/**
- * struct dlp_ctrl_cmd_params - DLP modem comamnd/response
+ * struct dlp_command_params - DLP modem comamnd/response
* @data1: Command data (byte1)
* @data2: Command data (byte2)
* @data3: Command data (byte3)
};
/**
- * struct dlp_ctrl_cmd - DLP comamnd
+ * struct dlp_command - DLP comamnd
* @params: DLP modem comamnd/response
* @channel: the DLP channel context
* @status: Status of the transfer when completed
int status;
};
+/**
+ * struct dlp_ctrl_reset_ctx - reset context
+ * @cd_irq: the modem core dump interrupt line
+ * @rst_irq: the modem reset interrupt line
+ * @ongoing: Stating that a reset is ongoing
+ */
+struct dlp_ctrl_reset_ctx {
+ int cd_irq;
+ int rst_irq;
+ int ongoing;
+};
+
/*
* struct dlp_ctrl_context - CTRL channel private data
*
unsigned int gpio_mdm_pwr_on;
unsigned int gpio_mdm_rst_bbn;
unsigned int gpio_fcdp_rb;
+
+ /* Reset & Hangup contexts */
+ struct dlp_ctrl_reset_ctx reset;
};
/*
value = gpio_get_value(ctrl_ctx->gpio_mdm_rst_out);
- CRITICAL("Modem RESET_OUT 0x%x, reset_ignore: %d",
- value, dlp_drv.reset_ignore);
- if (dlp_drv.reset_ignore) {
+ CRITICAL("Modem RESET_OUT 0x%x, reset_ongoing: %d",
+ value, ctrl_ctx->reset.ongoing);
+ if (ctrl_ctx->reset.ongoing) {
/* Rising EDGE (Reset done) ? */
if (value)
complete(&ctrl_ctx->reset_done);
}
/* Unexpected reset received */
- dlp_drv.reset_ignore = 1;
+ ctrl_ctx->reset.ongoing = 1;
/* Call registered channels */
for (i = 0; i < DLP_CHANNEL_COUNT; i++) {
gpio_free(ctrl_ctx->gpio_mdm_pwr_on);
gpio_free(ctrl_ctx->gpio_mdm_rst_bbn);
- if (dlp_drv.core_dump_irq)
- free_irq(dlp_drv.core_dump_irq, dev);
+ if (ctrl_ctx->reset.cd_irq)
+ free_irq(ctrl_ctx->reset.cd_irq, dev);
- if (dlp_drv.reset_irq)
- free_irq(dlp_drv.reset_irq, dev);
+ if (ctrl_ctx->reset.rst_irq)
+ free_irq(ctrl_ctx->reset.rst_irq, dev);
EPILOG();
return 0;
}
-static int dlp_ctrl_configure_gpios(struct dlp_channel *ch_ctx,
+/*
+* @brief Configure IRQs & GPIOs
+*
+* @param ch_ctx
+* @param dev
+*
+* @return
+*/
+static inline int dlp_ctrl_configure_gpio(int gpio,
+ int direction,
+ int value,
+ const char *desc)
+{
+ int ret;
+
+ ret = gpio_request(gpio, "ifxHSIModem");
+
+ if (direction) {
+ ret += gpio_direction_output(gpio, value);
+ }
+ else {
+ ret += gpio_direction_input(gpio);
+ }
+
+ if (ret) {
+ CRITICAL("Unable to configure GPIO%d (%s)",
+ gpio,
+ desc);
+ ret = -ENODEV;
+ }
+
+ return ret;
+}
+
+static int dlp_ctrl_setup_irq_gpio(struct dlp_channel *ch_ctx,
struct device *dev)
{
int ret;
PROLOG();
- ret = gpio_request(ctrl_ctx->gpio_mdm_rst_bbn, "ifxHSIModem");
- ret += gpio_direction_output(ctrl_ctx->gpio_mdm_rst_bbn, 1);
- ret += gpio_export(ctrl_ctx->gpio_mdm_rst_bbn, 1);
+ /* Configure the RESET_BB gpio */
+ ret = dlp_ctrl_configure_gpio(ctrl_ctx->gpio_mdm_rst_bbn, 1, 1, "RST_BB");
if (ret) {
- CRITICAL("Unable to configure GPIO%d (RESET)",
- ctrl_ctx->gpio_mdm_rst_bbn);
- ret = -ENODEV;
goto free_ctx4;
}
- pr_info("dlp: gpio rst_bbn %d\n", ctrl_ctx->gpio_mdm_rst_bbn);
- ret = gpio_request(ctrl_ctx->gpio_mdm_pwr_on, "ifxHSIModem");
- ret += gpio_direction_output(ctrl_ctx->gpio_mdm_pwr_on, 1);
- ret += gpio_export(ctrl_ctx->gpio_mdm_pwr_on, 1);
+ /* Configure the ON gpio */
+ ret = dlp_ctrl_configure_gpio(ctrl_ctx->gpio_mdm_pwr_on, 1, 1, "ON");
if (ret) {
- CRITICAL("Unable to configure GPIO%d (ON)",
- ctrl_ctx->gpio_mdm_pwr_on);
- ret = -ENODEV;
goto free_ctx3;
}
- pr_info("dlp: gpio pwr_on %d\n", ctrl_ctx->gpio_mdm_pwr_on);
-
- /* set up irq for modem reset line */
- ret = gpio_request(ctrl_ctx->gpio_mdm_rst_out, "ifxHSIModem");
- ret += gpio_direction_input(ctrl_ctx->gpio_mdm_rst_out);
- ret += gpio_export(ctrl_ctx->gpio_mdm_rst_out, 0);
+ /* Configure the RESET_OUT gpio & irq */
+ ret = dlp_ctrl_configure_gpio(ctrl_ctx->gpio_mdm_rst_out, 0, 0, "RST_OUT");
if (ret) {
- CRITICAL("Unable to configure GPIO%d (RST_OUT)",
- ctrl_ctx->gpio_mdm_rst_out);
- ret = -ENODEV;
goto free_ctx2;
}
- dlp_drv.reset_irq = gpio_to_irq(ctrl_ctx->gpio_mdm_rst_out);
- if (dlp_drv.reset_irq < 0) {
+ ctrl_ctx->reset.rst_irq = gpio_to_irq(ctrl_ctx->gpio_mdm_rst_out);
+ if (ctrl_ctx->reset.rst_irq < 0) {
ret = -ENODEV;
goto free_ctx2;
}
- pr_info("dlp: gpio rst_out %d\n", ctrl_ctx->gpio_mdm_rst_out);
-
- ret = request_irq(dlp_drv.reset_irq,
+ ret = request_irq(ctrl_ctx->reset.rst_irq,
dlp_ctrl_reset_it,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, DRVNAME,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ DRVNAME,
ch_ctx);
if (ret) {
CRITICAL("IRQ request failed for GPIO%d (RST_OUT)",
- dlp_drv.reset_irq);
+ ctrl_ctx->reset.rst_irq);
ret = -ENODEV;
goto free_ctx2;
}
- /* set up input for core dump interrupt line */
- ret = gpio_request(ctrl_ctx->gpio_fcdp_rb, "ifxHSIModem");
- ret += gpio_direction_input(ctrl_ctx->gpio_fcdp_rb);
- ret += gpio_export(ctrl_ctx->gpio_fcdp_rb, 0);
+ /* Configure the CORE_DUMP gpio & irq */
+ ret = dlp_ctrl_configure_gpio(ctrl_ctx->gpio_fcdp_rb, 0, 0, "CORE_DUMP");
if (ret) {
- CRITICAL("Unable to configure GPIO%d (CORE DUMP)",
- ctrl_ctx->gpio_fcdp_rb);
- ret = -ENODEV;
goto free_all;
}
- dlp_drv.core_dump_irq = gpio_to_irq(ctrl_ctx->gpio_fcdp_rb);
- if (dlp_drv.core_dump_irq < 0) {
+ ctrl_ctx->reset.cd_irq = gpio_to_irq(ctrl_ctx->gpio_fcdp_rb);
+ if (ctrl_ctx->reset.cd_irq < 0) {
ret = -ENODEV;
goto free_all;
}
- ret = request_irq(dlp_drv.core_dump_irq,
+ ret = request_irq(ctrl_ctx->reset.cd_irq,
dlp_ctrl_coredump_it,
IRQF_TRIGGER_RISING, DRVNAME,
ch_ctx);
goto free_all;
}
- pr_info("dlp: gpio fcdp_rb %d\n", ctrl_ctx->gpio_fcdp_rb);
+ pr_info("dlp: GPIO (rst_bbn: %d, pwr_on: %d, rst_out: %d, fcdp_rb: %d)\n",
+ ctrl_ctx->gpio_mdm_rst_bbn,
+ ctrl_ctx->gpio_mdm_pwr_on,
+ ctrl_ctx->gpio_mdm_rst_out,
+ ctrl_ctx->gpio_fcdp_rb);
+
+ pr_info("dlp: IRQ (rst_out: %d, fcdp_rb: %d)\n",
+ ctrl_ctx->reset.rst_irq, ctrl_ctx->reset.cd_irq);
+
EPILOG();
return ret;
free_ctx4:
gpio_free(ctrl_ctx->gpio_mdm_rst_bbn);
- if (dlp_drv.core_dump_irq)
- free_irq(dlp_drv.core_dump_irq, dev);
+ if (ctrl_ctx->reset.cd_irq)
+ free_irq(ctrl_ctx->reset.cd_irq, dev);
- if (dlp_drv.reset_irq)
- free_irq(dlp_drv.reset_irq, dev);
+ if (ctrl_ctx->reset.rst_irq)
+ free_irq(ctrl_ctx->reset.rst_irq, dev);
EPILOG();
return ret;
/*
*
*/
-static void dlp_ctrl_save_rx_callbacks(struct dlp_ctrl_context *ctrl_ctx)
-{
- ctrl_ctx->start_rx_cb = dlp_drv.client->hsi_start_rx;
- ctrl_ctx->stop_rx_cb = dlp_drv.client->hsi_stop_rx;
-
- dlp_drv.client->hsi_start_rx = NULL;
- dlp_drv.client->hsi_stop_rx = NULL;
-}
-
-/*
- *
- */
-static void dlp_ctrl_restore_rx_callbacks(struct dlp_ctrl_context *ctrl_ctx)
-{
- dlp_drv.client->hsi_start_rx = ctrl_ctx->start_rx_cb;
- dlp_drv.client->hsi_stop_rx = ctrl_ctx->stop_rx_cb;
-
- ctrl_ctx->start_rx_cb = NULL;
- ctrl_ctx->stop_rx_cb = NULL;
-}
-
-/*
- *
- */
static struct dlp_command *dlp_ctrl_cmd_alloc(struct dlp_channel *ch_ctx,
unsigned char id,
unsigned char param1,
CRITICAL("hsi_async() failed, ret:%d", ret);
/* We have A BIG PROBLEM if the RX msg cant be */
- /* pushed again in the controller ==> */
+ /* pushed again in the controller ==> */
/* No response could be received (FIFO empty) */
/* Delete the received msg */
DLP_CTRL_CMD_TO_STR(id), ch_ctx->hsi_channel);
/* Backup RX callback */
- dlp_ctrl_save_rx_callbacks(ctrl_ctx);
+ dlp_save_rx_callbacks(&ctrl_ctx->start_rx_cb, &ctrl_ctx->stop_rx_cb);
/* Allocate the DLP command */
dlp_cmd = dlp_ctrl_cmd_alloc(ch_ctx, id, param1, param2, param3);
}
/* Restore RX callback */
- dlp_ctrl_restore_rx_callbacks(ctrl_ctx);
+ dlp_restore_rx_callbacks(&ctrl_ctx->start_rx_cb, &ctrl_ctx->stop_rx_cb);
/* Everything is OK */
EPILOG("%d", ret);
out:
/* Restore RX callback */
- dlp_ctrl_restore_rx_callbacks(ctrl_ctx);
+ dlp_restore_rx_callbacks(&ctrl_ctx->start_rx_cb, &ctrl_ctx->stop_rx_cb);
EPILOG("%d", ret);
return ret;
return ret;
}
+/*
+* @brief Set the modem readiness (READY/NOT READY) flag
+*
+* @param value
+*/
static inline void dlp_ctrl_set_modem_readiness(unsigned int value)
{
unsigned long flags;
/*
-* @brief
+* @brief A deferred work that :
+* - Send ECHO & Wait for the modem respose
+* - Set the modem readiness flag when the modem respond
*
* @param work
*/
{
struct dlp_channel *ch_ctx = dlp_drv.channels[DLP_CHANNEL_CTRL];
struct dlp_ctrl_context *ctrl_ctx = ch_ctx->ch_data;
- int ret;
+ unsigned char param1, param2, param3;
+ int ret = 0;
PROLOG();
wait_for_completion(&ctrl_ctx->reset_done);
/* Send ECHO continuously until the modem become ready */
+ param1 = PARAM1(DLP_ECHO_CMD_CHECKSUM);
+ param2 = PARAM2(DLP_ECHO_CMD_CHECKSUM);
+ param3 = PARAM3(DLP_ECHO_CMD_CHECKSUM);
+
do {
ret = 0;
- // ret = dlp_ctrl_send_echo_cmd(ctrl_ctx);
+ /* Send the ECHO command */
+#if 0 /* Not sent because even the response is received the
+ modem is not ready !
+ */
+ ret = dlp_ctrl_cmd_send(ch_ctx,
+ DLP_CMD_ECHO,
+ DLP_CMD_ECHO, param1, param2, param3);
+#endif
if (ret == 0) {
/* Set the modem state */
dlp_ctrl_set_modem_readiness(1);
* Toggle gpios required to bring up modem power and start modem.
* This can be called after the modem has been started to reset it.
*/
-int dlp_ctrl_modem_reset(struct dlp_channel *ch_ctx)
+void dlp_ctrl_modem_reset(struct dlp_channel *ch_ctx)
{
- struct dlp_ctrl_context *ctrl_ctx = ch_ctx->ch_data;
- int ret = 0;
+ struct dlp_channel *ctrl_ch = dlp_drv.channels[DLP_CHANNEL_CTRL];
+ struct dlp_ctrl_context *ctrl_ctx = ctrl_ch->ch_data;
PROLOG();
+ WARNING("Modem reset requested !");
+
/* AP requested reset => just ignore */
- dlp_drv.reset_ignore = 1;
+ ctrl_ctx->reset.ongoing = 1;
gpio_set_value(ctrl_ctx->gpio_mdm_rst_bbn, 0);
- mdelay(DLP_POWER_ON_INTERLINE_DELAY);
+ mdelay(DLP_DURATION_RST);
gpio_set_value(ctrl_ctx->gpio_mdm_rst_bbn, 1);
- msleep(DLP_POWER_ON_POST_DELAY);
+ msleep(DLP_POST_DELAY);
EPILOG();
- return ret;
}
/*
-* @brief
-*
-* @param ch_ctx
+* @brief Toggle gpios required to bring up modem power and start modem
*
-* @return
+* @param ch_ctx: IPC CTRL channel
*/
-static int dlp_ctrl_modem_power(struct dlp_channel *ch_ctx)
+static void dlp_ctrl_modem_power(struct dlp_channel *ch_ctx)
{
- int ret = 0;
- struct dlp_ctrl_context *ctrl_ctx = ch_ctx->ch_data;
+ struct dlp_channel *ctrl_ch = dlp_drv.channels[DLP_CHANNEL_CTRL];
+ struct dlp_ctrl_context *ctrl_ctx = ctrl_ch->ch_data;
PROLOG();
+ WARNING("Modem power requested !");
+
/* AP requested reset => just ignore */
- dlp_drv.reset_ignore = 1;
+ ctrl_ctx->reset.ongoing = 1;
gpio_set_value(ctrl_ctx->gpio_mdm_pwr_on, 1);
- msleep(DLP_POWER_ON_INTERLINE_DELAY);
+ udelay(DLP_DURATION_ON1);
gpio_set_value(ctrl_ctx->gpio_mdm_pwr_on, 0);
- msleep(DLP_POWER_ON_POST_DELAY);
+ msleep(DLP_POST_DELAY);
EPILOG();
- return ret;
}
/*
-* @brief
+* @brief Return TRUE when the modem is ready:
+* - The modem has toggled the RESET_OUT pin
+* - The modem has respond to the echo command
*
* @return
*/
}
/*
+* @brief Set the RESET ongoing flag value
+*
+*/
+inline void dlp_ctrl_set_reset_ongoing(int ongoing)
+{
+ struct dlp_ctrl_context *ctrl_ctx = DLP_CTRL_CTX;
+ ctrl_ctx->reset.ongoing = ongoing;
+}
+
+/*
+* @brief Get the RESET ongoing flag value
+*
+*/
+inline int dlp_ctrl_get_reset_ongoing(void)
+{
+ struct dlp_ctrl_context *ctrl_ctx = DLP_CTRL_CTX;
+ return ctrl_ctx->reset.ongoing;
+}
+
+/*
+* @brief Get the Modem hangup reasons value
+*
+*/
+inline int dlp_ctrl_get_hangup_reasons(void)
+{
+ struct dlp_channel *ch_ctx = DLP_CHANNEL_CTX(DLP_CHANNEL_CTRL);
+ int hangup_reasons;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ch_ctx->lock, flags);
+ hangup_reasons = ch_ctx->hangup.last_cause | ch_ctx->hangup.cause;
+ spin_unlock_irqrestore(&ch_ctx->lock, flags);
+
+ return hangup_reasons;
+}
+
+/*
+* @brief
+*
+* @param hsi_channel
+* @param hangup_reasons
+*/
+inline void dlp_ctrl_set_hangup_reasons(unsigned int hsi_channel,
+ int hangup_reasons)
+{
+ struct dlp_channel *ch_ctx = DLP_CHANNEL_CTX(DLP_CHANNEL_CTRL);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ch_ctx->lock, flags);
+ ch_ctx->hangup.cause |= ((hsi_channel << 4) | hangup_reasons);
+ ch_ctx->hangup.last_cause |= ((hsi_channel << 4) | hangup_reasons);
+ spin_unlock_irqrestore(&ch_ctx->lock, flags);
+}
+
+/*
* @brief
*
* @param index
init_completion(&ctrl_ctx->reset_done);
INIT_WORK(&ctrl_ctx->readiness_work, dlp_ctrl_ipc_readiness);
+ /* Init the RX/TX contexts */
+ init_completion(&ctrl_ctx->rx_done);
+ init_completion(&ctrl_ctx->tx_done);
+
+ dlp_xfer_ctx_init(ch_ctx, &ch_ctx->tx,
+ 0, 0, 0, NULL, HSI_MSG_WRITE);
+
+ dlp_xfer_ctx_init(ch_ctx, &ch_ctx->rx,
+ 0, 0, 0, NULL, HSI_MSG_READ);
+
/* Configure GPIOs */
ctrl_ctx->gpio_mdm_rst_out = pd->gpio_mdm_rst_out;
ctrl_ctx->gpio_mdm_pwr_on = pd->gpio_mdm_pwr_on;
ctrl_ctx->gpio_mdm_rst_bbn = pd->gpio_mdm_rst_bbn;
ctrl_ctx->gpio_fcdp_rb = pd->gpio_fcdp_rb;
- ret = dlp_ctrl_configure_gpios(ch_ctx, dev);
- if (ret) {
- goto free_ctx;
- }
-
- /* Power on the modem */
- ret = dlp_ctrl_modem_power(ch_ctx);
- if (ret) {
- ret = -ENODEV;
- dlp_ctrl_free_gpios(ch_ctx, dev);
- goto free_ctx;
- }
-
- /* Reset the modem */
- ret = dlp_ctrl_modem_reset(ch_ctx);
+ ret = dlp_ctrl_setup_irq_gpio(ch_ctx, dev);
if (ret) {
- ret = -ENODEV;
- dlp_ctrl_free_gpios(ch_ctx, dev);
goto free_ctx;
}
- /* Init the RX/TX completion */
- init_completion(&ctrl_ctx->rx_done);
- init_completion(&ctrl_ctx->tx_done);
-
/* Set ch_ctx, not yet done in the probe */
dlp_drv.channels[DLP_CHANNEL_CTRL] = ch_ctx;
/* Start the modem readiness worqeue */
queue_work(ctrl_ctx->readiness_wq, &ctrl_ctx->readiness_work);
+ /* Power on & Reset the modem */
+ dlp_ctrl_modem_power(ch_ctx);
+ dlp_ctrl_modem_reset(ch_ctx);
+
EPILOG();
return ch_ctx;
}
/*
-* @brief
+* @brief Delete any resources allocated by dlp_ctrl_ctx_create
*
-* @param ch_ctx
+* @param ch_ctx : The IPC CTRL channel
*
* @return
*/
/* Delete the modem readiness worqueue */
destroy_workqueue(ctrl_ctx->readiness_wq);
- /* Free GPIOs */
+ /* Free IRQs & GPIOs */
+ free_irq(ctrl_ctx->reset.cd_irq, (void *)ch_ctx);
+ free_irq(ctrl_ctx->reset.rst_irq, (void *)ch_ctx);
+
+ gpio_free(ctrl_ctx->gpio_fcdp_rb);
+ gpio_free(ctrl_ctx->gpio_mdm_rst_out);
+ gpio_free(ctrl_ctx->gpio_mdm_pwr_on);
+ gpio_free(ctrl_ctx->gpio_mdm_rst_bbn);
/* Free the CTRL context */
kfree(ctrl_ctx);
}
/*
- *
- */
-int dlp_ctrl_send_echo_cmd(struct dlp_channel *ch_ctx)
-{
- int ret = 0;
- unsigned char param1 = PARAM1(DLP_ECHO_CMD_CHECKSUM);
- unsigned char param2 = PARAM2(DLP_ECHO_CMD_CHECKSUM);
- unsigned char param3 = PARAM3(DLP_ECHO_CMD_CHECKSUM);
-
- PROLOG();
-
- /* Send the command */
- ret = dlp_ctrl_cmd_send(ch_ctx,
- DLP_CMD_ECHO,
- DLP_CMD_ECHO, param1, param2, param3);
- EPILOG();
- return ret;
-}
-
-/*
- *
- */
-int dlp_ctrl_send_open_conn_cmd(struct dlp_channel *ch_ctx)
+* @brief Open the specified IPC channel for communication
+* - Send the OPEN_CONN command to the modem
+* - Return the operation status
+*
+* @param ch_ctx : The IPC Channel to consider
+*
+* @return 0 when OK, error value otherwise
+*/
+int dlp_ctrl_open_channel(struct dlp_channel *ch_ctx)
{
int ret = 0;
unsigned char param1 = PARAM1(ch_ctx->pdu_size);
PROLOG();
- /* Send the command */
+ /* Send the OPEN_CONN command */
ret = dlp_ctrl_cmd_send(ch_ctx,
DLP_CMD_OPEN_CONN,
DLP_CMD_ACK, param1, param2, 0);
}
/*
- *
- */
-int dlp_ctrl_send_cancel_conn_cmd(struct dlp_channel *ch_ctx)
+* @brief Close the specified IPC channel
+* - Send the CANCEL_CONN command to the modem
+* - Return the operation status
+*
+* @param ch_ctx : The IPC Channel to consider
+*
+* @return 0 when OK, error value otherwise
+*/
+int dlp_ctrl_close_channel(struct dlp_channel *ch_ctx)
{
int ret = 0;
- // FIXME: both or just xmit/receive ?
- //unsigned char param3 = PARAM1(DLP_DIR_TRANSMIT_AND_RECEIVE);
- unsigned char param3;
+ unsigned long flags;
+ unsigned char param3 = PARAM1(DLP_DIR_TRANSMIT_AND_RECEIVE);
PROLOG();
- // FIXME: TX & RX dont work ==> the modem response is TX only
- param3 = PARAM1(DLP_DIR_TRANSMIT);
+ /* Reset the credits value */
+ spin_lock_irqsave(&ch_ctx->lock, flags);
+ ch_ctx->credits = 0;
+ spin_unlock_irqrestore(&ch_ctx->lock, flags);
/* Send the command */
ret = dlp_ctrl_cmd_send(ch_ctx,
- DLP_CMD_CANCEL_CONN, DLP_CMD_ACK, 0, 0, param3);
+ DLP_CMD_CANCEL_CONN, DLP_CMD_ACK, 0, 0, param3);
+
EPILOG();
return ret;
}
/*
- * Modem reset sysfs entries
- */
-static int set_modem_reset(const char *val, struct kernel_param *kp)
+* @brief Modem reset module_param set function
+* - This function is performing a modem reset
+*
+* @param val
+* @param kp
+*
+* @return 0
+*/
+static int do_modem_reset(const char *val, struct kernel_param *kp)
{
unsigned long reset_request;
- if (strict_strtoul(val, 16, &reset_request) < 0)
+ PROLOG();
+
+ if (strict_strtoul(val, 16, &reset_request) < 0) {
+ EPILOG();
return -EINVAL;
+ }
if (reset_request) {
struct dlp_channel *ch_ctx = dlp_drv.channels[DLP_CHANNEL_CTRL];
dlp_ctrl_modem_reset(ch_ctx);
}
+ EPILOG();
return 0;
}
-static int get_modem_reset(const char *val, struct kernel_param *kp)
+/*
+* @brief Return the reset ongoing flag value
+*
+* @param val
+* @param kp
+*
+* @return 0
+*/
+static int get_modem_reset(char *val, struct kernel_param *kp)
+{
+ struct dlp_ctrl_context *ctrl_ctx = DLP_CTRL_CTX;
+ return sprintf(val, "%d", ctrl_ctx->reset.ongoing);
+}
+
+/*
+ * Modem cold reset sysfs entries
+ *
+ * To do a modem cold reset we have to:
+ * - Set the EXT1P35VREN field to low during 20ms (V1P35CNT_W PMIC register)
+ * - set the EXT1P35VREN field to high during 10ms (V1P35CNT_W PMIC register)
+ */
+static int dp_modem_cold_reset(const char *val, struct kernel_param *kp)
{
- unsigned long reset_ongoing = 0;
+ long do_reset;
+ int ret = 0;
+ u16 addr = V1P35CNT_W;
+ u8 data, def_value;
+
+ PROLOG();
+
+ if (strict_strtol(val, 10, &do_reset) < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
- return sprintf(val, "%d", reset_ongoing);
+ WARNING("Modem cold reset requested !");
+
+ /* Need to do something ? */
+ if (!do_reset)
+ goto out;
+
+ /* Read the current registre value */
+ ret = intel_scu_ipc_readv(&addr, &def_value, 2);
+ if (ret) {
+ CRITICAL("intel_scu_ipc_readv() failed (ret: %d)", ret);
+ ret = -EINVAL;
+ goto exit_modem_cold_reset;
+ }
+
+ /* Write the new registre value (V1P35_OFF) */
+ data = (def_value & 0xf8) | V1P35_OFF;
+ ret = intel_scu_ipc_writev(&addr, &data, 1);
+ if (ret) {
+ CRITICAL("intel_scu_ipc_writev(OFF) failed (ret: %d)", ret);
+ ret = -EINVAL;
+ goto exit_modem_cold_reset;
+ }
+ usleep_range(COLD_BOOT_DELAY_OFF, COLD_BOOT_DELAY_OFF);
+
+ /* Write the new registre value (V1P35_ON) */
+ data = (def_value & 0xf8) | V1P35_ON;
+ ret = intel_scu_ipc_writev(&addr, &data, 1);
+ if (ret) {
+ CRITICAL("intel_scu_ipc_writev(ON) failed (ret: %d)", ret);
+ ret = -EINVAL;
+ goto exit_modem_cold_reset;
+ }
+ usleep_range(COLD_BOOT_DELAY_ON, COLD_BOOT_DELAY_ON);
+
+ /* FIXME : Do we really need this read operation ??? */
+ ret = intel_scu_ipc_readv(&addr, &data, 1);
+ if (ret) {
+ CRITICAL("intel_scu_ipc_readv() failed (ret: %d)", ret);
+ ret = -EINVAL;
+ goto exit_modem_cold_reset;
+ }
+
+ /* Write back the old registre value */
+ data = def_value;
+ ret = intel_scu_ipc_writev(&addr, &data, 1);
+ if (ret) {
+ CRITICAL("intel_scu_ipc_writev() failed (ret: %d)", ret);
+ ret = -EINVAL;
+ }
+
+ exit_modem_cold_reset:
+ /* Cold reset failed => Do a modem reset */
+ if (ret) {
+ CRITICAL("Cold reset failed => Doing a modem reset");
+ ret = do_modem_reset(val, kp);
+ }
+
+ out:
+ EPILOG();
+ return ret;
}
/*
- * Modem hangup sysfs entries
- */
-static int set_hangup_reasons(const char *val, struct kernel_param *kp)
+* @brief Modem Hangup reasons module_param set function
+* - This function is reseting the provided HUP reasons
+*
+* @param val
+* @param kp
+*
+* @return 0
+*/
+static int clear_hangup_reasons(const char *val, struct kernel_param *kp)
{
- unsigned long reset_request;
+ unsigned long reasons_to_clear;
- if (strict_strtoul(val, 16, &reset_request) < 0)
+ PROLOG();
+
+ if (strict_strtoul(val, 16, &reasons_to_clear) < 0)
return -EINVAL;
+ if (reasons_to_clear) {
+ unsigned long flags;
+ struct dlp_channel *ch_ctx = DLP_CHANNEL_CTX(DLP_CHANNEL_CTRL);
+
+ spin_lock_irqsave(&ch_ctx->lock, flags);
+ ch_ctx->hangup.last_cause &= ~reasons_to_clear;
+ ch_ctx->hangup.cause &= ~reasons_to_clear;
+ spin_unlock_irqrestore(&ch_ctx->lock, flags);
+ }
+
+ EPILOG();
return 0;
}
-static int get_hangup_reasons(const char *val, struct kernel_param *kp)
+/*
+* @brief Modem Hangup reasons module_param get function
+* - This function return the modem HUP reasons
+*
+* @param val
+* @param kp
+*
+* @return 0
+*/
+static int get_hangup_reasons(char *val, struct kernel_param *kp)
{
- unsigned long hangup_reasons = 0;
+ unsigned long hangup_reasons;
- return sprintf(val, "%d", hangup_reasons);
-}
+ PROLOG();
+ hangup_reasons = dlp_ctrl_get_hangup_reasons();
+ EPILOG();
+ return sprintf(val, "%lud", hangup_reasons);
+}
-module_param_call(reset_modem, set_modem_reset, get_modem_reset, NULL, 0644);
-module_param_call(hangup_reasons, set_hangup_reasons, get_hangup_reasons,
+module_param_call(cold_reset_modem, dp_modem_cold_reset, NULL, NULL, 0644);
+module_param_call(reset_modem, do_modem_reset, get_modem_reset, NULL, 0644);
+module_param_call(hangup_reasons, clear_hangup_reasons, get_hangup_reasons,
NULL, 0644);
/* Dump the PDU */
if (pdu->ttype == HSI_MSG_WRITE) {
dlp_pdu_dump(pdu, 0);
- //dlp_dbg_dump_pdu(pdu, 16, 160, 0);
+ /* dlp_dbg_dump_pdu(pdu, 16, 160, 0); */
}
err = hsi_async(pdu->cl, pdu);
if (!err) {
if ((pdu->ttype == HSI_MSG_WRITE) && (xfer_ctx->ctrl_len)) {
- mod_timer(&ch_ctx->hangup_timer,
- jiffies + ch_ctx->hangup_delay);
+ mod_timer(&ch_ctx->hangup.timer,
+ jiffies + usecs_to_jiffies(DLP_HANGUP_DELAY));
}
} else {
unsigned int ctrl_len;
if (!ctrl_len) {
struct dlp_channel *ch_ctx = xfer_ctx->channel;
- del_timer(&ch_ctx->hangup_timer);
+ del_timer(&ch_ctx->hangup.timer);
}
}
return;
if (dlp_ctx_is_state(xfer_ctx, IDLE)) {
+ int ret;
+
dlp_ctx_set_state(xfer_ctx, ACTIVE);
- hsi_start_tx(dlp_drv.client);
+ ret = hsi_start_tx(dlp_drv.client);
+ if (ret)
+ dlp_ctx_set_state(xfer_ctx, IDLE);
} else {
del_timer(&xfer_ctx->timer);
}
/**
* dlp_hsi_port_claim - Claim & setup the HSI port
- * @ch_ctx: a reference to related channel context
+ *
*/
-int dlp_hsi_port_claim(struct dlp_channel *ch_ctx)
+int dlp_hsi_port_claim(void)
{
int ret = 0;
- //PROLOG("hsi_ch: %d", ch_ctx->hsi_channel);
PROLOG();
- /* Claim the HSI port (if not claimed already) */
- if (atomic_inc_return(&dlp_drv.busy) != 1)
+ /* Claim the HSI port (if not already done) */
+ if (hsi_port_claimed(dlp_drv.client))
goto out;
/* Claim the HSI port */
/**
* dlp_hsi_port_unclaim - UnClaim (release) the HSI port
- * @ch_ctx: a reference to related channel context
+ *
*/
-inline void dlp_hsi_port_unclaim(struct dlp_channel *ch_ctx)
+inline void dlp_hsi_port_unclaim(void)
{
- //PROLOG("hsi_ch: %d", ch_ctx->hsi_channel);
PROLOG();
- if (atomic_dec_and_test(&dlp_drv.busy))
+ if (hsi_port_claimed(dlp_drv.client))
hsi_release_port(dlp_drv.client);
EPILOG();
EPILOG();
}
-/****************************************************************************
- *
- * RX/TX xfer contexts
- *
- ***************************************************************************/
+/*
+* @brief This function is used to deactivate the HSI client RX callbacks
+*
+* @param start_rx_cb : Start RX callback backup function
+* @param stop_rx_cb : Stop RX callback backup function
+*/
+void dlp_save_rx_callbacks(hsi_client_cb *start_rx_cb, hsi_client_cb *stop_rx_cb)
+{
+ PROLOG();
+
+ /* Save the current client CB */
+ (*start_rx_cb) = dlp_drv.client->hsi_start_rx;
+ (*stop_rx_cb) = dlp_drv.client->hsi_stop_rx;
+
+ /* Set to NULL the CB pointer */
+ dlp_drv.client->hsi_start_rx = NULL;
+ dlp_drv.client->hsi_stop_rx = NULL;
+
+ EPILOG();
+}
+
+/*
+* @brief This function is used to reactivate the HSI client RX callbacks
+*
+* @param start_rx_cb : Start RX callback to set
+* @param stop_rx_cb : Stop RX callback to set
+*/
+void dlp_restore_rx_callbacks(hsi_client_cb *start_rx_cb, hsi_client_cb *stop_rx_cb)
+{
+ PROLOG();
+
+ /* Restore the client CB */
+ dlp_drv.client->hsi_start_rx = (*start_rx_cb);
+ dlp_drv.client->hsi_stop_rx = (*stop_rx_cb);
+
+ /* Set to NULL the CB pointer */
+ start_rx_cb = NULL;
+ stop_rx_cb = NULL;
+
+ EPILOG();
+}
/**
* dlp_increase_pdus_pool - background work aimed at creating new pdus
xfer_ctx->channel->hsi_channel);
}
+/****************************************************************************
+ *
+ * Hangup/Reset management
+ *
+ ***************************************************************************/
+/*
+* dlp_hangup_ctx_init - Initialises the given hangup context
+*
+* @param ch_ctx : Channel context to consider
+* @param work_func : Work callback
+* @param timeout_func : Timeout callback
+* @param data : Timeout callback user data
+*/
+void dlp_hangup_ctx_init(struct dlp_channel *ch_ctx,
+ void (* work_func)(struct work_struct *work),
+ void (* timeout_func)(unsigned long int param),
+ void *data)
+{
+ PROLOG();
+
+ /* Init values */
+ ch_ctx->hangup.cause = 0;
+ ch_ctx->hangup.last_cause = 0;
+
+ /* Worker function */
+ INIT_WORK(&ch_ctx->hangup.work, work_func);
+
+ /* TX Timeout timer */
+ init_timer(&ch_ctx->hangup.timer);
+ ch_ctx->hangup.timer.function = timeout_func;
+ ch_ctx->hangup.timer.data = (unsigned long int)data;
+
+ EPILOG();
+}
+
+/**
+ * dlp_hangup_ctx_deinit - Clears a hangup context
+ * @hangup_ctx: a reference to the considered hangup context
+ */
+void dlp_hangup_ctx_deinit(struct dlp_channel *ch_ctx)
+{
+ struct dlp_xfer_ctx *xfer_ctx = &ch_ctx->tx;
+ unsigned long flags;
+ int is_hunging_up;
+
+ PROLOG();
+
+ write_lock_irqsave(&xfer_ctx->lock, flags);
+ is_hunging_up = (ch_ctx->hangup.cause);
+ write_unlock_irqrestore(&xfer_ctx->lock, flags);
+
+ /* No need to wait for the end of the calling work! */
+ if (! is_hunging_up) {
+ if (del_timer_sync(&ch_ctx->hangup.timer))
+ cancel_work_sync(&ch_ctx->hangup.work);
+ else
+ flush_work(&ch_ctx->hangup.work);
+ }
+
+ EPILOG();
+}
+
+/****************************************************************************
+ *
+ * RX/TX xfer contexts
+ *
+ ***************************************************************************/
+
/**
* dlp_xfer_ctx_init - initialise a TX or RX context after its creation
* @ch_ctx: a reference to its related channel context
/* NOTE : this array should be aligned with the context enum
* defined in the .h file */
dlp_context_delete delete_funcs[DLP_CHANNEL_COUNT] = {
- dlp_ctrl_ctx_delete, /* 0 : CTRL */
- dlp_tty_ctx_delete, /* 1 : TTY */
- dlp_net_ctx_delete, /* 2 : NET */
- dlp_net_ctx_delete, /* 3 : NET */
- dlp_net_ctx_delete}; /* 4 : NET */
+ dlp_ctrl_ctx_delete, /* CTRL */
+ dlp_tty_ctx_delete, /* TTY */
+ dlp_net_ctx_delete, /* NET */
+ dlp_net_ctx_delete, /* NET */
+ dlp_net_ctx_delete}; /* NET */
PROLOG();
if (dlp_drv.channels[i]) {
delete_funcs[i] (dlp_drv.channels[i]);
dlp_drv.channels[i] = NULL;
-
}
}
/* NOTE : this array should be aligned with the context enum
* defined in the .h file */
dlp_context_create create_funcs[DLP_CHANNEL_COUNT] = {
- dlp_ctrl_ctx_create, /* 0 : CTRL */
- dlp_tty_ctx_create, /* 1 : TTY */
- dlp_net_ctx_create, /* 2 : NET */
- dlp_net_ctx_create, /* 3 : NET */
- dlp_net_ctx_create}; /* 4 : NET */
+ dlp_ctrl_ctx_create, /* CTRL */
+ dlp_tty_ctx_create, /* TTY */
+ dlp_net_ctx_create, /* NET */
+ dlp_net_ctx_create, /* NET */
+ dlp_net_ctx_create}; /* NET */
PROLOG();
WARNING("HSI device is not DMA capable");
}
+ /* Save IPC controller configs */
+ dlp_drv.ipc_rx_cfg = client->rx_cfg;
+ dlp_drv.ipc_tx_cfg = client->tx_cfg;
+
+ /* Save the Boot/Flashing controller config */
+ /* And deactivate the "Channel description" bits */
+ /* because not managed by the modem */
+ dlp_drv.flash_rx_cfg = client->rx_cfg;
+ dlp_drv.flash_tx_cfg = client->tx_cfg;
+ dlp_drv.flash_rx_cfg.channels = 1;
+ dlp_drv.flash_tx_cfg.channels = 1;
+
/* FIXME: Claim the HSI port */
- ret = dlp_hsi_port_claim(NULL);
+ ret = dlp_hsi_port_claim();
if (ret) {
goto out;
}
hsi_client_set_drvdata(client, dlp_drv.channels[DLP_CHANNEL_TTY]);
/* Create /proc/hsi-dlp */
- // FIXME
+ // FIXME: Will be removed
//if (dlp_drv.debug)
{
proc_create_data(DRVNAME, S_IRUGO, NULL, &dlp_proc_ops, NULL);
dlp_driver_cleanup();
/* UnClaim the HSI port */
- dlp_hsi_port_unclaim(NULL);
+ dlp_hsi_port_unclaim();
EPILOG();
return 0;
*/
static int __init dlp_module_init(void)
{
- int err;
+ int err, debug_value;
+
+ /* Save the debug param value */
+ debug_value = dlp_drv.debug;
/* Initialization */
memset(&dlp_drv, 0, sizeof(struct dlp_driver));
- /* FIXME : to be removed */
- dlp_drv.debug = 0x0;
+ /* Restore the debug param value */
+ dlp_drv.debug = debug_value;
PROLOG();
goto del_wq;
}
- /* Not used yet (claimed) */
- atomic_set(&dlp_drv.busy, 0);
+ /* Create the workqueue for TTY line discipline buffer flush */
+ dlp_drv.forwarding_wq = create_singlethread_workqueue(DRVNAME "-hwq");
+ if (unlikely(!dlp_drv.forwarding_wq)) {
+ CRITICAL("Unable to create TTY forwarding workqueue");
+ err = -EFAULT;
+ goto del_2wq;
+ }
/* Now, register the client */
err = hsi_register_client_driver(&dlp_driver_setup);
if (unlikely(err)) {
CRITICAL("hsi_register_client_driver() failed (%d)", err);
- goto del_2wq;
+ goto del_3wq;
}
EPILOG("driver initialized");
return 0;
+del_3wq:
+ destroy_workqueue(dlp_drv.forwarding_wq);
+
del_2wq:
destroy_workqueue(dlp_drv.tx_hangup_wq);
+
del_wq:
destroy_workqueue(dlp_drv.recycle_wq);
{
PROLOG();
+ destroy_workqueue(dlp_drv.forwarding_wq);
destroy_workqueue(dlp_drv.recycle_wq);
destroy_workqueue(dlp_drv.tx_hangup_wq);
MODULE_AUTHOR("Faouaz Tenoutit <faouazx.tenoutit@intel.com>");
MODULE_DESCRIPTION("LTE protocol driver over HSI for IMC modems");
MODULE_LICENSE("GPL");
-MODULE_VERSION("1.0-HSI-LTE");
+MODULE_VERSION("1.1-HSI-LTE");
#include <linux/hsi/hsi_dlp.h>
#include <linux/tty.h>
#include <linux/netdevice.h>
+#include <linux/wait.h>
#include "../dlp_debug.h"
/* Maximal number of pdu allocation failure prior firing an error message */
#define DLP_PDU_ALLOC_RETRY_MAX_CNT 10
-/* Delays for powering up/resetting the modem (in milliseconds) */
-#define DLP_POWER_ON_INTERLINE_DELAY 1
-#define DLP_POWER_ON_POST_DELAY 200
-
/* Round-up the pdu and header length to a multiple of 4-bytes to align
* on the HSI 4-byte granularity*/
#define DLP_PDU_LENGTH (((CONFIG_HSI_DLP_PDU_LENGTH+3)/4)*4)
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
+/*
+ * Get a ref to the given channel context
+ */
+#define DLP_CHANNEL_CTX(hsi_ch) dlp_drv.channels[hsi_ch]
+
/* RX and TX state machine definitions */
enum {
IDLE,
};
/**
+ * struct dlp_ctrl_hangup_ctx - Hangup management context
+ * @cause: Current cause of the hangup
+ * @last_cause: Previous cause of the hangup
+ * @timer: TX timeout timner
+ * @work: TX timeout deferred work
+ */
+struct dlp_hangup_ctx {
+ unsigned int cause;
+ unsigned int last_cause;
+ struct timer_list timer;
+ struct work_struct work;
+};
+
+/**
* struct dlp_channel - HSI channel context
* @client: reference to this HSI client
* @credits: credits value (nb of pdus that can be sent to the modem)
struct dlp_xfer_ctx rx;
/* Hangup management */
- struct timer_list hangup_timer;
- struct work_struct hangup_queue;
-
- int hangup_reason;
- int hangup_delay;
+ struct dlp_hangup_ctx hangup;
/* Reset & Coredump callbacks */
void (*modem_coredump_cb) (struct dlp_channel * ch_ctx);
* @channels: array of DLP Channel contex references
* @is_dma_capable: a flag to check if the ctrl supports the DMA
* @controller: a reference to the HSI controller
- * @busy: port usage (claiming) counter
* @channels: a reference to the HSI client
* @recycle_wq: Workqueue for submitting pdu-recycling background tasks
* @tx_hangup_wq: Workqueue for submitting tx timeout hangup background tasks
* @modem_ready: The modem is up & running
- * @core_dump_irq:
- * @reset_irq:
- * @reset_ignore:
+ * @lock: Used for modem ready flag lock
+ * @ipc_tx_cfg: HSI client configuration (Used for IPC TX)
+ * @ipc_xx_cfg: HSI client configuration (Used for IPC RX)
+ * @flash_tx_cfg: HSI client configuration (Used for Boot/Flashing TX)
+ * @flash_rx_cfg: HSI client configuration (Used for Boot/Flashing RX)
*
* @debug: Debug variable
*/
struct hsi_client *client;
struct device *controller;
- /* Port claiming counter */
- atomic_t busy;
+ /* Workqueue for tty buffer forwarding */
+ struct workqueue_struct *forwarding_wq;
struct workqueue_struct *recycle_wq;
struct workqueue_struct *tx_hangup_wq;
/* Modem readiness */
int modem_ready;
spinlock_t lock;
- spinlock_t at_lock;
- /* Modem coredump & reset */
- int core_dump_irq;
- int reset_irq;
- int reset_ignore;
+ /* Modem boot/flashing */
+ struct hsi_config ipc_tx_cfg;
+ struct hsi_config ipc_rx_cfg;
+
+ struct hsi_config flash_tx_cfg;
+ struct hsi_config flash_rx_cfg;
/* Debug variables */
int debug;
inline void dlp_stop_rx(struct dlp_xfer_ctx *xfer_ctx,
struct dlp_channel *ch_ctx);
-int dlp_hsi_port_claim(struct dlp_channel *ch_ctx);
+int dlp_hsi_port_claim(void);
+
+inline void dlp_hsi_port_unclaim(void);
+
+void dlp_save_rx_callbacks(hsi_client_cb *start_rx_cb,
+ hsi_client_cb *stop_rx_cb);
-inline void dlp_hsi_port_unclaim(struct dlp_channel *ch_ctx);
+void dlp_restore_rx_callbacks(hsi_client_cb *start_rx_cb,
+ hsi_client_cb *stop_rx_cb);
/****************************************************************************
*
- * RX/TX xfer contexts
+ * Hangup/Reset management
*
***************************************************************************/
+void dlp_hangup_ctx_init(struct dlp_channel *ch_ctx,
+ void (* work_func)(struct work_struct *work),
+ void (* timeout_func)(unsigned long int param),
+ void *data);
+
+void dlp_hangup_ctx_deinit(struct dlp_channel *ch_ctx);
+
+/****************************************************************************
+ *
+ * RX/TX xfer contexts
+ *
+ ***************************************************************************/
void dlp_xfer_ctx_init(struct dlp_channel *ch_ctx,
struct dlp_xfer_ctx *xfer_ctx,
unsigned int delay,
int dlp_ctrl_ctx_delete(struct dlp_channel *ch_ctx);
-int dlp_ctrl_modem_reset(struct dlp_channel *ch_ctx);
+void dlp_ctrl_modem_reset(struct dlp_channel *ch_ctx);
-inline unsigned int dlp_ctrl_modem_is_ready(void);
+inline int dlp_ctrl_get_reset_ongoing(void);
+
+inline void dlp_ctrl_set_reset_ongoing(int ongoing);
-int dlp_ctrl_send_echo_cmd(struct dlp_channel *ch_ctx);
+inline int dlp_ctrl_get_hangup_reasons(void);
+
+inline void dlp_ctrl_set_hangup_reasons(unsigned int hsi_channel,
+ int hangup_reasons);
+
+inline unsigned int dlp_ctrl_modem_is_ready(void);
-int dlp_ctrl_send_open_conn_cmd(struct dlp_channel *ch_ctx);
+int dlp_ctrl_open_channel(struct dlp_channel *ch_ctx);
-int dlp_ctrl_send_cancel_conn_cmd(struct dlp_channel *ch_ctx);
+int dlp_ctrl_close_channel(struct dlp_channel *ch_ctx);
/****************************************************************************
*
#define DEBUG_TAG 0x8
#define DEBUG_VAR dlp_drv.debug
-/* Defaut HSI TX timeout delay (in microseconds) */
-#define NET_HANGUP_DELAY 1000000 /* 1 sec */
-
/* Defaut NET stack TX timeout delay (in milliseconds) */
#define DLP_NET_TX_DELAY 20000 /* 20 sec */
PROLOG();
- ch_ctx->hangup_reason |= reason;
+ ch_ctx->hangup.cause |= reason;
/* Stop the NET IF */
if (!netif_queue_stopped(net_ctx->ndev))
PROLOG("%s", net_ctx->ndev->name);
+ /* TX xfer done => Reset the "ongoing" flag */
+ dlp_ctrl_set_reset_ongoing(0);
+
/* TX done, free the skb */
dev_kfree_skb(msg_param->skb);
write_lock_irqsave(&xfer_ctx->lock, flags);
dlp_hsi_controller_pop(xfer_ctx);
- /* Decrease the pdus counter */
- xfer_ctx->all_len--;
-
/* Still have queued TX pdu ? */
if (xfer_ctx->ctrl_len) {
- mod_timer(&ch_ctx->hangup_timer,
- jiffies + ch_ctx->hangup_delay);
+ mod_timer(&ch_ctx->hangup.timer,
+ jiffies + usecs_to_jiffies(DLP_HANGUP_DELAY));
} else {
- del_timer(&ch_ctx->hangup_timer);
+ del_timer(&ch_ctx->hangup.timer);
}
write_unlock_irqrestore(&xfer_ctx->lock, flags);
CRITICAL("Hangup (Timeout) !");
- ch_ctx->hangup_reason |= DLP_MODEM_HU_TIMEOUT;
- queue_work(dlp_drv.tx_hangup_wq, &ch_ctx->hangup_queue);
+ ch_ctx->hangup.cause |= DLP_MODEM_HU_TIMEOUT;
+ queue_work(dlp_drv.tx_hangup_wq, &ch_ctx->hangup.work);
EPILOG();
}
*/
static void dlp_net_hsi_tx_timeout(struct work_struct *work)
{
- //struct dlp_channel *ch_ctx = container_of(work, struct dlp_channel,
- // hangup_queue);
+/*
+ struct dlp_channel *ch_ctx = DLP_CHANNEL_CTX(DLP_CHANNEL_TTY);
+ struct dlp_net_context *net_ctx = ch_ctx->ch_data;
+*/
- /* FIXME : TBD */
+ /* FIXME : Stop the NETIF ? */
CRITICAL("HSI TX timeout");
}
goto out;
}
- /* Claim the HSI port */
- ret = dlp_hsi_port_claim(ch_ctx);
- if (ret) {
- goto out;
- }
-
// FIXME: To be removed (done in dlp_net_ctx_create)
- ret = dlp_ctrl_send_open_conn_cmd(ch_ctx);
+ ret = dlp_ctrl_open_channel(ch_ctx);
if (ret) {
- CRITICAL("dlp_ctrl_send_open_conn_cmd() failed !");
+ CRITICAL("dlp_ctrl_open_channel() failed !");
ret = -EIO;
- goto unclaim;
+ goto out;
}
/* Push all RX pdus */
EPILOG();
return ret;
-unclaim:
- dlp_hsi_port_unclaim(ch_ctx);
-
out:
EPILOG();
return ret;
int dlp_net_stop(struct net_device *dev)
{
- int ret;
struct dlp_channel *ch_ctx = netdev_priv(dev);
struct dlp_xfer_ctx *tx_ctx;
struct dlp_xfer_ctx *rx_ctx;
+ int ret;
PROLOG("%s, hsi_ch:%d", dev->name, ch_ctx->hsi_channel);
tx_ctx = &ch_ctx->tx;
rx_ctx = &ch_ctx->rx;
- del_timer_sync(&ch_ctx->hangup_timer);
+ del_timer_sync(&ch_ctx->hangup.timer);
/* Stop the NET IF */
if (!netif_queue_stopped(dev))
netif_stop_queue(dev);
- // FIXME: To be removed (should be done in dlp_net_ctx_delete)
- ret = dlp_ctrl_send_cancel_conn_cmd(ch_ctx);
+ ret = dlp_ctrl_close_channel(ch_ctx);
if (ret) {
- CRITICAL("dlp_ctrl_send_cancel_conn_cmd() failed !");
+ CRITICAL("dlp_ctrl_close_channel() failed !");
}
- del_timer_sync(&ch_ctx->hangup_timer);
- hsi_flush(dlp_drv.client);
+ /* FIXME : Will flush everything */
+ // hsi_flush(dlp_drv.client);
/* RX */
del_timer_sync(&rx_ctx->timer);
dlp_ctx_set_state(tx_ctx, IDLE);
- /* Release the HSI port */
- dlp_hsi_port_unclaim(ch_ctx);
-
EPILOG();
return 0;
}
if (!net_ctx->net_padd) {
CRITICAL("No more memory to allocate padding buffer");
ret = -ENOMEM;
- goto out;
+ goto free_dev;
}
/* Register the net device */
net_ctx->ndev = ndev;
ch_ctx = netdev_priv(ndev);
- /* Save params */
ch_ctx->ch_data = net_ctx;
ch_ctx->hsi_channel = index;
ch_ctx->credits = 0;
ch_ctx->tx.config = client->tx_cfg;
spin_lock_init(&ch_ctx->lock);
- init_timer(&ch_ctx->hangup_timer);
init_waitqueue_head(&ch_ctx->tx_empty_event);
- INIT_WORK(&ch_ctx->hangup_queue, dlp_net_hsi_tx_timeout);
- ch_ctx->hangup_delay = from_usecs(NET_HANGUP_DELAY);
- ch_ctx->hangup_timer.function = dlp_net_hangup_timer_cb;
- ch_ctx->hangup_timer.data = (unsigned long int)ch_ctx;
+ /* Hangup context */
+ dlp_hangup_ctx_init(ch_ctx,
+ dlp_net_hsi_tx_timeout,
+ dlp_net_hangup_timer_cb,
+ ch_ctx);
ch_ctx->modem_coredump_cb = dlp_net_mdm_coredump_cb;
ch_ctx->modem_reset_cb = dlp_net_mdm_reset_cb;
DLP_HSI_RX_WAIT_FIFO, DLP_HSI_RX_CTRL_FIFO,
dlp_net_complete_rx, HSI_MSG_READ);
- /* FIXME : to be activated */
/* Open the HSI channel */
- //ret = dlp_ctrl_send_open_conn_cmd(ch_ctx);
- //if (ret) {
- // CRITICAL("dlp_ctrl_send_open_conn_cmd() failed !");
- // goto free_dev;
- //}
+#if 0 /* FIXME : to be activated when the modem boot time is optimized */
+ ret = dlp_ctrl_open_channel(ch_ctx);
+ if (ret) {
+ CRITICAL("dlp_ctrl_open_channel() failed !");
+ goto free_dev;
+ }
+#endif
/* Allocate RX FIFOs in background */
queue_work(dlp_drv.recycle_wq, &ch_ctx->rx.increase_pool);
int ret = 0;
struct dlp_net_context *net_ctx = ch_ctx->ch_data;
- del_timer_sync(&ch_ctx->hangup_timer);
+ /* Clear the hangup context */
+ dlp_hangup_ctx_deinit(ch_ctx);
/* Unregister the net device */
unregister_netdev(net_ctx->ndev);
dlp_xfer_ctx_clear(&ch_ctx->rx);
dlp_xfer_ctx_clear(&ch_ctx->tx);
- /* FIXME : to be activated */
+#if 0 /* FIXME : Not supported by the modem */
/* Release the HSI channel */
- // ret = dlp_ctrl_send_cancel_conn_cmd(ch_ctx);
- //if (ret) {
- // CRITICAL("dlp_ctrl_send_cancel_conn_cmd() failed !");
- //}
+ ret = dlp_ctrl_send_cancel_conn_cmd(ch_ctx);
+ if (ret) {
+ CRITICAL("dlp_ctrl_send_cancel_conn_cmd() failed !");
+ }
+#endif
/* Free the padding buffer */
dlp_buffer_free(net_ctx->net_padd,
#include <linux/hsi/hsi.h>
#include <linux/dma-mapping.h>
#include <linux/gpio.h>
-#include <linux/wait.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#define DEBUG_TAG 0x4
#define DEBUG_VAR dlp_drv.debug
-#define TTYNAME "tty"CONFIG_HSI_DLP_TTY_NAME
+#define IPC_TTYNAME "tty"CONFIG_HSI_DLP_IPC_TTY_NAME
#define RX_TTY_FORWARDING_BIT (1<<DLP_GLOBAL_STATE_SZ)
#define RX_TTY_REFORWARD_BIT (2<<DLP_GLOBAL_STATE_SZ)
* @tty_prt: TTY port struct
* @buffer_wq: Workqueue for tty buffer flush
* @ch_ctx : Channel context ref
+ * @do_tty_forward: dedicated TTY forwarding work structure
*/
struct dlp_tty_context {
struct tty_port tty_prt;
struct tty_driver *tty_drv;
struct dlp_channel *ch_ctx;
+ struct work_struct do_tty_forward;
};
/*
PROLOG();
- ch_ctx->hangup_reason |= reason;
+ ch_ctx->hangup.cause |= reason;
tty = tty_port_tty_get(&tty_ctx->tty_prt);
if (tty) {
tty_hangup(tty);
}
/**
- * dlp_tty_forward - RX data TTY forwarding helper function
+ * _dlp_forward_tty - RX data TTY forwarding helper function
* @tty: a reference to the TTY where the data shall be forwarded
* @xfer_ctx: a reference to the RX context where the FIFO of waiting pdus sits
*
* - Restart delayed job if some data is remaining in the waiting FIFO or if the
* controller FIFO is not full yet.
*/
-static void dlp_tty_forward(struct tty_struct *tty,
+static void _dlp_forward_tty(struct tty_struct *tty,
struct dlp_xfer_ctx *xfer_ctx)
{
struct hsi_msg *pdu;
PROLOG();
- if (dlp_ctx_has_flag(xfer_ctx, RX_TTY_FORWARDING_BIT)) {
- dlp_ctx_set_flag(xfer_ctx, RX_TTY_REFORWARD_BIT);
- return;
- }
-
/* Initialised to 1 to prevent unexpected TTY forwarding resume
* function when there is no TTY or when it is throttled */
- dlp_ctx_set_flag(xfer_ctx, RX_TTY_FORWARDING_BIT);
-
copied = 1;
do_push = 0;
del_timer(&xfer_ctx->timer);
-shoot_again_now:
-
read_lock_irqsave(&xfer_ctx->lock, flags);
while (xfer_ctx->wait_len > 0) {
tty_flag = TTY_FRAME;
if (unlikely(!tty))
- goto recycle_pdu;
+ goto free_pdu;
while (pdu->actual_len > 0) {
* forwarding resume function schedule */
copied = 1;
dlp_fifo_wait_push_back(xfer_ctx, pdu);
-
goto no_more_tty_insert;
}
if (copied == 0) {
dlp_fifo_wait_push_back(xfer_ctx, pdu);
-
goto no_more_tty_insert;
}
}
-recycle_pdu:
+free_pdu:
/* Reset the pdu offset & length */
dlp_pdu_reset(xfer_ctx,
pdu,
read_unlock_irqrestore(&xfer_ctx->lock, flags);
no_more_tty_insert:
- /* Schedule a flip since called from complete_rx() in an interrupt
- * context instead of tty_flip_buffer_push() */
- if (do_push)
+ if (do_push) {
+ /* Schedule a flip since called from complete_rx() in an interrupt
+ * context instead of tty_flip_buffer_push() */
tty_schedule_flip(tty);
-
- /* If some reforwarding request occur in the meantime, do this now */
- if (dlp_ctx_has_flag(xfer_ctx, RX_TTY_REFORWARD_BIT)) {
- dlp_ctx_clear_flag(xfer_ctx, RX_TTY_REFORWARD_BIT);
- goto shoot_again_now;
}
- dlp_ctx_clear_flag(xfer_ctx, RX_TTY_FORWARDING_BIT);
-
/* Push any available pud to the CTRL */
ret = dlp_pop_recycled_push_ctrl(xfer_ctx);
}
/**
+ * dlp_do_tty_forward - forwarding data to the above line discipline
+ * @work: a reference to work queue element
+ */
+static void dlp_do_tty_forward(struct work_struct *work)
+{
+ struct dlp_tty_context *tty_ctx;
+ struct tty_struct *tty;
+
+ PROLOG();
+
+ tty_ctx = container_of(work, struct dlp_tty_context, do_tty_forward);
+ tty = tty_port_tty_get(&tty_ctx->tty_prt);
+ if (tty) {
+ /* Lock really needed ?
+ * We are using a single thread workqueue,
+ * so works are executed sequentially */
+ _dlp_forward_tty(tty, &tty_ctx->ch_ctx->rx);
+ tty_kref_put(tty);
+ }
+
+ EPILOG();
+}
+
+/**
* dlp_tty_rx_forward_retry - TTY forwarding retry job
* @param: a casted reference to the to the RX context where the FIFO of
* waiting pdus sits
{
struct dlp_xfer_ctx *xfer_ctx = (struct dlp_xfer_ctx *)param;
struct dlp_tty_context *tty_ctx = xfer_ctx->channel->ch_data;
- struct tty_struct *tty;
PROLOG();
-
- tty = tty_port_tty_get(&tty_ctx->tty_prt);
-
- dlp_tty_forward(tty, xfer_ctx);
-
- if (tty)
- tty_kref_put(tty);
-
+ queue_work(dlp_drv.forwarding_wq, &tty_ctx->do_tty_forward);
EPILOG();
}
/* Get the context reference from the driver data if already opened */
ch_ctx = (struct dlp_channel *)tty->driver_data;
- if (!ch_ctx)
- return;
-
- dlp_tty_forward(tty, &ch_ctx->rx);
+ if (ch_ctx) {
+ struct dlp_tty_context *tty_ctx = ch_ctx->ch_data;
+ queue_work(dlp_drv.forwarding_wq, &tty_ctx->do_tty_forward);
+ }
EPILOG();
}
PROLOG();
- dlp_drv.reset_ignore = 0;
+ /* TX xfer done => Reset the "ongoing" flag */
+ dlp_ctrl_set_reset_ongoing(0);
/* Dump the PDU */
// dlp_pdu_dump(pdu, 1);
/* Still have queued TX pdu ? */
if (xfer_ctx->ctrl_len) {
- mod_timer(&ch_ctx->hangup_timer,
- jiffies + ch_ctx->hangup_delay);
+ mod_timer(&ch_ctx->hangup.timer,
+ jiffies + usecs_to_jiffies(DLP_HANGUP_DELAY));
} else {
- del_timer(&ch_ctx->hangup_timer);
+ del_timer(&ch_ctx->hangup.timer);
}
read_unlock_irqrestore(&xfer_ctx->lock, flags);
{
struct dlp_xfer_ctx *xfer_ctx = pdu->context;
struct dlp_tty_context *tty_ctx = xfer_ctx->channel->ch_data;
- struct tty_struct *tty;
unsigned long flags;
PROLOG();
- tty = tty_port_tty_get(&tty_ctx->tty_prt);
-
/* Dump the PDU : Do it before or after the actual_len update ? */
- dlp_pdu_dump(pdu, 1);
+ /* dlp_pdu_dump(pdu, 1); */
/* Check and update the PDU len & status */
dlp_pdu_update(tty_ctx->ch_ctx, pdu);
dlp_fifo_wait_push(xfer_ctx, pdu);
- dlp_tty_forward(tty, xfer_ctx);
-
- if (tty)
- tty_kref_put(tty);
-
+ queue_work(dlp_drv.forwarding_wq, &tty_ctx->do_tty_forward);
EPILOG();
}
/* Configure the DLP channel */
// FIXME: To be removed (should be done in dlp_tty_ctx_create)
if (tty->count == 1) {
- ret = dlp_ctrl_send_open_conn_cmd(ch_ctx);
+ ret = dlp_ctrl_open_channel(ch_ctx);
if (ret) {
- CRITICAL("dlp_ctrl_send_open_conn_cmd() failed !");
+ CRITICAL("dlp_ctrl_open_channel() failed !");
goto out;
}
}
struct dlp_tty_context *tty_ctx;
struct dlp_xfer_ctx *tx_ctx;
struct dlp_xfer_ctx *rx_ctx;
+ int ret;
PROLOG();
rx_ctx = &ch_ctx->rx;
/* we need hang_up timer alive to avoid long wait here */
- if (!(ch_ctx->hangup_reason & DLP_MODEM_HU_TIMEOUT))
+ if (!(ch_ctx->hangup.cause & DLP_MODEM_HU_TIMEOUT))
dlp_tty_wait_until_ctx_sent(ch_ctx, 0);
- del_timer_sync(&ch_ctx->hangup_timer);
+ del_timer_sync(&ch_ctx->hangup.timer);
// FIXME: FTE
// hsi_flush(dlp_drv.client);
dlp_ctx_set_state(tx_ctx, IDLE);
/* Close the HSI channel */
-#if 0 /* FIXME : Not supported by the modem */
- if (port->tty->count == 1) {
- int ret;
- ret = dlp_ctrl_send_cancel_conn_cmd(ch_ctx);
- if (ret) {
- CRITICAL("dlp_ctrl_send_cancel_conn_cmd() failed !");
- }
+ ret = dlp_ctrl_close_channel(ch_ctx);
+ if (ret) {
+ CRITICAL("dlp_ctrl_close_channel() failed !");
}
-#endif
PDEBUG("tty port shut down");
EPILOG();
tty_ctx = ch_ctx->ch_data;
ret = tty_port_open(&tty_ctx->tty_prt, tty, filp);
if (ret)
- CRITICAL("TTY open failed (%d)", ret);
+ CRITICAL("TTY port open failed (%d)", ret);
/* Set the TTY_NO_WRITE_SPLIT to transfer as much data as possible on
* the first write request. This shall not introduce denial of service
}
/**
- * dlp_tty_hangup_timer_cb - timer function for tx timeout hangup request
+ * dlp_tty_tx_timeout - timer function for tx timeout hangup request
* @param: a reference to the channel to consider
*/
-static void dlp_tty_hangup_timer_cb(unsigned long int param)
+static void dlp_tty_tx_timeout(unsigned long int param)
{
struct dlp_channel *ch_ctx = (struct dlp_channel *)param;
CRITICAL("Tx timeout");
- ch_ctx->hangup_reason |= DLP_MODEM_HU_TIMEOUT;
- queue_work(dlp_drv.tx_hangup_wq, &ch_ctx->hangup_queue);
+ ch_ctx->hangup.cause |= DLP_MODEM_HU_TIMEOUT;
+ queue_work(dlp_drv.tx_hangup_wq, &ch_ctx->hangup.work);
EPILOG();
}
/**
- * dlp_tty_handle_tx_timeout - work queue function called from hangup timer
+ * dlp_tty_do_tx_hangup - initiate a hangup (TX timeout, modem reset or core dump)
* @work: a reference to work queue element
*
* Required since port shutdown calls a mutex that might sleep
*/
-static void dlp_tty_handle_tx_timeout(struct work_struct *work)
+static void dlp_tty_do_tx_hangup(struct work_struct *work)
{
- struct dlp_channel *ch_ctx = container_of(work, struct dlp_channel,
- hangup_queue);
+ struct dlp_channel *ch_ctx = DLP_CHANNEL_CTX(DLP_CHANNEL_TTY);
struct dlp_tty_context *tty_ctx = ch_ctx->ch_data;
struct tty_struct *tty;
PROLOG();
- wake_up(&ch_ctx->tx_empty_event);
tty = tty_port_tty_get(&tty_ctx->tty_prt);
if (tty) {
- tty_hangup(tty);
+ tty_vhangup(tty);
tty_kref_put(tty);
}
{
struct dlp_xfer_ctx *ch_ctx =
&((struct dlp_channel *)tty->driver_data)->tx;
- unsigned int room;
+ int room;
unsigned long flags;
PROLOG();
{
struct dlp_xfer_ctx *ch_ctx =
&((struct dlp_channel *)tty->driver_data)->tx;
- unsigned int buffered;
+ int buffered;
unsigned long flags;
PROLOG();
break;
case HSI_DLP_MODEM_STATE:
- data = !(dlp_drv.reset_ignore);
+ data = !(dlp_ctrl_get_reset_ongoing());
return put_user(data, (unsigned int __user *)arg);
break;
case HSI_DLP_GET_HANGUP_REASON:
- ret = put_user(ch_ctx->hangup_reason,
+ ret = put_user(ch_ctx->hangup.cause,
(unsigned int __user *)arg);
write_lock_irqsave(&ch_ctx->rx.lock, flags);
- ch_ctx->hangup_reason = 0;
+ ch_ctx->hangup.cause = 0;
write_unlock_irqrestore(&ch_ctx->rx.lock, flags);
return ret;
break;
/* Allocate & configure the TTY driver */
new_drv = alloc_tty_driver(1);
- if (unlikely(!new_drv)) {
- CRITICAL("alloc_tty_driver() failed !");
+ if (!new_drv) {
+ CRITICAL("alloc_tty_driver() failed");
goto free_ctx;
}
- /* Configure the TTY */
new_drv->magic = TTY_DRIVER_MAGIC;
new_drv->owner = THIS_MODULE;
new_drv->driver_name = DRVNAME;
- new_drv->name = TTYNAME;
+ new_drv->name = IPC_TTYNAME;
new_drv->minor_start = 0;
new_drv->num = 1;
new_drv->type = TTY_DRIVER_TYPE_SERIAL;
/* Register the TTY driver */
ret = tty_register_driver(new_drv);
- if (unlikely(ret)) {
+ if (ret) {
CRITICAL("tty_register_driver() failed (%d)", ret);
goto free_drv;
}
- /* Save params */
ch_ctx->ch_data = tty_ctx;
ch_ctx->hsi_channel = index;
ch_ctx->credits = 0;
ch_ctx->rx.config = client->rx_cfg;
ch_ctx->tx.config = client->tx_cfg;
- dlp_drv.reset_ignore = 1;
-
spin_lock_init(&ch_ctx->lock);
- init_timer(&ch_ctx->hangup_timer);
init_waitqueue_head(&ch_ctx->tx_empty_event);
- INIT_WORK(&ch_ctx->hangup_queue, dlp_tty_handle_tx_timeout);
- ch_ctx->hangup_delay = from_usecs(DLP_HANGUP_DELAY);
- ch_ctx->hangup_timer.function = dlp_tty_hangup_timer_cb;
- ch_ctx->hangup_timer.data = (unsigned long int)ch_ctx;
+ /* Hangup context */
+ dlp_hangup_ctx_init(ch_ctx,
+ dlp_tty_do_tx_hangup,
+ dlp_tty_tx_timeout,
+ ch_ctx);
ch_ctx->modem_coredump_cb = dlp_tty_mdm_coredump_cb;
ch_ctx->modem_reset_cb = dlp_tty_mdm_reset_cb;
DLP_HSI_RX_WAIT_FIFO, DLP_HSI_RX_CTRL_FIFO,
dlp_tty_complete_rx, HSI_MSG_READ);
+ INIT_WORK(&tty_ctx->do_tty_forward, dlp_do_tty_forward);
+
ch_ctx->tx.timer.function = dlp_tty_tx_stop;
ch_ctx->rx.timer.function = dlp_tty_rx_forward_retry;
tty_port_init(&(tty_ctx->tty_prt));
tty_ctx->tty_prt.ops = &dlp_port_tty_ops;
- if (unlikely(!tty_register_device(new_drv, 0, dev))) {
+ if (!tty_register_device(new_drv, 0, dev)) {
CRITICAL("tty_register_device() failed (%d)!", ret);
goto unreg_drv;
}
PROLOG();
- del_timer(&ch_ctx->hangup_timer);
+ /* Clear the hangup context */
+ dlp_hangup_ctx_deinit(ch_ctx);
/* Unregister device */
tty_unregister_device(tty_ctx->tty_drv, 0);