diff options
author | Philippe Skowronski <philippe.skowronski@qorvo.com> | 2023-02-06 15:27:31 +0100 |
---|---|---|
committer | Victor Liu <victorliu@google.com> | 2023-02-07 17:49:17 +0000 |
commit | 59662009ebec4d82043603ded082bcf13036cbe1 (patch) | |
tree | 46e875c06d64cd13232bddbe30fbc10a0b2d8b3a | |
parent | f0e1c5d3b3ba4175d05fa62842e619c77ee37ac8 (diff) | |
download | uwb-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.c | 3 | ||||
-rw-r--r-- | hsspi.c | 3 | ||||
-rw-r--r-- | qm35-spi.c | 70 | ||||
-rw-r--r-- | qm35.h | 1 |
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); @@ -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); @@ -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) { @@ -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; }; |