summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilippe Skowronski <philippe.skowronski@qorvo.com>2022-12-16 15:02:03 +0100
committerVictor Liu <victorliu@google.com>2022-12-19 15:26:00 +0000
commitbcc705f6cab9b04de6c28b299b821c6e286c5e4f (patch)
treeb382475cafc666cef49ceaee01d1ef25bcb6f4c8
parente2c9ecbfbe750046b838a01c3078cfbc6a20e574 (diff)
downloaduwb-bcc705f6cab9b04de6c28b299b821c6e286c5e4f.tar.gz
Revert "Revert "[R-5.2.2] uwb: update qm35 driver to release R5.2.2""
This reverts commit 2f7d8b7fd5a922910a921a33c7c7c1a3d7483504. Change-Id: Idf3069bcb124b1f5efcaae4f011114ec2fb46ede Signed-off-by: Philippe Skowronski <philippe.skowronski@qorvo.com>
-rw-r--r--hsspi.c118
-rw-r--r--hsspi.h45
-rw-r--r--qm35-spi.c26
3 files changed, 111 insertions, 78 deletions
diff --git a/hsspi.c b/hsspi.c
index 34d5954..b5e80e7 100644
--- a/hsspi.c
+++ b/hsspi.c
@@ -44,7 +44,8 @@
#define STC_SOC_ERR BIT(4)
#define SS_READY_TIMEOUT_MS (250)
-#define INTER_CS_ACTIVE_TIME_US (10)
+
+#define MAX_SUCCESSIVE_ERRORS (5)
struct hsspi_work {
struct list_head list;
@@ -133,13 +134,26 @@ static int hsspi_wait_ss_ready(struct hsspi *hsspi)
{
int ret;
+ if (!test_bit(HSSPI_FLAGS_SS_BUSY, hsspi->flags)) {
+ /* The ss_ready went low, so the fw is not busy anymore,
+ * if the ss_ready is high, we can proceed, else,
+ * either the fw went to sleep or crashed, in any case
+ * we need to wait for it to be ready again.
+ */
+ clear_bit(HSSPI_FLAGS_SS_READY, hsspi->flags);
+ if (gpiod_get_value(hsspi->gpio_ss_rdy)) {
+ return 0;
+ }
+ }
+
ret = wait_event_interruptible_timeout(
hsspi->wq_ready,
test_and_clear_bit(HSSPI_FLAGS_SS_READY, hsspi->flags),
msecs_to_jiffies(SS_READY_TIMEOUT_MS));
if (ret == 0) {
- dev_err(&hsspi->spi->dev, "timed out waiting for ss_ready(%d)\n",
- test_bit(HSSPI_FLAGS_SS_READY, hsspi->flags));
+ dev_warn(&hsspi->spi->dev,
+ "timed out waiting for ss_ready(%d)\n",
+ test_bit(HSSPI_FLAGS_SS_READY, hsspi->flags));
return -EAGAIN;
}
if (ret < 0) {
@@ -150,35 +164,6 @@ static int hsspi_wait_ss_ready(struct hsspi *hsspi)
}
/**
- * wait_cs_active_min_interval() - waits the time necessary to respect the
- * minimium interval between two chip selects active.
- *
- * @hsspi: &struct hsspi
- */
-static void wait_cs_active_min_interval(struct hsspi *hsspi)
-{
- /* The HSSPI chip select shall not be activated too early after
- * it was deselected.
- */
- ktime_t delta = hsspi->next_cs_active_time - ktime_get();
- if (delta > 0) {
- delta = ktime_to_us(delta);
- usleep_range(delta, delta);
- }
-}
-
-/**
- * update_next_cs_interval() - update the next time the chip select
- * can be activated.
- *
- * @hsspi: &struct hsspi
- */
-static void update_next_cs_interval(struct hsspi *hsspi)
-{
- hsspi->next_cs_active_time = ktime_add(ktime_get(), INTER_CS_ACTIVE_TIME_US);
-}
-
-/**
* spi_xfer() - Single SPI transfer
*
* @hsspi: &struct hsspi
@@ -201,34 +186,26 @@ static int spi_xfer(struct hsspi *hsspi, const void *tx, void *rx,
.len = length,
},
};
- int ret, retry = 20;
+ int ret, retry = 2;
hsspi->soc->flags = 0;
hsspi->soc->ul = 0;
hsspi->soc->length = 0;
- if (hsspi->spi_error)
- return hsspi->spi_error;
-
do {
- /* The HSSPI chip select shall not be activated too early after
- * it was deselected.
- */
- wait_cs_active_min_interval(hsspi);
hsspi->wakeup_enter(hsspi);
ret = hsspi_wait_ss_ready(hsspi);
if (ret < 0) {
hsspi->wakeup_release(hsspi);
- update_next_cs_interval(hsspi);
continue;
}
if (test_sleep_after_ss_ready_us > 0)
usleep_range(test_sleep_after_ss_ready_us, test_sleep_after_ss_ready_us+1);
+ hsspi_set_spi_slave_busy(hsspi);
ret = spi_sync_transfer(hsspi->spi, xfers, length ? 2 : 1);
hsspi->wakeup_release(hsspi);
- update_next_cs_interval(hsspi);
trace_hsspi_spi_xfer(&hsspi->spi->dev, hsspi->host, hsspi->soc, ret);
@@ -238,7 +215,9 @@ static int spi_xfer(struct hsspi *hsspi, const void *tx, void *rx,
}
if (!(hsspi->soc->flags & STC_SOC_RDY)) {
- dev_err(&hsspi->spi->dev, "FW not ready (flags 0x%02x)\n", hsspi->soc->flags);
+ dev_err(&hsspi->spi->dev,
+ "FW not ready (flags %#02x)\n",
+ hsspi->soc->flags);
ret = -EAGAIN;
continue;
}
@@ -257,8 +236,10 @@ static void check_soc_flag(const struct device *dev, const char *func_name,
expected = is_tx ? 0x0 : STC_SOC_OA;
- if ((soc_flags & (STC_SOC_ERR|STC_SOC_OA)) != expected)
- dev_warn(dev, "%s: bad soc flags 0x%hhx\n", func_name, soc_flags);
+ if ((soc_flags & (STC_SOC_ERR | STC_SOC_OA)) != expected) {
+ dev_warn(dev, "%s: bad soc flags %#hhx, expected %#hhx\n",
+ func_name, soc_flags, expected);
+ }
}
/**
@@ -382,7 +363,9 @@ static int hsspi_pre_read(struct hsspi *hsspi)
static int hsspi_thread_fn(void *data)
{
struct hsspi *hsspi = data;
+ static int successive_errors;
+ successive_errors = 0;
while (1) {
struct hsspi_work *hw;
int ret;
@@ -408,7 +391,7 @@ static int hsspi_thread_fn(void *data)
} else {
dev_err(&hsspi->spi->dev,
"unknown hsspi_work type: %d\n", hw->type);
- ret = -EINVAL;
+ continue;
}
} else
/* If there is no work, we are here because
@@ -417,11 +400,24 @@ static int hsspi_thread_fn(void *data)
ret = hsspi_pre_read(hsspi);
if (ret) {
- spin_lock(&hsspi->lock);
- hsspi->state = HSSPI_ERROR;
- spin_unlock(&hsspi->lock);
+ successive_errors++;
- hsspi->spi_error = ret;
+ if (successive_errors > MAX_SUCCESSIVE_ERRORS) {
+ dev_err(&hsspi->spi->dev,
+ "Max successive errors %d reached, likely entered ROM code...\n",
+ successive_errors);
+
+ /* When the device reboots, the ROM code might raise
+ * ss_ready; if a SPI transfer is requested, the AP
+ * will initiate the SPI xfer and the ROM code will
+ * enter its command mode infinite loop...
+ * No choice but rebooting the device.
+ */
+ hsspi->reset_qm35(hsspi);
+ successive_errors = 0;
+ }
+ } else {
+ successive_errors = 0;
}
}
return 0;
@@ -436,7 +432,6 @@ int hsspi_init(struct hsspi *hsspi, struct spi_device *spi)
hsspi->state = HSSPI_STOPPED;
hsspi->spi = spi;
- hsspi->spi_error = -EAGAIN;
init_waitqueue_head(&hsspi->wq);
init_waitqueue_head(&hsspi->wq_ready);
@@ -454,6 +449,12 @@ int hsspi_init(struct hsspi *hsspi, struct spi_device *spi)
return 0;
}
+void hsspi_set_gpios(struct hsspi *hsspi, struct gpio_desc *gpio_ss_rdy, struct gpio_desc *gpio_exton)
+{
+ hsspi->gpio_ss_rdy = gpio_ss_rdy;
+ hsspi->gpio_exton = gpio_exton;
+}
+
int hsspi_deinit(struct hsspi *hsspi)
{
int i;
@@ -555,18 +556,26 @@ int hsspi_unregister(struct hsspi *hsspi, struct hsspi_layer *layer)
return 0;
}
+void hsspi_clear_spi_slave_busy(struct hsspi *hsspi)
+{
+ clear_bit(HSSPI_FLAGS_SS_BUSY, hsspi->flags);
+}
+
+void hsspi_set_spi_slave_busy(struct hsspi *hsspi)
+{
+ set_bit(HSSPI_FLAGS_SS_BUSY, hsspi->flags);
+}
+
void hsspi_set_spi_slave_ready(struct hsspi *hsspi)
{
- clear_bit(HSSPI_FLAGS_OFF, hsspi->flags);
set_bit(HSSPI_FLAGS_SS_READY, hsspi->flags);
wake_up_interruptible(&hsspi->wq_ready);
}
-void hsspi_set_spi_slave_off(struct hsspi *hsspi)
+void hsspi_clear_spi_slave_ready(struct hsspi *hsspi)
{
clear_bit(HSSPI_FLAGS_SS_READY, hsspi->flags);
- set_bit(HSSPI_FLAGS_OFF, hsspi->flags);
}
void hsspi_set_output_data_waiting(struct hsspi *hsspi)
@@ -647,7 +656,6 @@ void hsspi_start(struct hsspi *hsspi)
spin_lock(&hsspi->lock);
hsspi->state = HSSPI_RUNNING;
- hsspi->spi_error = 0;
spin_unlock(&hsspi->lock);
diff --git a/hsspi.h b/hsspi.h
index bdb32c5..5ddfeb7 100644
--- a/hsspi.h
+++ b/hsspi.h
@@ -127,7 +127,7 @@ struct hsspi_layer {
enum hsspi_flags {
HSSPI_FLAGS_SS_IRQ = 0,
HSSPI_FLAGS_SS_READY = 1,
- HSSPI_FLAGS_OFF = 2,
+ HSSPI_FLAGS_SS_BUSY = 2,
HSSPI_FLAGS_MAX = 3,
};
@@ -165,11 +165,16 @@ struct hsspi {
void (*wakeup_enter)(struct hsspi *hsspi);
void (*wakeup_release)(struct hsspi *hsspi);
+ // reset QM35
+ void (*reset_qm35)(struct hsspi *hsspi);
+
struct spi_device *spi;
- int spi_error;
struct stc_header *host, *soc;
ktime_t next_cs_active_time;
+
+ struct gpio_desc *gpio_ss_rdy;
+ struct gpio_desc *gpio_exton;
};
/**
@@ -184,6 +189,8 @@ struct hsspi {
*
*/
int hsspi_init(struct hsspi *hsspi, struct spi_device *spi);
+void hsspi_set_gpios(struct hsspi *hsspi, struct gpio_desc *gpio_ss_rdy,
+ struct gpio_desc *gpio_exton);
/**
* hsspi_deinit() - Initialiaze the HSSPI
@@ -225,28 +232,32 @@ int hsspi_unregister(struct hsspi *hsspi, struct hsspi_layer *layer);
*
* This function is called in the ss_ready irq handler. It notices the
* HSSPI driver that the QM is ready for transfer.
- *
- * The HSSPI must work with or without the ss_rdy gpio. The current
- * implementation is far from ideal regarding this requirement.
- *
- * W/o the gpio we should send SPI transfer right away and retry it if
- * the RDY bit was not present in the SOC STC header.
*/
void hsspi_set_spi_slave_ready(struct hsspi *hsspi);
/**
- * hsspi_set_spi_slave_off() - tell the hsspi that qm35 went asleep
+ * hsspi_clear_spi_slave_ready() - tell the hsspi that the ss_ready has
+ * been lowered meaning that the fw is busy or asleep,
+ * @hsspi: pointer to a &struct hsspi
+ */
+void hsspi_clear_spi_slave_ready(struct hsspi *hsspi);
+
+/**
+ * hsspi_set_spi_slave_busy() - tell the hsspi that the ss_ready has
+ * not been lowered and raised again meaning that the fw is busy,
+ * @hsspi: pointer to a &struct hsspi
+ */
+void hsspi_set_spi_slave_busy(struct hsspi *hsspi);
+
+/**
+ * hsspi_clear_spi_slave_busy() - tell the hsspi that the ss_ready has
+ * been lowered and raised again meaning that the fw is not busy anymore,
* @hsspi: pointer to a &struct hsspi
*
- * This function is called in the exton irq handler. It notices the
- * HSSPI driver that the QM went asleep.
- *
- * The HSSPI must work with or without the exton gpio but the sleep
- * states on the qm35 would be affected.
- *
- * W/o the gpio we will ignore the fact that qm35 might have gone asleep.
+ * This function is called in the ss_ready irq handler. It notices the
+ * HSSPI driver that the QM has acknowledged the last SPI xfer.
*/
-void hsspi_set_spi_slave_off(struct hsspi *hsspi);
+void hsspi_clear_spi_slave_busy(struct hsspi *hsspi);
/**
* hsspi_set_output_data_waiting() - tell the hsspi that the ss_irq is active
diff --git a/qm35-spi.c b/qm35-spi.c
index c5e30b8..6d26ac0 100644
--- a/qm35-spi.c
+++ b/qm35-spi.c
@@ -97,6 +97,10 @@ int qmrom_retries = QMROM_RETRIES;
module_param(qmrom_retries, int, 0444);
MODULE_PARM_DESC(qmrom_retries, "QMROM retries");
+int reset_on_error = 1;
+module_param(reset_on_error, int, 0444);
+MODULE_PARM_DESC(reset_on_error, "Reset the QM35 on successive errors");
+
static uint8_t qm_soc_id[ROM_SOC_ID_LEN];
static uint16_t qm_dev_id;
@@ -328,6 +332,7 @@ static irqreturn_t qm35_ss_rdy_handler(int irq, void *data)
old_time = current_time;
#endif
+ hsspi_clear_spi_slave_busy(&qm35_hdl->hsspi);
hsspi_set_spi_slave_ready(&qm35_hdl->hsspi);
return IRQ_HANDLED;
@@ -355,11 +360,21 @@ static void qm35_wakeup_release(struct hsspi *hsspi)
gpiod_set_value(qm35_hdl->gpio_wakeup, 0);
}
+static void qm35_reset_hook(struct hsspi *hsspi)
+{
+ struct qm35_ctx *qm35_hdl =
+ container_of(hsspi, struct qm35_ctx, hsspi);
+
+ if (reset_on_error)
+ qm35_reset(qm35_hdl, QM_RESET_LOW_MS);
+ usleep_range(QM_BEFORE_RESET_MS * 1000, QM_BEFORE_RESET_MS * 1000);
+}
+
static irqreturn_t qm35_exton_handler(int irq, void *data)
{
struct qm35_ctx *qm35_hdl = data;
- hsspi_set_spi_slave_off(&qm35_hdl->hsspi);
+ hsspi_clear_spi_slave_ready(&qm35_hdl->hsspi);
return IRQ_HANDLED;
}
@@ -525,6 +540,7 @@ static int hsspi_irqs_setup(struct qm35_ctx *qm35_ctx)
qm35_ctx->hsspi.odw_cleared = reenable_ss_irq;
qm35_ctx->hsspi.wakeup_enter = qm35_wakeup_enter;
qm35_ctx->hsspi.wakeup_release = qm35_wakeup_release;
+ qm35_ctx->hsspi.reset_qm35 = qm35_reset_hook;
ret = devm_request_irq(&qm35_ctx->spi->dev, qm35_ctx->spi->irq,
&qm35_irq_handler, ss_irqflags,
@@ -548,7 +564,7 @@ static int hsspi_irqs_setup(struct qm35_ctx *qm35_ctx)
return ret;
if (!gpiod_get_value(qm35_ctx->gpio_exton))
- hsspi_set_spi_slave_off(&qm35_ctx->hsspi);
+ hsspi_clear_spi_slave_ready(&qm35_ctx->hsspi);
}
/* Get spi csn */
@@ -742,12 +758,10 @@ static int qm35_probe(struct spi_device *spi)
goto log_layer_unregister;
}
+ hsspi_set_gpios(&qm35_ctx->hsspi, qm35_ctx->gpio_ss_rdy,
+ qm35_ctx->gpio_exton);
hsspi_start(&qm35_ctx->hsspi);
- /* we can have missed an edge */
- if (gpiod_get_value(qm35_ctx->gpio_ss_rdy))
- hsspi_set_spi_slave_ready(&qm35_ctx->hsspi);
-
ret = misc_register(&qm35_ctx->uci_dev);
if (ret) {
dev_err(&spi->dev, "Failed to register uci device\n");