esoc: Add support for sdxpoorwills
Add support for sdxpoorwills, using mdm9x55 data as a starting point.
Change-Id: Ic1abf2997781e84ab20270d974794ed37c20d62a
Signed-off-by: Raghavendra Rao Ananta <rananta@codeaurora.org>
diff --git a/drivers/esoc/Kconfig b/drivers/esoc/Kconfig
index 3c65f69..4613150 100644
--- a/drivers/esoc/Kconfig
+++ b/drivers/esoc/Kconfig
@@ -38,7 +38,7 @@
allow logging of different esoc driver traces.
config ESOC_MDM_4x
- bool "Add support for external mdm9x25/mdm9x35/mdm9x55"
+ bool "Add support for external modem"
help
In some Qualcomm Technologies, Inc. boards, an external modem such as
mdm9x25 or mdm9x35 is connected to a primary msm. The primary soc can
@@ -49,7 +49,7 @@
tristate "Command engine for 4x series external modems"
help
Provides a command engine to control the behavior of an external modem
- such as mdm9x25/mdm9x35/mdm9x55/QSC. Allows the primary soc to put the
+ such as mdm9x25/mdm9x35/mdm9x55/sdxpoorwills/QSC. Allows the primary soc to put the
external modem in a specific mode. Also listens for events on the
external modem.
diff --git a/drivers/esoc/esoc-mdm-4x.c b/drivers/esoc/esoc-mdm-4x.c
index 677e21d..bbec9d3 100644
--- a/drivers/esoc/esoc-mdm-4x.c
+++ b/drivers/esoc/esoc-mdm-4x.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2015, 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -794,6 +794,28 @@
mdm->gpio_state_running = NULL;
return retval;
}
+
+static void mdm_release_ipc_gpio(struct mdm_ctrl *mdm)
+{
+ int i;
+
+ if (!mdm)
+ return;
+
+ for (i = 0; i < NUM_GPIOS; ++i)
+ if (gpio_is_valid(MDM_GPIO(mdm, i)))
+ gpio_free(MDM_GPIO(mdm, i));
+}
+
+static void mdm_free_irq(struct mdm_ctrl *mdm)
+{
+ if (!mdm)
+ return;
+
+ free_irq(mdm->errfatal_irq, mdm);
+ free_irq(mdm->status_irq, mdm);
+}
+
static int mdm9x25_setup_hw(struct mdm_ctrl *mdm,
const struct mdm_ops *ops,
struct platform_device *pdev)
@@ -1028,6 +1050,108 @@
return 0;
}
+static int sdxpoorwills_setup_hw(struct mdm_ctrl *mdm,
+ const struct mdm_ops *ops,
+ struct platform_device *pdev)
+{
+ int ret;
+ struct device_node *node;
+ struct esoc_clink *esoc;
+ const struct esoc_clink_ops *clink_ops = ops->clink_ops;
+ const struct mdm_pon_ops *pon_ops = ops->pon_ops;
+
+ mdm->dev = &pdev->dev;
+ mdm->pon_ops = pon_ops;
+ node = pdev->dev.of_node;
+
+ esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL);
+ if (IS_ERR_OR_NULL(esoc)) {
+ dev_err(mdm->dev, "cannot allocate esoc device\n");
+ return PTR_ERR(esoc);
+ }
+
+ esoc->pdev = pdev;
+
+ mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
+ if (!mdm->mdm_queue) {
+ dev_err(mdm->dev, "could not create mdm_queue\n");
+ return -ENOMEM;
+ }
+
+ mdm->irq_mask = 0;
+ mdm->ready = false;
+
+ ret = mdm_dt_parse_gpios(mdm);
+ if (ret) {
+ dev_err(mdm->dev, "Failed to parse DT gpios\n");
+ goto err_destroy_wrkq;
+ }
+
+ ret = mdm_pon_dt_init(mdm);
+ if (ret) {
+ dev_err(mdm->dev, "Failed to parse PON DT gpio\n");
+ goto err_destroy_wrkq;
+ }
+
+ ret = mdm_pinctrl_init(mdm);
+ if (ret) {
+ dev_err(mdm->dev, "Failed to init pinctrl\n");
+ goto err_destroy_wrkq;
+ }
+
+ ret = mdm_pon_setup(mdm);
+ if (ret) {
+ dev_err(mdm->dev, "Failed to setup PON\n");
+ goto err_destroy_wrkq;
+ }
+
+ ret = mdm_configure_ipc(mdm, pdev);
+ if (ret) {
+ dev_err(mdm->dev, "Failed to configure the ipc\n");
+ goto err_release_ipc;
+ }
+
+ esoc->name = SDXPOORWILLS_LABEL;
+ esoc->link_name = SDXPOORWILLS_PCIE;
+
+ ret = of_property_read_string(node, "qcom,mdm-link-info",
+ &esoc->link_info);
+ if (ret)
+ dev_info(mdm->dev, "esoc link info missing\n");
+
+ esoc->clink_ops = clink_ops;
+ esoc->parent = mdm->dev;
+ esoc->owner = THIS_MODULE;
+ esoc->np = pdev->dev.of_node;
+ set_esoc_clink_data(esoc, mdm);
+
+ ret = esoc_clink_register(esoc);
+ if (ret) {
+ dev_err(mdm->dev, "esoc registration failed\n");
+ goto err_free_irq;
+ }
+ dev_dbg(mdm->dev, "esoc registration done\n");
+
+ init_completion(&mdm->debug_done);
+ INIT_WORK(&mdm->mdm_status_work, mdm_status_fn);
+ INIT_WORK(&mdm->restart_reason_work, mdm_get_restart_reason);
+ INIT_DELAYED_WORK(&mdm->mdm2ap_status_check_work, mdm2ap_status_check);
+ mdm->get_restart_reason = false;
+ mdm->debug_fail = false;
+ mdm->esoc = esoc;
+ mdm->init = 0;
+
+ return 0;
+
+err_free_irq:
+ mdm_free_irq(mdm);
+err_release_ipc:
+ mdm_release_ipc_gpio(mdm);
+err_destroy_wrkq:
+ destroy_workqueue(mdm->mdm_queue);
+ return ret;
+}
+
static struct esoc_clink_ops mdm_cops = {
.cmd_exe = mdm_cmd_exe,
.get_status = mdm_get_status,
@@ -1053,6 +1177,12 @@
.pon_ops = &mdm9x55_pon_ops,
};
+static struct mdm_ops sdxpoorwills_ops = {
+ .clink_ops = &mdm_cops,
+ .config_hw = sdxpoorwills_setup_hw,
+ .pon_ops = &sdxpoorwills_pon_ops,
+};
+
static const struct of_device_id mdm_dt_match[] = {
{ .compatible = "qcom,ext-mdm9x25",
.data = &mdm9x25_ops, },
@@ -1060,6 +1190,8 @@
.data = &mdm9x35_ops, },
{ .compatible = "qcom,ext-mdm9x55",
.data = &mdm9x55_ops, },
+ { .compatible = "qcom,ext-sdxpoorwills",
+ .data = &sdxpoorwills_ops, },
{},
};
MODULE_DEVICE_TABLE(of, mdm_dt_match);
diff --git a/drivers/esoc/esoc-mdm-drv.c b/drivers/esoc/esoc-mdm-drv.c
index 77ae84b..4291bbc 100644
--- a/drivers/esoc/esoc-mdm-drv.c
+++ b/drivers/esoc/esoc-mdm-drv.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -309,6 +309,10 @@
.name = "MDM9x55",
.data = NULL,
},
+ {
+ .name = "SDXPOORWILLS",
+ .data = NULL,
+ },
};
static struct esoc_drv esoc_ssr_drv = {
diff --git a/drivers/esoc/esoc-mdm-pon.c b/drivers/esoc/esoc-mdm-pon.c
index 0e85776..9624275 100644
--- a/drivers/esoc/esoc-mdm-pon.c
+++ b/drivers/esoc/esoc-mdm-pon.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2015, 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -60,6 +60,24 @@
return 0;
}
+/* This function can be called from atomic context. */
+static int sdxpoorwills_toggle_soft_reset(struct mdm_ctrl *mdm, bool atomic)
+{
+ int soft_reset_direction_assert = mdm->soft_reset_inverted;
+
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
+ soft_reset_direction_assert);
+ /*
+ * Allow PS hold assert to be detected
+ */
+ if (!atomic)
+ usleep_range(80000, 180000);
+ else
+ mdelay(100);
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
+ !soft_reset_direction_assert);
+ return 0;
+}
static int mdm4x_do_first_power_on(struct mdm_ctrl *mdm)
{
@@ -99,6 +117,7 @@
{
struct device *dev = mdm->dev;
int soft_reset_direction = mdm->soft_reset_inverted ? 1 : 0;
+
/* Assert the soft reset line whether mdm2ap_status went low or not */
gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
soft_reset_direction);
@@ -135,6 +154,27 @@
return 0;
}
+static int sdxpoorwills_power_down(struct mdm_ctrl *mdm)
+{
+ struct device *dev = mdm->dev;
+ int soft_reset_direction = mdm->soft_reset_inverted ? 1 : 0;
+
+ /* Assert the soft reset line whether mdm2ap_status went low or not */
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
+ soft_reset_direction);
+ dev_info(dev, "Doing a hard reset\n");
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
+ soft_reset_direction);
+ /*
+ * Currently, there is a debounce timer on the charm PMIC. It is
+ * necessary to hold the PMIC RESET low for 325ms
+ * for the reset to fully take place. Sleep here to ensure the
+ * reset has occurred before the function exits.
+ */
+ mdelay(325);
+ return 0;
+}
+
static void mdm4x_cold_reset(struct mdm_ctrl *mdm)
{
if (!gpio_is_valid(MDM_GPIO(mdm, AP2MDM_SOFT_RESET)))
@@ -158,6 +198,16 @@
!mdm->soft_reset_inverted);
}
+static void sdxpoorwills_cold_reset(struct mdm_ctrl *mdm)
+{
+ dev_info(mdm->dev, "Triggering mdm cold reset");
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
+ !!mdm->soft_reset_inverted);
+ mdelay(600);
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
+ !mdm->soft_reset_inverted);
+}
+
static int mdm4x_pon_dt_init(struct mdm_ctrl *mdm)
{
int val;
@@ -215,3 +265,12 @@
.dt_init = mdm4x_pon_dt_init,
.setup = mdm4x_pon_setup,
};
+
+struct mdm_pon_ops sdxpoorwills_pon_ops = {
+ .pon = mdm4x_do_first_power_on,
+ .soft_reset = sdxpoorwills_toggle_soft_reset,
+ .poff_force = sdxpoorwills_power_down,
+ .cold_reset = sdxpoorwills_cold_reset,
+ .dt_init = mdm4x_pon_dt_init,
+ .setup = mdm4x_pon_setup,
+};
diff --git a/drivers/esoc/esoc-mdm.h b/drivers/esoc/esoc-mdm.h
index 621d913..baf4e0b 100644
--- a/drivers/esoc/esoc-mdm.h
+++ b/drivers/esoc/esoc-mdm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2015, 2017-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -35,6 +35,8 @@
#define MDM9x35_HSIC "HSIC"
#define MDM9x55_LABEL "MDM9x55"
#define MDM9x55_PCIE "PCIe"
+#define SDXPOORWILLS_LABEL "SDXPOORWILLS"
+#define SDXPOORWILLS_PCIE "PCIe"
#define MDM2AP_STATUS_TIMEOUT_MS 120000L
#define MDM_MODEM_TIMEOUT 3000
#define DEF_RAMDUMP_TIMEOUT 120000
@@ -150,4 +152,5 @@
extern struct mdm_pon_ops mdm9x25_pon_ops;
extern struct mdm_pon_ops mdm9x35_pon_ops;
extern struct mdm_pon_ops mdm9x55_pon_ops;
+extern struct mdm_pon_ops sdxpoorwills_pon_ops;
#endif