mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-06 03:45:24 -04:00
Update UWB driver to version 7.4
Update UWB driver to version 7.4 to support 1.2v platform Change-Id: Iace483f72aeba6deaacd9cd91cb844aabbaa279f Signed-off-by: zhuangnk1 <zhuangnk1@lenovo.com> Reviewed-on: https://gerrit.mot.com/2778728 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Tao Sun <suntao2@motorola.com> Submit-Approved: Jira Key
This commit is contained in:
parent
d3c84c683b
commit
a584e3c44b
22 changed files with 927 additions and 440 deletions
|
|
@ -145,12 +145,10 @@ static ssize_t debug_traces_read(struct file *filp, char __user *buff,
|
|||
{
|
||||
char *entry;
|
||||
rb_entry_size_t entry_size;
|
||||
//struct qm35_ctx *qm35_hdl;
|
||||
uint16_t ret;
|
||||
struct debug *debug;
|
||||
|
||||
debug = priv_from_file(filp);
|
||||
//qm35_hdl = container_of(debug, struct qm35_ctx, debug);
|
||||
|
||||
if (!debug->trace_ops)
|
||||
return -ENOSYS;
|
||||
|
|
@ -233,46 +231,6 @@ static int debug_traces_release(struct inode *inodep, struct file *filep)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t debug_coredump_read(struct file *filep, char __user *buff,
|
||||
size_t count, loff_t *off)
|
||||
{
|
||||
//struct qm35_ctx *qm35_hdl;
|
||||
struct debug *debug;
|
||||
char *cd;
|
||||
size_t cd_len = 0;
|
||||
|
||||
debug = priv_from_file(filep);
|
||||
//qm35_hdl = container_of(debug, struct qm35_ctx, debug);
|
||||
|
||||
if (!debug->coredump_ops)
|
||||
return -ENOSYS;
|
||||
|
||||
cd = debug->coredump_ops->coredump_get(debug, &cd_len);
|
||||
|
||||
return simple_read_from_buffer(buff, count, off, cd, cd_len);
|
||||
}
|
||||
|
||||
static ssize_t debug_coredump_write(struct file *filp, const char __user *buff,
|
||||
size_t count, loff_t *off)
|
||||
{
|
||||
struct debug *debug;
|
||||
u8 force;
|
||||
|
||||
debug = priv_from_file(filp);
|
||||
|
||||
if (kstrtou8_from_user(buff, count, 10, &force))
|
||||
return -EFAULT;
|
||||
|
||||
if (debug->coredump_ops && force != 0)
|
||||
debug->coredump_ops->coredump_force(debug);
|
||||
else if (force == 0)
|
||||
pr_warn("qm35: write non null value to force coredump\n");
|
||||
else
|
||||
return -ENOSYS;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t debug_hw_reset_write(struct file *filp, const char __user *buff,
|
||||
size_t count, loff_t *off)
|
||||
{
|
||||
|
|
@ -326,12 +284,6 @@ static const struct file_operations debug_traces_fops = {
|
|||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static const struct file_operations debug_coredump_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = debug_coredump_read,
|
||||
.write = debug_coredump_write,
|
||||
};
|
||||
|
||||
static const struct file_operations debug_hw_reset_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.write = debug_hw_reset_write,
|
||||
|
|
@ -481,13 +433,6 @@ int debug_init(struct debug *debug)
|
|||
goto unregister;
|
||||
}
|
||||
|
||||
file = debugfs_create_file("coredump", 0444, debug->fw_dir, debug,
|
||||
&debug_coredump_fops);
|
||||
if (!file) {
|
||||
pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw/coredump\n");
|
||||
goto unregister;
|
||||
}
|
||||
|
||||
file = debugfs_create_file("test_sleep_hsspi_ms", 0200, debug->fw_dir,
|
||||
debug, &debug_test_hsspi_sleep_fops);
|
||||
if (!file) {
|
||||
|
|
|
|||
|
|
@ -50,17 +50,11 @@ struct debug_trace_ops {
|
|||
int (*get_soc_id)(struct debug *dbg, uint8_t *soc_id);
|
||||
};
|
||||
|
||||
struct debug_coredump_ops {
|
||||
char *(*coredump_get)(struct debug *dbg, size_t *len);
|
||||
int (*coredump_force)(struct debug *dbg);
|
||||
};
|
||||
|
||||
struct debug {
|
||||
struct dentry *root_dir;
|
||||
struct dentry *fw_dir;
|
||||
struct dentry *chip_dir;
|
||||
const struct debug_trace_ops *trace_ops;
|
||||
const struct debug_coredump_ops *coredump_ops;
|
||||
struct wait_queue_head wq;
|
||||
struct file *pv_filp;
|
||||
struct mutex pv_filp_lock;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
* QM35 LOG layer HSSPI Protocol
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
|
@ -46,8 +47,6 @@
|
|||
#include "qm35.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define FWUPDATER_SPI_SPEED_HZ 20000000
|
||||
|
||||
extern int fu_spi_speed_hz;
|
||||
extern int qmrom_spi_speed_hz;
|
||||
|
||||
|
|
@ -65,11 +64,18 @@ static struct qm35_ctx *rom_test_prepare(struct file *filp)
|
|||
|
||||
qm35_hsspi_stop(qm35_hdl);
|
||||
qmrom_set_log_device(&qm35_hdl->spi->dev, LOG_DBG);
|
||||
|
||||
enable_irq(qm35_hdl->ss_rdy_irq);
|
||||
|
||||
qm35_hdl->flashing = true;
|
||||
return qm35_hdl;
|
||||
}
|
||||
|
||||
static void rom_test_unprepare(struct qm35_ctx *qm35_hdl)
|
||||
{
|
||||
disable_irq_nosync(qm35_hdl->ss_rdy_irq);
|
||||
|
||||
qm35_hdl->flashing = false;
|
||||
qmrom_set_log_device(&qm35_hdl->spi->dev, LOG_WARN);
|
||||
qm35_hsspi_start(qm35_hdl);
|
||||
}
|
||||
|
|
@ -88,12 +94,12 @@ static struct firmware *file2firmware(const char *filename)
|
|||
filename, 0, (void **)&firmware->data, INT_MAX,
|
||||
&firmware->size, READING_FIRMWARE);
|
||||
if (bytes_read < 0) {
|
||||
pr_err("kernel_read_file_from_path(%s) returned %d\n",
|
||||
pr_err("qm35: kernel_read_file_from_path(%s) returned %d\n",
|
||||
filename, (int)bytes_read);
|
||||
goto fail;
|
||||
}
|
||||
if (bytes_read != firmware->size) {
|
||||
pr_err("kernel_read_file_from_path returned %zu; expected %zu\n",
|
||||
pr_err("qm35: kernel_read_file_from_path returned %zu; expected %zu\n",
|
||||
bytes_read, firmware->size);
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -106,7 +112,7 @@ static struct firmware *file2firmware(const char *filename)
|
|||
&size, INT_MAX,
|
||||
READING_FIRMWARE);
|
||||
if (ret < 0) {
|
||||
pr_err("kernel_read_file_from_path(%s) returned %d\n",
|
||||
pr_err("qm35: kernel_read_file_from_path(%s) returned %d\n",
|
||||
filename, ret);
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -114,8 +120,8 @@ static struct firmware *file2firmware(const char *filename)
|
|||
}
|
||||
#endif
|
||||
|
||||
print_hex_dump(KERN_DEBUG, "Bin file:", DUMP_PREFIX_ADDRESS, 16, 1,
|
||||
firmware->data, 16, false);
|
||||
print_hex_dump(KERN_DEBUG, "qm35: Bin file:", DUMP_PREFIX_ADDRESS, 16,
|
||||
1, firmware->data, 16, false);
|
||||
return firmware;
|
||||
|
||||
fail:
|
||||
|
|
@ -133,24 +139,24 @@ static ssize_t rom_probe(struct file *filp, const char __user *buff,
|
|||
struct qm35_ctx *qm35_hdl = rom_test_prepare(filp);
|
||||
struct qmrom_handle *h;
|
||||
|
||||
pr_info("Starting the probe test...\n");
|
||||
h = qmrom_init(&qm35_hdl->spi->dev, qm35_hdl, qm35_hdl->gpio_ss_rdy,
|
||||
qmrom_spi_speed_hz, QMROM_RETRIES,
|
||||
pr_info("qm35: Starting the probe test...\n");
|
||||
h = qmrom_init(&qm35_hdl->spi->dev, qm35_hdl, qm35_hdl,
|
||||
qm35_hdl->gpio_ss_irq, qmrom_spi_speed_hz, QMROM_RETRIES,
|
||||
qmrom_spi_reset_device, DEVICE_GEN_QM357XX);
|
||||
if (!h) {
|
||||
pr_err("qmrom_init failed\n");
|
||||
pr_err("qm35: qmrom_init failed\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
pr_info("chip_revision %#2x\n", h->chip_rev);
|
||||
pr_info("device version %#02x\n", h->device_version);
|
||||
pr_info("qm35: chip_revision %#2x\n", h->chip_rev);
|
||||
pr_info("qm35: device version %#02x\n", h->device_version);
|
||||
if (h->chip_rev != 0xa001) {
|
||||
pr_info("lcs_state %u\n", h->qm357xx_soc_info.lcs_state);
|
||||
print_hex_dump(KERN_DEBUG, "soc_id:", DUMP_PREFIX_NONE, 16, 1,
|
||||
h->qm357xx_soc_info.soc_id,
|
||||
pr_info("qm35: lcs_state %u\n", h->qm357xx_soc_info.lcs_state);
|
||||
print_hex_dump(KERN_DEBUG, "qm35: soc_id:", DUMP_PREFIX_NONE,
|
||||
16, 1, h->qm357xx_soc_info.soc_id,
|
||||
sizeof(h->qm357xx_soc_info.soc_id), false);
|
||||
print_hex_dump(KERN_DEBUG, "uuid:", DUMP_PREFIX_NONE, 16, 1,
|
||||
h->qm357xx_soc_info.uuid,
|
||||
print_hex_dump(KERN_DEBUG, "qm35: uuid:", DUMP_PREFIX_NONE, 16,
|
||||
1, h->qm357xx_soc_info.uuid,
|
||||
sizeof(h->qm357xx_soc_info.uuid), false);
|
||||
}
|
||||
qmrom_deinit(h);
|
||||
|
|
@ -179,15 +185,15 @@ static ssize_t rom_flash_dbg_cert(struct file *filp, const char __user *buff,
|
|||
|
||||
err = copy_from_user(filename, buff, count);
|
||||
if (err) {
|
||||
pr_err("copy_from_user failed with error %d\n", err);
|
||||
pr_err("qm35: copy_from_user failed with error %d\n", err);
|
||||
count = err;
|
||||
goto end;
|
||||
}
|
||||
filename[count - 1] = '\0';
|
||||
certificate = file2firmware(filename);
|
||||
if (!certificate || certificate->size != DEBUG_CERTIFICATE_SIZE) {
|
||||
pr_err("%s: file retrieval failed, abort (%s)\n", __func__,
|
||||
certificate ? "wrong size" : "not found");
|
||||
pr_err("qm35: %s: file retrieval failed, abort (%s)\n",
|
||||
__func__, certificate ? "wrong size" : "not found");
|
||||
count = -1;
|
||||
goto end;
|
||||
}
|
||||
|
|
@ -195,21 +201,24 @@ static ssize_t rom_flash_dbg_cert(struct file *filp, const char __user *buff,
|
|||
/* Flash the debug certificate */
|
||||
pr_info("Flashing debug certificate %s...\n", filename);
|
||||
|
||||
h = qmrom_init(&qm35_hdl->spi->dev, qm35_hdl, qm35_hdl->gpio_ss_rdy,
|
||||
qmrom_spi_speed_hz, QMROM_RETRIES,
|
||||
h = qmrom_init(&qm35_hdl->spi->dev, qm35_hdl, qm35_hdl,
|
||||
qm35_hdl->gpio_ss_irq, qmrom_spi_speed_hz, QMROM_RETRIES,
|
||||
qmrom_spi_reset_device, DEVICE_GEN_QM357XX);
|
||||
if (!h) {
|
||||
pr_err("qmrom_init failed\n");
|
||||
pr_err("qm35: qmrom_init failed\n");
|
||||
goto end;
|
||||
}
|
||||
err = qm357xx_rom_flash_dbg_cert(h, certificate);
|
||||
if (err)
|
||||
pr_err("Flashing debug certificate %s failed with %d!\n",
|
||||
pr_err("qm35: Flashing debug certificate %s failed with %d!\n",
|
||||
filename, err);
|
||||
else
|
||||
pr_info("Flashing debug certificate %s succeeded!\n", filename);
|
||||
pr_info("qm35: Flashing debug certificate %s succeeded!\n",
|
||||
filename);
|
||||
|
||||
end:
|
||||
if (filename)
|
||||
kfree(filename);
|
||||
if (h)
|
||||
qmrom_deinit(h);
|
||||
if (certificate) {
|
||||
|
|
@ -231,20 +240,21 @@ static ssize_t rom_erase_dbg_cert(struct file *filp, const char __user *buff,
|
|||
struct qmrom_handle *h = NULL;
|
||||
int err;
|
||||
|
||||
pr_info("Erasing debug certificate...\n");
|
||||
pr_info("qm35: Erasing debug certificate...\n");
|
||||
|
||||
h = qmrom_init(&qm35_hdl->spi->dev, qm35_hdl, qm35_hdl->gpio_ss_rdy,
|
||||
qmrom_spi_speed_hz, QMROM_RETRIES,
|
||||
h = qmrom_init(&qm35_hdl->spi->dev, qm35_hdl, qm35_hdl,
|
||||
qm35_hdl->gpio_ss_irq, qmrom_spi_speed_hz, QMROM_RETRIES,
|
||||
qmrom_spi_reset_device, DEVICE_GEN_QM357XX);
|
||||
if (!h) {
|
||||
pr_err("qmrom_init failed\n");
|
||||
pr_err("qm35: qmrom_init failed\n");
|
||||
goto end;
|
||||
}
|
||||
err = qm357xx_rom_erase_dbg_cert(h);
|
||||
if (err)
|
||||
pr_err("Erasing debug certificate failed with %d!\n", err);
|
||||
pr_err("qm35: Erasing debug certificate failed with %d!\n",
|
||||
err);
|
||||
else
|
||||
pr_info("Erasing debug certificate succeeded!\n");
|
||||
pr_info("qm35: Erasing debug certificate succeeded!\n");
|
||||
|
||||
end:
|
||||
if (h)
|
||||
|
|
@ -274,34 +284,33 @@ static ssize_t rom_flash_fw(struct file *filp, const char __user *buff,
|
|||
|
||||
rc = copy_from_user(filename, buff, count);
|
||||
if (rc) {
|
||||
pr_err("copy_from_user failed with error %d\n", rc);
|
||||
pr_err("qm35: copy_from_user failed with error %d\n", rc);
|
||||
goto end;
|
||||
}
|
||||
filename[count - 1] = '\0';
|
||||
fw = file2firmware(filename);
|
||||
if (!fw) {
|
||||
pr_err("%s: file %s retrieval failed, abort\n", __func__,
|
||||
pr_err("qm35: %s: file %s retrieval failed, abort\n", __func__,
|
||||
filename);
|
||||
rc = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
pr_info("Flashing image %s (%pK->data %pK)...\n", filename, fw,
|
||||
pr_info("qm35: Flashing image %s (%pK->data %pK)...\n", filename, fw,
|
||||
fw->data);
|
||||
|
||||
h = qmrom_init(&qm35_hdl->spi->dev, qm35_hdl, qm35_hdl->gpio_ss_rdy,
|
||||
qmrom_spi_speed_hz, QMROM_RETRIES,
|
||||
h = qmrom_init(&qm35_hdl->spi->dev, qm35_hdl, qm35_hdl,
|
||||
qm35_hdl->gpio_ss_irq, qmrom_spi_speed_hz, QMROM_RETRIES,
|
||||
qmrom_spi_reset_device, DEVICE_GEN_QM357XX);
|
||||
if (!h) {
|
||||
pr_err("qmrom_init failed\n");
|
||||
rc = -1;
|
||||
pr_err("qm35: qmrom_init failed\n");
|
||||
goto end;
|
||||
}
|
||||
rc = qm357xx_rom_flash_fw(h, fw);
|
||||
if (rc)
|
||||
pr_err("Flashing firmware %s failed with %d!\n", filename, rc);
|
||||
pr_err("qm35: Flashing firmware %s failed with %d!\n", filename,
|
||||
rc);
|
||||
else
|
||||
pr_info("Flashing firmware %s succeeded!\n", filename);
|
||||
pr_info("qm35: Flashing firmware %s succeeded!\n", filename);
|
||||
|
||||
end:
|
||||
kfree(filename);
|
||||
|
|
@ -336,52 +345,52 @@ static ssize_t rom_flash_fw_pkg(struct file *filp, const char __user *buff,
|
|||
}
|
||||
rc = copy_from_user(filename, buff, count);
|
||||
if (rc) {
|
||||
pr_err("copy_from_user failed with error %d\n", rc);
|
||||
pr_err("qm35: copy_from_user failed with error %d\n", rc);
|
||||
goto end;
|
||||
}
|
||||
filename[count - 1] = '\0';
|
||||
fw = file2firmware(filename);
|
||||
if (!fw || !fw->data) {
|
||||
pr_err("%s: file %s retrieval failed (%s), abort\n", __func__,
|
||||
filename, fw ? "no data read" : "not found");
|
||||
rc = -1;
|
||||
pr_err("qm35: %s file %s retrieval failed (%s), abort\n",
|
||||
__func__, filename, fw ? "no data read" : "not found");
|
||||
goto end;
|
||||
}
|
||||
|
||||
pr_info("Flashing fw_updater...\n");
|
||||
|
||||
h = qmrom_init(&qm35_hdl->spi->dev, qm35_hdl, qm35_hdl->gpio_ss_rdy,
|
||||
qmrom_spi_speed_hz, QMROM_RETRIES,
|
||||
pr_info("qm35: Flashing fw_updater...\n");
|
||||
h = qmrom_init(&qm35_hdl->spi->dev, qm35_hdl, qm35_hdl,
|
||||
qm35_hdl->gpio_ss_irq, qmrom_spi_speed_hz, QMROM_RETRIES,
|
||||
qmrom_spi_reset_device, DEVICE_GEN_QM357XX);
|
||||
if (!h) {
|
||||
pr_err("qmrom_init failed\n");
|
||||
rc = -1;
|
||||
pr_err("qm35: qmrom_init failed\n");
|
||||
goto end;
|
||||
}
|
||||
h->skip_check_fw_boot = true;
|
||||
rc = qm357xx_rom_flash_fw(h, fw);
|
||||
h->skip_check_fw_boot = false;
|
||||
if (rc) {
|
||||
pr_err("Flashing fw_updater failed with %d!\n", rc);
|
||||
pr_err("qm35: Flashing fw_updater failed with %d!\n", rc);
|
||||
goto end;
|
||||
}
|
||||
|
||||
pr_info("Flashing fw_updater succeeded, flashing the fw package now...\n");
|
||||
pr_info("qm35: Flashing fw_updater succeeded, flashing the fw package now...\n");
|
||||
qmrom_spi_set_freq(fu_spi_speed_hz);
|
||||
|
||||
rc = qm357xx_rom_fw_macro_pkg_get_fw_idx(fw, 1, &fw_size, &fw_data);
|
||||
if (rc) {
|
||||
pr_err("%s: FW MACRO PACKAGE corupted = %d\n", __func__, rc);
|
||||
pr_err("qm35: %s FW MACRO PACKAGE corupted = %d\n", __func__,
|
||||
rc);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (*(uint32_t *)fw_data == CRYPTO_FIRMWARE_PACK_MAGIC_VALUE) {
|
||||
run_fwupdater(h, fw_data, fw_size);
|
||||
rc = run_fwupdater(h, fw_data, fw_size);
|
||||
} else {
|
||||
rc = -EINVAL;
|
||||
pr_err("FW PACKAGE not found - %04x! fw_size = %d\n",
|
||||
pr_err("qm35: FW PACKAGE not found - %04x! fw_size = %d\n",
|
||||
*(uint32_t *)fw_data, fw_size);
|
||||
goto end;
|
||||
}
|
||||
pr_info("FW package flashing done, rebooting the QM...\n");
|
||||
pr_info("qm35: FW package flashing %s (rc = %d), rebooting the QM...\n",
|
||||
rc ? "failed" : "succeeded", rc);
|
||||
|
||||
end:
|
||||
qmrom_spi_reset_device(qm35_hdl);
|
||||
|
|
|
|||
|
|
@ -141,6 +141,8 @@ static int hsspi_wait_ss_ready(struct hsspi *hsspi)
|
|||
{
|
||||
int ret;
|
||||
|
||||
hsspi->waiting_ss_rdy = true;
|
||||
|
||||
if (!test_bit(HSSPI_FLAGS_SS_BUSY, hsspi->flags)) {
|
||||
/* The ss_ready went low, so the fw is not busy anymore,
|
||||
* if the ss_ready is high, we can proceed, else,
|
||||
|
|
@ -180,6 +182,8 @@ static int hsspi_wait_ss_ready(struct hsspi *hsspi)
|
|||
*/
|
||||
if (!gpiod_get_value(hsspi->gpio_ss_rdy))
|
||||
return -EAGAIN;
|
||||
|
||||
hsspi->waiting_ss_rdy = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -270,6 +274,7 @@ static int spi_xfer(struct hsspi *hsspi, const void *tx, void *rx,
|
|||
hsspi->soc->flags);
|
||||
}
|
||||
|
||||
hsspi->waiting_ss_rdy = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -376,7 +381,8 @@ static int hsspi_tx(struct hsspi *hsspi, struct hsspi_layer *layer,
|
|||
/* Ignore tx check flags */
|
||||
check_soc_flag(&hsspi->spi->dev, __func__, hsspi->soc->flags, true);
|
||||
|
||||
if (hsspi->host->flags & STC_HOST_PRD)
|
||||
if ((hsspi->host->flags & STC_HOST_PRD) &&
|
||||
(hsspi->soc->flags & STC_SOC_ODW))
|
||||
return hsspi_rx(hsspi, hsspi->soc->ul, hsspi->soc->length);
|
||||
|
||||
return ret;
|
||||
|
|
@ -406,7 +412,11 @@ static int hsspi_pre_read(struct hsspi *hsspi)
|
|||
/* Ignore pre-read check flags */
|
||||
check_soc_flag(&hsspi->spi->dev, __func__, hsspi->soc->flags, true);
|
||||
|
||||
return hsspi_rx(hsspi, hsspi->soc->ul, hsspi->soc->length);
|
||||
if (hsspi->soc->flags & STC_SOC_ODW)
|
||||
return hsspi_rx(hsspi, hsspi->soc->ul, hsspi->soc->length);
|
||||
else
|
||||
/* Pre-read error. Maybe FW is a little late to setup HSSPI header. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -36,13 +36,15 @@
|
|||
#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 };
|
||||
enum {
|
||||
UL_RESERVED,
|
||||
UL_BOOT_FLASH,
|
||||
UL_UCI_APP,
|
||||
UL_COREDUMP,
|
||||
UL_LOG,
|
||||
UL_TEST_HSSPI,
|
||||
UL_MAX_IDX
|
||||
};
|
||||
|
||||
struct stc_header {
|
||||
u8 flags;
|
||||
|
|
@ -172,6 +174,8 @@ struct hsspi {
|
|||
|
||||
struct gpio_desc *gpio_ss_rdy;
|
||||
struct gpio_desc *gpio_exton;
|
||||
|
||||
volatile bool waiting_ss_rdy;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ static uint16_t coredump_get_checksum(struct coredump_layer *layer)
|
|||
return crc;
|
||||
}
|
||||
|
||||
static void corredump_on_expired_timer(struct timer_list *timer)
|
||||
static void coredump_on_expired_timer(struct timer_list *timer)
|
||||
{
|
||||
struct coredump_layer *layer =
|
||||
container_of(timer, struct coredump_layer, timer);
|
||||
|
|
@ -234,7 +234,8 @@ static void coredump_received(struct hsspi_layer *hlayer,
|
|||
break;
|
||||
|
||||
case COREDUMP_BODY_NTF:
|
||||
pr_info("qm35: coredump: saving coredump data with len: %d [%d/%d]\n",
|
||||
pr_debug(
|
||||
"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);
|
||||
|
|
@ -253,6 +254,8 @@ static void coredump_received(struct hsspi_layer *hlayer,
|
|||
|
||||
coredump_send_rcv_status(layer, layer->coredump_status);
|
||||
|
||||
if (layer->coredump_status == COREDUMP_RCV_ACK)
|
||||
wake_up_interruptible(&layer->wq);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -289,18 +292,27 @@ static const struct hsspi_layer_ops coredump_ops = {
|
|||
.sent = coredump_sent,
|
||||
};
|
||||
|
||||
char *debug_coredump_get(struct debug *dbg, size_t *len)
|
||||
void coredump_layer_init(struct coredump_layer *layer)
|
||||
{
|
||||
char *data;
|
||||
struct qm35_ctx *qm35_hdl = container_of(dbg, struct qm35_ctx, debug);
|
||||
|
||||
*len = qm35_hdl->coredump_layer.coredump_data_wr_idx;
|
||||
data = qm35_hdl->coredump_layer.coredump_data;
|
||||
|
||||
return data;
|
||||
layer->hlayer.name = "QM35 COREDUMP";
|
||||
layer->hlayer.id = UL_COREDUMP;
|
||||
layer->hlayer.ops = &coredump_ops;
|
||||
layer->coredump_data = NULL;
|
||||
layer->coredump_data_wr_idx = 0;
|
||||
layer->coredump_size = 0;
|
||||
layer->coredump_crc = 0;
|
||||
layer->coredump_status = 0;
|
||||
init_waitqueue_head(&layer->wq);
|
||||
timer_setup(&layer->timer, coredump_on_expired_timer, 0);
|
||||
}
|
||||
|
||||
int debug_coredump_force(struct debug *dbg)
|
||||
void coredump_layer_deinit(struct coredump_layer *layer)
|
||||
{
|
||||
wake_up_interruptible(&layer->wq);
|
||||
kfree(layer->coredump_data);
|
||||
}
|
||||
|
||||
bool coredump_layer_force_coredump(struct coredump_layer *coredump)
|
||||
{
|
||||
struct coredump_packet *p;
|
||||
struct coredump_common_hdr hdr = { .cmd_id = COREDUMP_FORCE_CMD };
|
||||
|
|
@ -308,7 +320,7 @@ int debug_coredump_force(struct debug *dbg)
|
|||
|
||||
pr_info("qm35: force coredump");
|
||||
|
||||
qm35_hdl = container_of(dbg, struct qm35_ctx, debug);
|
||||
qm35_hdl = container_of(coredump, struct qm35_ctx, coredump_layer);
|
||||
|
||||
p = coredump_packet_alloc(sizeof(hdr));
|
||||
if (!p)
|
||||
|
|
@ -320,30 +332,22 @@ int debug_coredump_force(struct debug *dbg)
|
|||
&p->blk);
|
||||
}
|
||||
|
||||
static const struct debug_coredump_ops debug_coredump_ops = {
|
||||
.coredump_get = debug_coredump_get,
|
||||
.coredump_force = debug_coredump_force,
|
||||
};
|
||||
|
||||
int coredump_layer_init(struct coredump_layer *layer, struct debug *debug)
|
||||
bool coredump_layer_new_coredump_available(struct coredump_layer *coredump)
|
||||
{
|
||||
layer->hlayer.name = "QM35 COREDUMP";
|
||||
layer->hlayer.id = UL_COREDUMP;
|
||||
layer->hlayer.ops = &coredump_ops;
|
||||
|
||||
layer->coredump_data = NULL;
|
||||
layer->coredump_data_wr_idx = 0;
|
||||
layer->coredump_size = 0;
|
||||
layer->coredump_crc = 0;
|
||||
layer->coredump_status = 0;
|
||||
timer_setup(&layer->timer, corredump_on_expired_timer, 0);
|
||||
|
||||
debug->coredump_ops = &debug_coredump_ops;
|
||||
|
||||
return 0;
|
||||
return coredump->coredump_status == COREDUMP_RCV_ACK;
|
||||
}
|
||||
|
||||
void coredump_layer_deinit(struct coredump_layer *layer)
|
||||
uint8_t *coredump_layer_get_coredump(struct coredump_layer *coredump)
|
||||
{
|
||||
kfree(layer->coredump_data);
|
||||
return coredump->coredump_data;
|
||||
}
|
||||
|
||||
uint32_t coredump_layer_get_coredump_size(struct coredump_layer *coredump)
|
||||
{
|
||||
return coredump->coredump_data_wr_idx;
|
||||
}
|
||||
|
||||
void coredump_layer_reset_status(struct coredump_layer *coredump)
|
||||
{
|
||||
coredump->coredump_status = COREDUMP_RCV_NACK;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,11 +29,11 @@
|
|||
#ifndef __HSSPI_COREDUMP_H__
|
||||
#define __HSSPI_COREDUMP_H__
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include "hsspi.h"
|
||||
#include "debug.h"
|
||||
|
||||
struct coredump_packet {
|
||||
struct hsspi_block blk;
|
||||
|
|
@ -47,9 +47,51 @@ struct coredump_layer {
|
|||
uint16_t coredump_crc;
|
||||
uint8_t coredump_status;
|
||||
struct timer_list timer;
|
||||
wait_queue_head_t wq;
|
||||
};
|
||||
|
||||
int coredump_layer_init(struct coredump_layer *coredump, struct debug *debug);
|
||||
void coredump_layer_init(struct coredump_layer *coredump);
|
||||
void coredump_layer_deinit(struct coredump_layer *coredump);
|
||||
|
||||
/**
|
||||
* coredump_layer_force_coredump() - force coredump
|
||||
* @coredump: pointer to &struct coredump_layer
|
||||
*
|
||||
* Return: true if ok, false otherwise.
|
||||
*/
|
||||
bool coredump_layer_force_coredump(struct coredump_layer *coredump);
|
||||
|
||||
/**
|
||||
* coredump_layer_new_coredump_available() - checks if the layer has
|
||||
* ack coredump data to be read
|
||||
* @coredump: pointer to &struct coredump_layer
|
||||
*
|
||||
* Return: true if coredump is available, false otherwise.
|
||||
*/
|
||||
bool coredump_layer_new_coredump_available(struct coredump_layer *coredump);
|
||||
|
||||
/**
|
||||
* coredump_layer_get_coredump() - get coredump
|
||||
* @coredump: pointer to &struct coredump_layer
|
||||
*
|
||||
* Return: a &uint8_t pointing to the coredump data if succeed, NULL otherwise
|
||||
*/
|
||||
uint8_t *coredump_layer_get_coredump(struct coredump_layer *coredump);
|
||||
|
||||
/**
|
||||
* coredump_layer_get_coredump_size() - get coredump size
|
||||
* @coredump: pointer to &struct coredump_layer
|
||||
*
|
||||
* Return: a uint32_t holding the size of the current coredump
|
||||
*/
|
||||
uint32_t coredump_layer_get_coredump_size(struct coredump_layer *coredump);
|
||||
|
||||
/**
|
||||
* coredump_layer_reset_status() - resets coredump status
|
||||
* @coredump: pointer to &struct coredump_layer
|
||||
*
|
||||
* Return: void,
|
||||
*/
|
||||
void coredump_layer_reset_status(struct coredump_layer *coredump);
|
||||
|
||||
#endif // __HSSPI_COREDUMP_H__
|
||||
|
|
|
|||
|
|
@ -134,6 +134,7 @@ static int parse_log_sources_response(struct log_layer *layer, uint8_t *data,
|
|||
{
|
||||
struct qm35_ctx *qm35_hdl;
|
||||
int idx = 0;
|
||||
uint16_t current_len = 0;
|
||||
|
||||
qm35_hdl = container_of(layer, struct qm35_ctx, log_layer);
|
||||
|
||||
|
|
@ -150,8 +151,15 @@ static int parse_log_sources_response(struct log_layer *layer, uint8_t *data,
|
|||
layer->log_modules[idx].debug = &qm35_hdl->debug;
|
||||
layer->log_modules[idx].id = *data++;
|
||||
layer->log_modules[idx].lvl = *data++;
|
||||
strcpy(layer->log_modules[idx].name, (char *)data);
|
||||
data += (strlen((char *)data) + 1);
|
||||
current_len = strlen((char *)data) + 1;
|
||||
if (current_len > sizeof(layer->log_modules[idx].name)) {
|
||||
pr_err("qm35: log module name bigger than allocated buffer: current_len = %d bytes\n",
|
||||
current_len);
|
||||
strncpy(layer->log_modules[idx].name, "ERROR", 6);
|
||||
} else
|
||||
strncpy(layer->log_modules[idx].name, (char *)data,
|
||||
current_len);
|
||||
data += current_len;
|
||||
|
||||
debug_create_module_entry(&qm35_hdl->debug,
|
||||
&layer->log_modules[idx]);
|
||||
|
|
@ -169,8 +177,6 @@ static int parse_get_log_lvl_response(struct log_layer *layer, uint8_t *data,
|
|||
src_id = *data++;
|
||||
src_lvl = *data;
|
||||
|
||||
//qm35_hdl = container_of(layer, struct qm35_ctx, log_layer);
|
||||
|
||||
for (idx = 0; idx < layer->log_modules_count; idx++) {
|
||||
if (layer->log_modules[idx].id == src_id) {
|
||||
layer->log_modules[idx].lvl = src_lvl;
|
||||
|
|
@ -240,7 +246,7 @@ static void log_received(struct hsspi_layer *hlayer, struct hsspi_block *blk,
|
|||
|
||||
switch (hdr.cmd_id) {
|
||||
case LOG_CID_TRACE_NTF:
|
||||
rb_push(&layer->rb, body, hdr.b_size);
|
||||
rb_push(&layer->rb, (const char *)body, hdr.b_size);
|
||||
debug_new_trace_available(&qm35_hdl->debug);
|
||||
break;
|
||||
case LOG_CID_SET_LOG_LVL:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR Apache-2.0
|
||||
/*
|
||||
* Copyright 2023 Qorvo US, Inc.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __KERNEL__
|
||||
#include <stddef.h>
|
||||
|
|
@ -12,24 +17,39 @@
|
|||
|
||||
/* Extract from C0 rom code */
|
||||
#define MAX_CERTIFICATE_SIZE 0x400
|
||||
#define MAX_CHUNK_SIZE 4096
|
||||
#define WAIT_SS_RDY_TIMEOUT 100
|
||||
#define MAX_CHUNK_SIZE 3072
|
||||
#define WAIT_REBOOT_DELAY_MS 250
|
||||
#define WAIT_SS_RDY_CHUNK_TIMEOUT 100
|
||||
#define WAIT_SS_RDY_STATUS_TIMEOUT 10
|
||||
#define RESULT_RETRIES 3
|
||||
#define RESULT_CMD_INTERVAL_MS 50
|
||||
#define CRC_TYPE uint32_t
|
||||
#define CRC_SIZE (sizeof(CRC_TYPE))
|
||||
#define TRANPORT_HEADER_SIZE (sizeof(struct stc) + CRC_SIZE)
|
||||
#define EMERGENCY_SPI_FREQ 1000000 /* 1MHz */
|
||||
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
#ifndef __KERNEL__
|
||||
_Static_assert(sizeof(struct stc) + MAX_CERTIFICATE_SIZE < MAX_CHUNK_SIZE);
|
||||
_Static_assert(MAX_CHUNK_SIZE >= CRYPTO_IMAGES_CERT_PKG_SIZE);
|
||||
_Static_assert(TRANPORT_HEADER_SIZE + MAX_CERTIFICATE_SIZE < MAX_CHUNK_SIZE);
|
||||
#endif
|
||||
|
||||
/* local stats */
|
||||
static int gstats_spi_errors;
|
||||
static int gstats_ss_rdy_timeouts;
|
||||
|
||||
static int send_data_chunks(struct qmrom_handle *handle, char *data,
|
||||
size_t size);
|
||||
static int check_fw_boot(struct qmrom_handle *handle);
|
||||
|
||||
int run_fwupdater(struct qmrom_handle *handle, char *fwpkg_bin, size_t size)
|
||||
{
|
||||
int rc;
|
||||
|
||||
gstats_spi_errors = 0;
|
||||
gstats_ss_rdy_timeouts = 0;
|
||||
|
||||
if (size < sizeof(struct fw_pkg_hdr_t) +
|
||||
sizeof(struct fw_pkg_img_hdr_t) +
|
||||
CRYPTO_IMAGES_CERT_PKG_SIZE +
|
||||
|
|
@ -52,194 +72,304 @@ static int run_fwupdater_get_status(struct qmrom_handle *handle,
|
|||
struct fw_updater_status_t *status)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
CRC_TYPE *crc = (CRC_TYPE *)(hstc + 1);
|
||||
bool owa;
|
||||
memset(hstc, 0, TRANPORT_HEADER_SIZE + sizeof(*status));
|
||||
|
||||
while (i++ < RESULT_RETRIES) {
|
||||
// Poll the QM
|
||||
qmrom_spi_wait_for_ready_line(handle->ss_rdy_handle,
|
||||
WAIT_SS_RDY_TIMEOUT);
|
||||
sstc->all = 0;
|
||||
hstc->all = 0;
|
||||
*crc = 0;
|
||||
qmrom_spi_transfer(handle->spi_handle, (char *)sstc,
|
||||
(const char *)hstc, sizeof(hstc));
|
||||
// LOG_INFO("Poll received:\n");
|
||||
// hexdump(LOG_INFO, sstc, sizeof(sstc));
|
||||
/* Line might have been read ready before QM actually handled the previous cmd */
|
||||
qmrom_msleep(RESULT_CMD_INTERVAL_MS);
|
||||
|
||||
(const char *)hstc, TRANPORT_HEADER_SIZE);
|
||||
qmrom_spi_wait_for_ready_line(handle->ss_rdy_handle,
|
||||
WAIT_SS_RDY_TIMEOUT);
|
||||
WAIT_SS_RDY_STATUS_TIMEOUT);
|
||||
sstc->all = 0;
|
||||
hstc->all = 0;
|
||||
hstc->host_flags.pre_read = 1;
|
||||
*crc = 0;
|
||||
qmrom_spi_transfer(handle->spi_handle, (char *)sstc,
|
||||
(const char *)hstc, sizeof(hstc));
|
||||
(const char *)hstc, TRANPORT_HEADER_SIZE);
|
||||
// LOG_INFO("Pre-Read received:\n");
|
||||
// hexdump(LOG_INFO, sstc, sizeof(sstc));
|
||||
|
||||
/* Stops the loop when QM has a result to share */
|
||||
owa = sstc->soc_flags.out_waiting;
|
||||
qmrom_spi_wait_for_ready_line(handle->ss_rdy_handle,
|
||||
WAIT_SS_RDY_TIMEOUT);
|
||||
WAIT_SS_RDY_STATUS_TIMEOUT);
|
||||
sstc->all = 0;
|
||||
hstc->all = 0;
|
||||
hstc->host_flags.read = 1;
|
||||
hstc->len = sizeof(*status);
|
||||
*crc = 0;
|
||||
qmrom_spi_transfer(handle->spi_handle, (char *)sstc,
|
||||
(const char *)hstc,
|
||||
sizeof(hstc) + sizeof(*status));
|
||||
TRANPORT_HEADER_SIZE + sizeof(*status));
|
||||
// LOG_INFO("Read received:\n");
|
||||
// hexdump(LOG_INFO, sstc, sizeof(hstc) + sizeof(uint32_t));
|
||||
// hexdump(LOG_INFO, sstc, sizeof(*hstc) + sizeof(uint32_t));
|
||||
if (owa) {
|
||||
memcpy(status, sstc->payload, sizeof(*status));
|
||||
break;
|
||||
if (status->magic == FWUPDATER_STATUS_MAGIC)
|
||||
break;
|
||||
}
|
||||
qmrom_spi_wait_for_ready_line(handle->ss_rdy_handle,
|
||||
WAIT_SS_RDY_STATUS_TIMEOUT);
|
||||
// Failed to get the status, reduces the spi speed to
|
||||
// an emergency speed to maximize the chance to get the
|
||||
// final status
|
||||
qmrom_spi_set_freq(EMERGENCY_SPI_FREQ);
|
||||
gstats_spi_errors++;
|
||||
}
|
||||
if (!owa) {
|
||||
if (status->magic != FWUPDATER_STATUS_MAGIC) {
|
||||
LOG_ERR("Timedout waiting for result\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct stc *prepare_hstc(bool copy_data, char *tx, char *data, size_t len)
|
||||
static CRC_TYPE checksum(const void *data, const size_t size)
|
||||
{
|
||||
struct stc *hstc = (struct stc *)(copy_data ? tx : data);
|
||||
CRC_TYPE crc = 0;
|
||||
CRC_TYPE *ptr = (CRC_TYPE *)data;
|
||||
CRC_TYPE remainder = size & (CRC_SIZE - 1);
|
||||
size_t idx;
|
||||
|
||||
for (idx = 0; idx < size; idx += CRC_SIZE, ptr++)
|
||||
crc += *ptr;
|
||||
|
||||
if (remainder) {
|
||||
crc += ((uint8_t *)data)[size - 1];
|
||||
if (remainder > 1) {
|
||||
crc += ((uint8_t *)data)[size - 2];
|
||||
if (remainder > 2) {
|
||||
crc += ((uint8_t *)data)[size - 3];
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
static void prepare_hstc(struct stc *hstc, char *data, size_t len)
|
||||
{
|
||||
CRC_TYPE *crc = (CRC_TYPE *)(hstc + 1);
|
||||
void *payload = crc + 1;
|
||||
|
||||
hstc->all = 0;
|
||||
hstc->host_flags.write = 1;
|
||||
hstc->len = len;
|
||||
if (copy_data)
|
||||
memcpy(hstc->payload, data, len);
|
||||
return hstc;
|
||||
hstc->len = len + CRC_SIZE;
|
||||
*crc = checksum(data, len);
|
||||
#ifdef CONFIG_INJECT_ERROR
|
||||
*crc += 2;
|
||||
#endif
|
||||
memcpy(payload, data, len);
|
||||
}
|
||||
|
||||
static int xfer_payload_prep_next(struct qmrom_handle *handle,
|
||||
const char *step_name, struct stc *hstc,
|
||||
struct stc *sstc, struct stc *hstc_next,
|
||||
char **data, size_t *size)
|
||||
{
|
||||
int rc = 0, nb_retry = CONFIG_NB_RETRIES;
|
||||
CRC_TYPE *crc = (CRC_TYPE *)(hstc + 1);
|
||||
|
||||
do {
|
||||
int ss_rdy_rc, irq_up;
|
||||
sstc->all = 0;
|
||||
rc = qmrom_spi_transfer(handle->spi_handle, (char *)sstc,
|
||||
(const char *)hstc,
|
||||
hstc->len + sizeof(struct stc));
|
||||
if (hstc_next) {
|
||||
/* Don't wait idle, prepare the next hstc to be sent */
|
||||
size_t to_send = MIN(MAX_CHUNK_SIZE, *size);
|
||||
prepare_hstc(hstc_next, *data, to_send);
|
||||
*size -= to_send;
|
||||
*data += to_send;
|
||||
hstc_next = NULL;
|
||||
}
|
||||
ss_rdy_rc = qmrom_spi_wait_for_ready_line(
|
||||
handle->ss_rdy_handle, WAIT_SS_RDY_CHUNK_TIMEOUT);
|
||||
if (ss_rdy_rc) {
|
||||
LOG_ERR("%s Waiting for ss-rdy failed with %d (nb_retry %d , crc 0x%x)\n",
|
||||
step_name, ss_rdy_rc, nb_retry, *crc);
|
||||
gstats_ss_rdy_timeouts++;
|
||||
rc = -EAGAIN;
|
||||
}
|
||||
irq_up = qmrom_spi_read_irq_line(handle->ss_irq_handle);
|
||||
if ((!rc && !sstc->soc_flags.ready) || irq_up) {
|
||||
LOG_ERR("%s Retry rc %d, sstc 0x%08x, irq %d, crc %08x\n",
|
||||
step_name, rc, sstc->all, irq_up, *crc);
|
||||
rc = -EAGAIN;
|
||||
gstats_spi_errors++;
|
||||
}
|
||||
#ifdef CONFIG_INJECT_ERROR
|
||||
(*crc)--;
|
||||
#endif
|
||||
} while (rc && --nb_retry > 0);
|
||||
if (rc) {
|
||||
LOG_ERR("%s transfer failed with %d - (sstc 0x%08x)\n",
|
||||
step_name, rc, sstc->all);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int xfer_payload(struct qmrom_handle *handle, const char *step_name,
|
||||
struct stc *hstc, struct stc *sstc)
|
||||
{
|
||||
return xfer_payload_prep_next(handle, step_name, hstc, sstc, NULL, NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static int send_data_chunks(struct qmrom_handle *handle, char *data,
|
||||
size_t size)
|
||||
{
|
||||
struct fw_updater_status_t status;
|
||||
struct stc *hstc, *sstc, *hstc_current, *hstc_next;
|
||||
uint32_t chunk_nr = 0;
|
||||
char *rx, *tx;
|
||||
struct stc *hstc, *sstc;
|
||||
size_t to_send;
|
||||
CRC_TYPE *crc;
|
||||
int rc = 0;
|
||||
bool copy_data = !qmrom_data_dma_able(data);
|
||||
|
||||
qmrom_alloc(rx, MAX_CHUNK_SIZE + sizeof(struct stc));
|
||||
qmrom_alloc(tx, MAX_CHUNK_SIZE + sizeof(struct stc));
|
||||
LOG_DBG("chunk_nr:%u\n", chunk_nr);
|
||||
|
||||
qmrom_alloc(rx, MAX_CHUNK_SIZE + TRANPORT_HEADER_SIZE);
|
||||
qmrom_alloc(tx, 2 * (MAX_CHUNK_SIZE + TRANPORT_HEADER_SIZE));
|
||||
if (!rx || !tx) {
|
||||
LOG_ERR("Rx/Tx buffers allocation failure\n");
|
||||
if (rx)
|
||||
qmrom_free(rx);
|
||||
if (tx)
|
||||
qmrom_free(tx);
|
||||
return -1;
|
||||
rc = -ENOMEM;
|
||||
goto exit_nomem;
|
||||
}
|
||||
|
||||
hstc = (struct stc *)tx;
|
||||
sstc = (struct stc *)rx;
|
||||
hstc = (struct stc *)tx;
|
||||
hstc_current = hstc;
|
||||
hstc_next = (struct stc *)&tx[MAX_CHUNK_SIZE + TRANPORT_HEADER_SIZE];
|
||||
crc = (CRC_TYPE *)(hstc + 1);
|
||||
|
||||
/* wait for the QM to be ready */
|
||||
rc = qmrom_spi_wait_for_ready_line(handle->ss_rdy_handle,
|
||||
WAIT_SS_RDY_CHUNK_TIMEOUT);
|
||||
if (rc)
|
||||
LOG_ERR("Waiting for ss-rdy failed with %d\n", rc);
|
||||
|
||||
/* Sending the fw package header */
|
||||
LOG_INFO("Sending the fw package header (copy_data is %d)\n", copy_data);
|
||||
hstc->all = 0;
|
||||
hstc->host_flags.write = 1;
|
||||
hstc->len = sizeof(struct fw_pkg_hdr_t);
|
||||
size -= sizeof(struct fw_pkg_hdr_t);
|
||||
memcpy(&tx[sizeof(struct stc)], data, sizeof(struct fw_pkg_hdr_t));
|
||||
/* Move the data to the next offset minus the stc footprint */
|
||||
data += sizeof(struct fw_pkg_hdr_t) - (copy_data ? 0 : sizeof(struct stc));
|
||||
qmrom_spi_wait_for_ready_line(handle->ss_rdy_handle,
|
||||
WAIT_SS_RDY_TIMEOUT);
|
||||
rc = qmrom_spi_transfer(handle->spi_handle, rx, tx,
|
||||
sizeof(struct fw_pkg_hdr_t) + sizeof(struct stc));
|
||||
prepare_hstc(hstc, data, sizeof(struct fw_pkg_hdr_t));
|
||||
LOG_INFO("Sending the fw package header (%zu bytes, crc is 0x%08x)\n",
|
||||
sizeof(struct fw_pkg_hdr_t), *crc);
|
||||
// hexdump(LOG_INFO, hstc->payload + 4, sizeof(struct fw_pkg_hdr_t));
|
||||
rc = xfer_payload(handle, "fw package header", hstc, sstc);
|
||||
if (rc)
|
||||
{
|
||||
LOG_ERR("First qmrom_spi_transfer failed with %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
// hexdump(LOG_INFO, tx + sizeof(struct stc), sizeof(struct fw_pkg_hdr_t));
|
||||
goto exit;
|
||||
/* Move the data to the next offset minus the header footprint */
|
||||
size -= sizeof(struct fw_pkg_hdr_t);
|
||||
data += sizeof(struct fw_pkg_hdr_t);
|
||||
|
||||
/* Sending the image header */
|
||||
LOG_INFO("Sending the image header\n");
|
||||
hstc = prepare_hstc(copy_data, tx, data, sizeof(struct fw_pkg_img_hdr_t));
|
||||
prepare_hstc(hstc, data, sizeof(struct fw_pkg_img_hdr_t));
|
||||
LOG_INFO("Sending the image header (%zu bytes crc 0x%08x)\n",
|
||||
sizeof(struct fw_pkg_img_hdr_t), *crc);
|
||||
// hexdump(LOG_INFO, hstc->payload + 4, sizeof(struct fw_pkg_img_hdr_t));
|
||||
rc = xfer_payload(handle, "image header", hstc, sstc);
|
||||
if (rc)
|
||||
goto exit;
|
||||
size -= sizeof(struct fw_pkg_img_hdr_t);
|
||||
// hexdump(LOG_INFO, data + sizeof(struct stc),
|
||||
// sizeof(struct fw_pkg_img_hdr_t));
|
||||
data += sizeof(struct fw_pkg_img_hdr_t);
|
||||
qmrom_spi_wait_for_ready_line(handle->ss_rdy_handle,
|
||||
WAIT_SS_RDY_TIMEOUT);
|
||||
qmrom_spi_transfer(handle->spi_handle, rx, (const char *)hstc,
|
||||
sizeof(struct fw_pkg_img_hdr_t) +
|
||||
sizeof(struct stc));
|
||||
|
||||
/* Sending the cert chain */
|
||||
LOG_INFO("Sending the cert chain\n");
|
||||
#if MAX_CHUNK_SIZE < CRYPTO_IMAGES_CERT_PKG_SIZE
|
||||
hstc = prepare_hstc(true, tx, data, CRYPTO_IMAGES_CERT_KEY_SIZE);
|
||||
size -= CRYPTO_IMAGES_CERT_KEY_SIZE;
|
||||
data += CRYPTO_IMAGES_CERT_KEY_SIZE;
|
||||
qmrom_spi_wait_for_ready_line(handle->ss_rdy_handle,
|
||||
WAIT_SS_RDY_TIMEOUT);
|
||||
qmrom_spi_transfer(handle->spi_handle, rx, tx,
|
||||
CRYPTO_IMAGES_CERT_KEY_SIZE);
|
||||
|
||||
hstc = prepare_hstc(true, tx, data, CRYPTO_IMAGES_CERT_KEY_SIZE);
|
||||
size -= CRYPTO_IMAGES_CERT_KEY_SIZE;
|
||||
data += CRYPTO_IMAGES_CERT_KEY_SIZE;
|
||||
qmrom_spi_wait_for_ready_line(handle->ss_rdy_handle,
|
||||
WAIT_SS_RDY_TIMEOUT);
|
||||
qmrom_spi_transfer(handle->spi_handle, rx, tx,
|
||||
CRYPTO_IMAGES_CERT_KEY_SIZE);
|
||||
|
||||
hstc = prepare_hstc(true, tx, data, CRYPTO_IMAGES_CERT_CONTENT_SIZE);
|
||||
size -= CRYPTO_IMAGES_CERT_CONTENT_SIZE;
|
||||
data += CRYPTO_IMAGES_CERT_CONTENT_SIZE;
|
||||
qmrom_spi_wait_for_ready_line(handle->ss_rdy_handle,
|
||||
WAIT_SS_RDY_TIMEOUT);
|
||||
qmrom_spi_transfer(handle->spi_handle, rx, tx,
|
||||
CRYPTO_IMAGES_CERT_CONTENT_SIZE);
|
||||
#else
|
||||
hstc = prepare_hstc(copy_data, tx, data, CRYPTO_IMAGES_CERT_PKG_SIZE);
|
||||
prepare_hstc(hstc, data, CRYPTO_IMAGES_CERT_PKG_SIZE);
|
||||
LOG_INFO("Sending the cert chain (%d bytes crc 0x%08x)\n",
|
||||
CRYPTO_IMAGES_CERT_PKG_SIZE, *crc);
|
||||
rc = xfer_payload(handle, "cert chain", hstc, sstc);
|
||||
if (rc)
|
||||
goto exit;
|
||||
size -= CRYPTO_IMAGES_CERT_PKG_SIZE;
|
||||
// hexdump(LOG_INFO, data + sizeof(struct stc), 100);
|
||||
data += CRYPTO_IMAGES_CERT_PKG_SIZE;
|
||||
qmrom_spi_wait_for_ready_line(handle->ss_rdy_handle,
|
||||
WAIT_SS_RDY_TIMEOUT);
|
||||
qmrom_spi_transfer(handle->spi_handle, rx, (const char *)hstc,
|
||||
CRYPTO_IMAGES_CERT_PKG_SIZE + sizeof(struct stc));
|
||||
#endif
|
||||
|
||||
/* Sending the fw image */
|
||||
LOG_INFO("Sending the image\n");
|
||||
LOG_INFO("Sending the image (%zu bytes)\n", size);
|
||||
LOG_DBG("Sending a chunk (%zu bytes crc 0x%08x)\n",
|
||||
MIN(MAX_CHUNK_SIZE, size), *crc);
|
||||
prepare_hstc(hstc_current, data, MIN(MAX_CHUNK_SIZE, size));
|
||||
size -= hstc_current->len - CRC_SIZE;
|
||||
data += hstc_current->len - CRC_SIZE;
|
||||
do {
|
||||
to_send = MIN(MAX_CHUNK_SIZE, size);
|
||||
LOG_DBG("Sending a chunk of %zu bytes\n", to_send);
|
||||
hstc = prepare_hstc(copy_data, tx, data, to_send);
|
||||
qmrom_spi_wait_for_ready_line(handle->ss_rdy_handle,
|
||||
WAIT_SS_RDY_TIMEOUT);
|
||||
qmrom_spi_transfer(handle->spi_handle, rx, (const char *)hstc,
|
||||
to_send + sizeof(struct stc));
|
||||
data += to_send;
|
||||
size -= to_send;
|
||||
rc = xfer_payload_prep_next(handle, "data chunk", hstc_current,
|
||||
sstc, hstc_next, &data, &size);
|
||||
if (rc)
|
||||
goto exit;
|
||||
chunk_nr++;
|
||||
/* swap hstcs */
|
||||
hstc = hstc_current;
|
||||
hstc_current = hstc_next;
|
||||
hstc_next = hstc;
|
||||
} while (size);
|
||||
|
||||
qmrom_spi_wait_for_ready_line(handle->ss_rdy_handle,
|
||||
WAIT_SS_RDY_TIMEOUT);
|
||||
/* Sends the last now */
|
||||
rc = xfer_payload_prep_next(handle, "data chunk", hstc_current, sstc,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
exit:
|
||||
// tries to get the flashing status anyway...
|
||||
rc = run_fwupdater_get_status(handle, hstc, sstc, &status);
|
||||
if (rc) {
|
||||
LOG_ERR("run_fwupdater_get_status returned %d\n", rc);
|
||||
} else {
|
||||
if (!rc) {
|
||||
if (status.status) {
|
||||
LOG_ERR("%s failed, fw updater status %#x (suberror %#x)\n",
|
||||
__func__, status.status, status.suberror);
|
||||
LOG_ERR("Flashing failed, fw updater status %#x (errors: sub %#x, crc %u, rram %u, crypto %d)\n",
|
||||
status.status, status.suberror,
|
||||
status.crc_errors, status.rram_errors,
|
||||
status.crypto_errors);
|
||||
rc = status.status;
|
||||
} else {
|
||||
LOG_INFO("%s succeeded\n", __func__);
|
||||
}
|
||||
}
|
||||
if (gstats_ss_rdy_timeouts + gstats_spi_errors +
|
||||
status.crc_errors + status.rram_errors +
|
||||
status.crypto_errors) {
|
||||
LOG_WARN(
|
||||
"Flashing succeeded with errors (host %u, ss_rdy_timeout %u, QM %u, crc %u, rram %u, crypto %d)\n",
|
||||
gstats_spi_errors,
|
||||
gstats_ss_rdy_timeouts,
|
||||
status.spi_errors, status.crc_errors,
|
||||
status.rram_errors,
|
||||
status.crypto_errors);
|
||||
} else {
|
||||
LOG_INFO(
|
||||
"Flashing succeeded without any errors\n");
|
||||
}
|
||||
|
||||
if (!handle->skip_check_fw_boot)
|
||||
rc = check_fw_boot(handle);
|
||||
}
|
||||
} else {
|
||||
LOG_ERR("run_fwupdater_get_status returned %d\n", rc);
|
||||
}
|
||||
exit_nomem:
|
||||
if (rx)
|
||||
qmrom_free(rx);
|
||||
if (tx)
|
||||
qmrom_free(tx);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int check_fw_boot(struct qmrom_handle *handle)
|
||||
{
|
||||
uint8_t raw_flags;
|
||||
struct stc hstc, sstc;
|
||||
|
||||
handle->dev_ops.reset(handle->reset_handle);
|
||||
|
||||
qmrom_msleep(WAIT_REBOOT_DELAY_MS);
|
||||
|
||||
// Poll the QM
|
||||
sstc.all = 0;
|
||||
hstc.all = 0;
|
||||
while(sstc.all == 0)
|
||||
qmrom_spi_transfer(handle->spi_handle, (char *)&sstc,
|
||||
(const char *)&hstc, sizeof(hstc));
|
||||
|
||||
raw_flags = sstc.raw_flags;
|
||||
/* The ROM code sends the same quartets for the first byte of each xfers */
|
||||
if (((raw_flags & 0xf0) >> 4) == (raw_flags & 0xf)) {
|
||||
LOG_ERR("%s: firmware not properly started: %#x\n",
|
||||
__func__, raw_flags);
|
||||
return -2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ int qm357xx_rom_b0_probe_device(struct qmrom_handle *handle)
|
|||
if (handle->chip_rev != CHIP_REVISION_B0) {
|
||||
LOG_ERR("%s: wrong chip revision 0x%x\n", __func__,
|
||||
handle->chip_rev);
|
||||
handle->chip_rev = -1;
|
||||
handle->chip_rev = CHIP_REVISION_UNKNOWN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -378,7 +378,7 @@ static int qm357xx_rom_b0_flash_debug_cert(struct qmrom_handle *handle,
|
|||
WAITING_FOR_DEBUG_CERT_DATA);
|
||||
check_stcs(__func__, __LINE__, handle);
|
||||
qmrom_msleep(SPI_READY_TIMEOUT_MS);
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int qm357xx_rom_b0_erase_debug_cert(struct qmrom_handle *handle)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,11 @@
|
|||
#define CHIP_VERSION_DEV_REV_PAYLOAD_OFFSET 6
|
||||
#define CHUNK_SIZE_C0 2040
|
||||
#define SPI_READY_TIMEOUT_MS_C0 200
|
||||
#define SPI_ERROR_DETECTED_DELAY_MS 65
|
||||
|
||||
#ifndef CONFIG_CHUNK_FLASHING_RETRIES
|
||||
#define CONFIG_CHUNK_FLASHING_RETRIES 10
|
||||
#endif
|
||||
|
||||
#ifdef C0_WRITE_STATS
|
||||
#include <linux/ktime.h>
|
||||
|
|
@ -187,11 +192,11 @@ static int qm357xx_rom_c0_poll_cmd_resp(struct qmrom_handle *handle)
|
|||
} else
|
||||
qm357xx_rom_c0_poll_soc(handle);
|
||||
} while (retries--);
|
||||
if (retries <= 0)
|
||||
LOG_ERR("%s failed after %d replies\n", __func__,
|
||||
handle->comms_retries);
|
||||
|
||||
return retries > 0 ? 0 : -1;
|
||||
LOG_ERR("%s failed after %d replies\n", __func__,
|
||||
handle->comms_retries);
|
||||
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
int qm357xx_rom_c0_probe_device(struct qmrom_handle *handle)
|
||||
|
|
@ -235,7 +240,7 @@ int qm357xx_rom_c0_probe_device(struct qmrom_handle *handle)
|
|||
((handle->chip_rev != CHIP_REVISION_C2))) {
|
||||
LOG_ERR("%s: wrong chip revision %#x\n", __func__,
|
||||
handle->chip_rev);
|
||||
handle->chip_rev = -1;
|
||||
handle->chip_rev = CHIP_REVISION_UNKNOWN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -311,7 +316,7 @@ static int qm357xx_rom_c0_flash_data(struct qmrom_handle *handle,
|
|||
struct firmware *fw, uint8_t cmd,
|
||||
uint8_t resp, bool skip_last_check)
|
||||
{
|
||||
int rc, sent = 0;
|
||||
int rc, sent = 0, nb_poll_retry, chunk = 0;
|
||||
const char *bin_data = (const char *)fw->data;
|
||||
#ifdef C0_WRITE_STATS
|
||||
ktime_t start_time;
|
||||
|
|
@ -338,6 +343,18 @@ static int qm357xx_rom_c0_flash_data(struct qmrom_handle *handle,
|
|||
break;
|
||||
}
|
||||
qm357xx_rom_c0_poll_soc(handle);
|
||||
nb_poll_retry = CONFIG_CHUNK_FLASHING_RETRIES;
|
||||
while (handle->sstc->soc_flags.err && --nb_poll_retry >= 0) {
|
||||
qmrom_msleep(SPI_ERROR_DETECTED_DELAY_MS);
|
||||
qm357xx_rom_c0_poll_soc(handle);
|
||||
LOG_ERR("%s: spi error detected for cmd %#x chunk %d, retry %d, soc_flags 0x%02x\n",
|
||||
__func__, cmd, chunk,
|
||||
CONFIG_CHUNK_FLASHING_RETRIES - nb_poll_retry,
|
||||
handle->sstc->raw_flags);
|
||||
rc = qm357xx_rom_write_size_cmd32_c0(
|
||||
handle, cmd, tx_bytes, bin_data - tx_bytes);
|
||||
qm357xx_rom_c0_poll_soc(handle);
|
||||
}
|
||||
#ifdef C0_WRITE_STATS
|
||||
if (tx_bytes == CHUNK_SIZE_C0)
|
||||
update_write_max_chunk_stats(start_time);
|
||||
|
|
@ -354,6 +371,7 @@ static int qm357xx_rom_c0_flash_data(struct qmrom_handle *handle,
|
|||
else
|
||||
return SPI_PROTO_WRONG_RESP;
|
||||
}
|
||||
chunk++;
|
||||
}
|
||||
qmrom_msleep(SPI_READY_TIMEOUT_MS_C0);
|
||||
return 0;
|
||||
|
|
@ -364,10 +382,12 @@ qm357xx_rom_c0_flash_unstitched_fw(struct qmrom_handle *handle,
|
|||
const struct unstitched_firmware *all_fws)
|
||||
{
|
||||
int rc = 0;
|
||||
uint8_t flash_cmd = handle->qm357xx_soc_info.lcs_state ==
|
||||
CC_BSV_SECURE_LCS ?
|
||||
ROM_CMD_C0_SEC_LOAD_OEM_IMG_TO_RRAM :
|
||||
ROM_CMD_C0_SEC_LOAD_ICV_IMG_TO_RRAM;
|
||||
uint8_t flash_cmd =
|
||||
handle->qm357xx_soc_info.lcs_state == CC_BSV_SECURE_LCS ||
|
||||
handle->qm357xx_soc_info.lcs_state ==
|
||||
CC_BSV_RMA_LCS ?
|
||||
ROM_CMD_C0_SEC_LOAD_OEM_IMG_TO_RRAM :
|
||||
ROM_CMD_C0_SEC_LOAD_ICV_IMG_TO_RRAM;
|
||||
|
||||
if (all_fws->key1_crt->data[HBK_LOC] == HBK_2E_ICV &&
|
||||
handle->qm357xx_soc_info.lcs_state != CC_BSV_CHIP_MANUFACTURE_LCS) {
|
||||
|
|
@ -378,7 +398,8 @@ qm357xx_rom_c0_flash_unstitched_fw(struct qmrom_handle *handle,
|
|||
}
|
||||
|
||||
if (all_fws->key1_crt->data[HBK_LOC] == HBK_2E_OEM &&
|
||||
handle->qm357xx_soc_info.lcs_state != CC_BSV_SECURE_LCS) {
|
||||
handle->qm357xx_soc_info.lcs_state != CC_BSV_SECURE_LCS &&
|
||||
handle->qm357xx_soc_info.lcs_state != CC_BSV_RMA_LCS) {
|
||||
LOG_ERR("%s: Trying to flash an OEM fw on a non OEM platform\n",
|
||||
__func__);
|
||||
rc = -EINVAL;
|
||||
|
|
@ -416,6 +437,7 @@ qm357xx_rom_c0_flash_unstitched_fw(struct qmrom_handle *handle,
|
|||
LOG_ERR("%s: Waiting for WAITING_FOR_FIRST_KEY_CERT(%#x) but got %#x\n",
|
||||
__func__, WAITING_FOR_FIRST_KEY_CERT,
|
||||
handle->sstc->payload[0]);
|
||||
rc = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
|
@ -447,6 +469,43 @@ qm357xx_rom_c0_flash_unstitched_fw(struct qmrom_handle *handle,
|
|||
dump_stats();
|
||||
#endif
|
||||
|
||||
if (qmrom_spi_read_irq_line(handle->ss_irq_handle)) {
|
||||
int retries = handle->comms_retries;
|
||||
|
||||
do {
|
||||
/* A final product id error likely occured */
|
||||
qmrom_pre_read_c0(handle);
|
||||
qmrom_read_c0(handle);
|
||||
rc = handle->sstc->payload[0];
|
||||
if (rc) {
|
||||
LOG_ERR("%s: flashing error %d (0x%x) detected\n",
|
||||
__func__, rc, rc);
|
||||
break;
|
||||
}
|
||||
} while (--retries);
|
||||
|
||||
if (retries <= 0) {
|
||||
LOG_ERR("%s: flashing error detected but couldn't be fetched\n",
|
||||
__func__);
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Flashing is done, the fw should reboot, check we are not still talking to the ROM code */
|
||||
if (!rc && !handle->skip_check_fw_boot) {
|
||||
uint8_t raw_flags;
|
||||
|
||||
qmrom_msleep(SPI_READY_TIMEOUT_MS_C0);
|
||||
qm357xx_rom_c0_poll_soc(handle);
|
||||
raw_flags = handle->sstc->raw_flags;
|
||||
/* The ROM code sends the same quartets for the first byte of each xfers */
|
||||
if (((raw_flags & 0xf0) >> 4) == (raw_flags & 0xf)) {
|
||||
LOG_ERR("%s: firmware not properly started: %#x\n",
|
||||
__func__, raw_flags);
|
||||
rc = -2;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
qmrom_free(all_fws->fw_img);
|
||||
qmrom_free(all_fws->fw_crt);
|
||||
|
|
|
|||
|
|
@ -80,16 +80,16 @@ int qm357xx_rom_probe_device(struct qmrom_handle *handle)
|
|||
{
|
||||
int rc;
|
||||
|
||||
/* Test B0 first */
|
||||
//rc = qm357xx_rom_b0_probe_device(handle);
|
||||
//if (!rc)
|
||||
//return rc;
|
||||
|
||||
/* Test C0 next */
|
||||
/* Test C0 first */
|
||||
rc = qm357xx_rom_c0_probe_device(handle);
|
||||
if (!rc)
|
||||
return rc;
|
||||
|
||||
/* Test B0 next */
|
||||
rc = qm357xx_rom_b0_probe_device(handle);
|
||||
if (!rc)
|
||||
return rc;
|
||||
|
||||
/* None matched!!! */
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -315,22 +315,33 @@ int qm357xx_rom_unpack_fw_macro_pkg(const struct firmware *fw,
|
|||
struct unstitched_firmware *all_fws)
|
||||
{
|
||||
int rc = 0;
|
||||
uint8_t *p_key1;
|
||||
uint8_t *p_key2;
|
||||
uint8_t *p_crt;
|
||||
uint8_t *p_fw;
|
||||
char *fw_data;
|
||||
uint32_t fw_size;
|
||||
struct fw_pkg_img_hdr_t *fw_pkg_img_hdr;
|
||||
struct firmware fw_pkg;
|
||||
|
||||
rc = qm357xx_rom_fw_macro_pkg_get_fw_idx(fw, 0, &fw_size, &fw_data);
|
||||
if (rc) {
|
||||
LOG_ERR("%s: FW MACRO PACKAGE corupted = %d\n", __func__, rc);
|
||||
goto err;
|
||||
return rc;
|
||||
}
|
||||
|
||||
fw_pkg.data = (const uint8_t *)fw_data;
|
||||
fw_pkg.size = fw_size;
|
||||
return qm357xx_rom_unpack_fw_pkg(&fw_pkg, all_fws);
|
||||
}
|
||||
|
||||
int qm357xx_rom_unpack_fw_pkg(const struct firmware *fw_pkg,
|
||||
struct unstitched_firmware *all_fws)
|
||||
{
|
||||
int rc = 0;
|
||||
uint8_t *p_key1;
|
||||
uint8_t *p_key2;
|
||||
uint8_t *p_crt;
|
||||
uint8_t *p_fw;
|
||||
struct fw_pkg_img_hdr_t *fw_pkg_img_hdr;
|
||||
|
||||
fw_pkg_img_hdr =
|
||||
(struct fw_pkg_img_hdr_t *)(fw_data +
|
||||
(struct fw_pkg_img_hdr_t *)(fw_pkg->data +
|
||||
sizeof(struct fw_pkg_hdr_t));
|
||||
|
||||
if (fw_pkg_img_hdr->magic != CRYPTO_FIRMWARE_IMAGE_MAGIC_VALUE) {
|
||||
|
|
|
|||
|
|
@ -121,8 +121,9 @@ int qmrom_probe_device(struct qmrom_handle *handle,
|
|||
}
|
||||
|
||||
struct qmrom_handle *qmrom_init(void *spi_handle, void *reset_handle,
|
||||
void *ss_rdy_handle, int spi_speed,
|
||||
int comms_retries, reset_device_fn reset,
|
||||
void *ss_rdy_handle, void *ss_irq_handle,
|
||||
int spi_speed, int comms_retries,
|
||||
reset_device_fn reset,
|
||||
enum device_generation_e dev_gen_hint)
|
||||
{
|
||||
struct qmrom_handle *handle;
|
||||
|
|
@ -144,10 +145,12 @@ struct qmrom_handle *qmrom_init(void *spi_handle, void *reset_handle,
|
|||
handle->spi_handle = spi_handle;
|
||||
handle->reset_handle = reset_handle;
|
||||
handle->ss_rdy_handle = ss_rdy_handle;
|
||||
handle->ss_irq_handle = ss_irq_handle;
|
||||
handle->comms_retries = comms_retries;
|
||||
handle->chip_rev = CHIP_REVISION_UNKNOWN;
|
||||
handle->device_version = -1;
|
||||
handle->spi_speed = spi_speed;
|
||||
handle->skip_check_fw_boot = false;
|
||||
|
||||
handle->dev_ops.reset = reset;
|
||||
|
||||
|
|
|
|||
|
|
@ -56,12 +56,20 @@
|
|||
|
||||
#include "qm35.h"
|
||||
#include "uci_ioctls.h"
|
||||
#include "qm35_coredump_ioctls.h"
|
||||
#include "hsspi.h"
|
||||
#include "hsspi_uci.h"
|
||||
#include "hsspi_test.h"
|
||||
#include "hsspi_coredump.h"
|
||||
|
||||
#define QM35_REGULATOR_DELAY_US 1000
|
||||
#define QMROM_RETRIES 10
|
||||
|
||||
#ifndef CONFIG_FLASHING_RETRIES
|
||||
#define CONFIG_FLASHING_RETRIES 10 /* Intentionally high value */
|
||||
#endif
|
||||
|
||||
/* Redefine certificate error here since original definitions are separate */
|
||||
#define REGULATORS_ENABLED(x) (x->vdd1 || x->vdd2 || x->vdd3 || x->vdd4)
|
||||
|
||||
#ifndef NO_UWB_HAL
|
||||
|
|
@ -99,6 +107,11 @@ static bool wake_use_csn = false;
|
|||
module_param(wake_use_csn, bool, 0644);
|
||||
MODULE_PARM_DESC(wake_use_csn, "Use HSSPI CSn pin to wake up QM35");
|
||||
|
||||
static bool wake_on_ssirq = true;
|
||||
module_param(wake_on_ssirq, bool, 0644);
|
||||
MODULE_PARM_DESC(wake_on_ssirq,
|
||||
"Allow QM35 to wakeup the platform using ss_irq");
|
||||
|
||||
int trace_spi_xfers;
|
||||
module_param(trace_spi_xfers, int, 0644);
|
||||
MODULE_PARM_DESC(trace_spi_xfers, "Trace all the SPI transfers");
|
||||
|
|
@ -107,6 +120,10 @@ int qmrom_retries = QMROM_RETRIES;
|
|||
module_param(qmrom_retries, int, 0644);
|
||||
MODULE_PARM_DESC(qmrom_retries, "QMROM retries");
|
||||
|
||||
int flashing_retries = CONFIG_FLASHING_RETRIES;
|
||||
module_param(flashing_retries, int, 0644);
|
||||
MODULE_PARM_DESC(flashing_retries, "Flashing retries");
|
||||
|
||||
int reset_on_error = 1;
|
||||
module_param(reset_on_error, int, 0644);
|
||||
MODULE_PARM_DESC(reset_on_error, "Reset the QM35 on successive errors");
|
||||
|
|
@ -292,7 +309,7 @@ static ssize_t uci_write(struct file *filp, const char __user *buf, size_t len,
|
|||
|
||||
p->write_done = ∁
|
||||
|
||||
if (copy_from_user(p->data, buf, len)) {
|
||||
if (copy_from_user(p->data, buf, (u16)len)) {
|
||||
ret = -EFAULT;
|
||||
goto free;
|
||||
}
|
||||
|
|
@ -304,7 +321,7 @@ static ssize_t uci_write(struct file *filp, const char __user *buf, size_t len,
|
|||
|
||||
wait_for_completion(&comp);
|
||||
|
||||
ret = p->status ? p->status : len;
|
||||
ret = p->status ? p->status : (u16)len;
|
||||
free:
|
||||
uci_packet_free(p);
|
||||
return ret;
|
||||
|
|
@ -335,12 +352,85 @@ static const struct file_operations uci_fops = {
|
|||
.poll = uci_poll,
|
||||
};
|
||||
|
||||
/*
|
||||
* coredump_ioctl() - ioctl operation for coredump device.
|
||||
*
|
||||
*/
|
||||
static long coredump_ioctl(struct file *filp, unsigned int cmd,
|
||||
unsigned long args)
|
||||
{
|
||||
void __user *argp = (void __user *)args;
|
||||
struct miscdevice *dev = filp->private_data;
|
||||
struct qm35_ctx *qm35_hdl =
|
||||
container_of(dev, struct qm35_ctx, coredump_dev);
|
||||
|
||||
switch (cmd) {
|
||||
case QM35_COREDUMP_FORCE: {
|
||||
return coredump_layer_force_coredump(&qm35_hdl->coredump_layer);
|
||||
}
|
||||
case QM35_COREDUMP_GET_SIZE: {
|
||||
uint32_t size = coredump_layer_get_coredump_size(
|
||||
&qm35_hdl->coredump_layer);
|
||||
if (copy_to_user(argp, &size, sizeof(size)))
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
dev_err(&qm35_hdl->spi->dev, "unknown ioctl %x to %s device\n",
|
||||
cmd, qm35_hdl->uci_dev.name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t coredump_read(struct file *filp, char __user *buf, size_t len,
|
||||
loff_t *off)
|
||||
{
|
||||
struct miscdevice *dev = filp->private_data;
|
||||
struct qm35_ctx *qm35_hdl =
|
||||
container_of(dev, struct qm35_ctx, coredump_dev);
|
||||
ssize_t ret = 0;
|
||||
uint32_t size =
|
||||
coredump_layer_get_coredump_size(&qm35_hdl->coredump_layer);
|
||||
uint8_t *cd = coredump_layer_get_coredump(&qm35_hdl->coredump_layer);
|
||||
|
||||
if (IS_ERR(cd))
|
||||
return PTR_ERR(cd);
|
||||
|
||||
ret = simple_read_from_buffer(buf, len, off, cd, size);
|
||||
if (ret == 0 || *off == size) {
|
||||
/* reset coredump */
|
||||
coredump_layer_reset_status(&qm35_hdl->coredump_layer);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __poll_t coredump_poll(struct file *filp, struct poll_table_struct *wait)
|
||||
{
|
||||
struct miscdevice *dev = filp->private_data;
|
||||
struct qm35_ctx *qm35_ctx =
|
||||
container_of(dev, struct qm35_ctx, coredump_dev);
|
||||
__poll_t mask = 0;
|
||||
|
||||
poll_wait(filp, &qm35_ctx->coredump_layer.wq, wait);
|
||||
|
||||
if (coredump_layer_new_coredump_available(&qm35_ctx->coredump_layer))
|
||||
mask |= EPOLLIN;
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static const struct file_operations coredump_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = coredump_ioctl,
|
||||
.read = coredump_read,
|
||||
.poll = coredump_poll,
|
||||
};
|
||||
|
||||
static irqreturn_t qm35_irq_handler(int irq, void *qm35_ctx)
|
||||
{
|
||||
struct qm35_ctx *qm35_hdl = qm35_ctx;
|
||||
|
||||
//printk("qm35 ss-irq handler");
|
||||
|
||||
spin_lock(&qm35_hdl->lock);
|
||||
qm35_hdl->state = QM35_CTRL_STATE_READY;
|
||||
spin_unlock(&qm35_hdl->lock);
|
||||
|
|
@ -362,7 +452,6 @@ static void reenable_ss_irq(struct hsspi *hsspi)
|
|||
static irqreturn_t qm35_ss_rdy_handler(int irq, void *data)
|
||||
{
|
||||
struct qm35_ctx *qm35_hdl = data;
|
||||
|
||||
#ifdef CONFIG_QM35_DEBOUNCE_TIME_US
|
||||
static ktime_t old_time;
|
||||
ktime_t current_time;
|
||||
|
|
@ -382,8 +471,13 @@ static irqreturn_t qm35_ss_rdy_handler(int irq, void *data)
|
|||
if (wake_use_wakeup)
|
||||
gpiod_set_value(qm35_hdl->gpio_wakeup, 0);
|
||||
|
||||
hsspi_clear_spi_slave_busy(&qm35_hdl->hsspi);
|
||||
hsspi_set_spi_slave_ready(&qm35_hdl->hsspi);
|
||||
if (!qm35_hdl->flashing) {
|
||||
hsspi_clear_spi_slave_busy(&qm35_hdl->hsspi);
|
||||
hsspi_set_spi_slave_ready(&qm35_hdl->hsspi);
|
||||
} else {
|
||||
qm35_hdl->qmrom_qm_ready = true;
|
||||
wake_up_interruptible(&qm35_hdl->qmrom_wq_ready);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
|
@ -414,55 +508,49 @@ static irqreturn_t qm35_exton_handler(int irq, void *data)
|
|||
struct qm35_ctx *qm35_hdl = data;
|
||||
|
||||
hsspi_clear_spi_slave_ready(&qm35_hdl->hsspi);
|
||||
|
||||
if (qm35_hdl->hsspi.waiting_ss_rdy)
|
||||
qm35_wakeup(&qm35_hdl->hsspi);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void qm35_hsspi_start(struct qm35_ctx *qm35_hdl)
|
||||
{
|
||||
int irq;
|
||||
|
||||
/* nothing to do as HSSPI is already started */
|
||||
if (qm35_hdl->hsspi.state == HSSPI_RUNNING)
|
||||
return;
|
||||
|
||||
irq = gpiod_to_irq(qm35_hdl->gpio_ss_rdy);
|
||||
if (irq >= 0) {
|
||||
enable_irq(irq);
|
||||
enable_irq(qm35_hdl->ss_rdy_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.
|
||||
*/
|
||||
/* 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);
|
||||
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;
|
||||
|
||||
/* nothing to do as HSSPI is already stopped */
|
||||
if (qm35_hdl->hsspi.state == HSSPI_STOPPED)
|
||||
return;
|
||||
|
||||
hsspi_stop(&qm35_hdl->hsspi);
|
||||
|
||||
irq = gpiod_to_irq(qm35_hdl->gpio_ss_rdy);
|
||||
if (irq >= 0) {
|
||||
disable_irq_nosync(irq);
|
||||
disable_irq_nosync(qm35_hdl->ss_rdy_irq);
|
||||
|
||||
clear_bit(HSSPI_FLAGS_SS_READY, qm35_hdl->hsspi.flags);
|
||||
}
|
||||
clear_bit(HSSPI_FLAGS_SS_READY, qm35_hdl->hsspi.flags);
|
||||
}
|
||||
|
||||
int qm35_reset_sync(struct qm35_ctx *qm35_hdl)
|
||||
|
|
@ -476,13 +564,105 @@ int qm35_reset_sync(struct qm35_ctx *qm35_hdl)
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qm_firmware_flash_fw(struct qm35_ctx *qm35_hdl,
|
||||
struct qmrom_handle *h,
|
||||
const struct firmware *fw)
|
||||
{
|
||||
int rc = 0, nb_retries = flashing_retries;
|
||||
do {
|
||||
/* If the previous flashing failed, re-enter the QM
|
||||
* rom code so it will be in the same initial state
|
||||
*/
|
||||
if (rc)
|
||||
qmrom_reboot_bootloader(h);
|
||||
|
||||
rc = qm357xx_rom_flash_fw(h, fw);
|
||||
if (rc)
|
||||
dev_err(&qm35_hdl->spi->dev,
|
||||
"Attempt %d: flashing failed with %d!\n",
|
||||
flashing_retries - nb_retries, rc);
|
||||
if (rc == PEG_ERR_FIRST_KEY_CERT_OR_FW_VER)
|
||||
break;
|
||||
} while (rc && --nb_retries > 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int qm_firmware_flash_macro_pkg(struct qm35_ctx *qm35_hdl,
|
||||
struct qmrom_handle *h,
|
||||
const struct firmware *fw)
|
||||
{
|
||||
int rc, nb_retries = flashing_retries;
|
||||
char *fw_data;
|
||||
uint32_t fw_size;
|
||||
|
||||
rc = qm357xx_rom_fw_macro_pkg_get_fw_idx(fw, 1, &fw_size, &fw_data);
|
||||
if (rc) {
|
||||
dev_err(&qm35_hdl->spi->dev,
|
||||
"FW MACRO PACKAGE corrupted = %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
if (*(uint32_t *)fw_data != CRYPTO_FIRMWARE_PACK_MAGIC_VALUE) {
|
||||
rc = -EINVAL;
|
||||
dev_err(&qm35_hdl->spi->dev,
|
||||
"FW PACKAGE not found - magic is %04x, size is %d\n",
|
||||
*(uint32_t *)fw_data, fw_size);
|
||||
return rc;
|
||||
}
|
||||
|
||||
do {
|
||||
/* If the previous flashing failed, re-enter the QM
|
||||
* rom code so it will be in the same initial state
|
||||
*/
|
||||
if (spi_speed_hz)
|
||||
qmrom_spi_set_freq(spi_speed_hz);
|
||||
else
|
||||
qmrom_spi_set_freq(DEFAULT_SPI_CLOCKRATE);
|
||||
|
||||
if (rc)
|
||||
qmrom_reboot_bootloader(h);
|
||||
|
||||
// The Macro Package contain the fw updater and the package
|
||||
// simply flash the first and provide the second to
|
||||
// the updater lib
|
||||
h->skip_check_fw_boot = true;
|
||||
rc = qm357xx_rom_flash_fw(h, fw);
|
||||
h->skip_check_fw_boot = false;
|
||||
if (rc) {
|
||||
dev_err(&qm35_hdl->spi->dev,
|
||||
"Attempt %d: fw updater flashing failed with %d!\n",
|
||||
flashing_retries - nb_retries, rc);
|
||||
if (rc == PEG_ERR_FIRST_KEY_CERT_OR_FW_VER)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
// now flash the firmware proper
|
||||
if (fu_spi_speed_hz)
|
||||
qmrom_spi_set_freq(fu_spi_speed_hz);
|
||||
else
|
||||
qmrom_spi_set_freq(FWUPDATER_SPI_SPEED_HZ);
|
||||
rc = run_fwupdater(h, fw_data, fw_size);
|
||||
if (rc) {
|
||||
dev_err(&qm35_hdl->spi->dev,
|
||||
"Attempt %d: fw app flashing failed with %d!\n",
|
||||
flashing_retries - nb_retries, rc);
|
||||
}
|
||||
} while (rc && --nb_retries > 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int qm_firmware_flashing(void *handle, struct qmrom_handle *h,
|
||||
bool *is_macro_pkg, bool use_prod_fw)
|
||||
{
|
||||
struct qm35_ctx *qm35_hdl = (struct qm35_ctx *)handle;
|
||||
struct spi_device *spi = qm35_hdl->spi;
|
||||
int rc = 0;
|
||||
const struct firmware *fw;
|
||||
int ret = 0;
|
||||
#ifdef C0_WRITE_STATS
|
||||
uint64_t elapsed_time_ns;
|
||||
ktime_t start_time;
|
||||
#endif
|
||||
|
||||
fw = qmrom_spi_get_firmware(&spi->dev, h, is_macro_pkg, use_prod_fw);
|
||||
if (fw == NULL) {
|
||||
|
|
@ -490,59 +670,29 @@ static int qm_firmware_flashing(void *handle, struct qmrom_handle *h,
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (!*is_macro_pkg) {
|
||||
ret = qm357xx_rom_flash_fw(h, fw);
|
||||
dev_dbg(&spi->dev, "Return qm357xx_rom_flash_fw = %d!\n", ret);
|
||||
} else {
|
||||
// The Macro Package contain the fw updater and the package
|
||||
// simply flash the first and provide the second to
|
||||
// the updater lib
|
||||
ret = qm357xx_rom_flash_fw(h, fw);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev,
|
||||
"Firmware updater download failed with %d!\n",
|
||||
ret);
|
||||
} else {
|
||||
// now flash the firmware proper
|
||||
char *fw_data;
|
||||
uint32_t fw_size;
|
||||
ret = qm357xx_rom_fw_macro_pkg_get_fw_idx(
|
||||
fw, 1, &fw_size, &fw_data);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev,
|
||||
"FW MACRO PACKAGE corupted = %d\n",
|
||||
ret);
|
||||
goto error;
|
||||
}
|
||||
#ifdef C0_WRITE_STATS
|
||||
start_time = ktime_get();
|
||||
#endif
|
||||
|
||||
qm35_hdl->flashing = true;
|
||||
if (!*is_macro_pkg)
|
||||
rc = qm_firmware_flash_fw(qm35_hdl, h, fw);
|
||||
else
|
||||
rc = qm_firmware_flash_macro_pkg(qm35_hdl, h, fw);
|
||||
|
||||
if (*(uint32_t *)fw_data ==
|
||||
CRYPTO_FIRMWARE_PACK_MAGIC_VALUE) {
|
||||
#ifdef C0_WRITE_STATS
|
||||
ktime_t start_time = ktime_get();
|
||||
uint64_t elapsed_time_ns;
|
||||
elapsed_time_ns = ktime_to_ns(ktime_sub(ktime_get(), start_time));
|
||||
if (!rc)
|
||||
dev_warn(&spi->dev, "Firmware flashed in %llu us\n",
|
||||
div_u64(elapsed_time_ns, 1000));
|
||||
#endif
|
||||
ret = run_fwupdater(h, fw_data, fw_size);
|
||||
#ifdef C0_WRITE_STATS
|
||||
elapsed_time_ns = ktime_to_ns(
|
||||
ktime_sub(ktime_get(), start_time));
|
||||
dev_warn(
|
||||
&spi->dev,
|
||||
"Fw_updater flashed - %u bytes in %llu us\n",
|
||||
fw_size,
|
||||
div_u64(elapsed_time_ns, 1000));
|
||||
#endif
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
dev_err(&spi->dev,
|
||||
"FW PACKAGE not found - %04x! fw_size = %d\n",
|
||||
*(uint32_t *)fw_data, fw_size);
|
||||
}
|
||||
}
|
||||
error:
|
||||
qmrom_spi_reset_device(qm35_hdl);
|
||||
}
|
||||
|
||||
qmrom_spi_release_firmware(fw);
|
||||
return ret;
|
||||
qm35_hdl->flashing = false;
|
||||
|
||||
// reset the device anyway
|
||||
qmrom_spi_reset_device(qm35_hdl);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int qm_firmware_load(struct qm35_ctx *qm35_hdl)
|
||||
|
|
@ -555,9 +705,11 @@ static int qm_firmware_load(struct qm35_ctx *qm35_hdl)
|
|||
|
||||
qm35_set_state(qm35_hdl, QM35_CTRL_STATE_FW_DOWNLOADING);
|
||||
|
||||
qmrom_set_log_device(&spi->dev, LOG_DBG);
|
||||
qmrom_set_log_device(&spi->dev, LOG_WARN);
|
||||
|
||||
h = qmrom_init(&spi->dev, qm35_hdl, qm35_hdl->gpio_ss_rdy,
|
||||
enable_irq(qm35_hdl->ss_rdy_irq);
|
||||
|
||||
h = qmrom_init(&spi->dev, qm35_hdl, qm35_hdl, qm35_hdl->gpio_ss_irq,
|
||||
qmrom_spi_speed_hz, qmrom_retries,
|
||||
qmrom_spi_reset_device, DEVICE_GEN_QM357XX);
|
||||
if (!h) {
|
||||
|
|
@ -587,7 +739,7 @@ static int qm_firmware_load(struct qm35_ctx *qm35_hdl)
|
|||
"SoC info not supported on chip revision A0\n");
|
||||
}
|
||||
|
||||
dev_info(&spi->dev, "Starting device flashing!\n");
|
||||
dev_dbg(&spi->dev, "Starting device flashing!\n");
|
||||
ret = qm_firmware_flashing(qm35_hdl, h, &is_macro_pkg, true);
|
||||
if (ret) {
|
||||
qmrom_reboot_bootloader(h);
|
||||
|
|
@ -597,9 +749,11 @@ static int qm_firmware_load(struct qm35_ctx *qm35_hdl)
|
|||
if (ret)
|
||||
dev_err(&spi->dev, "Firmware download failed with %d!\n", ret);
|
||||
else
|
||||
dev_info(&spi->dev, "Device flashing completed!\n");
|
||||
dev_info(&spi->dev, "Device flashing succeeded!\n");
|
||||
|
||||
out:
|
||||
disable_irq_nosync(qm35_hdl->ss_rdy_irq);
|
||||
|
||||
qm35_set_state(qm35_hdl, state);
|
||||
|
||||
return ret;
|
||||
|
|
@ -640,7 +794,6 @@ int qm_get_soc_id(struct qm35_ctx *qm35_hdl, uint8_t *soc_id)
|
|||
static int hsspi_irqs_setup(struct qm35_ctx *qm35_ctx)
|
||||
{
|
||||
int ret, irq;
|
||||
unsigned long ss_irqflags;
|
||||
|
||||
/* Get READY GPIO */
|
||||
qm35_ctx->gpio_ss_rdy =
|
||||
|
|
@ -650,14 +803,15 @@ static int hsspi_irqs_setup(struct qm35_ctx *qm35_ctx)
|
|||
|
||||
irq = gpiod_to_irq(qm35_ctx->gpio_ss_rdy);
|
||||
if (irq < 0) {
|
||||
dev_err(&qm35_ctx->spi->dev, "%s: gpiod_to_irq returns %d",
|
||||
__func__, irq);
|
||||
dev_err(&qm35_ctx->spi->dev,
|
||||
"%s: gpiod_to_irq(ss-ready) returns %d", __func__, irq);
|
||||
return irq;
|
||||
}
|
||||
qm35_ctx->ss_rdy_irq = irq;
|
||||
ret = devm_request_irq(&qm35_ctx->spi->dev, irq, &qm35_ss_rdy_handler,
|
||||
IRQF_TRIGGER_RISING, "hsspi-ss-rdy", qm35_ctx);
|
||||
if (ret) {
|
||||
dev_err(&qm35_ctx->spi->dev, "%s: devm_request_irq returns %d",
|
||||
dev_err(&qm35_ctx->spi->dev, "%s: devm_request_irq returned %d",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -667,33 +821,44 @@ static int hsspi_irqs_setup(struct qm35_ctx *qm35_ctx)
|
|||
disable_irq(irq);
|
||||
|
||||
/* get SS_IRQ GPIO */
|
||||
qm35_ctx->gpio_ss_irq = devm_gpiod_get_optional(&qm35_ctx->spi->dev,
|
||||
"ss-irq", GPIOD_IN);
|
||||
qm35_ctx->gpio_ss_irq =
|
||||
devm_gpiod_get(&qm35_ctx->spi->dev, "ss-irq", GPIOD_IN);
|
||||
if (IS_ERR(qm35_ctx->gpio_ss_irq)) {
|
||||
dev_err(&qm35_ctx->spi->dev,
|
||||
"%s: gpiod_get_index(ss-irq) returned %pK", __func__,
|
||||
qm35_ctx->gpio_ss_irq);
|
||||
}
|
||||
|
||||
if (qm35_ctx->gpio_ss_irq) {
|
||||
if (IS_ERR(qm35_ctx->gpio_ss_irq)) {
|
||||
dev_err(&qm35_ctx->spi->dev, "error with ss-irq");
|
||||
return PTR_ERR(qm35_ctx->gpio_ss_irq);
|
||||
qm35_ctx->spi->irq = gpiod_to_irq(qm35_ctx->gpio_ss_irq);
|
||||
if (qm35_ctx->spi->irq < 0) {
|
||||
dev_err(&qm35_ctx->spi->dev,
|
||||
"%s: gpiod_to_irq(ss-irq) returned %d", __func__,
|
||||
qm35_ctx->spi->irq);
|
||||
return qm35_ctx->spi->irq;
|
||||
}
|
||||
ret = devm_request_irq(&qm35_ctx->spi->dev, qm35_ctx->spi->irq,
|
||||
&qm35_irq_handler, IRQF_TRIGGER_HIGH,
|
||||
"hsspi-ss-irq", qm35_ctx);
|
||||
if (ret) {
|
||||
dev_err(&qm35_ctx->spi->dev, "%s: devm_request_irq returned %d",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (wake_on_ssirq) {
|
||||
ret = enable_irq_wake(qm35_ctx->spi->irq);
|
||||
if (ret) {
|
||||
dev_err(&qm35_ctx->spi->dev,
|
||||
"%s: enable_irq_wake returned %d", __func__,
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
qm35_ctx->spi->irq = gpiod_to_irq(qm35_ctx->gpio_ss_irq);
|
||||
ss_irqflags = IRQF_TRIGGER_HIGH;
|
||||
dev_err(&qm35_ctx->spi->dev, "ss-irq ready with gpio");
|
||||
} else {
|
||||
ss_irqflags = irq_get_trigger_type(qm35_ctx->spi->irq);
|
||||
dev_err(&qm35_ctx->spi->dev, "ss-irq ready with irq-type");
|
||||
}
|
||||
|
||||
qm35_ctx->hsspi.odw_cleared = reenable_ss_irq;
|
||||
qm35_ctx->hsspi.wakeup = qm35_wakeup;
|
||||
qm35_ctx->hsspi.reset_qm35 = qm35_reset_hook;
|
||||
|
||||
ret = devm_request_irq(&qm35_ctx->spi->dev, qm35_ctx->spi->irq,
|
||||
&qm35_irq_handler, ss_irqflags, "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);
|
||||
|
|
@ -701,8 +866,15 @@ static int hsspi_irqs_setup(struct qm35_ctx *qm35_ctx)
|
|||
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),
|
||||
irq = gpiod_to_irq(qm35_ctx->gpio_exton);
|
||||
if (irq < 0) {
|
||||
dev_err(&qm35_ctx->spi->dev,
|
||||
"%s: gpiod_to_irq(exton) returned %d", __func__,
|
||||
irq);
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&qm35_ctx->spi->dev, irq,
|
||||
&qm35_exton_handler,
|
||||
IRQF_TRIGGER_FALLING, "hsspi-exton",
|
||||
qm35_ctx);
|
||||
|
|
@ -832,6 +1004,7 @@ static int qm35_probe(struct spi_device *spi)
|
|||
{
|
||||
struct qm35_ctx *qm35_ctx;
|
||||
struct miscdevice *uci_misc;
|
||||
struct miscdevice *coredump_misc;
|
||||
struct clk *uwb_clk;
|
||||
int ret = 0;
|
||||
|
||||
|
|
@ -858,6 +1031,7 @@ static int qm35_probe(struct spi_device *spi)
|
|||
qm35_ctx->spi = spi;
|
||||
qm35_ctx->log_qm_traces = log_qm_traces;
|
||||
spin_lock_init(&qm35_ctx->lock);
|
||||
init_waitqueue_head(&qm35_ctx->qmrom_wq_ready);
|
||||
|
||||
spi_set_drvdata(spi, qm35_ctx);
|
||||
|
||||
|
|
@ -896,6 +1070,12 @@ static int qm35_probe(struct spi_device *spi)
|
|||
uci_misc->fops = &uci_fops;
|
||||
uci_misc->parent = &spi->dev;
|
||||
|
||||
coredump_misc = &qm35_ctx->coredump_dev;
|
||||
coredump_misc->minor = MISC_DYNAMIC_MINOR;
|
||||
coredump_misc->name = COREDUMP_DEV_NAME;
|
||||
coredump_misc->fops = &coredump_fops;
|
||||
coredump_misc->parent = &spi->dev;
|
||||
|
||||
/* we need the debugfs root initialized here to be able
|
||||
* to display the soc info populated if flash_on_probe
|
||||
* is set for chips different than A0
|
||||
|
|
@ -922,9 +1102,7 @@ static int qm35_probe(struct spi_device *spi)
|
|||
if (ret)
|
||||
goto debug_deinit;
|
||||
|
||||
ret = coredump_layer_init(&qm35_ctx->coredump_layer, &qm35_ctx->debug);
|
||||
if (ret)
|
||||
goto hsspi_test_deinit;
|
||||
coredump_layer_init(&qm35_ctx->coredump_layer);
|
||||
|
||||
ret = log_layer_init(&qm35_ctx->log_layer, &qm35_ctx->debug);
|
||||
if (ret)
|
||||
|
|
@ -966,10 +1144,19 @@ static int qm35_probe(struct spi_device *spi)
|
|||
qm35_hsspi_start(qm35_ctx);
|
||||
}
|
||||
|
||||
ret = misc_register(&qm35_ctx->coredump_dev);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to register coredump device\n");
|
||||
goto log_layer_unregister;
|
||||
}
|
||||
|
||||
dev_info(&spi->dev, "Registered: [%s] misc device\n",
|
||||
coredump_misc->name);
|
||||
|
||||
ret = misc_register(&qm35_ctx->uci_dev);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to register uci device\n");
|
||||
goto log_layer_unregister;
|
||||
goto coredump_misc_deregister;
|
||||
}
|
||||
|
||||
dev_info(&spi->dev, "Registered: [%s] misc device\n", uci_misc->name);
|
||||
|
|
@ -977,6 +1164,8 @@ static int qm35_probe(struct spi_device *spi)
|
|||
dev_info(&spi->dev, "QM35 spi driver version " DRV_VERSION " probed\n");
|
||||
return 0;
|
||||
|
||||
coredump_misc_deregister:
|
||||
misc_deregister(&qm35_ctx->coredump_dev);
|
||||
log_layer_unregister:
|
||||
hsspi_unregister(&qm35_ctx->hsspi, &qm35_ctx->log_layer.hlayer);
|
||||
coredump_layer_unregister:
|
||||
|
|
@ -985,7 +1174,6 @@ 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);
|
||||
|
|
@ -1007,6 +1195,7 @@ static void qm35_remove(struct spi_device *spi)
|
|||
struct qm35_ctx *qm35_hdl = spi_get_drvdata(spi);
|
||||
|
||||
misc_deregister(&qm35_hdl->uci_dev);
|
||||
misc_deregister(&qm35_hdl->coredump_dev);
|
||||
|
||||
qm35_hsspi_stop(qm35_hdl);
|
||||
|
||||
|
|
@ -1030,10 +1219,36 @@ static void qm35_remove(struct spi_device *spi)
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int qm35_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct qm35_ctx *qm35_hdl = dev_get_drvdata(dev);
|
||||
|
||||
qm35_hsspi_stop(qm35_hdl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qm35_pm_resume(struct device *dev)
|
||||
{
|
||||
struct qm35_ctx *qm35_hdl = dev_get_drvdata(dev);
|
||||
|
||||
qm35_hsspi_start(qm35_hdl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(qm35_spi_ops, qm35_pm_suspend, qm35_pm_resume);
|
||||
#if KERNEL_VERSION(5, 17, 0) > LINUX_VERSION_CODE
|
||||
#define pm_sleep_ptr(_ptr) (IS_ENABLED(CONFIG_PM_SLEEP) ? (_ptr) : NULL)
|
||||
#endif
|
||||
|
||||
static struct spi_driver qm35_spi_driver = {
|
||||
.driver = {
|
||||
.name = "qm35",
|
||||
.of_match_table = of_match_ptr(qm35_dt_ids),
|
||||
.pm = pm_sleep_ptr(&qm35_spi_ops),
|
||||
},
|
||||
.probe = qm35_probe,
|
||||
.remove = qm35_remove,
|
||||
|
|
|
|||
|
|
@ -4,18 +4,23 @@
|
|||
#define __QM35_H___
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/miscdevice.h>
|
||||
|
||||
#include "uci_ioctls.h"
|
||||
#include "qm35_coredump_ioctls.h"
|
||||
#include "hsspi.h"
|
||||
#include "hsspi_uci.h"
|
||||
#include "hsspi_coredump.h"
|
||||
#include "hsspi_log.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define FWUPDATER_SPI_SPEED_HZ 20000000
|
||||
#define DEFAULT_SPI_CLOCKRATE 3000000
|
||||
|
||||
#define DEBUG_CERTIFICATE_SIZE 2560
|
||||
#define QM_RESET_LOW_MS 2
|
||||
/*
|
||||
|
|
@ -24,7 +29,7 @@
|
|||
#define QM_BOOT_MS 450
|
||||
#define QM_BEFORE_RESET_MS 450
|
||||
|
||||
#define DRV_VERSION "7.1.0-rc2"
|
||||
#define DRV_VERSION "magenta-7.4.0-rc2"
|
||||
|
||||
struct regulator;
|
||||
|
||||
|
|
@ -35,6 +40,7 @@ struct regulator;
|
|||
struct qm35_ctx {
|
||||
unsigned int state;
|
||||
struct miscdevice uci_dev;
|
||||
struct miscdevice coredump_dev;
|
||||
struct spi_device *spi;
|
||||
struct gpio_desc *gpio_csn;
|
||||
struct gpio_desc *gpio_reset;
|
||||
|
|
@ -42,6 +48,7 @@ struct qm35_ctx {
|
|||
struct gpio_desc *gpio_ss_irq;
|
||||
struct gpio_desc *gpio_exton;
|
||||
struct gpio_desc *gpio_wakeup;
|
||||
int ss_rdy_irq;
|
||||
spinlock_t lock;
|
||||
bool out_data_wait;
|
||||
bool out_active;
|
||||
|
|
@ -57,6 +64,11 @@ struct qm35_ctx {
|
|||
struct regulator *vdd4;
|
||||
bool regulators_enabled;
|
||||
bool log_qm_traces;
|
||||
|
||||
/* qmrom support */
|
||||
struct wait_queue_head qmrom_wq_ready;
|
||||
bool qmrom_qm_ready;
|
||||
bool flashing;
|
||||
};
|
||||
|
||||
static inline unsigned int qm35_get_state(struct qm35_ctx *qm35_hdl)
|
||||
|
|
@ -78,7 +90,7 @@ static inline int qm35_reset(struct qm35_ctx *qm35_hdl, int timeout_ms)
|
|||
if (qm35_hdl->gpio_reset) {
|
||||
qm35_set_state(qm35_hdl, QM35_CTRL_STATE_RESET);
|
||||
gpiod_set_value(qm35_hdl->gpio_reset, 1);
|
||||
usleep_range(timeout_ms * 1000, timeout_ms * 1000);
|
||||
usleep_range(timeout_ms * 1000UL, timeout_ms * 1000UL);
|
||||
gpiod_set_value(qm35_hdl->gpio_reset, 0);
|
||||
qm35_set_state(qm35_hdl, QM35_CTRL_STATE_UNKNOWN);
|
||||
return 0;
|
||||
|
|
|
|||
14
drivers/uwb/qm35/qm35_coredump_ioctls.h
Normal file
14
drivers/uwb/qm35/qm35_coredump_ioctls.h
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef __QM35_COREDUMP_IOCTLS_H___
|
||||
#define __QM35_COREDUMP_IOCTLS_H___
|
||||
|
||||
#include <asm/ioctl.h>
|
||||
|
||||
#define COREDUMP_DEV_NAME "qm35-coredump"
|
||||
#define COREDUMP_IOC_TYPE 'U'
|
||||
|
||||
#define QM35_COREDUMP_FORCE _IO(COREDUMP_IOC_TYPE, 1)
|
||||
#define QM35_COREDUMP_GET_SIZE _IOR(COREDUMP_IOC_TYPE, 2, unsigned int)
|
||||
|
||||
#endif /* __QM35_COREDUMP_IOCTLS_H___ */
|
||||
|
|
@ -26,6 +26,7 @@
|
|||
* QM35 FW ROM protocol SPI ops
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <linux/uwb/qmrom_spi.h>
|
||||
|
|
@ -51,6 +52,7 @@ void qmrom_set_fwname(const char *name)
|
|||
int qmrom_spi_transfer(void *handle, char *rbuf, const char *wbuf, size_t size)
|
||||
{
|
||||
struct spi_device *spi = (struct spi_device *)handle;
|
||||
struct qm35_ctx *qm35_ctx = spi_get_drvdata(spi);
|
||||
int rc;
|
||||
|
||||
struct spi_transfer xfer[] = {
|
||||
|
|
@ -62,6 +64,7 @@ int qmrom_spi_transfer(void *handle, char *rbuf, const char *wbuf, size_t size)
|
|||
},
|
||||
};
|
||||
|
||||
qm35_ctx->qmrom_qm_ready = false;
|
||||
rc = spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
|
||||
|
||||
if (trace_spi_xfers) {
|
||||
|
|
@ -174,11 +177,17 @@ void qmrom_spi_release_firmware(const struct firmware *fw)
|
|||
|
||||
int qmrom_spi_wait_for_ready_line(void *handle, unsigned int timeout_ms)
|
||||
{
|
||||
int count_down = (int)timeout_ms;
|
||||
while (!gpiod_get_value(handle) && (--count_down >= 0)) {
|
||||
udelay(1000);
|
||||
}
|
||||
return gpiod_get_value(handle) ? 0 : -1;
|
||||
struct qm35_ctx *qm35_ctx = (struct qm35_ctx *)handle;
|
||||
|
||||
wait_event_interruptible_timeout(qm35_ctx->qmrom_wq_ready,
|
||||
qm35_ctx->qmrom_qm_ready,
|
||||
msecs_to_jiffies(timeout_ms));
|
||||
return gpiod_get_value(qm35_ctx->gpio_ss_rdy) > 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
int qmrom_spi_read_irq_line(void *handle)
|
||||
{
|
||||
return gpiod_get_value(handle);
|
||||
}
|
||||
|
||||
void qmrom_spi_set_freq(unsigned int freq)
|
||||
|
|
|
|||
|
|
@ -14,13 +14,14 @@
|
|||
#define QM35_CTRL_POWER _IOW(UCI_IOC_TYPE, 4, unsigned int)
|
||||
|
||||
/* qm35 states */
|
||||
enum { QM35_CTRL_STATE_UNKNOWN = 0x0000,
|
||||
QM35_CTRL_STATE_OFF = 0x0001,
|
||||
QM35_CTRL_STATE_RESET = 0x0002,
|
||||
QM35_CTRL_STATE_COREDUMP = 0x0004,
|
||||
QM35_CTRL_STATE_READY = 0x0008,
|
||||
QM35_CTRL_STATE_FW_DOWNLOADING = 0x0010,
|
||||
QM35_CTRL_STATE_UCI_APP = 0x0020,
|
||||
enum qm35_ctrl_state {
|
||||
QM35_CTRL_STATE_UNKNOWN = 0x0000,
|
||||
QM35_CTRL_STATE_OFF = 0x0001,
|
||||
QM35_CTRL_STATE_RESET = 0x0002,
|
||||
QM35_CTRL_STATE_COREDUMP = 0x0004,
|
||||
QM35_CTRL_STATE_READY = 0x0008,
|
||||
QM35_CTRL_STATE_FW_DOWNLOADING = 0x0010,
|
||||
QM35_CTRL_STATE_UCI_APP = 0x0020,
|
||||
};
|
||||
|
||||
#endif /* __UCI_IOCTLS_H___ */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR Apache-2.0
|
||||
/*
|
||||
* Copyright 2021 Qorvo US, Inc.
|
||||
* Copyright 2023 Qorvo US, Inc.
|
||||
*
|
||||
*/
|
||||
|
||||
|
|
@ -17,6 +17,12 @@
|
|||
#include "qmrom.h"
|
||||
#include "qm357xx_fwpkg.h"
|
||||
|
||||
#ifndef CONFIG_NB_RETRIES
|
||||
#define CONFIG_NB_RETRIES 10
|
||||
#endif
|
||||
|
||||
// #define CONFIG_INJECT_ERROR 1
|
||||
|
||||
enum fw_pkg_error_e {
|
||||
FW_PKG_SUCESS = 0,
|
||||
FW_PKG_DOWNLOAD_ERROR,
|
||||
|
|
@ -52,10 +58,17 @@ enum fw_pkg_error_e {
|
|||
|
||||
/*! Firmware Update Status fields */
|
||||
struct fw_updater_status_t {
|
||||
uint32_t magic;
|
||||
uint32_t status;
|
||||
uint32_t suberror;
|
||||
uint32_t spi_errors;
|
||||
uint32_t crc_errors;
|
||||
uint32_t rram_errors;
|
||||
uint32_t crypto_errors;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define FWUPDATER_STATUS_MAGIC 0xCAFECAFE
|
||||
|
||||
void run_fwupdater_unit_tests(void *spi_handle);
|
||||
int run_fwupdater(struct qmrom_handle *handle, char *fwpkg_bin, size_t size);
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,8 @@ int qm357xx_rom_fw_macro_pkg_get_fw_idx(const struct firmware *fw, int idx,
|
|||
uint32_t *fw_size, char **fw_fata);
|
||||
int qm357xx_rom_unpack_fw_macro_pkg(const struct firmware *fw,
|
||||
struct unstitched_firmware *all_fws);
|
||||
int qm357xx_rom_unpack_fw_pkg(const struct firmware *fw_pkg,
|
||||
struct unstitched_firmware *all_fws);
|
||||
int qm357xx_rom_flash_dbg_cert(struct qmrom_handle *handle,
|
||||
struct firmware *dbg_cert);
|
||||
int qm357xx_rom_erase_dbg_cert(struct qmrom_handle *handle);
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ struct qmrom_handle {
|
|||
void *spi_handle;
|
||||
void *reset_handle;
|
||||
void *ss_rdy_handle;
|
||||
void *ss_irq_handle;
|
||||
int comms_retries;
|
||||
enum device_generation_e dev_gen;
|
||||
enum chip_revision_e chip_rev;
|
||||
|
|
@ -98,11 +99,13 @@ struct qmrom_handle {
|
|||
struct qm358xx_soc_infos qm358xx_soc_info;
|
||||
};
|
||||
bool is_be;
|
||||
bool skip_check_fw_boot;
|
||||
};
|
||||
|
||||
struct qmrom_handle *qmrom_init(void *spi_handle, void *reset_handle,
|
||||
void *ss_rdy_handle, int spi_speed,
|
||||
int comms_retries, reset_device_fn reset,
|
||||
void *ss_rdy_handle, void *ss_irq_handle,
|
||||
int spi_speed, int comms_retries,
|
||||
reset_device_fn reset,
|
||||
enum device_generation_e dev_gen_hint);
|
||||
void qmrom_deinit(struct qmrom_handle *handle);
|
||||
int qmrom_reboot_bootloader(struct qmrom_handle *handle);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
#ifndef __QMROM_SPI_H__
|
||||
#define __QMROM_SPI_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <linux/stddef.h>
|
||||
#include "qmrom_error.h"
|
||||
#include "qmrom.h"
|
||||
|
||||
|
|
@ -56,6 +56,7 @@ const struct firmware *qmrom_spi_get_firmware_package(void *handle,
|
|||
int lcs_state);
|
||||
void qmrom_spi_release_firmware(const struct firmware *fw);
|
||||
int qmrom_spi_wait_for_ready_line(void *handle, unsigned int timeout_ms);
|
||||
int qmrom_spi_read_irq_line(void *handle);
|
||||
void qmrom_spi_set_freq(unsigned int freq);
|
||||
unsigned int qmrom_spi_get_freq(void);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue