@@ -9,7 +9,7 @@ rmi_core-$(CONFIG_RMI4_F03) += rmi_f03.o
rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o
rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o
rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o
-rmi_core-$(CONFIG_RMI4_F34) += rmi_f34.o rmi_f34v7.o
+rmi_core-$(CONFIG_RMI4_F34) += rmi_f34.o rmi_f34v7.o rmi_f34v6.o
rmi_core-$(CONFIG_RMI4_F3A) += rmi_f3a.o
rmi_core-$(CONFIG_RMI4_F54) += rmi_f54.o
rmi_core-$(CONFIG_RMI4_F55) += rmi_f55.o
@@ -49,6 +49,9 @@ static int rmi_f34_command(struct f34_data *f34, u8 command,
struct rmi_device *rmi_dev = fn->rmi_dev;
int ret;
+ if (f34->bl_version == 6)
+ return rmi_f34v6_command(f34, command, timeout);
+
if (write_bl_id) {
ret = rmi_f34_write_bootloader_id(f34);
if (ret)
@@ -112,6 +115,8 @@ static irqreturn_t rmi_f34_attention(int irq, void *ctx)
if (!ret && !(status & 0x7f))
complete(&f34->v5.cmd_done);
+ } else if (f34->bl_version == 6) {
+ rmi_f34v6_read_flash_status(f34, &status);
} else {
ret = rmi_read_block(f34->fn->rmi_dev,
f34->fn->fd.data_base_addr +
@@ -132,11 +137,16 @@ static int rmi_f34_write_blocks(struct f34_data *f34, const void *data,
{
struct rmi_function *fn = f34->fn;
struct rmi_device *rmi_dev = fn->rmi_dev;
- u16 address = fn->fd.data_base_addr + F34_BLOCK_DATA_OFFSET;
u8 start_address[] = { 0, 0 };
+ u16 address;
int i;
int ret;
+ if (f34->bl_version == 6)
+ address = fn->fd.data_base_addr + F34_BLOCK_DATA_OFFSET_V6;
+ else
+ address = fn->fd.data_base_addr + F34_BLOCK_DATA_OFFSET;
+
ret = rmi_write_block(rmi_dev, fn->fd.data_base_addr,
start_address, sizeof(start_address));
if (ret) {
@@ -320,7 +330,7 @@ static ssize_t rmi_driver_bootloader_id_show(struct device *dev,
if (fn) {
f34 = dev_get_drvdata(&fn->dev);
- if (f34->bl_version == 5)
+ if (f34->bl_version == 5 || f34->bl_version == 6)
return scnprintf(buf, PAGE_SIZE, "%c%c\n",
f34->bootloader_id[0],
f34->bootloader_id[1]);
@@ -375,7 +385,7 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
dev_err(dev, "%s: LTS not supported\n", __func__);
return -ENODEV;
}
- } else if (f34->bl_version != 5) {
+ } else if (f34->bl_version != 5 && f34->bl_version != 6) {
dev_warn(dev, "F34 V%d not supported!\n",
data->f34_container->fd.function_version);
return -ENODEV;
@@ -384,7 +394,7 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
/* Enter flash mode */
if (f34->bl_version == 7)
ret = rmi_f34v7_start_reflash(f34, fw);
- else
+ else /* v5 & v6 */
ret = rmi_f34_enable_flash(f34);
if (ret)
return ret;
@@ -415,7 +425,7 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
/* Perform firmware update */
if (f34->bl_version == 7)
ret = rmi_f34v7_do_reflash(f34, fw);
- else
+ else /* v5 & v6 */
ret = rmi_f34_update_firmware(f34, fw);
if (ret) {
@@ -532,11 +542,12 @@ static int rmi_f34_probe(struct rmi_function *fn)
f34->fn = fn;
dev_set_drvdata(&fn->dev, f34);
- /* v5 code only supported version 0, try V7 probe */
- if (version > 0)
+ if (version == 1)
+ return rmi_f34v6_probe(f34);
+ else if (version > 1)
return rmi_f34v7_probe(f34);
-
- f34->bl_version = 5;
+ else
+ f34->bl_version = 5;
ret = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr,
f34_queries, sizeof(f34_queries));
@@ -12,6 +12,7 @@
/* F34 register offsets. */
#define F34_BLOCK_DATA_OFFSET 2
+#define F34_BLOCK_DATA_OFFSET_V6 1
/* F34 commands */
#define F34_WRITE_FW_BLOCK 0x2
@@ -40,6 +41,9 @@
#define V7_PAYLOAD_OFFSET 5
#define V7_BOOTLOADER_ID_OFFSET 1
+/* F34 V6 defines */
+#define V6_BOOTLOADER_ID_OFFSET 0
+
#define IMAGE_HEADER_VERSION_10 0x10
#define CONFIG_ID_SIZE 32
@@ -248,7 +252,7 @@ struct rmi_f34_firmware {
u8 data[];
};
-struct f34v5_data {
+struct f34v5v6_data {
u16 block_size;
u16 fw_blocks;
u16 config_blocks;
@@ -300,13 +304,17 @@ struct f34_data {
int update_size;
union {
- struct f34v5_data v5;
+ struct f34v5v6_data v5;
+ struct f34v5v6_data v6;
struct f34v7_data v7;
};
};
+int rmi_f34v6_command(struct f34_data *f34, u8 cmd, unsigned int timeout);
+int rmi_f34v6_read_flash_status(struct f34_data *f34, u8 *status);
int rmi_f34v7_start_reflash(struct f34_data *f34, const struct firmware *fw);
int rmi_f34v7_do_reflash(struct f34_data *f34, const struct firmware *fw);
int rmi_f34v7_probe(struct f34_data *f34);
+int rmi_f34v6_probe(struct f34_data *f34);
#endif /* _RMI_F34_H */
new file mode 100644
@@ -0,0 +1,258 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021 Linaro Ltd <loic.poulain@linaro.org>
+ */
+#define DEBUG
+
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <asm/unaligned.h>
+
+#include "rmi_driver.h"
+#include "rmi_f34.h"
+
+#define F34_V6_DATA_FLASH_CMD_OFF 2
+#define F34_V6_DATA_FLASH_STS_OFF 3
+#define F34_V6_DATA_BLOCK_OFF 1
+
+#define F34_V6_DATA_FLASH_STS_MSK 0x3f
+#define F34_V6_DATA_PROGE_STS_MSK 0x80
+#define F34_V6_DATA_FLASH_CMD_MSK 0x3f
+
+struct f34v6_query_00 {
+ __u8 bootloader_id0;
+ __u8 bootloader_id1;
+ __u8 bootloader_minor;
+ __u8 bootloader_major;
+ __le32 firmware_id;
+} __packed;
+
+struct f34v6_query_02 {
+ __le16 block_size;
+} __packed;
+
+struct f34v6_query_03 {
+ __le16 fw_block_count;
+ __le16 ui_config_block_count;
+ __le16 perm_config_block_count;
+ __le16 bootloader_config_block_count;
+} __packed;
+
+struct f34v6_ctrl_00 {
+ __u8 config_id[4];
+} __packed;
+
+struct f34v6_data_02 {
+ __u8 flash_cmd;
+} __packed;
+
+struct f34v6_data_03 {
+ __u8 status;
+} __packed;
+
+static int rmi_f34v6_read_version(struct f34_data *f34)
+{
+ struct rmi_function *fn = f34->fn;
+ struct f34v6_query_00 query_00;
+ int ret;
+
+ ret = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr, &query_00,
+ sizeof(query_00));
+ if (ret)
+ return ret;
+
+ f34->bootloader_id[0] = query_00.bootloader_id0;
+ f34->bootloader_id[1] = query_00.bootloader_id1;
+ f34->bl_version = 6;
+
+ return 0;
+}
+
+static int rmi_f34v6_read_block_info(struct f34_data *f34)
+{
+ struct rmi_function *fn = f34->fn;
+ struct f34v6_query_02 query_02;
+ struct f34v6_query_03 query_03;
+ int ret;
+
+ ret = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr + 2, &query_02,
+ sizeof(query_02));
+ if (ret)
+ return ret;
+
+ f34->v6.block_size = get_unaligned_le16(&query_02.block_size);
+
+ ret = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr + 3, &query_03,
+ sizeof(query_03));
+ if (ret)
+ return ret;
+
+ f34->v6.fw_blocks = get_unaligned_le16(&query_03.fw_block_count);
+ f34->v6.config_blocks = get_unaligned_le16(&query_03.ui_config_block_count);
+
+ return 0;
+}
+
+static int rmi_f34v6_read_config_id(struct f34_data *f34)
+{
+ struct rmi_function *fn = f34->fn;
+ struct f34v6_ctrl_00 ctrl_00;
+ int ret;
+
+ ret = rmi_read_block(fn->rmi_dev, fn->fd.control_base_addr,
+ &ctrl_00, sizeof(ctrl_00));
+ if (ret) {
+ dev_err(&fn->dev, "Failed to read config ID\n");
+ return ret;
+ }
+
+ snprintf(f34->configuration_id, sizeof(f34->configuration_id),
+ "%02x%02x%02x%02x", ctrl_00.config_id[0], ctrl_00.config_id[1],
+ ctrl_00.config_id[2], ctrl_00.config_id[3]);
+
+ return 0;
+}
+
+int rmi_f34v6_read_flash_status(struct f34_data *f34, u8 *status)
+{
+ struct rmi_function *fn = f34->fn;
+ struct f34v6_data_03 data_03;
+ struct f34v6_data_02 data_02;
+ int ret;
+
+ /* Any command running ? */
+ ret = rmi_read_block(fn->rmi_dev,
+ fn->fd.data_base_addr + F34_V6_DATA_FLASH_CMD_OFF,
+ &data_02, sizeof(data_02));
+ if (ret) {
+ dev_err(&fn->dev, "Failed to read flash command\n");
+ return ret;
+ }
+
+ /* Retrieve command Status */
+ ret = rmi_read_block(fn->rmi_dev,
+ fn->fd.data_base_addr + F34_V6_DATA_FLASH_STS_OFF,
+ &data_03, sizeof(data_03));
+ if (ret) {
+ dev_err(&fn->dev, "Failed to read flash status\n");
+ return ret;
+ }
+
+ if (status)
+ *status = data_03.status & F34_V6_DATA_FLASH_STS_MSK;
+
+ if ((data_03.status & F34_V6_DATA_PROGE_STS_MSK) !=
+ (f34->v6.status & F34_V6_DATA_PROGE_STS_MSK)) {
+ dev_info(&fn->dev, "Programming %s\n",
+ data_03.status & F34_V6_DATA_PROGE_STS_MSK ? "enabled" : "disabled");
+ }
+
+ WRITE_ONCE(f34->v6.status, data_03.status);
+
+ if (!(data_02.flash_cmd & F34_V6_DATA_FLASH_CMD_MSK))
+ complete(&f34->v6.cmd_done);
+
+ return 0;
+}
+
+static int rmi_f34v6_write_command(struct f34_data *f34, u8 cmd)
+{
+ struct rmi_function *fn = f34->fn;
+ int ret;
+
+ if (cmd == F34_ENABLE_FLASH_PROG || cmd == F34_ERASE_ALL || cmd == F34_ERASE_CONFIG) {
+ fn->rmi_dev->driver->set_irq_bits(fn->rmi_dev, fn->irq_mask);
+
+ /* Special case requiring identification */
+ ret = rmi_write_block(fn->rmi_dev,
+ fn->fd.data_base_addr + F34_V6_DATA_BLOCK_OFF,
+ f34->bootloader_id, 2);
+ if (ret) {
+ dev_err(&fn->dev, "%s: bootloader-id write error %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
+ rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: writing cmd %02X\n", __func__, cmd);
+
+ ret = rmi_write_block(fn->rmi_dev,
+ fn->fd.data_base_addr + F34_V6_DATA_FLASH_CMD_OFF,
+ &cmd, sizeof(cmd));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int rmi_f34v6_command(struct f34_data *f34, u8 cmd, unsigned int timeout)
+{
+ struct rmi_function *fn = f34->fn;
+ int ret;
+
+ init_completion(&f34->v6.cmd_done);
+
+ ret = rmi_f34v6_write_command(f34, cmd);
+ if (ret)
+ dev_err(&fn->dev, "%s: cmd %#02x error %d\n", __func__, cmd, ret);
+
+ ret = wait_for_completion_timeout(&f34->v6.cmd_done, msecs_to_jiffies(timeout));
+ if (!ret) {
+ rmi_f34v6_read_flash_status(f34, NULL);
+
+ dev_err(&fn->dev, "%s: cmd %#02x timed out, status: %#02x\n",
+ __func__, cmd, READ_ONCE(f34->v6.status));
+
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+int rmi_f34v6_probe(struct f34_data *f34)
+{
+ struct device *dev = &f34->fn->dev;
+ int ret;
+
+ init_completion(&f34->v6.cmd_done);
+ mutex_init(&f34->v6.flash_mutex);
+
+ ret = rmi_f34v6_read_version(f34);
+ if (ret) {
+ dev_err(dev, "Failed to read version\n");
+ return ret;
+ }
+
+ ret = rmi_f34v6_read_block_info(f34);
+ if (ret) {
+ dev_err(dev, "Failed to block info\n");
+ return ret;
+ }
+
+ ret = rmi_f34v6_read_config_id(f34);
+ if (ret) {
+ dev_err(dev, "Failed to block info\n");
+ return ret;
+ }
+
+ ret = rmi_f34v6_read_flash_status(f34, NULL);
+ if (ret) {
+ dev_err(dev, "Failed to read status\n");
+ return ret;
+ }
+
+ rmi_dbg(RMI_DEBUG_FN, dev, "Bootloader ID: %s\n", f34->bootloader_id);
+ rmi_dbg(RMI_DEBUG_FN, dev, "Configuration ID: %s\n", f34->configuration_id);
+ rmi_dbg(RMI_DEBUG_FN, dev, "Block size: %d\n", f34->v6.block_size);
+ rmi_dbg(RMI_DEBUG_FN, dev, "FW blocks: %d\n", f34->v6.fw_blocks);
+ rmi_dbg(RMI_DEBUG_FN, dev, "CFG blocks: %d\n", f34->v6.config_blocks);
+ rmi_dbg(RMI_DEBUG_FN, dev, "Programming: %s\n",
+ f34->v6.status & F34_V6_DATA_PROGE_STS_MSK ? "enabled" : "disabled");
+
+ return 0;
+}
The firmware upgrade for V6 bootloaders is slighlty different from V5. This has been tested with synaptics S3408 for both firmware & config upgrade. Signed-off-by: Loic Poulain <loic.poulain@linaro.org> --- drivers/input/rmi4/Makefile | 2 +- drivers/input/rmi4/rmi_f34.c | 29 +++-- drivers/input/rmi4/rmi_f34.h | 12 +- drivers/input/rmi4/rmi_f34v6.c | 258 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 289 insertions(+), 12 deletions(-) create mode 100644 drivers/input/rmi4/rmi_f34v6.c