Modem reset implementation (via sysfs) + eDLP update
authorFaouaz TENOUTIT <faouazx.tenoutit@intel.com>
Wed, 21 Mar 2012 15:53:58 +0000 (16:53 +0100)
committerbuildbot <buildbot@intel.com>
Thu, 12 Apr 2012 13:40:49 +0000 (06:40 -0700)
BZ: 26113

dlp_main.c
- Update the module version (1.1)
- Use the hsi_port_claimed to check if the HSI client is claimed
- Use the user provided debug parameter value (when used as module)
- Create a worqueue used for TTY data forwarding

dlp_ctrl.c
- Backport the modem cold reset implementation from MFLD
- Add log when a modem reset/power is requested
    - Correct the modem reset & power timing values
- Factorize the call of gpio_request/gpio_directions to one function
- Remove the gpio_export call, to stop exporting GPIO => Any applications
 should use the eDLP module params/ioctls to perform actions

dlp_net.c:
- Use the same value of TX timeout for TTY/NET channels
- Add the reset/hangup management
- No need to clainm the HSI port by the NET if (already done by the probe)
- Dont call the hsi_flusi in the net_stop function (avoid flushing everything)
- Send the CANCEL_CONN when closing the NET interface

dlp_tty.c:
    - Add a workqueue for TTY data forwrading (not the rx_complete anymore)
- Add the reset/hangup management
- Change TTYNAME defien to IPC_TTYNAME
- Send the CANCEL_CONN when closing the TTY port

Faouaz TENOUTIT <faouazx.tenoutit@intel.com>

Change-Id: I869f811da89b821d2b93a56bd8606c92037f435d
Signed-off-by: Faouaz TENOUTIT <faouazx.tenoutit@intel.com>
Reviewed-on: http://android.intel.com:8080/40000
Reviewed-by: Roulliere, Pierre <pierre.roulliere@intel.com>
Reviewed-by: Stoltz-douchet, OlivierX <olivierx.stoltz-douchet@intel.com>
Reviewed-by: Predon, Frederic <frederic.predon@intel.com>
Reviewed-by: Lebsir, SamiX <samix.lebsir@intel.com>
Tested-by: Lebsir, SamiX <samix.lebsir@intel.com>
Reviewed-by: buildbot <buildbot@intel.com>
Tested-by: buildbot <buildbot@intel.com>
drivers/hsi/clients/Kconfig
drivers/hsi/clients/dlp_ctrl.c
drivers/hsi/clients/dlp_main.c
drivers/hsi/clients/dlp_main.h
drivers/hsi/clients/dlp_net.c
drivers/hsi/clients/dlp_tty.c

index dc36c87..0286951 100644 (file)
@@ -114,11 +114,11 @@ config HSI_DLP
 
 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
index 0ba4137..480ca9c 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/wait.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
+#include <asm/intel_scu_ipc.h>
 
 #include "dlp_main.h"
 
@@ -111,8 +112,21 @@ enum {
        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)
@@ -129,7 +143,7 @@ struct dlp_command_params {
 };
 
 /**
- * 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
@@ -141,6 +155,18 @@ struct dlp_command {
        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
  *
@@ -173,6 +199,9 @@ struct dlp_ctrl_context {
        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;
 };
 
 /*
@@ -215,9 +244,9 @@ static irqreturn_t dlp_ctrl_reset_it(int irq, void *data)
 
        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);
@@ -226,7 +255,7 @@ static irqreturn_t dlp_ctrl_reset_it(int irq, void *data)
        }
 
        /* Unexpected reset received */
