summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Peng <robinpeng@google.com>2022-10-22 13:46:04 +0000
committerRobin Peng <robinpeng@google.com>2022-10-22 13:46:04 +0000
commit947dd331c9066fa60b5128a79c2922fbe89ad489 (patch)
tree55bb420ba2e5f85789e6c361c033bc6eea420b1f
parent34225404d0141fe30f99d954b5b670f6480a4cde (diff)
parentc2872d0980131d7b5671d405f6ff6a7d1e9d8fd6 (diff)
downloaduwb-947dd331c9066fa60b5128a79c2922fbe89ad489.tar.gz
Merge android13-gs-pixel-5.15 into android14-gs-pixel-5.15
Bug: 236259002 Signed-off-by: Robin Peng <robinpeng@google.com> Change-Id: Id15761fc3671c9a4d6f45bbaf278e420a5bea29b
-rw-r--r--Kbuild1
-rw-r--r--debug.c120
-rw-r--r--debug.h1
-rw-r--r--hsspi.c146
-rw-r--r--hsspi.h36
-rw-r--r--hsspi_coredump.c30
-rw-r--r--hsspi_coredump.h5
-rw-r--r--hsspi_log.c6
-rw-r--r--hsspi_test.c171
-rw-r--r--hsspi_test.h39
-rw-r--r--libqmrom/include/qmrom_spi.h2
-rw-r--r--libqmrom/src/qmrom.c4
-rw-r--r--qm35-spi.c419
-rw-r--r--qm35-trace.h6
-rw-r--r--qm35.h28
-rw-r--r--qmrom_spi.c28
-rw-r--r--uci_ioctls.h2
17 files changed, 862 insertions, 182 deletions
diff --git a/Kbuild b/Kbuild
index fb1cc0f..94ff5e2 100644
--- a/Kbuild
+++ b/Kbuild
@@ -14,6 +14,7 @@ qm35-y := \
hsspi_log.o \
hsspi_coredump.o \
debug.o \
+ hsspi_test.o
qm35-$(CONFIG_EVENT_TRACING) += qm35-trace.o
CFLAGS_qm35-trace.o = -I$(srctree)/$(src)
diff --git a/debug.c b/debug.c
index 77488bd..f7f3496 100644
--- a/debug.c
+++ b/debug.c
@@ -31,12 +31,15 @@
#include <linux/fsnotify.h>
#include <qmrom.h>
+#include <qmrom_spi.h>
#include "qm35.h"
#include "debug.h"
+#include "hsspi_test.h"
static const struct file_operations debug_enable_fops;
static const struct file_operations debug_log_level_fops;
+static const struct file_operations debug_test_hsspi_sleep_fops;
static void *priv_from_file(const struct file *filp)
{
@@ -121,6 +124,18 @@ static ssize_t debug_log_level_read(struct file *filp, char __user *buff,
sizeof(log_level));
}
+static ssize_t debug_test_hsspi_sleep_write(struct file *filp, const char __user *buff,
+ size_t count, loff_t *off)
+{
+ int sleep_inter_frame_ms;
+
+ if (kstrtoint_from_user(buff, count, 10, &sleep_inter_frame_ms))
+ return -EFAULT;
+
+ hsspi_test_set_inter_frame_ms(sleep_inter_frame_ms);
+ return count;
+}
+
static ssize_t debug_traces_read(struct file *filp, char __user *buff,
size_t count, loff_t *off)
{
@@ -232,6 +247,78 @@ static ssize_t debug_coredump_read(struct file *filep, char __user *buff,
return simple_read_from_buffer(buff, count, off, cd, cd_len);
}
+static int debug_debug_certificate_open(struct inode *inodep, struct file *filep)
+{
+ struct debug *debug = priv_from_file(filep);
+
+ if (debug->certificate != NULL)
+ return -EBUSY;
+
+ debug->certificate = kmalloc(sizeof(*debug->certificate) + DEBUG_CERTIFICATE_SIZE,
+ GFP_KERNEL);
+ if (debug->certificate == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int debug_debug_certificate_close(struct inode *inodep, struct file *filep)
+{
+ struct debug *debug = priv_from_file(filep);
+ struct qm35_ctx *qm35_hdl;
+ int ret;
+ const char *operation = filep->f_pos ? "flashing" : "erasing";
+
+ qm35_hdl = container_of(debug, struct qm35_ctx, debug);
+
+ qm35_hsspi_stop(qm35_hdl);
+
+ dev_dbg(&qm35_hdl->spi->dev, "%s debug certificate (%lld bytes)\n", operation,
+ filep->f_pos);
+
+ if (filep->f_pos) {
+
+ debug->certificate->size = filep->f_pos;
+
+ } else {
+
+ /* WA: qmrom_erase_dbg_cert is not working, waiting to find the root
+ * cause, workaround is to write a zeroed certificate
+ * ret = qmrom_erase_dbg_cert(qm35_hdl->spi, qmrom_spi_reset_device, qm35_hdl);
+ */
+ debug->certificate->size = DEBUG_CERTIFICATE_SIZE;
+ memset(debug->certificate->data, 0, DEBUG_CERTIFICATE_SIZE);
+ }
+
+ ret = qmrom_flash_dbg_cert(qm35_hdl->spi, debug->certificate,
+ qmrom_spi_reset_device, qm35_hdl);
+
+ if (ret)
+ dev_err(&qm35_hdl->spi->dev, "%s debug certificate fails: %d\n", operation, ret);
+ else
+ dev_info(&qm35_hdl->spi->dev, "%s debug certificate success\n", operation);
+
+ msleep(QM_BEFORE_RESET_MS);
+ qm35_reset(qm35_hdl, QM_RESET_LOW_MS);
+ msleep(QM_BOOT_MS);
+
+ qm35_hsspi_start(qm35_hdl);
+
+ kfree(debug->certificate);
+ debug->certificate = NULL;
+
+ return 0;
+}
+
+static ssize_t debug_debug_certificate_write(struct file *filp,
+ const char __user *buff, size_t count, loff_t *off)
+{
+ struct debug *debug = priv_from_file(filp);
+
+ return simple_write_to_buffer(debug->certificate->data,
+ DEBUG_CERTIFICATE_SIZE, off, buff, count);
+}
+
static const struct file_operations debug_enable_fops = {
.owner = THIS_MODULE,
.write = debug_enable_write,
@@ -244,6 +331,11 @@ static const struct file_operations debug_log_level_fops = {
.read = debug_log_level_read
};
+static const struct file_operations debug_test_hsspi_sleep_fops = {
+ .owner = THIS_MODULE,
+ .write = debug_test_hsspi_sleep_write
+};
+
static const struct file_operations debug_traces_fops = {
.owner = THIS_MODULE,
.open = debug_traces_open,
@@ -258,6 +350,13 @@ static const struct file_operations debug_coredump_fops = {
.read = debug_coredump_read,
};
+static const struct file_operations debug_debug_certificate_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_debug_certificate_open,
+ .write = debug_debug_certificate_write,
+ .release = debug_debug_certificate_close,
+};
+
int debug_create_module_entry(struct debug *debug,
struct log_module *log_module)
{
@@ -271,7 +370,7 @@ int debug_create_module_entry(struct debug *debug,
return -1;
}
- file = debugfs_create_file("log_level", 0666, dir, log_module,
+ file = debugfs_create_file("log_level", 0644, dir, log_module,
&debug_log_level_fops);
if (!file) {
pr_err("qm35: failed to create /sys/kernel/debug/uwb0/%s/log_level\n",
@@ -330,7 +429,7 @@ int debug_init(struct debug *debug, struct dentry *root)
goto unregister;
}
- file = debugfs_create_file("enable", 0666, debug->fw_dir, debug,
+ file = debugfs_create_file("enable", 0644, debug->fw_dir, debug,
&debug_enable_fops);
if (!file) {
pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw/enable\n");
@@ -351,13 +450,28 @@ int debug_init(struct debug *debug, struct dentry *root)
goto unregister;
}
- file = debugfs_create_file("devid", S_IRUGO, debug->fw_dir, debug,
+ file = debugfs_create_file("devid", 0444, debug->fw_dir, debug,
&debug_devid_fops);
if (!file) {
pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw/devid\n");
goto unregister;
}
+ debug->certificate = NULL;
+ file = debugfs_create_file("debug_certificate", 0200, debug->fw_dir, debug,
+ &debug_debug_certificate_fops);
+ if (!file) {
+ pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw/debug_certificate\n");
+ goto unregister;
+ }
+
+ file = debugfs_create_file("test_sleep_hsspi_ms", 0200, debug->fw_dir, debug,
+ &debug_test_hsspi_sleep_fops);
+ if (!file) {
+ pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw/test_sleep_hsspi\n");
+ goto unregister;
+ }
+
return 0;
unregister:
diff --git a/debug.h b/debug.h
index c272acd..d054f92 100644
--- a/debug.h
+++ b/debug.h
@@ -61,6 +61,7 @@ struct debug {
struct wait_queue_head wq;
struct file *pv_filp;
struct mutex pv_filp_lock;
+ struct binary_chunk *certificate;
};
// TODO move this from here to a commom place for both log layer and debug
diff --git a/hsspi.c b/hsspi.c
index 4290327..b8329aa 100644
--- a/hsspi.c
+++ b/hsspi.c
@@ -27,6 +27,7 @@
*/
#include <linux/kernel.h>
+#include <linux/delay.h>
#include "qm35-trace.h"
#include "hsspi.h"
@@ -43,6 +44,7 @@
#define STC_SOC_ERR BIT(4)
#define SS_READY_TIMEOUT_MS (250)
+#define INTER_CS_ACTIVE_TIME_US (10)
struct hsspi_work {
struct list_head list;
@@ -56,6 +58,8 @@ struct hsspi_work {
};
};
+int test_sleep_after_ss_ready_us = 0;
+
static inline bool layer_id_is_valid(struct hsspi *hsspi, u8 ul)
{
return (ul < ARRAY_SIZE(hsspi->layers));
@@ -119,6 +123,62 @@ static bool is_txrx_waiting(struct hsspi *hsspi)
}
/**
+ * hsspi_wait_ss_ready() - waits for ss_ready to be up
+ *
+ * @hsspi: &struct hsspi
+ *
+ * Return: 0 if ss_ready is up and EAGAIN if not.
+ */
+static int hsspi_wait_ss_ready(struct hsspi *hsspi)
+{
+ int ret;
+
+ 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));
+ return -EAGAIN;
+ }
+ if (ret < 0) {
+ dev_err(&hsspi->spi->dev, "Error %d while waiting for ss_ready\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+/**
+ * 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
@@ -141,7 +201,7 @@ static int spi_xfer(struct hsspi *hsspi, const void *tx, void *rx,
.len = length,
},
};
- int ret;
+ int ret, retry = 3;
hsspi->soc->flags = 0;
hsspi->soc->ul = 0;
@@ -150,29 +210,57 @@ static int spi_xfer(struct hsspi *hsspi, const void *tx, void *rx,
if (hsspi->spi_error)
return hsspi->spi_error;
- 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, "not able to caught ss_ready: %d\n", ret);
- return -EAGAIN;
- }
+ 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;
+ }
- ret = spi_sync_transfer(hsspi->spi, xfers, length ? 2 : 1);
+ if (test_sleep_after_ss_ready_us > 0)
+ usleep_range(test_sleep_after_ss_ready_us, test_sleep_after_ss_ready_us+1);
- trace_hsspi_spi_xfer(&hsspi->spi->dev, hsspi->host, hsspi->soc, ret);
+ ret = spi_sync_transfer(hsspi->spi, xfers, length ? 2 : 1);
+ hsspi->wakeup_release(hsspi);
+ update_next_cs_interval(hsspi);
- if (ret)
- dev_err(&hsspi->spi->dev, "spi_sync_transfer: %d\n", ret);
- else if (!(hsspi->soc->flags & STC_SOC_RDY)) {
- ret = -EAGAIN;
- dev_err(&hsspi->spi->dev, "FW not ready\n");
- }
+ trace_hsspi_spi_xfer(&hsspi->spi->dev, hsspi->host, hsspi->soc, ret);
+
+ if (ret) {
+ dev_err(&hsspi->spi->dev, "spi_sync_transfer: %d\n", ret);
+ continue;
+ }
+
+ if (!(hsspi->soc->flags & STC_SOC_RDY)) {
+ dev_err(&hsspi->spi->dev, "FW not ready (flags 0x%02x)\n", hsspi->soc->flags);
+ ret = -EAGAIN;
+ continue;
+ }
+
+ /* All looks good! */
+ break;
+ } while ((ret == -EAGAIN) && (--retry > 0));
return ret;
}
+static void check_soc_flag(const struct device *dev, const char *func_name,
+ u8 soc_flags, bool is_tx)
+{
+ u8 expected;
+
+ 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);
+}
+
/**
* hsspi_rx() - request data from the QM35 on the HSSPI
*
@@ -205,6 +293,17 @@ static int hsspi_rx(struct hsspi *hsspi, u8 ul, u16 length)
} else
ret = spi_xfer(hsspi, NULL, NULL, 0);
+ if (ret)
+ return ret;
+
+ check_soc_flag(&hsspi->spi->dev, __func__, hsspi->soc->flags, false);
+
+ if ((hsspi->soc->ul != ul) || (hsspi->soc->length != length))
+ dev_warn(&hsspi->spi->dev,
+ "%s: received %hhu %hu but expecting %hhu %hu\n",
+ __func__, hsspi->soc->ul, hsspi->soc->length,
+ ul, length);
+
if (!(hsspi->soc->flags & STC_SOC_ODW)
&& test_and_clear_bit(HSSPI_FLAGS_SS_IRQ, hsspi->flags))
hsspi->odw_cleared(hsspi);
@@ -241,6 +340,8 @@ static int hsspi_tx(struct hsspi *hsspi, struct hsspi_layer *layer,
if (ret)
return ret;
+ check_soc_flag(&hsspi->spi->dev, __func__, hsspi->soc->flags, true);
+
if (hsspi->host->flags & STC_HOST_PRD)
return hsspi_rx(hsspi, hsspi->soc->ul, hsspi->soc->length);
@@ -268,6 +369,8 @@ static int hsspi_pre_read(struct hsspi *hsspi)
if (ret)
return ret;
+ check_soc_flag(&hsspi->spi->dev, __func__, hsspi->soc->flags, true);
+
return hsspi_rx(hsspi, hsspi->soc->ul, hsspi->soc->length);
}
@@ -454,11 +557,18 @@ int hsspi_unregister(struct hsspi *hsspi, struct hsspi_layer *layer)
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)
+{
+ clear_bit(HSSPI_FLAGS_SS_READY, hsspi->flags);
+ set_bit(HSSPI_FLAGS_OFF, hsspi->flags);
+}
+
void hsspi_set_output_data_waiting(struct hsspi *hsspi)
{
set_bit(HSSPI_FLAGS_SS_IRQ, hsspi->flags);
@@ -521,7 +631,7 @@ int hsspi_send(struct hsspi *hsspi, struct hsspi_layer *layer,
if (ret) {
kfree(tx_work);
- dev_err(&hsspi->spi->dev, "hsspi_send: %d\n", ret);
+ dev_err(&hsspi->spi->dev, "%s: %d\n", __func__, ret);
return ret;
}
diff --git a/hsspi.h b/hsspi.h
index 3ce6bc1..bdb32c5 100644
--- a/hsspi.h
+++ b/hsspi.h
@@ -29,12 +29,23 @@
#ifndef __HSSPI_H__
#define __HSSPI_H__
+#include <linux/gpio.h>
#include <linux/kthread.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/spi/spi.h>
#include <linux/wait.h>
+enum {
+ UL_RESERVED,
+ UL_BOOT_FLASH,
+ UL_UCI_APP,
+ UL_COREDUMP,
+ UL_LOG,
+ UL_TEST_HSSPI,
+ UL_MAX_IDX
+};
+
struct stc_header {
u8 flags;
u8 ul;
@@ -116,7 +127,8 @@ struct hsspi_layer {
enum hsspi_flags {
HSSPI_FLAGS_SS_IRQ = 0,
HSSPI_FLAGS_SS_READY = 1,
- HSSPI_FLAGS_MAX = 2,
+ HSSPI_FLAGS_OFF = 2,
+ HSSPI_FLAGS_MAX = 3,
};
enum hsspi_state {
@@ -138,7 +150,7 @@ enum hsspi_state {
struct hsspi {
spinlock_t lock; /* protect work_list, layers and state */
struct list_head work_list;
- struct hsspi_layer *layers[5];
+ struct hsspi_layer *layers[UL_MAX_IDX];
enum hsspi_state state;
DECLARE_BITMAP(flags, HSSPI_FLAGS_MAX);
@@ -149,10 +161,15 @@ struct hsspi {
// re-enable SS_IRQ
void (*odw_cleared)(struct hsspi *hsspi);
+ // wakeup QM35
+ void (*wakeup_enter)(struct hsspi *hsspi);
+ void (*wakeup_release)(struct hsspi *hsspi);
+
struct spi_device *spi;
int spi_error;
struct stc_header *host, *soc;
+ ktime_t next_cs_active_time;
};
/**
@@ -218,6 +235,20 @@ int hsspi_unregister(struct hsspi *hsspi, struct hsspi_layer *layer);
void hsspi_set_spi_slave_ready(struct hsspi *hsspi);
/**
+ * hsspi_set_spi_slave_off() - tell the hsspi that qm35 went asleep
+ * @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.
+ */
+void hsspi_set_spi_slave_off(struct hsspi *hsspi);
+
+/**
* hsspi_set_output_data_waiting() - tell the hsspi that the ss_irq is active
* @hsspi: pointer to a &struct hsspi
*
@@ -283,5 +314,4 @@ void hsspi_start(struct hsspi *hsspi);
*/
void hsspi_stop(struct hsspi *hsspi);
-
#endif // __HSSPI_H__
diff --git a/hsspi_coredump.c b/hsspi_coredump.c
index e797f7d..209a0e1 100644
--- a/hsspi_coredump.c
+++ b/hsspi_coredump.c
@@ -43,7 +43,7 @@ struct __packed coredump_common_hdr {
};
struct __packed coredump_hdr_ntf {
- uint16_t size;
+ uint32_t size;
uint16_t crc;
};
@@ -79,7 +79,7 @@ static int coredump_send_rcv_status(struct coredump_layer *layer, uint8_t ack)
struct coredump_rcv_status rcv;
struct qm35_ctx *qm35_hdl;
- pr_info("qm35: coredump: sent status %s\n",
+ pr_info("qm35: coredump: sending status %s\n",
layer->coredump_status == COREDUMP_RCV_ACK ? "ACK" : "NACK");
qm35_hdl = container_of(layer, struct qm35_ctx, coredump_layer);
@@ -88,9 +88,6 @@ static int coredump_send_rcv_status(struct coredump_layer *layer, uint8_t ack)
if (!p)
return -ENOMEM;
- pr_info("qm35: coredump: sent status %s\n",
- layer->coredump_status == COREDUMP_RCV_ACK ? "ACK" : "NACK");
-
hdr.cmd_id = COREDUMP_RCV_STATUS;
rcv.ack = ack;
@@ -103,7 +100,7 @@ static int coredump_send_rcv_status(struct coredump_layer *layer, uint8_t ack)
static uint16_t coredump_get_checksum(struct coredump_layer *layer)
{
- uint16_t idx = 0;
+ uint32_t idx;
uint16_t crc = 0;
for (idx = 0; idx < layer->coredump_data_wr_idx; idx++) {
@@ -121,7 +118,7 @@ static void corredump_on_expired_timer(struct timer_list *timer)
struct coredump_layer *layer =
container_of(timer, struct coredump_layer, timer);
- pr_warn("qm35: coredump receive timer expired");
+ pr_warn("qm35: coredump receive timer expired\n");
coredump_send_rcv_status(layer, layer->coredump_status);
}
@@ -150,6 +147,8 @@ static struct hsspi_block *coredump_get(struct hsspi_layer *hlayer, u16 length)
static void coredump_header_ntf_received(struct coredump_layer *layer,
struct coredump_hdr_ntf chn)
{
+ void *data;
+
pr_info("qm35: coredump: receiving coredump with len: %d and crc: 0x%x\n",
chn.size, chn.crc);
@@ -158,11 +157,12 @@ static void coredump_header_ntf_received(struct coredump_layer *layer,
layer->coredump_crc = chn.crc;
layer->coredump_status = COREDUMP_RCV_NACK;
- layer->coredump_data = krealloc(layer->coredump_data,
- layer->coredump_size,
- GFP_KERNEL);
+ data = krealloc(layer->coredump_data, layer->coredump_size,
+ GFP_KERNEL);
- if (!layer->coredump_data)
+ if (data)
+ layer->coredump_data = data;
+ else
pr_err("qm35: failed to allocate coredump mem\n");
}
@@ -231,12 +231,12 @@ static void coredump_received(struct hsspi_layer *hlayer,
break;
case COREDUMP_BODY_NTF:
- pr_info("qm35: coredump: saving coredump data with len: %d\n",
- cch_body_size);
+ pr_info("qm35: coredump: saving coredump data with len: %d [%d/%d]\n",
+ cch_body_size, layer->coredump_data_wr_idx + cch_body_size,
+ layer->coredump_size);
- if (coredump_body_ntf_received(layer, cch_body, cch_body_size)) {
+ if (coredump_body_ntf_received(layer, cch_body, cch_body_size))
break;
- }
if (layer->coredump_data_wr_idx == layer->coredump_size) {
uint16_t crc = coredump_get_checksum(layer);
diff --git a/hsspi_coredump.h b/hsspi_coredump.h
index f225dca..c7403fc 100644
--- a/hsspi_coredump.h
+++ b/hsspi_coredump.h
@@ -42,8 +42,8 @@ struct coredump_packet {
struct coredump_layer {
struct hsspi_layer hlayer;
void *coredump_data;
- uint16_t coredump_data_wr_idx;
- uint16_t coredump_size;
+ uint32_t coredump_data_wr_idx;
+ uint32_t coredump_size;
uint16_t coredump_crc;
uint8_t coredump_status;
struct timer_list timer;
@@ -53,4 +53,3 @@ int coredump_layer_init(struct coredump_layer *coredump, struct debug *debug);
void coredump_layer_deinit(struct coredump_layer *coredump);
#endif // __HSSPI_COREDUMP_H__
-
diff --git a/hsspi_log.c b/hsspi_log.c
index 617c5f6..e9575f7 100644
--- a/hsspi_log.c
+++ b/hsspi_log.c
@@ -223,7 +223,7 @@ static void log_received(struct hsspi_layer *hlayer, struct hsspi_block *blk,
qm35_hdl = container_of(layer, struct qm35_ctx, log_layer);
if (blk->length < sizeof(struct log_packet_hdr)) {
- pr_err("qm35: log packet header too small: %d bytes",
+ pr_err("qm35: log packet header too small: %d bytes\n",
blk->length);
goto out;
}
@@ -232,7 +232,7 @@ static void log_received(struct hsspi_layer *hlayer, struct hsspi_block *blk,
body = blk->data + sizeof(struct log_packet_hdr);
if (blk->length < sizeof(struct log_packet_hdr) + hdr.b_size) {
- pr_err("qm35: incomplete log packet: %d/%d bytes",
+ pr_err("qm35: incomplete log packet: %d/%d bytes\n",
blk->length, hdr.b_size);
goto out;
}
@@ -285,7 +285,7 @@ static void log_enable_set(struct debug *dbg, int enable)
qm35_hdl = container_of(dbg, struct qm35_ctx, debug);
if (qm35_hdl->log_layer.enabled) {
- pr_warn("qm35: logging already enabled");
+ pr_warn("qm35: logging already enabled\n");
return;
}
diff --git a/hsspi_test.c b/hsspi_test.c
new file mode 100644
index 0000000..598157e
--- /dev/null
+++ b/hsspi_test.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * This file is part of the QM35 UCI stack for linux.
+ *
+ * Copyright (c) 2021 Qorvo US, Inc.
+ *
+ * This software is provided under the GNU General Public License, version 2
+ * (GPLv2), as well as under a Qorvo commercial license.
+ *
+ * You may choose to use this software under the terms of the GPLv2 License,
+ * version 2 ("GPLv2"), as published by the Free Software Foundation.
+ * You should have received a copy of the GPLv2 along with this program. If
+ * not, see <http://www.gnu.org/licenses/>.
+ *
+ * This program is distributed under the GPLv2 in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GPLv2 for more
+ * details.
+ *
+ * If you cannot meet the requirements of the GPLv2, you may not use this
+ * software for any purpose without first obtaining a commercial license from
+ * Qorvo.
+ * Please contact Qorvo to inquire about licensing terms.
+ *
+ * QM35 UCI layer HSSPI Protocol
+ */
+
+#include "hsspi_test.h"
+#include "hsspi_uci.h"
+#include <linux/printk.h>
+#include <linux/delay.h>
+
+int hsspi_test_registered(struct hsspi_layer *upper_layer);
+void hsspi_test_unregistered(struct hsspi_layer *upper_layer);
+struct hsspi_block *hsspi_test_get(struct hsspi_layer *upper_layer, u16 length);
+void hsspi_test_received(struct hsspi_layer *upper_layer,
+ struct hsspi_block *blk, int status);
+void hsspi_test_sent(struct hsspi_layer *upper_layer,
+ struct hsspi_block *blk, int status);
+
+struct hsspi_layer_ops test_hsspi_layer_ops = {
+ .registered = hsspi_test_registered,
+ .unregistered = hsspi_test_unregistered,
+ .get = hsspi_test_get,
+ .received = hsspi_test_received,
+ .sent = hsspi_test_sent,
+};
+
+struct hsspi_layer test_hsspi_layer = {
+ .name = "hsspi_test_layer",
+ .id = UL_TEST_HSSPI,
+ .ops = &test_hsspi_layer_ops,
+};
+
+static struct hsspi *ghsspi;
+int sleep_inter_frame_ms;
+extern int test_sleep_after_ss_ready_us;
+
+int hsspi_test_init(struct hsspi *hsspi)
+{
+ ghsspi = hsspi;
+ return hsspi_register(hsspi, &test_hsspi_layer);
+}
+
+void hsspi_test_deinit(struct hsspi *hsspi)
+{
+ hsspi_unregister(hsspi, &test_hsspi_layer);
+}
+
+int hsspi_test_registered(struct hsspi_layer *upper_layer)
+{
+ return 0;
+}
+
+void hsspi_test_unregistered(struct hsspi_layer *upper_layer)
+{}
+
+static int check_rx(const u8 *rx, int len)
+{
+ int idx = 0, err = 0;
+ for ( ; idx < len ; idx++) {
+ if (rx[idx] != (idx & 0xff)) {
+ pr_err("hsspi test: check_rx rx[%u] != %u\n",
+ idx, rx[idx]);
+ print_hex_dump(KERN_DEBUG, "rx:", DUMP_PREFIX_ADDRESS,
+ 16, 1, rx, len, false);
+ err = -5963;
+ break;
+ }
+ }
+ return err;
+}
+
+struct hsspi_block *hsspi_test_get(struct hsspi_layer *layer, u16 length)
+{
+ struct hsspi_block *blk = kzalloc(sizeof(*blk) + length, GFP_KERNEL);
+ if (blk) {
+ blk->data = blk + 1;
+ blk->size = length;
+ blk->length = length;
+ }
+ return blk;
+}
+
+void hsspi_test_set_inter_frame_ms(int ms)
+{
+ sleep_inter_frame_ms = ms;
+}
+
+void hsspi_test_received(struct hsspi_layer *layer,
+ struct hsspi_block *blk, int status)
+{
+ static uint64_t bytes, msgs, errors, bytes0, msgs0, errors0;
+ static time64_t last_perf_dump;
+ int error = check_rx(blk->data, blk->length) ? 1 : 0;
+ time64_t now;
+ errors += error;
+
+ if (!last_perf_dump) {
+ last_perf_dump = ktime_get_seconds();
+ }
+ now = ktime_get_seconds();
+
+ /* inject latencies between each message and between the check
+ * of ss-ready and the xfer.
+ * The test is expected to fail if
+ * sleep_inter_frame_ms > CONFIG_PM_RET_SLEEP_DELAY_US
+ */
+ if (sleep_inter_frame_ms > 0) {
+ static int delay_us = 0;
+ test_sleep_after_ss_ready_us = sleep_inter_frame_ms * 1000 - delay_us;
+ usleep_range(delay_us, delay_us + 1);
+ delay_us += 100;
+ if (delay_us > sleep_inter_frame_ms * 1000)
+ delay_us = 0;
+ } else {
+ test_sleep_after_ss_ready_us = 0;
+ }
+ bytes += blk->length;
+ msgs++;
+ error |= hsspi_send(ghsspi, layer, blk);
+ if (error || ((msgs % 100) == 0))
+ pr_info("hsspi test: bytes received %llu, msgs %llu, errors %llu\n",
+ bytes, msgs, errors);
+ if (now > last_perf_dump) {
+ uint64_t dbytes = bytes >= bytes0 ? bytes - bytes0 : ~0ULL - bytes0 + bytes;
+ uint64_t dmsgs = msgs >= msgs0 ? msgs - msgs0 : ~0ULL - msgs0 + msgs;
+ uint64_t derrors = errors >= errors0 ? errors - errors0 : ~0ULL - errors0 + errors;
+ pr_info("hsspi test perfs: %llu B/s, %llu msgs/s, %llu errors/s\n",
+ dbytes / (now - last_perf_dump), dmsgs / (now - last_perf_dump),
+ derrors / (now - last_perf_dump));
+ bytes0 = bytes;
+ msgs0 = msgs;
+ errors0 = errors;
+ last_perf_dump = now;
+ }
+}
+
+void hsspi_test_sent(struct hsspi_layer *layer,
+ struct hsspi_block *blk, int status)
+{
+ static uint64_t bytes, msgs, errors;
+ errors += status ? 1 : 0;
+ msgs++;
+ bytes += blk->length;
+ if (status || ((msgs % 100) == 0))
+ pr_info("hsspi test: bytes sent %llu, msgs %llu, errors %llu\n",
+ bytes, msgs, errors);
+ kfree(blk);
+}
diff --git a/hsspi_test.h b/hsspi_test.h
new file mode 100644
index 0000000..3a8eefc
--- /dev/null
+++ b/hsspi_test.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * This file is part of the QM35 UCI stack for linux.
+ *
+ * Copyright (c) 2022 Qorvo US, Inc.
+ *
+ * This software is provided under the GNU General Public License, version 2
+ * (GPLv2), as well as under a Qorvo commercial license.
+ *
+ * You may choose to use this software under the terms of the GPLv2 License,
+ * version 2 ("GPLv2"), as published by the Free Software Foundation.
+ * You should have received a copy of the GPLv2 along with this program. If
+ * not, see <http://www.gnu.org/licenses/>.
+ *
+ * This program is distributed under the GPLv2 in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GPLv2 for more
+ * details.
+ *
+ * If you cannot meet the requirements of the GPLv2, you may not use this
+ * software for any purpose without first obtaining a commercial license from
+ * Qorvo.
+ * Please contact Qorvo to inquire about licensing terms.
+ *
+ * QM35 Ringbuffer
+ */
+
+#ifndef __HSSPI_TEST_H___
+#define __HSSPI_TEST_H___
+
+#include "hsspi.h"
+
+int hsspi_test_init(struct hsspi *hsspi);
+void hsspi_test_deinit(struct hsspi *hsspi);
+
+void hsspi_test_set_inter_frame_ms(int ms);
+
+#endif /* __HSSPI_TEST_H___ */ \ No newline at end of file
diff --git a/libqmrom/include/qmrom_spi.h b/libqmrom/include/qmrom_spi.h
index 3c3d4b3..35cce45 100644
--- a/libqmrom/include/qmrom_spi.h
+++ b/libqmrom/include/qmrom_spi.h
@@ -33,6 +33,8 @@ struct firmware {
#define SPI_ERR_GPIO_WRITE_CMD_INCOMPLETE SPI_ERR_BASE - 5
#define SPI_ERR_GPIO_READ_CMD_INCOMPLETE SPI_ERR_BASE - 6
#define SPI_ERR_READY_LINE_TIMEOUT SPI_ERR_BASE - 7
+#define SPI_ERR_WRITE_INCOMPLETE SPI_ERR_BASE - 8
+#define SPI_ERR_RW_INCOMPLETE SPI_ERR_BASE - 9
/*Make sure that the error ranges don't overlap */
#define SPI_ERR_LIB_BASE (SPI_ERR_BASE - 500)
diff --git a/libqmrom/src/qmrom.c b/libqmrom/src/qmrom.c
index 40deebd..1962434 100644
--- a/libqmrom/src/qmrom.c
+++ b/libqmrom/src/qmrom.c
@@ -840,7 +840,7 @@ int qmrom_reboot_bootloader(void *spi_handle,
void *reset_handle)
{
int rc;
- rc = qmrom_spi_set_cs_level(spi_handle, 1);
+ rc = qmrom_spi_set_cs_level(spi_handle, 0);
if (rc) {
LOG_ERR("%s: spi_set_cs_level(0) failed with %d\n", __func__,
rc);
@@ -852,7 +852,7 @@ int qmrom_reboot_bootloader(void *spi_handle,
qmrom_msleep(SPI_RST_LOW_DELAY_MS);
- rc = qmrom_spi_set_cs_level(spi_handle, 0);
+ rc = qmrom_spi_set_cs_level(spi_handle, 1);
if (rc) {
LOG_ERR("%s: spi_set_cs_level(1) failed with %d\n", __func__,
rc);
diff --git a/qm35-spi.c b/qm35-spi.c
index ce85e5b..97afd0c 100644
--- a/qm35-spi.c
+++ b/qm35-spi.c
@@ -39,6 +39,7 @@
#include <linux/uaccess.h>
#include <linux/firmware.h>
#include <linux/completion.h>
+#include <linux/regulator/consumer.h>
#ifdef CONFIG_QM35_DEBOUNCE_TIME_US
#include <linux/ktime.h>
#endif
@@ -52,14 +53,12 @@
#include "uci_ioctls.h"
#include "hsspi.h"
#include "hsspi_uci.h"
+#include "hsspi_test.h"
-#define QM_RESET_LOW_MS 2
-/*
- * value found using a SALAE
- */
-#define QM_BOOT_MS 450
+#define QM35_REGULATOR_DELAY_US 1000
static int qm_firmware_load(struct qm35_ctx *qm35_hdl);
+static void qm35_regulators_set(struct qm35_ctx *qm35_hdl, bool on);
static const struct file_operations uci_fops;
@@ -69,11 +68,7 @@ static const struct of_device_id qm35_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, qm35_dt_ids);
-static bool use_ss_ready = true;
-module_param(use_ss_ready, bool, 0444);
-MODULE_PARM_DESC(use_ss_ready, "Enable/disable use of ss_ready gpio");
-
-static bool flash_on_probe = true;
+static bool flash_on_probe = false;
module_param(flash_on_probe, bool, 0444);
MODULE_PARM_DESC(flash_on_probe, "Flash during the module probe");
@@ -81,6 +76,18 @@ static int spi_speed_hz;
module_param(spi_speed_hz, int, 0444);
MODULE_PARM_DESC(spi_speed_hz, "SPI speed (if not set use DTS's one)");
+static char *fwname = NULL;
+module_param(fwname, charp, 0444);
+MODULE_PARM_DESC(fwname, "Use fwname as firmware binary to flash QM35");
+
+static bool wake_use_wakeup = true;
+module_param(wake_use_wakeup, bool, 0444);
+MODULE_PARM_DESC(wake_use_wakeup, "Use wakeup pin to wake up QM35");
+
+static bool wake_use_csn = false;
+module_param(wake_use_csn, bool, 0444);
+MODULE_PARM_DESC(wake_use_csn, "Use HSSPI CSn pin to wake up QM35");
+
static uint8_t qm_soc_id[ROM_SOC_ID_LEN];
/*
@@ -116,32 +123,13 @@ static long uci_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
}
case QM35_CTRL_RESET:
{
- int irq;
-
- irq = (use_ss_ready && qm35_hdl->gpio_ss_rdy) ?
- gpiod_to_irq(qm35_hdl->gpio_ss_rdy) : -1;
-
- hsspi_stop(&qm35_hdl->hsspi);
-
- if (irq != -1) {
- disable_irq_nosync(irq);
-
- clear_bit(HSSPI_FLAGS_SS_READY, qm35_hdl->hsspi.flags);
- }
+ qm35_hsspi_stop(qm35_hdl);
ret = qm35_reset(qm35_hdl, QM_RESET_LOW_MS);
msleep(QM_BOOT_MS);
- if (irq != -1) {
- enable_irq(irq);
-#if 0
- if (gpiod_get_value(qm35_hdl->gpio_ss_rdy))
- hsspi_set_spi_slave_ready(&qm35_hdl->hsspi);
-#endif
- }
-
- hsspi_start(&qm35_hdl->hsspi);
+ qm35_hsspi_start(qm35_hdl);
if (ret)
return ret;
@@ -151,28 +139,13 @@ static long uci_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
}
case QM35_CTRL_FW_UPLOAD:
{
- int irq;
-
- irq = (use_ss_ready && qm35_hdl->gpio_ss_rdy) ?
- gpiod_to_irq(qm35_hdl->gpio_ss_rdy) : -1;
-
- hsspi_stop(&qm35_hdl->hsspi);
-
- if (irq != -1) {
- disable_irq_nosync(irq);
- clear_bit(HSSPI_FLAGS_SS_READY, qm35_hdl->hsspi.flags);
- }
+ qm35_hsspi_stop(qm35_hdl);
ret = qm_firmware_load(qm35_hdl);
- msleep(QM_BOOT_MS);
- if (irq != -1) {
- enable_irq(irq);
- if (gpiod_get_value(qm35_hdl->gpio_ss_rdy))
- hsspi_set_spi_slave_ready(&qm35_hdl->hsspi);
- }
+ msleep(QM_BOOT_MS);
- hsspi_start(&qm35_hdl->hsspi);
+ qm35_hsspi_start(qm35_hdl);
if (ret)
return ret;
@@ -180,6 +153,30 @@ static long uci_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
return copy_to_user(argp, &qm35_hdl->state,
sizeof(qm35_hdl->state)) ? -EFAULT : 0;
}
+ case QM35_CTRL_POWER:
+ {
+ unsigned int on;
+
+ ret = get_user(on, (unsigned int __user *)argp);
+ if (ret)
+ return ret;
+
+ qm35_hsspi_stop(qm35_hdl);
+
+ /*
+ * If there is no regulators, just reset the QM
+ */
+ 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);
+
+ msleep(QM_BOOT_MS);
+
+ qm35_hsspi_start(qm35_hdl);
+
+ return 0;
+ }
default:
dev_err(&qm35_hdl->spi->dev, "unknown ioctl %x to %s device\n",
cmd, qm35_hdl->uci_dev.name);
@@ -301,14 +298,8 @@ static void reenable_ss_irq(struct hsspi *hsspi)
{
struct qm35_ctx *qm35_hdl =
container_of(hsspi, struct qm35_ctx, hsspi);
- int irq;
-
- if (qm35_hdl->gpio_ss_irq)
- irq = gpiod_to_irq(qm35_hdl->gpio_ss_irq);
- else
- irq = qm35_hdl->spi->irq;
- enable_irq(irq);
+ enable_irq(qm35_hdl->spi->irq);
}
static irqreturn_t qm35_ss_rdy_handler(int irq, void *data)
@@ -332,20 +323,92 @@ static irqreturn_t qm35_ss_rdy_handler(int irq, void *data)
return IRQ_HANDLED;
}
+static void qm35_wakeup_enter(struct hsspi *hsspi)
+{
+ struct qm35_ctx *qm35_hdl =
+ container_of(hsspi, struct qm35_ctx, hsspi);
+
+ if (wake_use_csn)
+ gpiod_set_value(qm35_hdl->gpio_csn, 1);
+ if (wake_use_wakeup)
+ gpiod_set_value(qm35_hdl->gpio_wakeup, 1);
+}
+
+static void qm35_wakeup_release(struct hsspi *hsspi)
+{
+ struct qm35_ctx *qm35_hdl =
+ container_of(hsspi, struct qm35_ctx, hsspi);
+
+ if (wake_use_csn)
+ gpiod_set_value(qm35_hdl->gpio_csn, 0);
+ if (wake_use_wakeup)
+ gpiod_set_value(qm35_hdl->gpio_wakeup, 0);
+}
+
+static irqreturn_t qm35_exton_handler(int irq, void *data)
+{
+ struct qm35_ctx *qm35_hdl = data;
+
+ hsspi_set_spi_slave_off(&qm35_hdl->hsspi);
+ return IRQ_HANDLED;
+}
+
+void qm35_hsspi_start(struct qm35_ctx *qm35_hdl)
+{
+ int irq;
+
+ irq = gpiod_to_irq(qm35_hdl->gpio_ss_rdy);
+ if (irq >= 0) {
+ enable_irq(irq);
+#ifdef CONFIG_QM35_RISING_IRQ_NOT_TRIGGERED
+ /* Some IRQ controller will trigger a rising edge if
+ * the gpio is high when enabling the IRQ, some will
+ * not (RPI board for example). In the second case we
+ * can miss an event depending on if the level rise
+ * before or after enable_irq. Besides, the handler
+ * can also run *after* hsspi_start, breaking the
+ * hsspi thread with a false information. So let's
+ * sleep and force the SS_READY bit after.
+ */
+
+ if (gpiod_get_value(qm35_hdl->gpio_ss_rdy))
+ hsspi_set_spi_slave_ready(&qm35_hdl->hsspi);
+#endif
+ }
+
+ hsspi_start(&qm35_hdl->hsspi);
+
+}
+
+void qm35_hsspi_stop(struct qm35_ctx *qm35_hdl)
+{
+ int irq;
+
+ hsspi_stop(&qm35_hdl->hsspi);
+
+ irq = gpiod_to_irq(qm35_hdl->gpio_ss_rdy);
+ if (irq >= 0) {
+ disable_irq_nosync(irq);
+
+ clear_bit(HSSPI_FLAGS_SS_READY, qm35_hdl->hsspi.flags);
+ }
+}
+
static int qm_firmware_load(struct qm35_ctx *qm35_hdl)
{
struct spi_device *spi = qm35_hdl->spi;
unsigned int state = qm35_get_state(qm35_hdl);
int ret;
uint8_t uuid[ROM_UUID_LEN];
- uint8_t lcs_state = 0;
+ uint8_t lcs_state = CC_BSV_SECURE_LCS;
qm35_set_state(qm35_hdl, QM35_CTRL_STATE_FW_DOWNLOADING);
qmrom_set_log_device(&spi->dev, LOG_WARN);
dev_info(&spi->dev, "Get device info\n");
- ret = qmrom_get_soc_info(&spi->dev, qmrom_spi_reset_device, qm35_hdl, qm_soc_id, uuid, &lcs_state);
+ ret = qmrom_get_soc_info(&spi->dev, qmrom_spi_reset_device, qm35_hdl,
+ qm_soc_id, uuid, &lcs_state);
if (!ret) {
dev_info(&spi->dev, " devid: %*phN\n", ROM_SOC_ID_LEN, qm_soc_id);
dev_info(&spi->dev, " uuid: %*phN\n", ROM_UUID_LEN, uuid);
@@ -353,8 +416,9 @@ static int qm_firmware_load(struct qm35_ctx *qm35_hdl)
}
dev_info(&spi->dev, "Starting device flashing!\n");
- ret = qmrom_download_fw(spi, qmrom_spi_get_firmware, qmrom_spi_release_firmware,
- qmrom_spi_reset_device, qm35_hdl, 0, lcs_state);
+ ret = qmrom_download_fw(spi, qmrom_spi_get_firmware,
+ qmrom_spi_release_firmware,
+ qmrom_spi_reset_device, qm35_hdl, 0, lcs_state);
if (ret)
dev_err(&spi->dev, "Firmware download failed!\n");
@@ -386,38 +450,30 @@ int qm_get_soc_id(struct qm35_ctx *qm35_hdl, uint8_t *soc_id)
* SS_READY
* --------
*
- * If `ss-ready-gpios` exists in the DTS and `use_ss_ready` module
- * parameter is true, it is used. Otherwise, the driver will retries
- * transfers without the RDY bit.
+ * The `ss-ready-gpios` is mandatory. It is used by the FW to signal
+ * the driver that it can handle a SPI transaction.
*
* Return: 0 if no error, -errno otherwise
*/
static int hsspi_irqs_setup(struct qm35_ctx *qm35_ctx)
{
int ret;
- unsigned int ss_irq;
unsigned long ss_irqflags;
/* Get READY GPIO */
- qm35_ctx->gpio_ss_rdy = devm_gpiod_get_optional(
- &qm35_ctx->spi->dev, "ss-ready", GPIOD_IN);
+ qm35_ctx->gpio_ss_rdy = devm_gpiod_get(&qm35_ctx->spi->dev, "ss-ready", GPIOD_IN);
+ if (IS_ERR(qm35_ctx->gpio_ss_rdy))
+ return PTR_ERR(qm35_ctx->gpio_ss_rdy);
- if (use_ss_ready && qm35_ctx->gpio_ss_rdy) {
- if (IS_ERR(qm35_ctx->gpio_ss_rdy))
- return PTR_ERR(qm35_ctx->gpio_ss_rdy);
-
- ret = devm_request_irq(&qm35_ctx->spi->dev,
- gpiod_to_irq(qm35_ctx->gpio_ss_rdy),
- &qm35_ss_rdy_handler,
- IRQF_TRIGGER_RISING,
- "hsspi-ss-rdy", qm35_ctx);
- if (ret)
- return ret;
+ ret = devm_request_irq(&qm35_ctx->spi->dev, gpiod_to_irq(qm35_ctx->gpio_ss_rdy),
+ &qm35_ss_rdy_handler, IRQF_TRIGGER_RISING,
+ "hsspi-ss-rdy", qm35_ctx);
+ if (ret)
+ return ret;
- /* we can have miss an edge */
- if (gpiod_get_value(qm35_ctx->gpio_ss_rdy))
- hsspi_set_spi_slave_ready(&qm35_ctx->hsspi);
- }
+ /* we can have miss an edge */
+ if (gpiod_get_value(qm35_ctx->gpio_ss_rdy))
+ hsspi_set_spi_slave_ready(&qm35_ctx->hsspi);
/* get SS_IRQ GPIO */
qm35_ctx->gpio_ss_irq = devm_gpiod_get_optional(
@@ -427,30 +483,138 @@ static int hsspi_irqs_setup(struct qm35_ctx *qm35_ctx)
if (IS_ERR(qm35_ctx->gpio_ss_irq))
return PTR_ERR(qm35_ctx->gpio_ss_irq);
- ss_irq = gpiod_to_irq(qm35_ctx->gpio_ss_irq);
+ qm35_ctx->spi->irq = gpiod_to_irq(qm35_ctx->gpio_ss_irq);
ss_irqflags = IRQF_TRIGGER_HIGH;
} else {
- ss_irq = qm35_ctx->spi->irq;
- ss_irqflags = irq_get_trigger_type(ss_irq);
+ ss_irqflags = irq_get_trigger_type(qm35_ctx->spi->irq);
}
qm35_ctx->hsspi.odw_cleared = reenable_ss_irq;
+ qm35_ctx->hsspi.wakeup_enter = qm35_wakeup_enter;
+ qm35_ctx->hsspi.wakeup_release = qm35_wakeup_release;
- ret = devm_request_irq(&qm35_ctx->spi->dev, ss_irq,
+ ret = devm_request_irq(&qm35_ctx->spi->dev, qm35_ctx->spi->irq,
&qm35_irq_handler, ss_irqflags,
"hsspi-ss-irq", qm35_ctx);
if (ret)
return ret;
+ /* Get exton */
+ qm35_ctx->gpio_exton = devm_gpiod_get_optional(
+ &qm35_ctx->spi->dev, "exton", GPIOD_IN);
+ if (qm35_ctx->gpio_exton) {
+ if (IS_ERR(qm35_ctx->gpio_exton))
+ return PTR_ERR(qm35_ctx->gpio_exton);
+
+ ret = devm_request_irq(&qm35_ctx->spi->dev,
+ gpiod_to_irq(qm35_ctx->gpio_exton),
+ &qm35_exton_handler,
+ IRQF_TRIGGER_FALLING,
+ "hsspi-exton", qm35_ctx);
+ if (ret)
+ return ret;
+
+ if (!gpiod_get_value(qm35_ctx->gpio_exton))
+ hsspi_set_spi_slave_off(&qm35_ctx->hsspi);
+ }
+
+ /* Get spi csn */
+ if (wake_use_csn) {
+ qm35_ctx->gpio_csn = devm_gpiod_get(
+ &qm35_ctx->spi->dev, "csn", GPIOD_OUT_HIGH);
+ if (IS_ERR(qm35_ctx->gpio_csn))
+ return PTR_ERR(qm35_ctx->gpio_csn);
+ }
+
+ /* Get wakeup */
+ if (wake_use_wakeup) {
+ qm35_ctx->gpio_wakeup = devm_gpiod_get(
+ &qm35_ctx->spi->dev, "wakeup", GPIOD_OUT_LOW);
+ if (IS_ERR(qm35_ctx->gpio_wakeup))
+ return PTR_ERR(qm35_ctx->gpio_wakeup);
+ }
+
return 0;
}
+static int qm35_regulator_set_one(struct regulator *reg, bool on)
+{
+ if (!reg)
+ return 0;
+
+ return on ? regulator_enable(reg) : regulator_disable(reg);
+}
+
+static void qm35_regulators_set(struct qm35_ctx *qm35_hdl, bool on)
+{
+ static const char *str_fmt = "failed to %s %s regulator: %d\n";
+ struct device *dev = &qm35_hdl->spi->dev;
+ const char *on_str = on ? "enable" : "disable";
+ bool is_enabled;
+ int ret;
+
+ spin_lock(&qm35_hdl->lock);
+
+ is_enabled = qm35_hdl->regulators_enabled;
+ qm35_hdl->regulators_enabled = on;
+
+ spin_unlock(&qm35_hdl->lock);
+
+ /* nothing to do we are already in the desired state */
+ if (is_enabled == on)
+ return;
+
+ ret = qm35_regulator_set_one(qm35_hdl->vdd1, on);
+ if (ret)
+ dev_err(dev, str_fmt, on_str, "vdd1", ret);
+
+ ret = qm35_regulator_set_one(qm35_hdl->vdd2, on);
+ if (ret)
+ dev_err(dev, str_fmt, on_str, "vdd2", ret);
+
+ ret = qm35_regulator_set_one(qm35_hdl->vdd3, on);
+ if (ret)
+ dev_err(dev, str_fmt, on_str, "vdd3", ret);
+
+ /* wait for regulator stabilization */
+ usleep_range(QM35_REGULATOR_DELAY_US, QM35_REGULATOR_DELAY_US + 100);
+}
+
+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";
+ struct regulator *tmp;
+
+ tmp = devm_regulator_get_optional(dev, name);
+ if (IS_ERR(tmp)) {
+ dev_notice(dev, str_fmt, name, PTR_ERR(tmp));
+ tmp = NULL;
+ }
+ *reg = tmp;
+}
+
+static void qm35_regulators_setup(struct qm35_ctx *qm35_hdl)
+{
+ struct device *dev = &qm35_hdl->spi->dev;
+
+ 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_hdl->regulators_enabled = false;
+}
+
static int qm35_probe(struct spi_device *spi)
{
struct qm35_ctx *qm35_ctx;
struct miscdevice *uci_misc;
int ret = 0;
+ if (fwname) {
+ qmrom_set_fwname(fwname);
+ }
+
if (spi_speed_hz) {
spi->max_speed_hz = spi_speed_hz;
@@ -462,11 +626,15 @@ static int qm35_probe(struct spi_device *spi)
}
}
- qm35_ctx = devm_kzalloc(&spi->dev, sizeof(struct qm35_ctx),
- GFP_KERNEL);
+ qm35_ctx = devm_kzalloc(&spi->dev, sizeof(*qm35_ctx), GFP_KERNEL);
if (!qm35_ctx)
return -ENOMEM;
+ qm35_ctx->spi = spi;
+ spin_lock_init(&qm35_ctx->lock);
+
+ spi_set_drvdata(spi, qm35_ctx);
+
qm35_ctx->gpio_reset = devm_gpiod_get_optional(&spi->dev, "reset",
GPIOD_OUT_LOW);
if (IS_ERR(qm35_ctx->gpio_reset)) {
@@ -474,8 +642,10 @@ static int qm35_probe(struct spi_device *spi)
return ret;
}
- qm35_ctx->spi = spi;
- spin_lock_init(&qm35_ctx->lock);
+ 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;
@@ -486,24 +656,22 @@ static int qm35_probe(struct spi_device *spi)
ret = misc_register(&qm35_ctx->uci_dev);
if (ret) {
dev_err(&spi->dev, "Failed to register uci device\n");
- return ret;
+ goto poweroff;
}
dev_info(&spi->dev, "Registered: [%s] misc device\n", uci_misc->name);
qm35_ctx->state = QM35_CTRL_STATE_UNKNOWN;
- dev_set_drvdata(&spi->dev, qm35_ctx);
-
if (flash_on_probe) {
ret = qm_firmware_load(qm35_ctx);
if (ret)
- goto unregister;
+ goto misc_deregister;
}
ret = hsspi_init(&qm35_ctx->hsspi, spi);
if (ret)
- goto unregister;
+ goto misc_deregister;
ret = uci_layer_init(&qm35_ctx->uci_layer);
if (ret)
@@ -513,66 +681,83 @@ static int qm35_probe(struct spi_device *spi)
if (ret)
goto uci_layer_deinit;
+ ret = hsspi_test_init(&qm35_ctx->hsspi);
+ if (ret)
+ goto debug_deinit;
+
ret = coredump_layer_init(&qm35_ctx->coredump_layer, &qm35_ctx->debug);
if (ret)
- goto unregister;
+ goto hsspi_test_deinit;
ret = log_layer_init(&qm35_ctx->log_layer, &qm35_ctx->debug);
if (ret)
- goto debug_deinit;
+ goto coredump_layer_deinit;
ret = hsspi_register(&qm35_ctx->hsspi, &qm35_ctx->coredump_layer.hlayer);
if (ret)
- goto unregister;
+ goto log_layer_deinit;
ret = hsspi_register(&qm35_ctx->hsspi, &qm35_ctx->log_layer.hlayer);
if (ret)
- goto log_layer_deinit;
+ goto coredump_layer_unregister;
msleep(QM_BOOT_MS);
ret = hsspi_irqs_setup(qm35_ctx);
if (ret)
- goto hsspi_unregister;
+ goto log_layer_unregister;
hsspi_start(&qm35_ctx->hsspi);
dev_info(&spi->dev, "QM35 spi driver probed\n");
return 0;
-hsspi_unregister:
+log_layer_unregister:
hsspi_unregister(&qm35_ctx->hsspi, &qm35_ctx->log_layer.hlayer);
+coredump_layer_unregister:
+ hsspi_unregister(&qm35_ctx->hsspi, &qm35_ctx->coredump_layer.hlayer);
log_layer_deinit:
log_layer_deinit(&qm35_ctx->log_layer);
+coredump_layer_deinit:
+ coredump_layer_deinit(&qm35_ctx->coredump_layer);
+hsspi_test_deinit:
+ hsspi_test_deinit(&qm35_ctx->hsspi);
debug_deinit:
debug_deinit(&qm35_ctx->debug);
uci_layer_deinit:
uci_layer_deinit(&qm35_ctx->uci_layer);
hsspi_deinit:
hsspi_deinit(&qm35_ctx->hsspi);
-unregister:
+misc_deregister:
misc_deregister(&qm35_ctx->uci_dev);
+poweroff:
+ qm35_regulators_set(qm35_ctx, false);
return ret;
}
static int qm35_remove(struct spi_device *spi)
{
- struct qm35_ctx *qm35_hdl = dev_get_drvdata(&spi->dev);
-
- if (qm35_hdl) {
- hsspi_stop(&qm35_hdl->hsspi);
- hsspi_unregister(&qm35_hdl->hsspi, &qm35_hdl->log_layer.hlayer);
- hsspi_unregister(&qm35_hdl->hsspi, &qm35_hdl->coredump_layer.hlayer);
- log_layer_deinit(&qm35_hdl->log_layer);
- debug_deinit(&qm35_hdl->debug);
- coredump_layer_deinit(&qm35_hdl->coredump_layer);
- uci_layer_deinit(&qm35_hdl->uci_layer);
- hsspi_deinit(&qm35_hdl->hsspi);
- misc_deregister(&qm35_hdl->uci_dev);
- dev_info(&spi->dev, "Deregistered: [%s] misc device",
- qm35_hdl->uci_dev.name);
- dev_set_drvdata(&spi->dev, NULL);
- }
+ struct qm35_ctx *qm35_hdl = spi_get_drvdata(spi);
+
+ hsspi_stop(&qm35_hdl->hsspi);
+
+ hsspi_unregister(&qm35_hdl->hsspi, &qm35_hdl->log_layer.hlayer);
+ hsspi_unregister(&qm35_hdl->hsspi, &qm35_hdl->coredump_layer.hlayer);
+
+ log_layer_deinit(&qm35_hdl->log_layer);
+ coredump_layer_deinit(&qm35_hdl->coredump_layer);
+ hsspi_test_deinit(&qm35_hdl->hsspi);
+ debug_deinit(&qm35_hdl->debug);
+ uci_layer_deinit(&qm35_hdl->uci_layer);
+
+ hsspi_deinit(&qm35_hdl->hsspi);
+
+ misc_deregister(&qm35_hdl->uci_dev);
+
+ qm35_regulators_set(qm35_hdl, false);
+
+ dev_info(&spi->dev, "Deregistered: [%s] misc device\n",
+ qm35_hdl->uci_dev.name);
return 0;
}
diff --git a/qm35-trace.h b/qm35-trace.h
index ae9b1c1..5abb86a 100644
--- a/qm35-trace.h
+++ b/qm35-trace.h
@@ -56,7 +56,7 @@ TRACE_EVENT(hsspi_get_work,
__assign_str(dev, dev_name(dev));
__entry->type = type;
),
- TP_printk("[%s]: %s work\n", __get_str(dev),
+ TP_printk("[%s]: %s work", __get_str(dev),
show_work_type(__entry->type))
);
@@ -84,7 +84,7 @@ TRACE_EVENT(hsspi_is_txrx_waiting,
__entry->is_empty = is_empty;
__entry->state = state;
),
- TP_printk("[%s]: is_empty: %d state: %s\n", __get_str(dev),
+ TP_printk("[%s]: is_empty: %d state: %s", __get_str(dev),
__entry->is_empty, show_hsspi_state(__entry->state))
);
@@ -119,7 +119,7 @@ TRACE_EVENT(hsspi_spi_xfer,
STC_ASSIGN(soc, soc);
__entry->ret = ret;
),
- TP_printk("[%s]: host " STC_FMT " | soc " STC_FMT " rc=%d\n",
+ TP_printk("[%s]: host " STC_FMT " | soc " STC_FMT " rc=%d",
__get_str(dev), STC_ARG(host), STC_ARG(soc), __entry->ret)
);
diff --git a/qm35.h b/qm35.h
index 72b98d2..f8592c1 100644
--- a/qm35.h
+++ b/qm35.h
@@ -16,13 +16,15 @@
#include "hsspi_log.h"
#include "debug.h"
-enum {
- UL_RESERVED,
- UL_BOOT_FLASH,
- UL_UCI_APP,
- UL_COREDUMP,
- UL_LOG,
-};
+#define DEBUG_CERTIFICATE_SIZE 2560
+#define QM_RESET_LOW_MS 2
+/*
+ * value found using a SALAE
+ */
+#define QM_BOOT_MS 1450
+#define QM_BEFORE_RESET_MS 450
+
+struct regulator;
/**
* struct qm35_ctx - QM35 driver context
@@ -32,9 +34,12 @@ struct qm35_ctx {
unsigned int state;
struct miscdevice uci_dev;
struct spi_device *spi;
+ struct gpio_desc *gpio_csn;
struct gpio_desc *gpio_reset;
struct gpio_desc *gpio_ss_rdy;
struct gpio_desc *gpio_ss_irq;
+ struct gpio_desc *gpio_exton;
+ struct gpio_desc *gpio_wakeup;
spinlock_t lock;
bool out_data_wait;
bool out_active;
@@ -44,6 +49,10 @@ struct qm35_ctx {
struct coredump_layer coredump_layer;
struct log_layer log_layer;
struct debug debug;
+ struct regulator *vdd1;
+ struct regulator *vdd2;
+ struct regulator *vdd3;
+ bool regulators_enabled;
};
static inline unsigned int qm35_get_state(struct qm35_ctx *qm35_hdl)
@@ -76,4 +85,9 @@ static inline int qm35_reset(struct qm35_ctx *qm35_hdl, int timeout_ms)
int qm_get_soc_id(struct qm35_ctx *qm35_hdl, uint8_t *soc_id);
+void qm35_hsspi_start(struct qm35_ctx *qm35_hdl);
+void qm35_hsspi_stop(struct qm35_ctx *qm35_hdl);
+
+void qmrom_set_fwname(const char *name);
+
#endif /* __QM35_H___ */
diff --git a/qmrom_spi.c b/qmrom_spi.c
index 05c4f91..ca17ac1 100644
--- a/qmrom_spi.c
+++ b/qmrom_spi.c
@@ -33,6 +33,13 @@
#include "qm35.h"
+static const char *fwname = NULL;
+
+void qmrom_set_fwname(const char *name)
+{
+ fwname = name;
+}
+
int qmrom_spi_transfer(void *handle, char *rbuf, const char *wbuf, size_t size)
{
struct spi_device *spi = (struct spi_device *)handle;
@@ -58,7 +65,7 @@ int qmrom_spi_set_cs_level(void *handle, int level)
{
.tx_buf = &dummy,
.len = 1,
- .cs_change = level,
+ .cs_change = !level,
.speed_hz = DEFAULT_SPI_CLOCKRATE,
},
@@ -80,15 +87,20 @@ const struct firmware *qmrom_spi_get_firmware(void *handle,
{
const struct firmware *fw;
struct spi_device *spi = handle;
- char fw_name[16]; /* enough room to store "qm35_xx_xxx.bin" */
+ char _fw_name[16]; /* enough room to store "qm35_xx_xxx.bin" */
+ const char *fw_name = _fw_name;
int ret;
- if (revision == CHIP_REVISION_A0)
- snprintf(fw_name, sizeof(fw_name), "qm35_%02x.bin", revision);
- else
- snprintf(fw_name, sizeof(fw_name), "qm35_%02x_%.3s.bin",
- revision,
- lcs_state == CC_BSV_SECURE_LCS ? "oem" : "icv");
+ if (!fwname) {
+ if (revision == CHIP_REVISION_A0)
+ snprintf(_fw_name, sizeof(_fw_name), "qm35_%02x.bin", revision);
+ else
+ snprintf(_fw_name, sizeof(_fw_name), "qm35_%02x_%.3s.bin",
+ revision,
+ lcs_state == CC_BSV_SECURE_LCS ? "oem" : "icv");
+ } else {
+ fw_name = fwname;
+ }
dev_info(&spi->dev, "Requesting fw %s!\n", fw_name);
ret = request_firmware(&fw, fw_name, &spi->dev);
diff --git a/uci_ioctls.h b/uci_ioctls.h
index a91d643..a3a283a 100644
--- a/uci_ioctls.h
+++ b/uci_ioctls.h
@@ -11,6 +11,8 @@
#define QM35_CTRL_RESET _IOR(UCI_IOC_TYPE, 1, unsigned int)
#define QM35_CTRL_GET_STATE _IOR(UCI_IOC_TYPE, 2, unsigned int)
#define QM35_CTRL_FW_UPLOAD _IOR(UCI_IOC_TYPE, 3, unsigned int)
+#define QM35_CTRL_POWER _IOW(UCI_IOC_TYPE, 4, unsigned int)
+
/* qm35 states */
enum {