diff options
author | Robin Peng <robinpeng@google.com> | 2022-10-22 13:46:04 +0000 |
---|---|---|
committer | Robin Peng <robinpeng@google.com> | 2022-10-22 13:46:04 +0000 |
commit | 947dd331c9066fa60b5128a79c2922fbe89ad489 (patch) | |
tree | 55bb420ba2e5f85789e6c361c033bc6eea420b1f | |
parent | 34225404d0141fe30f99d954b5b670f6480a4cde (diff) | |
parent | c2872d0980131d7b5671d405f6ff6a7d1e9d8fd6 (diff) | |
download | uwb-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-- | Kbuild | 1 | ||||
-rw-r--r-- | debug.c | 120 | ||||
-rw-r--r-- | debug.h | 1 | ||||
-rw-r--r-- | hsspi.c | 146 | ||||
-rw-r--r-- | hsspi.h | 36 | ||||
-rw-r--r-- | hsspi_coredump.c | 30 | ||||
-rw-r--r-- | hsspi_coredump.h | 5 | ||||
-rw-r--r-- | hsspi_log.c | 6 | ||||
-rw-r--r-- | hsspi_test.c | 171 | ||||
-rw-r--r-- | hsspi_test.h | 39 | ||||
-rw-r--r-- | libqmrom/include/qmrom_spi.h | 2 | ||||
-rw-r--r-- | libqmrom/src/qmrom.c | 4 | ||||
-rw-r--r-- | qm35-spi.c | 419 | ||||
-rw-r--r-- | qm35-trace.h | 6 | ||||
-rw-r--r-- | qm35.h | 28 | ||||
-rw-r--r-- | qmrom_spi.c | 28 | ||||
-rw-r--r-- | uci_ioctls.h | 2 |
17 files changed, 862 insertions, 182 deletions
@@ -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) @@ -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: @@ -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 @@ -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; } @@ -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); @@ -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) ); @@ -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 { |