-       dlp_drv.reset_ignore = 1;
+       ctrl_ctx->reset.ongoing = 1;
 
        /* Call registered channels */
        for (i = 0; i < DLP_CHANNEL_COUNT; i++) {
@@ -263,17 +292,51 @@ static int dlp_ctrl_free_gpios(struct dlp_channel *ch_ctx,
        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;
@@ -281,77 +344,55 @@ static int dlp_ctrl_configure_gpios(struct dlp_channel *ch_ctx,
 
        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);
@@ -362,7 +403,15 @@ static int dlp_ctrl_configure_gpios(struct dlp_channel *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;
 
@@ -377,11 +426,11 @@ free_ctx3:
 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;
@@ -397,30 +446,6 @@ free_ctx4:
 /*
  *
  */
-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,
@@ -674,7 +699,7 @@ push_rx:
                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 */
@@ -713,7 +738,7 @@ static int dlp_ctrl_cmd_send(struct dlp_channel *ch_ctx,
               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);
@@ -804,7 +829,7 @@ static int dlp_ctrl_cmd_send(struct dlp_channel *ch_ctx,
        }
 
        /* 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);
@@ -820,7 +845,7 @@ free_cmd:
 
 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;
@@ -884,6 +909,11 @@ out:
        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;
@@ -895,7 +925,9 @@ static inline void dlp_ctrl_set_modem_readiness(unsigned int value)
 
 
 /*
-* @brief
+* @brief A deferred work that :
+*       - Send ECHO & Wait for the modem respose
+*       - Set the modem readiness flag when the modem respond
 *
 * @param work
 */
@@ -903,7 +935,8 @@ static void dlp_ctrl_ipc_readiness(struct work_struct *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();
 
@@ -914,9 +947,20 @@ static void dlp_ctrl_ipc_readiness(struct work_struct *work)
        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);
@@ -941,56 +985,58 @@ static void dlp_ctrl_ipc_readiness(struct work_struct *work)
  * 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
 */
@@ -1007,6 +1053,61 @@ inline unsigned int dlp_ctrl_modem_is_ready(void)
 }
 
 /*
+* @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
@@ -1055,37 +1156,27 @@ struct dlp_channel *dlp_ctrl_ctx_create(unsigned int index, struct device *dev)
        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;
 
@@ -1096,6 +1187,10 @@ struct dlp_channel *dlp_ctrl_ctx_create(unsigned int index, struct device *dev)
        /* 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;
 
@@ -1111,9 +1206,9 @@ out:
 }
 
 /*
-* @brief
+* @brief Delete any resources allocated by dlp_ctrl_ctx_create
 *
-* @param ch_ctx
+* @param ch_ctx : The IPC CTRL channel
 *
 * @return
 */
@@ -1127,7 +1222,14 @@ int dlp_ctrl_ctx_delete(struct dlp_channel *ch_ctx)
        /* 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);
@@ -1140,29 +1242,15 @@ int dlp_ctrl_ctx_delete(struct dlp_channel *ch_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);
@@ -1170,7 +1258,7 @@ int dlp_ctrl_send_open_conn_cmd(struct dlp_channel *ch_ctx)
 
        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);
@@ -1180,37 +1268,55 @@ int dlp_ctrl_send_open_conn_cmd(struct dlp_channel *ch_ctx)
 }
 
 /*
- *
- */
-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];
@@ -1218,38 +1324,161 @@ static int set_modem_reset(const char *val, struct kernel_param *kp)
                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);
 
index deff70d..f7419ed 100644 (file)
@@ -1214,14 +1214,14 @@ int dlp_hsi_controller_push(struct dlp_xfer_ctx *xfer_ctx, struct hsi_msg *pdu)
        /* 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;
@@ -1244,7 +1244,7 @@ int dlp_hsi_controller_push(struct dlp_xfer_ctx *xfer_ctx, struct hsi_msg *pdu)
 
                if (!ctrl_len) {
                        struct dlp_channel *ch_ctx = xfer_ctx->channel;
-                       del_timer(&ch_ctx->hangup_timer);
+                       del_timer(&ch_ctx->hangup.timer);
                }
        }
 
@@ -1290,9 +1290,13 @@ void dlp_hsi_start_tx(struct dlp_xfer_ctx *xfer_ctx)
                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);
        }
@@ -1341,17 +1345,16 @@ inline void dlp_stop_rx(struct dlp_xfer_ctx *xfer_ctx,
 
 /**
  * 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 */
@@ -1376,14 +1379,13 @@ out:
 
 /**
  * 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();
@@ -1428,11 +1430,47 @@ static void dlp_hsi_stop_rx_cb(struct hsi_client *cl)
        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
@@ -1504,6 +1542,74 @@ static void dlp_increase_pdus_pool(struct work_struct *work)
               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
@@ -1596,11 +1702,11 @@ static void dlp_driver_cleanup(void)
        /* 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();
 
@@ -1609,7 +1715,6 @@ static void dlp_driver_cleanup(void)
                if (dlp_drv.channels[i]) {
                        delete_funcs[i] (dlp_drv.channels[i]);
                        dlp_drv.channels[i] = NULL;
-
                }
        }
 
@@ -1638,11 +1743,11 @@ static int __init dlp_driver_probe(struct device *dev)
        /* 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();
 
@@ -1657,8 +1762,20 @@ static int __init dlp_driver_probe(struct device *dev)
                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;
        }
@@ -1679,7 +1796,7 @@ static int __init dlp_driver_probe(struct device *dev)
        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);
@@ -1718,7 +1835,7 @@ static int __exit dlp_driver_remove(struct device *dev)
        dlp_driver_cleanup();
 
        /* UnClaim the HSI port */
-       dlp_hsi_port_unclaim(NULL);
+       dlp_hsi_port_unclaim();
 
        EPILOG();
        return 0;
