summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilippe Skowronski <philippe.skowronski@qorvo.com>2023-02-06 15:27:31 +0100
committerVictor Liu <victorliu@google.com>2023-02-07 17:49:17 +0000
commit59662009ebec4d82043603ded082bcf13036cbe1 (patch)
tree46e875c06d64cd13232bddbe30fbc10a0b2d8b3a
parentf0e1c5d3b3ba4175d05fa62842e619c77ee37ac8 (diff)
downloaduwb-59662009ebec4d82043603ded082bcf13036cbe1.tar.gz
[R-6.0.3] qm35: fix kernel driver
Fixes: * fix race condition between the enabling/disabling of the regulators and the HSSPI start/stop by enabling regulators then start HSSPI, stop HSSPI then disable regulators * add support of vdd4 * assert reset line when regulators are off * add robustness for floating MISO Bug: 254111407 Bug: 266966191 Change-Id: Id285988542ffa9b57f3e8c1b165fc6b2e0635e55 Signed-off-by: Philippe Skowronski <philippe.skowronski@qorvo.com>
-rw-r--r--debug_qmrom.c3
-rw-r--r--hsspi.c3
-rw-r--r--qm35-spi.c70
-rw-r--r--qm35.h1
4 files changed, 62 insertions, 15 deletions
diff --git a/debug_qmrom.c b/debug_qmrom.c
index 9fbd6a4..8c77bd3 100644
--- a/debug_qmrom.c
+++ b/debug_qmrom.c
@@ -262,7 +262,8 @@ static ssize_t rom_flash_fw(struct file *filp, const char __user *buff,
goto end;
}
- pr_info("Flashing image %s (%pK->data %pK)...\n", filename, fw, fw->data);
+ pr_info("Flashing image %s (%pK->data %pK)...\n", filename, fw,
+ fw->data);
h = qmrom_init(&qm35_hdl->spi->dev, qm35_hdl, qm35_hdl->gpio_ss_rdy,
QMROM_RETRIES, qmrom_spi_reset_device);
diff --git a/hsspi.c b/hsspi.c
index 0be5a94..ad0d6ea 100644
--- a/hsspi.c
+++ b/hsspi.c
@@ -249,7 +249,8 @@ static int spi_xfer(struct hsspi *hsspi, const void *tx, void *rx,
continue;
}
- if (!(hsspi->soc->flags & STC_SOC_RDY)) {
+ if (!(hsspi->soc->flags & STC_SOC_RDY) ||
+ (hsspi->soc->flags == 0xff)) {
dev_err(&hsspi->spi->dev,
"FW not ready (flags %#02x)\n",
hsspi->soc->flags);
diff --git a/qm35-spi.c b/qm35-spi.c
index 45203f5..69efabf 100644
--- a/qm35-spi.c
+++ b/qm35-spi.c
@@ -57,6 +57,7 @@
#define QM35_REGULATOR_DELAY_US 1000
#define QMROM_RETRIES 10
+#define REGULATORS_ENABLED(x) (x->vdd1 || x->vdd2 || x->vdd3 || x->vdd4)
static int qm_firmware_load(struct qm35_ctx *qm35_hdl);
static void qm35_regulators_set(struct qm35_ctx *qm35_hdl, bool on);
@@ -105,6 +106,12 @@ int log_qm_traces = 1;
module_param(log_qm_traces, int, 0444);
MODULE_PARM_DESC(log_qm_traces, "Logs the QM35 traces in the kernel messages");
+bool reset_when_disabled = true;
+module_param(reset_when_disabled, bool, 0444);
+MODULE_PARM_DESC(
+ reset_when_disabled,
+ "Assert reset line when UWB is disabled (if at least one regulator is defined in DTS)");
+
static uint8_t qm_soc_id[ROM_SOC_ID_LEN];
static uint16_t qm_dev_id;
@@ -183,17 +190,23 @@ static long uci_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
qm35_hsspi_stop(qm35_hdl);
+ if (REGULATORS_ENABLED(qm35_hdl))
+ qm35_regulators_set(qm35_hdl, on);
+
/*
- * If there is no regulators, just reset the QM
+ * Always reset QM as regulators could be shared with
+ * other devices and power may not be controlled as
+ * expected
*/
- if (qm35_hdl->vdd1 || qm35_hdl->vdd2 || qm35_hdl->vdd3)
- qm35_regulators_set(qm35_hdl, on);
- else
- ret = qm35_reset(qm35_hdl, QM_RESET_LOW_MS);
+ if (!reset_when_disabled)
+ qm35_reset(qm35_hdl, QM_RESET_LOW_MS);
msleep(QM_BOOT_MS);
- qm35_hsspi_start(qm35_hdl);
+ /* if reset or power on */
+ if (!REGULATORS_ENABLED(qm35_hdl) ||
+ (REGULATORS_ENABLED(qm35_hdl) && on))
+ qm35_hsspi_start(qm35_hdl);
return 0;
}
@@ -384,6 +397,10 @@ void qm35_hsspi_start(struct qm35_ctx *qm35_hdl)
{
int irq;
+ /* nothing to do as HSSPI is already started */
+ if (qm35_hdl->hsspi.state == HSSPI_RUNNING)
+ return;
+
irq = gpiod_to_irq(qm35_hdl->gpio_ss_rdy);
if (irq >= 0) {
enable_irq(irq);
@@ -410,6 +427,10 @@ void qm35_hsspi_stop(struct qm35_ctx *qm35_hdl)
{
int irq;
+ /* nothing to do as HSSPI is already stopped */
+ if (qm35_hdl->hsspi.state == HSSPI_STOPPED)
+ return;
+
hsspi_stop(&qm35_hdl->hsspi);
irq = gpiod_to_irq(qm35_hdl->gpio_ss_rdy);
@@ -633,6 +654,14 @@ static void qm35_regulators_set(struct qm35_ctx *qm35_hdl, bool on)
if (is_enabled == on)
return;
+ if (reset_when_disabled && !on &&
+ qm35_get_state(qm35_hdl) != QM35_CTRL_STATE_RESET) {
+ if (qm35_hdl->gpio_reset) {
+ gpiod_set_value(qm35_hdl->gpio_reset, 1);
+ }
+ qm35_set_state(qm35_hdl, QM35_CTRL_STATE_RESET);
+ }
+
ret = qm35_regulator_set_one(qm35_hdl->vdd1, on);
if (ret)
dev_err(dev, str_fmt, on_str, "vdd1", ret);
@@ -645,14 +674,27 @@ static void qm35_regulators_set(struct qm35_ctx *qm35_hdl, bool on)
if (ret)
dev_err(dev, str_fmt, on_str, "vdd3", ret);
+ ret = qm35_regulator_set_one(qm35_hdl->vdd4, on);
+ if (ret)
+ dev_err(dev, str_fmt, on_str, "vdd4", ret);
+
/* wait for regulator stabilization */
usleep_range(QM35_REGULATOR_DELAY_US, QM35_REGULATOR_DELAY_US + 100);
+
+ if (reset_when_disabled && on &&
+ qm35_get_state(qm35_hdl) == QM35_CTRL_STATE_RESET) {
+ if (qm35_hdl->gpio_reset) {
+ gpiod_set_value(qm35_hdl->gpio_reset, 0);
+ }
+ qm35_set_state(qm35_hdl, QM35_CTRL_STATE_UNKNOWN);
+ }
}
static void qm35_regulators_setup_one(struct regulator **reg,
struct device *dev, const char *name)
{
- static const char *str_fmt = "failed to get %s regulator: %d\n";
+ static const char *str_fmt =
+ "%s regulator not defined in device tree: %d\n";
struct regulator *tmp;
tmp = devm_regulator_get_optional(dev, name);
@@ -670,8 +712,10 @@ static void qm35_regulators_setup(struct qm35_ctx *qm35_hdl)
qm35_regulators_setup_one(&qm35_hdl->vdd1, dev, "qm35-vdd1");
qm35_regulators_setup_one(&qm35_hdl->vdd2, dev, "qm35-vdd2");
qm35_regulators_setup_one(&qm35_hdl->vdd3, dev, "qm35-vdd3");
+ qm35_regulators_setup_one(&qm35_hdl->vdd4, dev, "qm35-vdd4");
qm35_hdl->regulators_enabled = false;
+ qm35_hdl->state = QM35_CTRL_STATE_RESET;
}
static int qm35_probe(struct spi_device *spi)
@@ -715,17 +759,12 @@ static int qm35_probe(struct spi_device *spi)
qm35_regulators_setup(qm35_ctx);
- /* power on */
- qm35_regulators_set(qm35_ctx, true);
-
uci_misc = &qm35_ctx->uci_dev;
uci_misc->minor = MISC_DYNAMIC_MINOR;
uci_misc->name = UCI_DEV_NAME;
uci_misc->fops = &uci_fops;
uci_misc->parent = &spi->dev;
- qm35_ctx->state = QM35_CTRL_STATE_UNKNOWN;
-
/* we need the debugfs root initialized here to be able
* to display the soc info populated if flash_on_probe
* is set for chips different than A0
@@ -776,14 +815,19 @@ static int qm35_probe(struct spi_device *spi)
goto log_layer_unregister;
if (flash_on_probe) {
+ qm35_regulators_set(qm35_ctx, true);
ret = qm_firmware_load(qm35_ctx);
+ qm35_regulators_set(qm35_ctx, false);
if (ret)
goto log_layer_unregister;
}
hsspi_set_gpios(&qm35_ctx->hsspi, qm35_ctx->gpio_ss_rdy,
qm35_ctx->gpio_exton);
- hsspi_start(&qm35_ctx->hsspi);
+
+ /* If regulators not available, QM is powered on */
+ if (!REGULATORS_ENABLED(qm35_ctx))
+ hsspi_start(&qm35_ctx->hsspi);
ret = misc_register(&qm35_ctx->uci_dev);
if (ret) {
diff --git a/qm35.h b/qm35.h
index beae5ab..5886270 100644
--- a/qm35.h
+++ b/qm35.h
@@ -52,6 +52,7 @@ struct qm35_ctx {
struct regulator *vdd1;
struct regulator *vdd2;
struct regulator *vdd3;
+ struct regulator *vdd4;
bool regulators_enabled;
bool log_qm_traces;
};