@@ -1743,13 +1860,16 @@ static struct hsi_client_driver dlp_driver_setup = {
  */
 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();
 
@@ -1769,21 +1889,30 @@ static int __init dlp_module_init(void)
                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);
 
@@ -1799,6 +1928,7 @@ static void __exit dlp_module_exit(void)
 {
        PROLOG();
 
+       destroy_workqueue(dlp_drv.forwarding_wq);
        destroy_workqueue(dlp_drv.recycle_wq);
        destroy_workqueue(dlp_drv.tx_hangup_wq);
 
@@ -1814,4 +1944,4 @@ MODULE_AUTHOR("Olivier Stoltz Douchet <olivierx.stoltz-douchet@intel.com>");
 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");
index 9bb5aef..d1ea950 100644 (file)
@@ -35,6 +35,7 @@
 #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,
@@ -211,6 +213,20 @@ struct dlp_xfer_ctx {
 };
 
 /**
+ * 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)
@@ -237,11 +253,7 @@ struct dlp_channel {
        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);
@@ -259,14 +271,15 @@ struct dlp_channel {
  * @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
  */
@@ -277,8 +290,8 @@ struct dlp_driver {
        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;
@@ -286,12 +299,13 @@ struct dlp_driver {
        /* 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;
@@ -431,16 +445,34 @@ void dlp_stop_tx(struct dlp_xfer_ctx *xfer_ctx);
 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,
@@ -467,15 +499,22 @@ struct dlp_channel *dlp_ctrl_ctx_create(unsigned int index,
 
 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);
 
 /****************************************************************************
  *
index 59a938d..a508ded 100644 (file)
@@ -44,9 +44,6 @@
 #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 */
 
@@ -90,7 +87,7 @@ static void dlp_net_modem_hangup(struct dlp_channel *ch_ctx, int reason)
 
        PROLOG();
 
-       ch_ctx->hangup_reason |= reason;
+       ch_ctx->hangup.cause |= reason;
 
        /* Stop the NET IF */
        if (!netif_queue_stopped(net_ctx->ndev))
@@ -189,6 +186,9 @@ static void dlp_net_complete_tx(struct hsi_msg *pdu)
 
        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);
 
@@ -206,15 +206,12 @@ static void dlp_net_complete_tx(struct hsi_msg *pdu)
        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);
@@ -344,8 +341,8 @@ static void dlp_net_hangup_timer_cb(unsigned long int param)
 
        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();
 }
@@ -357,10 +354,12 @@ static void dlp_net_hangup_timer_cb(unsigned long int param)
  */
 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");
 }
@@ -384,18 +383,12 @@ int dlp_net_open(struct net_device *dev)
                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 */
@@ -407,9 +400,6 @@ int dlp_net_open(struct net_device *dev)
        EPILOG();
        return ret;
 
-unclaim:
-       dlp_hsi_port_unclaim(ch_ctx);
-
 out:
        EPILOG();
        return ret;
@@ -417,30 +407,29 @@ out:
 
 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);
@@ -452,9 +441,6 @@ int dlp_net_stop(struct net_device *dev)
 
        dlp_ctx_set_state(tx_ctx, IDLE);
 
-       /* Release the HSI port */
-       dlp_hsi_port_unclaim(ch_ctx);
-
        EPILOG();
        return 0;
 }
@@ -828,7 +814,7 @@ struct dlp_channel *dlp_net_ctx_create(unsigned int index, struct device *dev)
        if (!net_ctx->net_padd) {
                CRITICAL("No more memory to allocate padding buffer");
                ret = -ENOMEM;
-               goto out;
+               goto free_dev;
        }
 
        /* Register the net device */
@@ -842,7 +828,6 @@ struct dlp_channel *dlp_net_ctx_create(unsigned int index, struct device *dev)
        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;
@@ -851,13 +836,13 @@ struct dlp_channel *dlp_net_ctx_create(unsigned int index, struct device *dev)
        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;
@@ -873,13 +858,14 @@ struct dlp_channel *dlp_net_ctx_create(unsigned int index, struct device *dev)
                          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);
@@ -900,7 +886,8 @@ int dlp_net_ctx_delete(struct dlp_channel *ch_ctx)
        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);
@@ -909,12 +896,13 @@ int dlp_net_ctx_delete(struct dlp_channel *ch_ctx)
        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,
index 816c333..3ce11d4 100644 (file)
@@ -40,7 +40,6 @@
 #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>
 
@@ -49,7 +48,7 @@
 #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;
 };
 
 /*
@@ -82,7 +83,7 @@ static void dlp_tty_modem_hangup(struct dlp_channel *ch_ctx, int reason)
 
        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);
@@ -195,7 +196,7 @@ static void dlp_tty_wakeup(struct dlp_channel *ch_ctx)
 }
 
 /**
- * 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
  *
@@ -207,7 +208,7 @@ static void dlp_tty_wakeup(struct dlp_channel *ch_ctx)
  *  - 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;
@@ -219,22 +220,13 @@ static void dlp_tty_forward(struct tty_struct *tty,
 
        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) {
@@ -253,7 +245,7 @@ shoot_again_now:
                        tty_flag = TTY_FRAME;
 
                if (unlikely(!tty))
-                       goto recycle_pdu;
+                       goto free_pdu;
 
                while (pdu->actual_len > 0) {
 
@@ -262,7 +254,6 @@ shoot_again_now:
                                 * forwarding resume function schedule */
                                copied = 1;
                                dlp_fifo_wait_push_back(xfer_ctx, pdu);
-
                                goto no_more_tty_insert;
                        }
 
@@ -281,12 +272,11 @@ shoot_again_now:
 
                        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,
@@ -301,19 +291,12 @@ recycle_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);
 
@@ -326,6 +309,30 @@ no_more_tty_insert:
 }
 
 /**
+ * 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
@@ -336,17 +343,9 @@ static void dlp_tty_rx_forward_retry(unsigned long param)
 {
        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();
 }
 
@@ -366,10 +365,10 @@ static void dlp_tty_rx_forward_resume(struct tty_struct *tty)
        /* 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();
 }
@@ -390,7 +389,8 @@ static void dlp_tty_complete_tx(struct hsi_msg *pdu)
 
        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);
@@ -407,10 +407,10 @@ static void dlp_tty_complete_tx(struct hsi_msg *pdu)
 
        /* 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);
@@ -453,15 +453,12 @@ static void dlp_tty_complete_rx(struct hsi_msg *pdu)
 {
        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);
@@ -480,11 +477,7 @@ static void dlp_tty_complete_rx(struct hsi_msg *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();
 }
 
@@ -604,9 +597,9 @@ static int dlp_tty_port_activate(struct tty_port *port, struct tty_struct *tty)
        /* 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;
                }
        }
@@ -635,6 +628,7 @@ static void dlp_tty_port_shutdown(struct tty_port *port)
        struct dlp_tty_context *tty_ctx;
        struct dlp_xfer_ctx *tx_ctx;
        struct dlp_xfer_ctx *rx_ctx;
+       int ret;
 
        PROLOG();
 
@@ -644,10 +638,10 @@ static void dlp_tty_port_shutdown(struct tty_port *port)
        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);
@@ -665,15 +659,10 @@ static void dlp_tty_port_shutdown(struct tty_port *port)
        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();
@@ -722,7 +711,7 @@ static int dlp_tty_open(struct tty_struct *tty, struct file *filp)
        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
@@ -770,10 +759,10 @@ void dlp_tty_tx_stop(unsigned long param)
 }
 
 /**
- * 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;
 
@@ -781,31 +770,29 @@ static void dlp_tty_hangup_timer_cb(unsigned long int 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);
        }
 
@@ -1014,7 +1001,7 @@ static int dlp_tty_write_room(struct tty_struct *tty)
 {
        struct dlp_xfer_ctx *ch_ctx =
            &((struct dlp_channel *)tty->driver_data)->tx;
-       unsigned int room;
+       int room;
        unsigned long flags;
 
        PROLOG();
@@ -1039,7 +1026,7 @@ static int dlp_tty_chars_in_buffer(struct tty_struct *tty)
 {
        struct dlp_xfer_ctx *ch_ctx =
            &((struct dlp_channel *)tty->driver_data)->tx;
-       unsigned int buffered;
+       int buffered;
        unsigned long flags;
 
        PROLOG();
@@ -1393,15 +1380,15 @@ static int dlp_tty_ioctl(struct tty_struct *tty,
                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;
@@ -1489,16 +1476,15 @@ struct dlp_channel *dlp_tty_ctx_create(unsigned int index, struct device *dev)
 
        /* 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;
@@ -1510,12 +1496,11 @@ struct dlp_channel *dlp_tty_ctx_create(unsigned int index, struct device *dev)
 
        /* 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;
@@ -1523,16 +1508,14 @@ struct dlp_channel *dlp_tty_ctx_create(unsigned int index, struct device *dev)
        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;
@@ -1548,6 +1531,8 @@ struct dlp_channel *dlp_tty_ctx_create(unsigned int index, struct device *dev)
                          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;
 
@@ -1555,7 +1540,7 @@ struct dlp_channel *dlp_tty_ctx_create(unsigned int index, struct device *dev)
        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;
        }
@@ -1594,7 +1579,8 @@ int dlp_tty_ctx_delete(struct dlp_channel *ch_ctx)
 
        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);