Signed-off-by: Nick Dyer <[email protected]>
Signed-off-by: Benjamin Tissoires <[email protected]>
---
drivers/input/rmi4/rmi_driver.c | 139 +++++++++++++++++++++++++---------------
1 file changed, 86 insertions(+), 53 deletions(-)
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index 4a88312..63c9e22 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -38,6 +38,8 @@ static void rmi_free_function_list(struct rmi_device *rmi_dev)
struct rmi_function *fn, *tmp;
struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev, "Freeing function list\n");
+
data->f01_container = NULL;
/* Doing it in the reverse order so F01 will be removed last */
@@ -842,15 +844,90 @@ static inline int rmi_driver_of_probe(struct device *dev,
}
#endif
+static int rmi_probe_interrupts(struct rmi_driver_data *data)
+{
+ struct rmi_device *rmi_dev = data->rmi_dev;
+ struct device *dev = &rmi_dev->dev;
+ int irq_count;
+ size_t size;
+ void *irq_memory;
+ int retval;
+
+ /*
+ * We need to count the IRQs and allocate their storage before scanning
+ * the PDT and creating the function entries, because adding a new
+ * function can trigger events that result in the IRQ related storage
+ * being accessed.
+ */
+ rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Counting IRQs.\n", __func__);
+ irq_count = 0;
+ retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_count_irqs);
+ if (retval < 0) {
+ dev_err(dev, "IRQ counting failed with code %d.\n", retval);
+ return retval;
+ }
+ data->irq_count = irq_count;
+ data->num_of_irq_regs = (data->irq_count + 7) / 8;
+
+ size = BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long);
+ irq_memory = devm_kzalloc(dev, size * 4, GFP_KERNEL);
+ if (!irq_memory) {
+ dev_err(dev, "Failed to allocate memory for irq masks.\n");
+ return retval;
+ }
+
+ data->irq_status = irq_memory + size * 0;
+ data->fn_irq_bits = irq_memory + size * 1;
+ data->current_irq_mask = irq_memory + size * 2;
+ data->new_irq_mask = irq_memory + size * 3;
+
+ return retval;
+}
+
+static int rmi_init_functions(struct rmi_driver_data *data)
+{
+ struct rmi_device *rmi_dev = data->rmi_dev;
+ struct device *dev = &rmi_dev->dev;
+ int irq_count;
+ int retval;
+
+ irq_count = 0;
+ rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Creating functions.\n", __func__);
+ retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_create_function);
+ if (retval < 0) {
+ dev_err(dev, "Function creation failed with code %d.\n",
+ retval);
+ goto err_destroy_functions;
+ }
+
+ if (!data->f01_container) {
+ dev_err(dev, "Missing F01 container!\n");
+ retval = -EINVAL;
+ goto err_destroy_functions;
+ }
+
+ retval = rmi_read_block(rmi_dev,
+ data->f01_container->fd.control_base_addr + 1,
+ data->current_irq_mask, data->num_of_irq_regs);
+ if (retval < 0) {
+ dev_err(dev, "%s: Failed to read current IRQ mask.\n",
+ __func__);
+ goto err_destroy_functions;
+ }
+
+ return 0;
+
+err_destroy_functions:
+ rmi_free_function_list(rmi_dev);
+ return retval;
+}
+
static int rmi_driver_probe(struct device *dev)
{
struct rmi_driver *rmi_driver;
struct rmi_driver_data *data;
struct rmi_device_platform_data *pdata;
struct rmi_device *rmi_dev;
- size_t size;
- void *irq_memory;
- int irq_count;
int retval;
rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Starting probe.\n",
@@ -916,35 +993,11 @@ static int rmi_driver_probe(struct device *dev)
PDT_PROPERTIES_LOCATION, retval);
}
- /*
- * We need to count the IRQs and allocate their storage before scanning
- * the PDT and creating the function entries, because adding a new
- * function can trigger events that result in the IRQ related storage
- * being accessed.
- */
- rmi_dbg(RMI_DEBUG_CORE, dev, "Counting IRQs.\n");
- irq_count = 0;
- retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_count_irqs);
- if (retval < 0) {
- dev_err(dev, "IRQ counting failed with code %d.\n", retval);
- goto err;
- }
- data->irq_count = irq_count;
- data->num_of_irq_regs = (data->irq_count + 7) / 8;
-
mutex_init(&data->irq_mutex);
- size = BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long);
- irq_memory = devm_kzalloc(dev, size * 4, GFP_KERNEL);
- if (!irq_memory) {
- dev_err(dev, "Failed to allocate memory for irq masks.\n");
+ retval = rmi_probe_interrupts(data);
+ if (retval)
goto err;
- }
-
- data->irq_status = irq_memory + size * 0;
- data->fn_irq_bits = irq_memory + size * 1;
- data->current_irq_mask = irq_memory + size * 2;
- data->new_irq_mask = irq_memory + size * 3;
if (rmi_dev->xport->input) {
/*
@@ -961,36 +1014,16 @@ static int rmi_driver_probe(struct device *dev)
dev_err(dev, "%s: Failed to allocate input device.\n",
__func__);
retval = -ENOMEM;
- goto err_destroy_functions;
+ goto err;
}
rmi_driver_set_input_params(rmi_dev, data->input);
data->input->phys = devm_kasprintf(dev, GFP_KERNEL,
"%s/input0", dev_name(dev));
}
- irq_count = 0;
- rmi_dbg(RMI_DEBUG_CORE, dev, "Creating functions.");
- retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_create_function);
- if (retval < 0) {
- dev_err(dev, "Function creation failed with code %d.\n",
- retval);
- goto err_destroy_functions;
- }
-
- if (!data->f01_container) {
- dev_err(dev, "Missing F01 container!\n");
- retval = -EINVAL;
- goto err_destroy_functions;
- }
-
- retval = rmi_read_block(rmi_dev,
- data->f01_container->fd.control_base_addr + 1,
- data->current_irq_mask, data->num_of_irq_regs);
- if (retval < 0) {
- dev_err(dev, "%s: Failed to read current IRQ mask.\n",
- __func__);
- goto err_destroy_functions;
- }
+ retval = rmi_init_functions(data);
+ if (retval)
+ goto err;
if (data->input) {
rmi_driver_set_input_name(rmi_dev, data->input);
--
2.7.4
Signed-off-by: Nick Dyer <[email protected]>
---
drivers/input/rmi4/rmi_driver.h | 1 +
drivers/input/rmi4/rmi_f01.c | 24 +++++++++++++++++++++++-
drivers/input/rmi4/rmi_f34.c | 16 ++++++++++++++++
3 files changed, 40 insertions(+), 1 deletion(-)
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index e22a77f..85cdb70 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -109,6 +109,7 @@ const char *rmi_f01_get_product_ID(struct rmi_function *fn);
u8 rmi_f01_get_manufacturer_ID(struct rmi_function *fn);
const char *rmi_f01_get_date_of_manufacture(struct rmi_function *fn);
u32 rmi_f01_get_firmware_ID(struct rmi_function *fn);
+u32 rmi_f01_get_package_ID(struct rmi_function *fn);
#ifdef CONFIG_RMI4_F34
int rmi_f34_create_sysfs(struct rmi_device *rmi_dev);
diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c
index 8624be8..463c810 100644
--- a/drivers/input/rmi4/rmi_f01.c
+++ b/drivers/input/rmi4/rmi_f01.c
@@ -54,6 +54,7 @@ struct f01_basic_properties {
u8 product_id[RMI_PRODUCT_ID_LENGTH + 1];
u16 productinfo;
u32 firmware_id;
+ u32 package_id;
};
/* F01 device status bits */
@@ -220,8 +221,22 @@ static int rmi_f01_read_properties(struct rmi_device *rmi_dev,
has_build_id_query = !!(queries[0] & BIT(1));
}
- if (has_package_id_query)
+ if (has_package_id_query) {
+ ret = rmi_read_block(rmi_dev, prod_info_addr,
+ queries, 4);
+ if (ret) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read package info: %d\n",
+ ret);
+ return ret;
+ }
+
+ props->package_id = queries[3] << 24 |
+ queries[2] << 16 |
+ queries[1] << 8 |
+ queries[0];
prod_info_addr++;
+ }
if (has_build_id_query) {
ret = rmi_read_block(rmi_dev, prod_info_addr, queries,
@@ -269,6 +284,13 @@ u32 rmi_f01_get_firmware_ID(struct rmi_function *fn)
return f01->properties.firmware_id;
}
+u32 rmi_f01_get_package_ID(struct rmi_function *fn)
+{
+ struct f01_data *f01 = dev_get_drvdata(&fn->dev);
+
+ return f01->properties.package_id;
+}
+
#ifdef CONFIG_OF
static int rmi_f01_of_probe(struct device *dev,
struct rmi_device_platform_data *pdata)
diff --git a/drivers/input/rmi4/rmi_f34.c b/drivers/input/rmi4/rmi_f34.c
index 10b9722..9840968 100644
--- a/drivers/input/rmi4/rmi_f34.c
+++ b/drivers/input/rmi4/rmi_f34.c
@@ -486,6 +486,21 @@ static ssize_t rmi_driver_configuration_id_show(struct device *dev,
static DEVICE_ATTR(configuration_id, 0444,
rmi_driver_configuration_id_show, NULL);
+static ssize_t rmi_driver_package_id_show(struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(dev);
+ struct rmi_function *fn = data->f01_container;
+
+ u32 package_id = rmi_f01_get_package_ID(fn);
+
+ return scnprintf(buf, PAGE_SIZE, "%04x.%04x\n",
+ package_id & 0xffff, (package_id >> 16) & 0xffff);
+}
+
+static DEVICE_ATTR(package_id, 0444, rmi_driver_package_id_show, NULL);
+
static int rmi_firmware_update(struct rmi_driver_data *data,
const struct firmware *fw);
@@ -545,6 +560,7 @@ static struct attribute *rmi_firmware_attrs[] = {
&dev_attr_manufacturer_id.attr,
&dev_attr_date_of_manufacture.attr,
&dev_attr_product_id.attr,
+ &dev_attr_package_id.attr,
&dev_attr_firmware_id.attr,
&dev_attr_update_fw.attr,
&dev_attr_update_fw_status.attr,
--
2.7.4
Add support for updating firmware, triggered by a sysfs attribute.
This patch has been tested on Synaptics S7300.
Signed-off-by: Nick Dyer <[email protected]>
Tested-by: Chris Healy <[email protected]>
---
drivers/input/rmi4/Kconfig | 11 +
drivers/input/rmi4/Makefile | 1 +
drivers/input/rmi4/rmi_bus.c | 3 +
drivers/input/rmi4/rmi_driver.c | 85 +++++--
drivers/input/rmi4/rmi_driver.h | 24 ++
drivers/input/rmi4/rmi_f01.c | 6 +
drivers/input/rmi4/rmi_f34.c | 486 ++++++++++++++++++++++++++++++++++++++++
drivers/input/rmi4/rmi_f34.h | 64 ++++++
include/linux/rmi.h | 1 +
9 files changed, 657 insertions(+), 24 deletions(-)
create mode 100644 drivers/input/rmi4/rmi_f34.c
create mode 100644 drivers/input/rmi4/rmi_f34.h
diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
index 4c8a558..9f10b1f 100644
--- a/drivers/input/rmi4/Kconfig
+++ b/drivers/input/rmi4/Kconfig
@@ -62,6 +62,17 @@ config RMI4_F30
Function 30 provides GPIO and LED support for RMI4 devices. This
includes support for buttons on TouchPads and ClickPads.
+config RMI4_F34
+ bool "RMI4 Function 34 (Device reflash)"
+ depends on RMI4_CORE
+ select FW_LOADER
+ help
+ Say Y here if you want to add support for RMI4 function 34.
+
+ Function 34 provides support for upgrading the firmware on the RMI4
+ device via the firmware loader interface. This is triggered using a
+ sysfs attribute.
+
config RMI4_F54
bool "RMI4 Function 54 (Analog diagnostics)"
depends on RMI4_CORE
diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile
index 0bafc85..5f165ad 100644
--- a/drivers/input/rmi4/Makefile
+++ b/drivers/input/rmi4/Makefile
@@ -7,6 +7,7 @@ rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.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_core-$(CONFIG_RMI4_F54) += rmi_f54.o
# Transports
diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c
index 3c6a1b5..178d388 100644
--- a/drivers/input/rmi4/rmi_bus.c
+++ b/drivers/input/rmi4/rmi_bus.c
@@ -314,6 +314,9 @@ static struct rmi_function_handler *fn_handlers[] = {
#ifdef CONFIG_RMI4_F30
&rmi_f30_handler,
#endif
+#ifdef CONFIG_RMI4_F34
+ &rmi_f34_handler,
+#endif
#ifdef CONFIG_RMI4_F54
&rmi_f54_handler,
#endif
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index adb3ee8..78fdcef 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -33,14 +33,21 @@
#define RMI_DEVICE_RESET_CMD 0x01
#define DEFAULT_RESET_DELAY_MS 100
-static void rmi_free_function_list(struct rmi_device *rmi_dev)
+void rmi_free_function_list(struct rmi_device *rmi_dev)
{
struct rmi_function *fn, *tmp;
struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev, "Freeing function list\n");
+ mutex_lock(&data->irq_mutex);
+
data->f01_container = NULL;
+ data->f34_container = NULL;
+ data->irq_status = NULL;
+ data->fn_irq_bits = NULL;
+ data->current_irq_mask = NULL;
+ data->new_irq_mask = NULL;
/* Doing it in the reverse order so F01 will be removed last */
list_for_each_entry_safe_reverse(fn, tmp,
@@ -48,7 +55,10 @@ static void rmi_free_function_list(struct rmi_device *rmi_dev)
list_del(&fn->node);
rmi_unregister_function(fn);
}
+
+ mutex_unlock(&data->irq_mutex);
}
+EXPORT_SYMBOL_GPL(rmi_free_function_list);
static int reset_one_function(struct rmi_function *fn)
{
@@ -142,8 +152,11 @@ int rmi_process_interrupt_requests(struct rmi_device *rmi_dev)
struct rmi_function *entry;
int error;
- if (!data)
+ mutex_lock(&data->irq_mutex);
+ if (!data || !data->irq_status || !data->f01_container) {
+ mutex_unlock(&data->irq_mutex);
return 0;
+ }
if (!rmi_dev->xport->attn_data) {
error = rmi_read_block(rmi_dev,
@@ -151,18 +164,13 @@ int rmi_process_interrupt_requests(struct rmi_device *rmi_dev)
data->irq_status, data->num_of_irq_regs);
if (error < 0) {
dev_err(dev, "Failed to read irqs, code=%d\n", error);
+ mutex_unlock(&data->irq_mutex);
return error;
}
}
- mutex_lock(&data->irq_mutex);
bitmap_and(data->irq_status, data->irq_status, data->current_irq_mask,
data->irq_count);
- /*
- * At this point, irq_status has all bits that are set in the
- * interrupt status register and are enabled.
- */
- mutex_unlock(&data->irq_mutex);
/*
* It would be nice to be able to use irq_chip to handle these
@@ -178,6 +186,8 @@ int rmi_process_interrupt_requests(struct rmi_device *rmi_dev)
if (data->input)
input_sync(data->input);
+ mutex_unlock(&data->irq_mutex);
+
return 0;
}
EXPORT_SYMBOL_GPL(rmi_process_interrupt_requests);
@@ -207,12 +217,18 @@ static int rmi_suspend_functions(struct rmi_device *rmi_dev)
struct rmi_function *entry;
int retval;
+ mutex_lock(&data->irq_mutex);
+
list_for_each_entry(entry, &data->function_list, node) {
retval = suspend_one_function(entry);
- if (retval < 0)
+ if (retval < 0) {
+ mutex_unlock(&data->irq_mutex);
return retval;
+ }
}
+ mutex_unlock(&data->irq_mutex);
+
return 0;
}
@@ -241,16 +257,21 @@ static int rmi_resume_functions(struct rmi_device *rmi_dev)
struct rmi_function *entry;
int retval;
+ mutex_lock(&data->irq_mutex);
+
list_for_each_entry(entry, &data->function_list, node) {
retval = resume_one_function(entry);
if (retval < 0)
+ mutex_unlock(&data->irq_mutex);
return retval;
}
+ mutex_unlock(&data->irq_mutex);
+
return 0;
}
-static int enable_sensor(struct rmi_device *rmi_dev)
+int rmi_enable_sensor(struct rmi_device *rmi_dev)
{
int retval = 0;
@@ -260,6 +281,7 @@ static int enable_sensor(struct rmi_device *rmi_dev)
return rmi_process_interrupt_requests(rmi_dev);
}
+EXPORT_SYMBOL_GPL(rmi_enable_sensor);
/**
* rmi_driver_set_input_params - set input device id and other data.
@@ -455,10 +477,9 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
RMI_SCAN_DONE : RMI_SCAN_CONTINUE;
}
-static int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
- int (*callback)(struct rmi_device *rmi_dev,
- void *ctx,
- const struct pdt_entry *entry))
+int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
+ int (*callback)(struct rmi_device *rmi_dev,
+ void *ctx, const struct pdt_entry *entry))
{
int page;
int retval = RMI_SCAN_DONE;
@@ -471,6 +492,7 @@ static int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
return retval < 0 ? retval : 0;
}
+EXPORT_SYMBOL_GPL(rmi_scan_pdt);
int rmi_read_register_desc(struct rmi_device *d, u16 addr,
struct rmi_register_descriptor *rdesc)
@@ -691,19 +713,15 @@ static int rmi_count_irqs(struct rmi_device *rmi_dev,
int *irq_count = ctx;
*irq_count += pdt->interrupt_source_count;
- if (pdt->function_number == 0x01) {
+ if (pdt->function_number == 0x01)
data->f01_bootloader_mode =
rmi_check_bootloader_mode(rmi_dev, pdt);
- if (data->f01_bootloader_mode)
- dev_warn(&rmi_dev->dev,
- "WARNING: RMI4 device is in bootloader mode!\n");
- }
return RMI_SCAN_CONTINUE;
}
-static int rmi_initial_reset(struct rmi_device *rmi_dev,
- void *ctx, const struct pdt_entry *pdt)
+int rmi_initial_reset(struct rmi_device *rmi_dev, void *ctx,
+ const struct pdt_entry *pdt)
{
int error;
@@ -738,6 +756,7 @@ static int rmi_initial_reset(struct rmi_device *rmi_dev,
/* F01 should always be on page 0. If we don't find it there, fail. */
return pdt->page_start == 0 ? RMI_SCAN_CONTINUE : -ENODEV;
}
+EXPORT_SYMBOL_GPL(rmi_initial_reset);
static int rmi_create_function(struct rmi_device *rmi_dev,
void *ctx, const struct pdt_entry *pdt)
@@ -779,6 +798,8 @@ static int rmi_create_function(struct rmi_device *rmi_dev,
if (pdt->function_number == 0x01)
data->f01_container = fn;
+ else if (pdt->function_number == 0x34)
+ data->f34_container = fn;
list_add_tail(&fn->node, &data->function_list);
@@ -819,6 +840,7 @@ static int rmi_driver_remove(struct device *dev)
{
struct rmi_device *rmi_dev = to_rmi_device(dev);
+ rmi_f34_remove_sysfs(rmi_dev);
rmi_free_function_list(rmi_dev);
return 0;
@@ -845,7 +867,7 @@ static inline int rmi_driver_of_probe(struct device *dev,
}
#endif
-static int rmi_probe_interrupts(struct rmi_driver_data *data)
+int rmi_probe_interrupts(struct rmi_driver_data *data)
{
struct rmi_device *rmi_dev = data->rmi_dev;
struct device *dev = &rmi_dev->dev;
@@ -867,6 +889,10 @@ static int rmi_probe_interrupts(struct rmi_driver_data *data)
dev_err(dev, "IRQ counting failed with code %d.\n", retval);
return retval;
}
+
+ if (data->f01_bootloader_mode)
+ dev_warn(&rmi_dev->dev, "Device in bootloader mode.\n");
+
data->irq_count = irq_count;
data->num_of_irq_regs = (data->irq_count + 7) / 8;
@@ -884,14 +910,17 @@ static int rmi_probe_interrupts(struct rmi_driver_data *data)
return retval;
}
+EXPORT_SYMBOL_GPL(rmi_probe_interrupts);
-static int rmi_init_functions(struct rmi_driver_data *data)
+int rmi_init_functions(struct rmi_driver_data *data)
{
struct rmi_device *rmi_dev = data->rmi_dev;
struct device *dev = &rmi_dev->dev;
int irq_count;
int retval;
+ mutex_lock(&data->irq_mutex);
+
irq_count = 0;
rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Creating functions.\n", __func__);
retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_create_function);
@@ -916,12 +945,16 @@ static int rmi_init_functions(struct rmi_driver_data *data)
goto err_destroy_functions;
}
+ mutex_unlock(&data->irq_mutex);
+
return 0;
err_destroy_functions:
rmi_free_function_list(rmi_dev);
+ mutex_unlock(&data->irq_mutex);
return retval;
}
+EXPORT_SYMBOL_GPL(rmi_init_functions);
static int rmi_driver_probe(struct device *dev)
{
@@ -1026,6 +1059,10 @@ static int rmi_driver_probe(struct device *dev)
if (retval)
goto err;
+ retval = rmi_f34_create_sysfs(rmi_dev);
+ if (retval)
+ goto err;
+
if (data->input) {
rmi_driver_set_input_name(rmi_dev, data->input);
if (!rmi_dev->xport->input) {
@@ -1039,7 +1076,7 @@ static int rmi_driver_probe(struct device *dev)
if (data->f01_container->dev.driver)
/* Driver already bound, so enable ATTN now. */
- return enable_sensor(rmi_dev);
+ return rmi_enable_sensor(rmi_dev);
return 0;
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index 8dfbebe..e627a3a 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -95,12 +95,36 @@ bool rmi_register_desc_has_subpacket(const struct rmi_register_desc_item *item,
bool rmi_is_physical_driver(struct device_driver *);
int rmi_register_physical_driver(void);
void rmi_unregister_physical_driver(void);
+void rmi_free_function_list(struct rmi_device *rmi_dev);
+int rmi_enable_sensor(struct rmi_device *rmi_dev);
+int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
+ int (*callback)(struct rmi_device *rmi_dev, void *ctx,
+ const struct pdt_entry *entry));
+int rmi_probe_interrupts(struct rmi_driver_data *data);
+int rmi_init_functions(struct rmi_driver_data *data);
+int rmi_initial_reset(struct rmi_device *rmi_dev, void *ctx,
+ const struct pdt_entry *pdt);
char *rmi_f01_get_product_ID(struct rmi_function *fn);
+#ifdef CONFIG_RMI4_F34
+int rmi_f34_create_sysfs(struct rmi_device *rmi_dev);
+void rmi_f34_remove_sysfs(struct rmi_device *rmi_dev);
+#else
+static inline int rmi_f34_create_sysfs(struct rmi_device *rmi_dev)
+{
+ return 0;
+}
+
+static inline void rmi_f34_remove_sysfs(struct rmi_device *rmi_dev)
+{
+}
+#endif /* CONFIG_RMI_F34 */
+
extern struct rmi_function_handler rmi_f01_handler;
extern struct rmi_function_handler rmi_f11_handler;
extern struct rmi_function_handler rmi_f12_handler;
extern struct rmi_function_handler rmi_f30_handler;
+extern struct rmi_function_handler rmi_f34_handler;
extern struct rmi_function_handler rmi_f54_handler;
#endif
diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c
index b5d2dfc..b64d1cd 100644
--- a/drivers/input/rmi4/rmi_f01.c
+++ b/drivers/input/rmi4/rmi_f01.c
@@ -62,6 +62,8 @@ struct f01_basic_properties {
#define RMI_F01_STATUS_CODE(status) ((status) & 0x0f)
/* The device has lost its configuration for some reason. */
#define RMI_F01_STATUS_UNCONFIGURED(status) (!!((status) & 0x80))
+/* The device is in bootloader mode */
+#define RMI_F01_STATUS_BOOTLOADER(status) ((status) & 0x40)
/* Control register bits */
@@ -593,6 +595,10 @@ static int rmi_f01_attention(struct rmi_function *fn,
return error;
}
+ if (RMI_F01_STATUS_BOOTLOADER(device_status))
+ dev_warn(&fn->dev,
+ "Device in bootloader mode, please update firmware\n");
+
if (RMI_F01_STATUS_UNCONFIGURED(device_status)) {
dev_warn(&fn->dev, "Device reset detected.\n");
error = rmi_dev->driver->reset_handler(rmi_dev);
diff --git a/drivers/input/rmi4/rmi_f34.c b/drivers/input/rmi4/rmi_f34.c
new file mode 100644
index 0000000..9e4cf34
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f34.c
@@ -0,0 +1,486 @@
+/*
+ * Copyright (c) 2007-2016, Synaptics Incorporated
+ * Copyright (C) 2016 Zodiac Inflight Innovations
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/firmware.h>
+#include <asm/unaligned.h>
+#include <asm/unaligned.h>
+
+#include "rmi_driver.h"
+#include "rmi_f34.h"
+
+static int rmi_f34_write_bootloader_id(struct f34_data *f34)
+{
+ struct rmi_function *fn = f34->fn;
+ struct rmi_device *rmi_dev = fn->rmi_dev;
+ u8 bootloader_id[F34_BOOTLOADER_ID_LEN];
+ int ret;
+
+ ret = rmi_read_block(rmi_dev, fn->fd.query_base_addr,
+ bootloader_id, sizeof(bootloader_id));
+ if (ret) {
+ dev_err(&fn->dev, "%s: Reading bootloader ID failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: writing bootloader id '%c%c'\n",
+ __func__, bootloader_id[0], bootloader_id[1]);
+
+ ret = rmi_write_block(rmi_dev,
+ fn->fd.data_base_addr + F34_BLOCK_DATA_OFFSET,
+ bootloader_id, sizeof(bootloader_id));
+ if (ret) {
+ dev_err(&fn->dev, "Failed to write bootloader ID: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rmi_f34_command(struct f34_data *f34, u8 command,
+ unsigned int timeout, bool write_bl_id)
+{
+ struct rmi_function *fn = f34->fn;
+ struct rmi_device *rmi_dev = fn->rmi_dev;
+ int ret;
+
+ if (write_bl_id) {
+ ret = rmi_f34_write_bootloader_id(f34);
+ if (ret)
+ return ret;
+ }
+
+ init_completion(&f34->cmd_done);
+
+ ret = rmi_read(rmi_dev, f34->ctrl_address, &f34->status);
+ if (ret) {
+ dev_err(&f34->fn->dev,
+ "%s: Failed to read cmd register: %d (command %#02x)\n",
+ __func__, ret, command);
+ return ret;
+ }
+
+ f34->status |= command & 0x0f;
+
+ ret = rmi_write(rmi_dev, f34->ctrl_address, f34->status);
+ if (ret < 0) {
+ dev_err(&f34->fn->dev,
+ "Failed to write F34 command %#02x: %d\n",
+ command, ret);
+ return ret;
+ }
+
+ if (!wait_for_completion_timeout(&f34->cmd_done,
+ msecs_to_jiffies(timeout))) {
+
+ ret = rmi_read(rmi_dev, f34->ctrl_address, &f34->status);
+ if (ret) {
+ dev_err(&f34->fn->dev,
+ "%s: cmd %#02x timed out: %d\n",
+ __func__, command, ret);
+ return ret;
+ }
+
+ if (f34->status & 0x7f) {
+ dev_err(&f34->fn->dev,
+ "%s: cmd %#02x timed out, status: %#02x\n",
+ __func__, command, f34->status);
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+static int rmi_f34_attention(struct rmi_function *fn, unsigned long *irq_bits)
+{
+ struct f34_data *f34 = dev_get_drvdata(&fn->dev);
+ int ret;
+
+ ret = rmi_read(f34->fn->rmi_dev, f34->ctrl_address, &f34->status);
+ rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: status: %#02x, ret: %d\n",
+ __func__, f34->status, ret);
+
+ if (!ret && !(f34->status & 0x7f))
+ complete(&f34->cmd_done);
+
+ return 0;
+}
+
+static int rmi_f34_write_blocks(struct f34_data *f34, const void *data,
+ int block_count, u8 command)
+{
+ 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 };
+ int i;
+ int ret;
+
+ ret = rmi_write_block(rmi_dev, fn->fd.data_base_addr,
+ start_address, sizeof(start_address));
+ if (ret) {
+ dev_err(&fn->dev, "Failed to write initial zeros: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < block_count; i++) {
+ ret = rmi_write_block(rmi_dev, address, data, f34->block_size);
+ if (ret) {
+ dev_err(&fn->dev,
+ "failed to write block #%d: %d\n", i, ret);
+ return ret;
+ }
+
+ ret = rmi_f34_command(f34, command, F34_IDLE_WAIT_MS, false);
+ if (ret) {
+ dev_err(&fn->dev,
+ "Failed to write command for block #%d: %d\n",
+ i, ret);
+ return ret;
+ }
+
+ rmi_dbg(RMI_DEBUG_FN, &fn->dev, "wrote block %d of %d\n",
+ i + 1, block_count);
+
+ data += f34->block_size;
+ }
+
+ return 0;
+}
+
+static int rmi_f34_write_firmware(struct f34_data *f34, const void *data)
+{
+ return rmi_f34_write_blocks(f34, data, f34->fw_blocks,
+ F34_WRITE_FW_BLOCK);
+}
+
+static int rmi_f34_write_config(struct f34_data *f34, const void *data)
+{
+ return rmi_f34_write_blocks(f34, data, f34->config_blocks,
+ F34_WRITE_CONFIG_BLOCK);
+}
+
+int rmi_f34_enable_flash(struct rmi_function *fn)
+{
+ struct f34_data *f34 = dev_get_drvdata(&fn->dev);
+
+ return rmi_f34_command(f34, F34_ENABLE_FLASH_PROG,
+ F34_ENABLE_WAIT_MS, true);
+}
+
+static int rmi_f34_flash_firmware(struct f34_data *f34,
+ const struct rmi_f34_firmware *syn_fw)
+{
+ struct rmi_function *fn = f34->fn;
+ int ret;
+
+ if (syn_fw->image_size) {
+ dev_info(&fn->dev, "Erasing firmware...\n");
+ ret = rmi_f34_command(f34, F34_ERASE_ALL,
+ F34_ERASE_WAIT_MS, true);
+ if (ret)
+ return ret;
+
+ dev_info(&fn->dev, "Writing firmware (%d bytes)...\n",
+ syn_fw->image_size);
+ ret = rmi_f34_write_firmware(f34, syn_fw->data);
+ if (ret)
+ return ret;
+ }
+
+ if (syn_fw->config_size) {
+ /*
+ * We only need to erase config if we haven't updated
+ * firmware.
+ */
+ if (!syn_fw->image_size) {
+ dev_info(&fn->dev, "Erasing config...\n");
+ ret = rmi_f34_command(f34, F34_ERASE_CONFIG,
+ F34_ERASE_WAIT_MS, true);
+ if (ret)
+ return ret;
+ }
+
+ dev_info(&fn->dev, "Writing config (%d bytes)...\n",
+ syn_fw->config_size);
+ ret = rmi_f34_write_config(f34,
+ &syn_fw->data[syn_fw->image_size]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int rmi_f34_update_firmware(struct rmi_function *fn, const struct firmware *fw)
+{
+ struct f34_data *f34 = dev_get_drvdata(&fn->dev);
+ const struct rmi_f34_firmware *syn_fw;
+ int ret;
+
+ syn_fw = (const struct rmi_f34_firmware *)fw->data;
+ BUILD_BUG_ON(offsetof(struct rmi_f34_firmware, data) !=
+ F34_FW_IMAGE_OFFSET);
+
+ rmi_dbg(RMI_DEBUG_FN, &fn->dev,
+ "FW size:%d, checksum:%08x, image_size:%d, config_size:%d\n",
+ (int)fw->size,
+ le32_to_cpu(syn_fw->checksum),
+ le32_to_cpu(syn_fw->image_size),
+ le32_to_cpu(syn_fw->config_size));
+
+ rmi_dbg(RMI_DEBUG_FN, &fn->dev,
+ "FW bootloader_id:%02x, product_id:%.*s, info: %02x%02x\n",
+ syn_fw->bootloader_version,
+ (int)sizeof(syn_fw->product_id), syn_fw->product_id,
+ syn_fw->product_info[0], syn_fw->product_info[1]);
+
+ if (syn_fw->image_size &&
+ syn_fw->image_size != f34->fw_blocks * f34->block_size) {
+ dev_err(&fn->dev,
+ "Bad firmware image: fw size %d, expected %d\n",
+ syn_fw->image_size,
+ f34->fw_blocks * f34->block_size);
+ ret = -EILSEQ;
+ goto out;
+ }
+
+ if (syn_fw->config_size &&
+ syn_fw->config_size != f34->config_blocks * f34->block_size) {
+ dev_err(&fn->dev,
+ "Bad firmware image: config size %d, expected %d\n",
+ syn_fw->config_size,
+ f34->config_blocks * f34->block_size);
+ ret = -EILSEQ;
+ goto out;
+ }
+
+ if (syn_fw->image_size && !syn_fw->config_size) {
+ dev_err(&fn->dev, "Bad firmware image: no config data\n");
+ ret = -EILSEQ;
+ goto out;
+ }
+
+ dev_info(&f34->fn->dev, "Firmware image OK\n");
+ mutex_lock(&f34->flash_mutex);
+
+ ret = rmi_f34_flash_firmware(f34, syn_fw);
+
+ mutex_unlock(&f34->flash_mutex);
+
+out:
+ return ret;
+}
+
+int rmi_f34_check_supported(struct rmi_function *fn)
+{
+ u8 version = fn->fd.function_version;
+
+ /* Only version 0 currently supported */
+ if (version == 0)
+ return 0;
+
+ dev_warn(&fn->dev, "F34 V%d not supported!\n", version);
+ return -ENODEV;
+}
+
+static int rmi_firmware_update(struct rmi_driver_data *data,
+ const struct firmware *fw)
+{
+ struct device *dev = &data->rmi_dev->dev;
+ int ret;
+
+ if (!data->f34_container) {
+ dev_warn(dev, "%s: No F34 present!\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = rmi_f34_check_supported(data->f34_container);
+ if (ret)
+ return ret;
+
+ /* Enter flash mode */
+ rmi_dbg(RMI_DEBUG_FN, dev, "Enabling flash\n");
+ ret = rmi_f34_enable_flash(data->f34_container);
+ if (ret)
+ return ret;
+
+ /* Tear down functions and re-probe */
+ rmi_free_function_list(data->rmi_dev);
+
+ ret = rmi_probe_interrupts(data);
+ if (ret)
+ return ret;
+
+ ret = rmi_init_functions(data);
+ if (ret)
+ return ret;
+
+ if (!data->f01_bootloader_mode || !data->f34_container) {
+ dev_warn(dev, "%s: No F34 present or not in bootloader!\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* Perform firmware update */
+ ret = rmi_f34_update_firmware(data->f34_container, fw);
+
+ /* Re-probe */
+ rmi_dbg(RMI_DEBUG_FN, dev, "Re-probing device\n");
+ rmi_free_function_list(data->rmi_dev);
+
+ ret = rmi_scan_pdt(data->rmi_dev, NULL, rmi_initial_reset);
+ if (ret < 0)
+ dev_warn(dev, "RMI reset failed!\n");
+
+ ret = rmi_probe_interrupts(data);
+ if (ret)
+ return ret;
+
+ ret = rmi_init_functions(data);
+ if (ret)
+ return ret;
+
+ if (data->f01_container->dev.driver)
+ /* Driver already bound, so enable ATTN now. */
+ return rmi_enable_sensor(data->rmi_dev);
+
+ rmi_dbg(RMI_DEBUG_FN, dev, "%s complete\n", __func__);
+
+ return ret;
+}
+
+static ssize_t rmi_driver_update_fw_store(struct device *dev,
+ struct device_attribute *dattr,
+ const char *buf, size_t count)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(dev);
+ char fw_name[NAME_MAX];
+ const struct firmware *fw;
+ size_t copy_count = count;
+ int ret;
+
+ if (count == 0 || count >= NAME_MAX)
+ return -EINVAL;
+
+ if (buf[count - 1] == '\0' || buf[count - 1] == '\n')
+ copy_count -= 1;
+
+ strncpy(fw_name, buf, copy_count);
+ fw_name[copy_count] = '\0';
+
+ ret = request_firmware(&fw, fw_name, dev);
+ if (ret)
+ return ret;
+
+ dev_info(dev, "Flashing %s\n", fw_name);
+
+ ret = rmi_firmware_update(data, fw);
+
+ release_firmware(fw);
+
+ return ret ?: count;
+}
+
+static DEVICE_ATTR(update_fw, 0200, NULL, rmi_driver_update_fw_store);
+
+static struct attribute *rmi_firmware_attrs[] = {
+ &dev_attr_update_fw.attr,
+ NULL
+};
+
+static struct attribute_group rmi_firmware_attr_group = {
+ .attrs = rmi_firmware_attrs,
+};
+
+static int rmi_f34_probe(struct rmi_function *fn)
+{
+ struct f34_data *f34;
+ unsigned char f34_queries[9];
+ bool has_config_id;
+ int ret;
+
+ ret = rmi_f34_check_supported(fn);
+ if (ret)
+ return ret;
+
+ f34 = devm_kzalloc(&fn->dev, sizeof(struct f34_data), GFP_KERNEL);
+ if (!f34)
+ return -ENOMEM;
+
+ f34->fn = fn;
+ dev_set_drvdata(&fn->dev, f34);
+
+ mutex_init(&f34->flash_mutex);
+ init_completion(&f34->cmd_done);
+
+ ret = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr,
+ f34_queries, sizeof(f34_queries));
+ if (ret) {
+ dev_err(&fn->dev, "%s: Failed to query properties\n",
+ __func__);
+ return ret;
+ }
+
+ snprintf(f34->bootloader_id, sizeof(f34->bootloader_id),
+ "%c%c", f34_queries[0], f34_queries[1]);
+
+ f34->block_size = get_unaligned_le16(&f34_queries[3]);
+ f34->fw_blocks = get_unaligned_le16(&f34_queries[5]);
+ f34->config_blocks = get_unaligned_le16(&f34_queries[7]);
+ f34->ctrl_address = fn->fd.data_base_addr + F34_BLOCK_DATA_OFFSET +
+ f34->block_size;
+ has_config_id = f34_queries[2] & (1 << 2);
+
+ rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Bootloader ID: %s\n", f34->bootloader_id);
+ rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Block size: %d\n", f34->block_size);
+ rmi_dbg(RMI_DEBUG_FN, &fn->dev, "FW blocks: %d\n", f34->fw_blocks);
+ rmi_dbg(RMI_DEBUG_FN, &fn->dev, "CFG blocks: %d\n", f34->config_blocks);
+
+ if (has_config_id) {
+ ret = rmi_read_block(fn->rmi_dev, fn->fd.control_base_addr,
+ f34_queries, sizeof(f34_queries));
+ if (ret) {
+ dev_err(&fn->dev, "Failed to read F34 config ID\n");
+ return ret;
+ }
+
+ snprintf(f34->configuration_id, sizeof(f34->configuration_id),
+ "%02x%02x%02x%02x",
+ f34_queries[0], f34_queries[1],
+ f34_queries[2], f34_queries[3]);
+
+ rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Configuration ID: %s\n",
+ f34->configuration_id);
+ }
+
+ return 0;
+}
+
+int rmi_f34_create_sysfs(struct rmi_device *rmi_dev)
+{
+ return sysfs_create_group(&rmi_dev->dev.kobj, &rmi_firmware_attr_group);
+}
+
+void rmi_f34_remove_sysfs(struct rmi_device *rmi_dev)
+{
+ sysfs_remove_group(&rmi_dev->dev.kobj, &rmi_firmware_attr_group);
+}
+
+struct rmi_function_handler rmi_f34_handler = {
+ .driver = {
+ .name = "rmi4_f34",
+ },
+ .func = 0x34,
+ .probe = rmi_f34_probe,
+ .attention = rmi_f34_attention,
+};
diff --git a/drivers/input/rmi4/rmi_f34.h b/drivers/input/rmi4/rmi_f34.h
new file mode 100644
index 0000000..0c19a58
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f34.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2007-2016, Synaptics Incorporated
+ * Copyright (C) 2016 Zodiac Inflight Innovations
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _RMI_F34_H
+#define _RMI_F34_H
+
+/* F34 image file offsets. */
+#define F34_FW_IMAGE_OFFSET 0x100
+
+/* F34 register offsets. */
+#define F34_BLOCK_DATA_OFFSET 2
+
+/* F34 commands */
+#define F34_WRITE_FW_BLOCK 0x2
+#define F34_ERASE_ALL 0x3
+#define F34_READ_CONFIG_BLOCK 0x5
+#define F34_WRITE_CONFIG_BLOCK 0x6
+#define F34_ERASE_CONFIG 0x7
+#define F34_ENABLE_FLASH_PROG 0xf
+
+#define F34_STATUS_IN_PROGRESS 0xff
+#define F34_STATUS_IDLE 0x80
+
+#define F34_IDLE_WAIT_MS 500
+#define F34_ENABLE_WAIT_MS 300
+#define F34_ERASE_WAIT_MS 5000
+
+#define F34_BOOTLOADER_ID_LEN 2
+
+struct rmi_f34_firmware {
+ __le32 checksum;
+ u8 pad1[3];
+ u8 bootloader_version;
+ __le32 image_size;
+ __le32 config_size;
+ u8 product_id[10];
+ u8 product_info[2];
+ u8 pad2[228];
+ u8 data[];
+};
+
+struct f34_data {
+ struct rmi_function *fn;
+
+ u16 block_size;
+ u16 fw_blocks;
+ u16 config_blocks;
+ u16 ctrl_address;
+ u8 status;
+
+ struct completion cmd_done;
+ struct mutex flash_mutex;
+
+ unsigned char bootloader_id[5];
+ unsigned char configuration_id[9];
+};
+
+#endif /* _RMI_F34_H */
diff --git a/include/linux/rmi.h b/include/linux/rmi.h
index e0aca14..a283a67 100644
--- a/include/linux/rmi.h
+++ b/include/linux/rmi.h
@@ -330,6 +330,7 @@ struct rmi_driver_data {
struct rmi_device *rmi_dev;
struct rmi_function *f01_container;
+ struct rmi_function *f34_container;
bool f01_bootloader_mode;
u32 attn_count;
--
2.7.4
Signed-off-by: Nick Dyer <[email protected]>
---
drivers/input/rmi4/rmi_bus.c | 3 +++
drivers/input/rmi4/rmi_driver.c | 1 +
2 files changed, 4 insertions(+)
diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c
index ef8c747..3c6a1b5 100644
--- a/drivers/input/rmi4/rmi_bus.c
+++ b/drivers/input/rmi4/rmi_bus.c
@@ -230,6 +230,9 @@ int rmi_register_function(struct rmi_function *fn)
void rmi_unregister_function(struct rmi_function *fn)
{
+ rmi_dbg(RMI_DEBUG_CORE, &fn->dev, "Unregistering F%02X.\n",
+ fn->fd.function_number);
+
device_del(&fn->dev);
of_node_put(fn->dev.of_node);
put_device(&fn->dev);
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index 63c9e22..adb3ee8 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -722,6 +722,7 @@ static int rmi_initial_reset(struct rmi_device *rmi_dev,
return RMI_SCAN_DONE;
}
+ rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev, "Sending reset\n");
error = rmi_write_block(rmi_dev, cmd_addr, &cmd_buf, 1);
if (error) {
dev_err(&rmi_dev->dev,
--
2.7.4
Port firmware update code from Samsung Galaxy S7 driver into
mainline framework.
This patch has been tested on Synaptics S7813.
Signed-off-by: Nick Dyer <[email protected]>
---
drivers/input/rmi4/Makefile | 2 +-
drivers/input/rmi4/rmi_driver.c | 56 +-
drivers/input/rmi4/rmi_f34.c | 146 ++--
drivers/input/rmi4/rmi_f34.h | 379 +++++++++-
drivers/input/rmi4/rmi_f34v7.c | 1451 +++++++++++++++++++++++++++++++++++++++
include/linux/rmi.h | 2 +-
6 files changed, 1940 insertions(+), 96 deletions(-)
create mode 100644 drivers/input/rmi4/rmi_f34v7.c
diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile
index 5f165ad..a74b6d5 100644
--- a/drivers/input/rmi4/Makefile
+++ b/drivers/input/rmi4/Makefile
@@ -7,7 +7,7 @@ rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.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_core-$(CONFIG_RMI4_F34) += rmi_f34.o rmi_f34v7.o
rmi_core-$(CONFIG_RMI4_F54) += rmi_f54.o
# Transports
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index 78fdcef..a9e2827 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -473,7 +473,7 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
return retval;
}
- return (data->f01_bootloader_mode || addr == pdt_start) ?
+ return (data->bootloader_mode || addr == pdt_start) ?
RMI_SCAN_DONE : RMI_SCAN_CONTINUE;
}
@@ -681,41 +681,49 @@ bool rmi_register_desc_has_subpacket(const struct rmi_register_desc_item *item,
subpacket) == subpacket;
}
-/* Indicates that flash programming is enabled (bootloader mode). */
-#define RMI_F01_STATUS_BOOTLOADER(status) (!!((status) & 0x40))
-
-/*
- * Given the PDT entry for F01, read the device status register to determine
- * if we're stuck in bootloader mode or not.
- *
- */
static int rmi_check_bootloader_mode(struct rmi_device *rmi_dev,
const struct pdt_entry *pdt)
{
- int error;
- u8 device_status;
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ int ret;
+ u8 status;
- error = rmi_read(rmi_dev, pdt->data_base_addr + pdt->page_start,
- &device_status);
- if (error) {
- dev_err(&rmi_dev->dev,
- "Failed to read device status: %d.\n", error);
- return error;
+ if (pdt->function_number == 0x34 && pdt->function_version > 1) {
+ ret = rmi_read(rmi_dev, pdt->data_base_addr, &status);
+ if (ret) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F34 status: %d.\n", ret);
+ return ret;
+ }
+
+ if (status & BIT(7))
+ data->bootloader_mode = true;
+ } else if (pdt->function_number == 0x01) {
+ ret = rmi_read(rmi_dev, pdt->data_base_addr, &status);
+ if (ret) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F01 status: %d.\n", ret);
+ return ret;
+ }
+
+ if (status & BIT(6))
+ data->bootloader_mode = true;
}
- return RMI_F01_STATUS_BOOTLOADER(device_status);
+ return 0;
}
static int rmi_count_irqs(struct rmi_device *rmi_dev,
void *ctx, const struct pdt_entry *pdt)
{
- struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
int *irq_count = ctx;
+ int ret;
*irq_count += pdt->interrupt_source_count;
- if (pdt->function_number == 0x01)
- data->f01_bootloader_mode =
- rmi_check_bootloader_mode(rmi_dev, pdt);
+
+ ret = rmi_check_bootloader_mode(rmi_dev, pdt);
+ if (ret < 0)
+ return ret;
return RMI_SCAN_CONTINUE;
}
@@ -884,13 +892,15 @@ int rmi_probe_interrupts(struct rmi_driver_data *data)
*/
rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Counting IRQs.\n", __func__);
irq_count = 0;
+ data->bootloader_mode = false;
+
retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_count_irqs);
if (retval < 0) {
dev_err(dev, "IRQ counting failed with code %d.\n", retval);
return retval;
}
- if (data->f01_bootloader_mode)
+ if (data->bootloader_mode)
dev_warn(&rmi_dev->dev, "Device in bootloader mode.\n");
data->irq_count = irq_count;
diff --git a/drivers/input/rmi4/rmi_f34.c b/drivers/input/rmi4/rmi_f34.c
index 9e4cf34..4f97613 100644
--- a/drivers/input/rmi4/rmi_f34.c
+++ b/drivers/input/rmi4/rmi_f34.c
@@ -12,6 +12,7 @@
#include <linux/firmware.h>
#include <asm/unaligned.h>
#include <asm/unaligned.h>
+#include <linux/bitops.h>
#include "rmi_driver.h"
#include "rmi_f34.h"
@@ -58,9 +59,9 @@ static int rmi_f34_command(struct f34_data *f34, u8 command,
return ret;
}
- init_completion(&f34->cmd_done);
+ init_completion(&f34->v5.cmd_done);
- ret = rmi_read(rmi_dev, f34->ctrl_address, &f34->status);
+ ret = rmi_read(rmi_dev, f34->v5.ctrl_address, &f34->v5.status);
if (ret) {
dev_err(&f34->fn->dev,
"%s: Failed to read cmd register: %d (command %#02x)\n",
@@ -68,9 +69,9 @@ static int rmi_f34_command(struct f34_data *f34, u8 command,
return ret;
}
- f34->status |= command & 0x0f;
+ f34->v5.status |= command & 0x0f;
- ret = rmi_write(rmi_dev, f34->ctrl_address, f34->status);
+ ret = rmi_write(rmi_dev, f34->v5.ctrl_address, f34->v5.status);
if (ret < 0) {
dev_err(&f34->fn->dev,
"Failed to write F34 command %#02x: %d\n",
@@ -78,10 +79,10 @@ static int rmi_f34_command(struct f34_data *f34, u8 command,
return ret;
}
- if (!wait_for_completion_timeout(&f34->cmd_done,
+ if (!wait_for_completion_timeout(&f34->v5.cmd_done,
msecs_to_jiffies(timeout))) {
- ret = rmi_read(rmi_dev, f34->ctrl_address, &f34->status);
+ ret = rmi_read(rmi_dev, f34->v5.ctrl_address, &f34->v5.status);
if (ret) {
dev_err(&f34->fn->dev,
"%s: cmd %#02x timed out: %d\n",
@@ -89,10 +90,10 @@ static int rmi_f34_command(struct f34_data *f34, u8 command,
return ret;
}
- if (f34->status & 0x7f) {
+ if (f34->v5.status & 0x7f) {
dev_err(&f34->fn->dev,
"%s: cmd %#02x timed out, status: %#02x\n",
- __func__, command, f34->status);
+ __func__, command, f34->v5.status);
return -ETIMEDOUT;
}
}
@@ -105,12 +106,15 @@ static int rmi_f34_attention(struct rmi_function *fn, unsigned long *irq_bits)
struct f34_data *f34 = dev_get_drvdata(&fn->dev);
int ret;
- ret = rmi_read(f34->fn->rmi_dev, f34->ctrl_address, &f34->status);
+ if (f34->bl_version != BL_V5)
+ return 0;
+
+ ret = rmi_read(f34->fn->rmi_dev, f34->v5.ctrl_address, &f34->v5.status);
rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: status: %#02x, ret: %d\n",
- __func__, f34->status, ret);
+ __func__, f34->v5.status, ret);
- if (!ret && !(f34->status & 0x7f))
- complete(&f34->cmd_done);
+ if (!ret && !(f34->v5.status & 0x7f))
+ complete(&f34->v5.cmd_done);
return 0;
}
@@ -133,7 +137,7 @@ static int rmi_f34_write_blocks(struct f34_data *f34, const void *data,
}
for (i = 0; i < block_count; i++) {
- ret = rmi_write_block(rmi_dev, address, data, f34->block_size);
+ ret = rmi_write_block(rmi_dev, address, data, f34->v5.block_size);
if (ret) {
dev_err(&fn->dev,
"failed to write block #%d: %d\n", i, ret);
@@ -151,7 +155,7 @@ static int rmi_f34_write_blocks(struct f34_data *f34, const void *data,
rmi_dbg(RMI_DEBUG_FN, &fn->dev, "wrote block %d of %d\n",
i + 1, block_count);
- data += f34->block_size;
+ data += f34->v5.block_size;
}
return 0;
@@ -159,26 +163,24 @@ static int rmi_f34_write_blocks(struct f34_data *f34, const void *data,
static int rmi_f34_write_firmware(struct f34_data *f34, const void *data)
{
- return rmi_f34_write_blocks(f34, data, f34->fw_blocks,
+ return rmi_f34_write_blocks(f34, data, f34->v5.fw_blocks,
F34_WRITE_FW_BLOCK);
}
static int rmi_f34_write_config(struct f34_data *f34, const void *data)
{
- return rmi_f34_write_blocks(f34, data, f34->config_blocks,
+ return rmi_f34_write_blocks(f34, data, f34->v5.config_blocks,
F34_WRITE_CONFIG_BLOCK);
}
-int rmi_f34_enable_flash(struct rmi_function *fn)
+int rmi_f34_enable_flash(struct f34_data *f34)
{
- struct f34_data *f34 = dev_get_drvdata(&fn->dev);
-
return rmi_f34_command(f34, F34_ENABLE_FLASH_PROG,
F34_ENABLE_WAIT_MS, true);
}
static int rmi_f34_flash_firmware(struct f34_data *f34,
- const struct rmi_f34_firmware *syn_fw)
+ const struct rmi_f34_firmware *syn_fw)
{
struct rmi_function *fn = f34->fn;
int ret;
@@ -221,9 +223,8 @@ static int rmi_f34_flash_firmware(struct f34_data *f34,
return 0;
}
-int rmi_f34_update_firmware(struct rmi_function *fn, const struct firmware *fw)
+int rmi_f34_update_firmware(struct f34_data *f34, const struct firmware *fw)
{
- struct f34_data *f34 = dev_get_drvdata(&fn->dev);
const struct rmi_f34_firmware *syn_fw;
int ret;
@@ -231,72 +232,61 @@ int rmi_f34_update_firmware(struct rmi_function *fn, const struct firmware *fw)
BUILD_BUG_ON(offsetof(struct rmi_f34_firmware, data) !=
F34_FW_IMAGE_OFFSET);
- rmi_dbg(RMI_DEBUG_FN, &fn->dev,
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
"FW size:%d, checksum:%08x, image_size:%d, config_size:%d\n",
(int)fw->size,
le32_to_cpu(syn_fw->checksum),
le32_to_cpu(syn_fw->image_size),
le32_to_cpu(syn_fw->config_size));
- rmi_dbg(RMI_DEBUG_FN, &fn->dev,
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
"FW bootloader_id:%02x, product_id:%.*s, info: %02x%02x\n",
syn_fw->bootloader_version,
(int)sizeof(syn_fw->product_id), syn_fw->product_id,
syn_fw->product_info[0], syn_fw->product_info[1]);
if (syn_fw->image_size &&
- syn_fw->image_size != f34->fw_blocks * f34->block_size) {
- dev_err(&fn->dev,
+ syn_fw->image_size != f34->v5.fw_blocks * f34->v5.block_size) {
+ dev_err(&f34->fn->dev,
"Bad firmware image: fw size %d, expected %d\n",
syn_fw->image_size,
- f34->fw_blocks * f34->block_size);
+ f34->v5.fw_blocks * f34->v5.block_size);
ret = -EILSEQ;
goto out;
}
if (syn_fw->config_size &&
- syn_fw->config_size != f34->config_blocks * f34->block_size) {
- dev_err(&fn->dev,
+ syn_fw->config_size != f34->v5.config_blocks * f34->v5.block_size) {
+ dev_err(&f34->fn->dev,
"Bad firmware image: config size %d, expected %d\n",
syn_fw->config_size,
- f34->config_blocks * f34->block_size);
+ f34->v5.config_blocks * f34->v5.block_size);
ret = -EILSEQ;
goto out;
}
if (syn_fw->image_size && !syn_fw->config_size) {
- dev_err(&fn->dev, "Bad firmware image: no config data\n");
+ dev_err(&f34->fn->dev, "Bad firmware image: no config data\n");
ret = -EILSEQ;
goto out;
}
dev_info(&f34->fn->dev, "Firmware image OK\n");
- mutex_lock(&f34->flash_mutex);
+ mutex_lock(&f34->v5.flash_mutex);
ret = rmi_f34_flash_firmware(f34, syn_fw);
- mutex_unlock(&f34->flash_mutex);
+ mutex_unlock(&f34->v5.flash_mutex);
out:
return ret;
}
-int rmi_f34_check_supported(struct rmi_function *fn)
-{
- u8 version = fn->fd.function_version;
-
- /* Only version 0 currently supported */
- if (version == 0)
- return 0;
-
- dev_warn(&fn->dev, "F34 V%d not supported!\n", version);
- return -ENODEV;
-}
-
static int rmi_firmware_update(struct rmi_driver_data *data,
const struct firmware *fw)
{
struct device *dev = &data->rmi_dev->dev;
+ struct f34_data *f34;
int ret;
if (!data->f34_container) {
@@ -304,13 +294,23 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
return -EINVAL;
}
- ret = rmi_f34_check_supported(data->f34_container);
- if (ret)
- return ret;
+ f34 = dev_get_drvdata(&data->f34_container->dev);
+
+ if (f34->bl_version == BL_V7) {
+ if (data->pdt_props & HAS_BSR) {
+ dev_err(dev, "%s: LTS not supported\n", __func__);
+ return -ENODEV;
+ }
+ } else if (data->f34_container->fd.function_version != 0) {
+ dev_err(dev, "%s: Version not supported\n", __func__);
+ return -ENODEV;
+ }
/* Enter flash mode */
- rmi_dbg(RMI_DEBUG_FN, dev, "Enabling flash\n");
- ret = rmi_f34_enable_flash(data->f34_container);
+ if (f34->bl_version == BL_V7)
+ ret = rmi_f34v7_start_reflash(f34, fw);
+ else
+ ret = rmi_f34_enable_flash(f34);
if (ret)
return ret;
@@ -325,14 +325,21 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
if (ret)
return ret;
- if (!data->f01_bootloader_mode || !data->f34_container) {
+ if (!data->bootloader_mode || !data->f34_container) {
dev_warn(dev, "%s: No F34 present or not in bootloader!\n",
- __func__);
+ __func__);
return -EINVAL;
}
+ f34 = dev_get_drvdata(&data->f34_container->dev);
+
/* Perform firmware update */
- ret = rmi_f34_update_firmware(data->f34_container, fw);
+ if (f34->bl_version == BL_V7)
+ ret = rmi_f34v7_do_reflash(f34, fw);
+ else
+ ret = rmi_f34_update_firmware(f34, fw);
+
+ dev_info(&f34->fn->dev, "Firmware update complete, status:%d\n", ret);
/* Re-probe */
rmi_dbg(RMI_DEBUG_FN, dev, "Re-probing device\n");
@@ -407,12 +414,9 @@ static int rmi_f34_probe(struct rmi_function *fn)
struct f34_data *f34;
unsigned char f34_queries[9];
bool has_config_id;
+ u8 version = fn->fd.function_version;
int ret;
- ret = rmi_f34_check_supported(fn);
- if (ret)
- return ret;
-
f34 = devm_kzalloc(&fn->dev, sizeof(struct f34_data), GFP_KERNEL);
if (!f34)
return -ENOMEM;
@@ -420,31 +424,37 @@ static int rmi_f34_probe(struct rmi_function *fn)
f34->fn = fn;
dev_set_drvdata(&fn->dev, f34);
- mutex_init(&f34->flash_mutex);
- init_completion(&f34->cmd_done);
+ /* v5 code only supported version 0, try V7 probe */
+ if (version > 0)
+ return rmi_f34v7_probe(f34);
+
+ f34->bl_version = BL_V5;
ret = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr,
f34_queries, sizeof(f34_queries));
if (ret) {
dev_err(&fn->dev, "%s: Failed to query properties\n",
- __func__);
+ __func__);
return ret;
}
snprintf(f34->bootloader_id, sizeof(f34->bootloader_id),
"%c%c", f34_queries[0], f34_queries[1]);
- f34->block_size = get_unaligned_le16(&f34_queries[3]);
- f34->fw_blocks = get_unaligned_le16(&f34_queries[5]);
- f34->config_blocks = get_unaligned_le16(&f34_queries[7]);
- f34->ctrl_address = fn->fd.data_base_addr + F34_BLOCK_DATA_OFFSET +
- f34->block_size;
+ mutex_init(&f34->v5.flash_mutex);
+ init_completion(&f34->v5.cmd_done);
+
+ f34->v5.block_size = get_unaligned_le16(&f34_queries[3]);
+ f34->v5.fw_blocks = get_unaligned_le16(&f34_queries[5]);
+ f34->v5.config_blocks = get_unaligned_le16(&f34_queries[7]);
+ f34->v5.ctrl_address = fn->fd.data_base_addr + F34_BLOCK_DATA_OFFSET +
+ f34->v5.block_size;
has_config_id = f34_queries[2] & (1 << 2);
rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Bootloader ID: %s\n", f34->bootloader_id);
- rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Block size: %d\n", f34->block_size);
- rmi_dbg(RMI_DEBUG_FN, &fn->dev, "FW blocks: %d\n", f34->fw_blocks);
- rmi_dbg(RMI_DEBUG_FN, &fn->dev, "CFG blocks: %d\n", f34->config_blocks);
+ rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Block size: %d\n", f34->v5.block_size);
+ rmi_dbg(RMI_DEBUG_FN, &fn->dev, "FW blocks: %d\n", f34->v5.fw_blocks);
+ rmi_dbg(RMI_DEBUG_FN, &fn->dev, "CFG blocks: %d\n", f34->v5.config_blocks);
if (has_config_id) {
ret = rmi_read_block(fn->rmi_dev, fn->fd.control_base_addr,
diff --git a/drivers/input/rmi4/rmi_f34.h b/drivers/input/rmi4/rmi_f34.h
index 0c19a58..9ab8ece 100644
--- a/drivers/input/rmi4/rmi_f34.h
+++ b/drivers/input/rmi4/rmi_f34.h
@@ -33,6 +33,317 @@
#define F34_BOOTLOADER_ID_LEN 2
+#define V7_FLASH_STATUS_OFFSET 0
+#define V7_PARTITION_ID_OFFSET 1
+#define V7_BLOCK_NUMBER_OFFSET 2
+#define V7_TRANSFER_LENGTH_OFFSET 3
+#define V7_COMMAND_OFFSET 4
+#define V7_PAYLOAD_OFFSET 5
+#define BOOTLOADER_ID_OFFSET 1
+
+#define V7_PARTITION_SUPPORT_BYTES 4
+
+#define SLEEP_MODE_NORMAL (0x00)
+
+#define IMAGE_HEADER_VERSION_10 0x10
+
+#define MAX_IMAGE_NAME_LEN 256
+#define SYNAPTICS_RMI4_PRODUCT_ID_SIZE 10
+#define PRODUCT_ID_SIZE 10
+
+#define MASK_8BIT 0xFF
+#define MASK_5BIT 0x1F
+
+#define ENABLE_WAIT_MS (1 * 1000)
+#define WRITE_WAIT_MS (3 * 1000)
+#define ERASE_WAIT_MS (5 * 1000)
+
+#define MIN_SLEEP_TIME_US 50
+#define MAX_SLEEP_TIME_US 100
+
+#define FORCE_UPDATE false
+
+#define HAS_BSR BIT(5)
+
+enum rmi_f34_bl_version {
+ BL_V5 = 5,
+ BL_V6 = 6,
+ BL_V7 = 7,
+};
+
+enum rmi_f34v7_flash_command2 {
+ CMD_V7_IDLE = 0x00,
+ CMD_V7_ENTER_BL,
+ CMD_V7_READ,
+ CMD_V7_WRITE,
+ CMD_V7_ERASE,
+ CMD_V7_ERASE_AP,
+ CMD_V7_SENSOR_ID,
+};
+
+enum rmi_f34v7_flash_command {
+ v7_CMD_IDLE = 0,
+ v7_CMD_WRITE_FW,
+ v7_CMD_WRITE_CONFIG,
+ v7_CMD_WRITE_LOCKDOWN,
+ v7_CMD_WRITE_GUEST_CODE,
+ v7_CMD_READ_CONFIG,
+ v7_CMD_ERASE_ALL,
+ v7_CMD_ERASE_UI_FIRMWARE,
+ v7_CMD_ERASE_UI_CONFIG,
+ v7_CMD_ERASE_BL_CONFIG,
+ v7_CMD_ERASE_DISP_CONFIG,
+ v7_CMD_ERASE_FLASH_CONFIG,
+ v7_CMD_ERASE_GUEST_CODE,
+ v7_CMD_ENABLE_FLASH_PROG,
+};
+
+enum rmi_f34v7_config_area {
+ v7_UI_CONFIG_AREA = 0,
+ v7_PM_CONFIG_AREA,
+ v7_BL_CONFIG_AREA,
+ v7_DP_CONFIG_AREA,
+ v7_FLASH_CONFIG_AREA,
+};
+
+enum rmi_f34v7_partition_id {
+ BOOTLOADER_PARTITION = 0x01,
+ DEVICE_CONFIG_PARTITION,
+ FLASH_CONFIG_PARTITION,
+ MANUFACTURING_BLOCK_PARTITION,
+ GUEST_SERIALIZATION_PARTITION,
+ GLOBAL_PARAMETERS_PARTITION,
+ CORE_CODE_PARTITION,
+ CORE_CONFIG_PARTITION,
+ GUEST_CODE_PARTITION,
+ DISPLAY_CONFIG_PARTITION,
+};
+
+struct f34v7_query_0 {
+ union {
+ struct {
+ unsigned char subpacket_1_size:3;
+ unsigned char has_config_id:1;
+ unsigned char f34_query0_b4:1;
+ unsigned char has_thqa:1;
+ unsigned char f34_query0_b6__7:2;
+ } __packed;
+ unsigned char data[1];
+ };
+};
+
+struct synaptics_rmi4_f34_query_01 {
+ union {
+ struct {
+ unsigned char reg_map:1;
+ unsigned char unlocked:1;
+ unsigned char has_config_id:1;
+ unsigned char has_perm_config:1;
+ unsigned char has_bl_config:1;
+ unsigned char has_disp_config:1;
+ unsigned char has_ctrl1:1;
+ unsigned char has_flash_query4:1;
+ } __packed;
+ unsigned char data[1];
+ };
+};
+
+struct f34v7_query_1_7 {
+ union {
+ struct {
+ /* query 1 */
+ unsigned char bl_minor_revision;
+ unsigned char bl_major_revision;
+
+ /* query 2 */
+ unsigned char bl_fw_id_7_0;
+ unsigned char bl_fw_id_15_8;
+ unsigned char bl_fw_id_23_16;
+ unsigned char bl_fw_id_31_24;
+
+ /* query 3 */
+ unsigned char minimum_write_size;
+ unsigned char block_size_7_0;
+ unsigned char block_size_15_8;
+ unsigned char flash_page_size_7_0;
+ unsigned char flash_page_size_15_8;
+
+ /* query 4 */
+ unsigned char adjustable_partition_area_size_7_0;
+ unsigned char adjustable_partition_area_size_15_8;
+
+ /* query 5 */
+ unsigned char flash_config_length_7_0;
+ unsigned char flash_config_length_15_8;
+
+ /* query 6 */
+ unsigned char payload_length_7_0;
+ unsigned char payload_length_15_8;
+
+ /* query 7 */
+ unsigned char f34_query7_b0:1;
+ unsigned char has_bootloader:1;
+ unsigned char has_device_config:1;
+ unsigned char has_flash_config:1;
+ unsigned char has_manufacturing_block:1;
+ unsigned char has_guest_serialization:1;
+ unsigned char has_global_parameters:1;
+ unsigned char has_core_code:1;
+ unsigned char has_core_config:1;
+ unsigned char has_guest_code:1;
+ unsigned char has_display_config:1;
+ unsigned char f34_query7_b11__15:5;
+ unsigned char f34_query7_b16__23;
+ unsigned char f34_query7_b24__31;
+ } __packed;
+ unsigned char data[21];
+ };
+};
+
+struct f34v7_data_1_5 {
+ union {
+ struct {
+ unsigned char partition_id:5;
+ unsigned char f34_data1_b5__7:3;
+ unsigned char block_offset_7_0;
+ unsigned char block_offset_15_8;
+ unsigned char transfer_length_7_0;
+ unsigned char transfer_length_15_8;
+ unsigned char command;
+ unsigned char payload_0;
+ unsigned char payload_1;
+ } __packed;
+ unsigned char data[8];
+ };
+};
+
+struct block_data {
+ const unsigned char *data;
+ int size;
+};
+
+struct partition_table {
+ unsigned char partition_id:5;
+ unsigned char byte_0_reserved:3;
+ unsigned char byte_1_reserved;
+ unsigned char partition_length_7_0;
+ unsigned char partition_length_15_8;
+ unsigned char start_physical_address_7_0;
+ unsigned char start_physical_address_15_8;
+ unsigned char partition_properties_7_0;
+ unsigned char partition_properties_15_8;
+} __packed;
+
+struct physical_address {
+ unsigned short ui_firmware;
+ unsigned short ui_config;
+ unsigned short dp_config;
+ unsigned short guest_code;
+};
+
+struct container_descriptor {
+ unsigned char content_checksum[4];
+ unsigned char container_id[2];
+ unsigned char minor_version;
+ unsigned char major_version;
+ unsigned char reserved_08;
+ unsigned char reserved_09;
+ unsigned char reserved_0a;
+ unsigned char reserved_0b;
+ unsigned char container_option_flags[4];
+ unsigned char content_options_length[4];
+ unsigned char content_options_address[4];
+ unsigned char content_length[4];
+ unsigned char content_address[4];
+};
+
+enum container_id {
+ TOP_LEVEL_CONTAINER = 0,
+ UI_CONTAINER,
+ UI_CONFIG_CONTAINER,
+ BL_CONTAINER,
+ BL_IMAGE_CONTAINER,
+ BL_CONFIG_CONTAINER,
+ BL_LOCKDOWN_INFO_CONTAINER,
+ PERMANENT_CONFIG_CONTAINER,
+ GUEST_CODE_CONTAINER,
+ BL_PROTOCOL_DESCRIPTOR_CONTAINER,
+ UI_PROTOCOL_DESCRIPTOR_CONTAINER,
+ RMI_SELF_DISCOVERY_CONTAINER,
+ RMI_PAGE_CONTENT_CONTAINER,
+ GENERAL_INFORMATION_CONTAINER,
+ DEVICE_CONFIG_CONTAINER,
+ FLASH_CONFIG_CONTAINER,
+ GUEST_SERIALIZATION_CONTAINER,
+ GLOBAL_PARAMETERS_CONTAINER,
+ CORE_CODE_CONTAINER,
+ CORE_CONFIG_CONTAINER,
+ DISPLAY_CONFIG_CONTAINER,
+};
+
+struct block_count {
+ unsigned short ui_firmware;
+ unsigned short ui_config;
+ unsigned short dp_config;
+ unsigned short fl_config;
+ unsigned short pm_config;
+ unsigned short bl_config;
+ unsigned short lockdown;
+ unsigned short guest_code;
+};
+
+struct image_header_10 {
+ unsigned char checksum[4];
+ unsigned char reserved_04;
+ unsigned char reserved_05;
+ unsigned char minor_header_version;
+ unsigned char major_header_version;
+ unsigned char reserved_08;
+ unsigned char reserved_09;
+ unsigned char reserved_0a;
+ unsigned char reserved_0b;
+ unsigned char top_level_container_start_addr[4];
+};
+
+struct image_metadata {
+ bool contains_firmware_id;
+ bool contains_bootloader;
+ bool contains_disp_config;
+ bool contains_guest_code;
+ bool contains_flash_config;
+ unsigned int firmware_id;
+ unsigned int checksum;
+ unsigned int bootloader_size;
+ unsigned int disp_config_offset;
+ unsigned char bl_version;
+ unsigned char product_id[PRODUCT_ID_SIZE + 1];
+ unsigned char cstmr_product_id[PRODUCT_ID_SIZE + 1];
+ struct block_data bootloader;
+ struct block_data ui_firmware;
+ struct block_data ui_config;
+ struct block_data dp_config;
+ struct block_data fl_config;
+ struct block_data bl_config;
+ struct block_data guest_code;
+ struct block_data lockdown;
+ struct block_count blkcount;
+ struct physical_address phyaddr;
+};
+
+struct register_offset {
+ unsigned char properties;
+ unsigned char properties_2;
+ unsigned char block_size;
+ unsigned char block_count;
+ unsigned char gc_block_count;
+ unsigned char flash_status;
+ unsigned char partition_id;
+ unsigned char block_number;
+ unsigned char transfer_length;
+ unsigned char flash_cmd;
+ unsigned char payload;
+};
+
struct rmi_f34_firmware {
__le32 checksum;
u8 pad1[3];
@@ -45,9 +356,7 @@ struct rmi_f34_firmware {
u8 data[];
};
-struct f34_data {
- struct rmi_function *fn;
-
+struct f34v5_data {
u16 block_size;
u16 fw_blocks;
u16 config_blocks;
@@ -56,9 +365,73 @@ struct f34_data {
struct completion cmd_done;
struct mutex flash_mutex;
+};
+
+struct f34v7_data {
+ bool initialized;
+ bool has_perm_config;
+ bool has_bl_config;
+ bool has_disp_config;
+ bool has_guest_code;
+ bool force_update;
+ unsigned char *read_config_buf;
+ unsigned char command;
+ unsigned char flash_status;
+ unsigned char productinfo1;
+ unsigned char productinfo2;
+ unsigned char properties_off;
+ unsigned char blk_size_off;
+ unsigned char blk_count_off;
+ unsigned char blk_data_off;
+ unsigned char properties2_off;
+ unsigned char guest_blk_count_off;
+ unsigned char flash_cmd_off;
+ unsigned char flash_status_off;
+ unsigned short block_size;
+ unsigned short fw_block_count;
+ unsigned short config_block_count;
+ unsigned short perm_config_block_count;
+ unsigned short bl_config_block_count;
+ unsigned short disp_config_block_count;
+ unsigned short guest_code_block_count;
+ unsigned short config_size;
+ unsigned short config_area;
+ char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1];
+ struct synaptics_rmi4_f34_query_01 flash_properties;
+ struct workqueue_struct *fwu_workqueue;
+ struct delayed_work fwu_work;
+
+ unsigned short flash_config_length;
+ unsigned short payload_length;
+ struct register_offset off;
+ unsigned char partitions;
+ unsigned short partition_table_bytes;
+ unsigned short read_config_buf_size;
+ struct block_count blkcount;
+ struct physical_address phyaddr;
+ struct image_metadata img;
+ bool new_partition_table;
+ const unsigned char *config_data;
+ const unsigned char *image;
+ bool in_bl_mode;
+};
+
+struct f34_data {
+ struct rmi_function *fn;
+
+ enum rmi_f34_bl_version bl_version;
unsigned char bootloader_id[5];
unsigned char configuration_id[9];
+
+ union {
+ struct f34v5_data v5;
+ struct f34v7_data v7;
+ };
};
+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);
+
#endif /* _RMI_F34_H */
diff --git a/drivers/input/rmi4/rmi_f34v7.c b/drivers/input/rmi4/rmi_f34v7.c
new file mode 100644
index 0000000..839f308
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f34v7.c
@@ -0,0 +1,1451 @@
+/*
+ * Copyright (c) 2016, Zodiac Inflight Innovations
+ * Copyright (c) 2007-2016, Synaptics Incorporated
+ * Copyright (C) 2012 Alexandra Chin <[email protected]>
+ * Copyright (C) 2012 Scott Lin <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/firmware.h>
+#include <asm/unaligned.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include "rmi_driver.h"
+#include "rmi_f34.h"
+
+static int rmi_f34v7_read_flash_status(struct f34_data *f34)
+{
+ unsigned char status;
+ unsigned char command;
+ int ret;
+
+ ret = rmi_read_block(f34->fn->rmi_dev,
+ f34->fn->fd.data_base_addr + f34->v7.off.flash_status,
+ &status,
+ sizeof(status));
+ if (ret < 0) {
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
+ "%s: Failed to read flash status\n", __func__);
+ return ret;
+ }
+
+ f34->v7.in_bl_mode = status >> 7;
+
+ f34->v7.flash_status = status & MASK_5BIT;
+
+ if (f34->v7.flash_status != 0x00) {
+ dev_err(&f34->fn->dev, "%s: status=%d, command=0x%02x\n",
+ __func__, f34->v7.flash_status, f34->v7.command);
+ }
+
+ ret = rmi_read_block(f34->fn->rmi_dev,
+ f34->fn->fd.data_base_addr + f34->v7.off.flash_cmd,
+ &command,
+ sizeof(command));
+ if (ret < 0) {
+ dev_err(&f34->fn->dev, "%s: Failed to read flash command\n",
+ __func__);
+ return ret;
+ }
+
+ f34->v7.command = command;
+
+ return 0;
+}
+
+static int rmi_f34v7_wait_for_idle(struct f34_data *f34, int timeout_ms)
+{
+ int count = 0;
+ int timeout_count = ((timeout_ms * 1000) / MAX_SLEEP_TIME_US) + 1;
+
+ do {
+ usleep_range(MIN_SLEEP_TIME_US, MAX_SLEEP_TIME_US);
+
+ count++;
+
+ rmi_f34v7_read_flash_status(f34);
+
+ if ((f34->v7.command == v7_CMD_IDLE) && (f34->v7.flash_status == 0x00)) {
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
+ "Idle status detected\n");
+ return 0;
+ }
+ } while (count < timeout_count);
+
+ dev_err(&f34->fn->dev, "%s: Timed out waiting for idle status\n", __func__);
+
+ return -ETIMEDOUT;
+}
+
+static int rmi_f34v7_write_command_single_transaction(struct f34_data *f34,
+ unsigned char cmd)
+{
+ int ret;
+ unsigned char base;
+ struct f34v7_data_1_5 data_1_5;
+
+ base = f34->fn->fd.data_base_addr;
+
+ memset(data_1_5.data, 0x00, sizeof(data_1_5.data));
+
+ switch (cmd) {
+ case v7_CMD_ERASE_ALL:
+ data_1_5.partition_id = CORE_CODE_PARTITION;
+ data_1_5.command = CMD_V7_ERASE_AP;
+ break;
+ case v7_CMD_ERASE_UI_FIRMWARE:
+ data_1_5.partition_id = CORE_CODE_PARTITION;
+ data_1_5.command = CMD_V7_ERASE;
+ break;
+ case v7_CMD_ERASE_BL_CONFIG:
+ data_1_5.partition_id = GLOBAL_PARAMETERS_PARTITION;
+ data_1_5.command = CMD_V7_ERASE;
+ break;
+ case v7_CMD_ERASE_UI_CONFIG:
+ data_1_5.partition_id = CORE_CONFIG_PARTITION;
+ data_1_5.command = CMD_V7_ERASE;
+ break;
+ case v7_CMD_ERASE_DISP_CONFIG:
+ data_1_5.partition_id = DISPLAY_CONFIG_PARTITION;
+ data_1_5.command = CMD_V7_ERASE;
+ break;
+ case v7_CMD_ERASE_FLASH_CONFIG:
+ data_1_5.partition_id = FLASH_CONFIG_PARTITION;
+ data_1_5.command = CMD_V7_ERASE;
+ break;
+ case v7_CMD_ERASE_GUEST_CODE:
+ data_1_5.partition_id = GUEST_CODE_PARTITION;
+ data_1_5.command = CMD_V7_ERASE;
+ break;
+ case v7_CMD_ENABLE_FLASH_PROG:
+ data_1_5.partition_id = BOOTLOADER_PARTITION;
+ data_1_5.command = CMD_V7_ENTER_BL;
+ break;
+ };
+
+ data_1_5.payload_0 = f34->bootloader_id[0];
+ data_1_5.payload_1 = f34->bootloader_id[1];
+
+ ret = rmi_write_block(f34->fn->rmi_dev,
+ base + f34->v7.off.partition_id,
+ data_1_5.data,
+ sizeof(data_1_5.data));
+ if (ret < 0) {
+ dev_err(&f34->fn->dev, "%s: Failed to write single transaction command\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rmi_f34v7_write_command(struct f34_data *f34, unsigned char cmd)
+{
+ int ret;
+ unsigned char base;
+ unsigned char command;
+
+ base = f34->fn->fd.data_base_addr;
+
+ switch (cmd) {
+ case v7_CMD_WRITE_FW:
+ case v7_CMD_WRITE_CONFIG:
+ case v7_CMD_WRITE_GUEST_CODE:
+ command = CMD_V7_WRITE;
+ break;
+ case v7_CMD_READ_CONFIG:
+ command = CMD_V7_READ;
+ break;
+ case v7_CMD_ERASE_ALL:
+ command = CMD_V7_ERASE_AP;
+ break;
+ case v7_CMD_ERASE_UI_FIRMWARE:
+ case v7_CMD_ERASE_BL_CONFIG:
+ case v7_CMD_ERASE_UI_CONFIG:
+ case v7_CMD_ERASE_DISP_CONFIG:
+ case v7_CMD_ERASE_FLASH_CONFIG:
+ case v7_CMD_ERASE_GUEST_CODE:
+ command = CMD_V7_ERASE;
+ break;
+ case v7_CMD_ENABLE_FLASH_PROG:
+ command = CMD_V7_ENTER_BL;
+ break;
+ default:
+ dev_err(&f34->fn->dev, "%s: Invalid command 0x%02x\n",
+ __func__, cmd);
+ return -EINVAL;
+ };
+
+ f34->v7.command = command;
+
+ switch (cmd) {
+ case v7_CMD_ERASE_ALL:
+ case v7_CMD_ERASE_UI_FIRMWARE:
+ case v7_CMD_ERASE_BL_CONFIG:
+ case v7_CMD_ERASE_UI_CONFIG:
+ case v7_CMD_ERASE_DISP_CONFIG:
+ case v7_CMD_ERASE_FLASH_CONFIG:
+ case v7_CMD_ERASE_GUEST_CODE:
+ case v7_CMD_ENABLE_FLASH_PROG:
+ ret = rmi_f34v7_write_command_single_transaction(f34, cmd);
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+ default:
+ break;
+ };
+
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: writing cmd %02X\n",
+ __func__, command);
+
+ ret = rmi_write_block(f34->fn->rmi_dev,
+ base + f34->v7.off.flash_cmd,
+ &command,
+ sizeof(command));
+ if (ret < 0) {
+ dev_err(&f34->fn->dev, "%s: Failed to write flash command\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rmi_f34v7_write_partition_id(struct f34_data *f34,
+ unsigned char cmd)
+{
+ int ret;
+ unsigned char base;
+ unsigned char partition;
+
+ base = f34->fn->fd.data_base_addr;
+
+ switch (cmd) {
+ case v7_CMD_WRITE_FW:
+ partition = CORE_CODE_PARTITION;
+ break;
+ case v7_CMD_WRITE_CONFIG:
+ case v7_CMD_READ_CONFIG:
+ if (f34->v7.config_area == v7_UI_CONFIG_AREA)
+ partition = CORE_CONFIG_PARTITION;
+ else if (f34->v7.config_area == v7_DP_CONFIG_AREA)
+ partition = DISPLAY_CONFIG_PARTITION;
+ else if (f34->v7.config_area == v7_PM_CONFIG_AREA)
+ partition = GUEST_SERIALIZATION_PARTITION;
+ else if (f34->v7.config_area == v7_BL_CONFIG_AREA)
+ partition = GLOBAL_PARAMETERS_PARTITION;
+ else if (f34->v7.config_area == v7_FLASH_CONFIG_AREA)
+ partition = FLASH_CONFIG_PARTITION;
+ break;
+ case v7_CMD_WRITE_GUEST_CODE:
+ partition = GUEST_CODE_PARTITION;
+ break;
+ case v7_CMD_ERASE_ALL:
+ partition = CORE_CODE_PARTITION;
+ break;
+ case v7_CMD_ERASE_BL_CONFIG:
+ partition = GLOBAL_PARAMETERS_PARTITION;
+ break;
+ case v7_CMD_ERASE_UI_CONFIG:
+ partition = CORE_CONFIG_PARTITION;
+ break;
+ case v7_CMD_ERASE_DISP_CONFIG:
+ partition = DISPLAY_CONFIG_PARTITION;
+ break;
+ case v7_CMD_ERASE_FLASH_CONFIG:
+ partition = FLASH_CONFIG_PARTITION;
+ break;
+ case v7_CMD_ERASE_GUEST_CODE:
+ partition = GUEST_CODE_PARTITION;
+ break;
+ case v7_CMD_ENABLE_FLASH_PROG:
+ partition = BOOTLOADER_PARTITION;
+ break;
+ default:
+ dev_err(&f34->fn->dev, "%s: Invalid command 0x%02x\n",
+ __func__, cmd);
+ return -EINVAL;
+ };
+
+ ret = rmi_write_block(f34->fn->rmi_dev,
+ base + f34->v7.off.partition_id,
+ &partition,
+ sizeof(partition));
+ if (ret < 0) {
+ dev_err(&f34->fn->dev, "%s: Failed to write partition ID\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rmi_f34v7_read_f34v7_partition_table(struct f34_data *f34)
+{
+ int ret;
+ unsigned char base;
+ unsigned char length[2];
+ unsigned short block_number = 0;
+
+ base = f34->fn->fd.data_base_addr;
+
+ f34->v7.config_area = v7_FLASH_CONFIG_AREA;
+
+ ret = rmi_f34v7_write_partition_id(f34, v7_CMD_READ_CONFIG);
+ if (ret < 0)
+ return ret;
+
+ ret = rmi_write_block(f34->fn->rmi_dev,
+ base + f34->v7.off.block_number,
+ (unsigned char *)&block_number,
+ sizeof(block_number));
+ if (ret < 0) {
+ dev_err(&f34->fn->dev, "%s: Failed to write block number\n",
+ __func__);
+ return ret;
+ }
+
+ length[0] = (unsigned char)(f34->v7.flash_config_length & MASK_8BIT);
+ length[1] = (unsigned char)(f34->v7.flash_config_length >> 8);
+
+ ret = rmi_write_block(f34->fn->rmi_dev,
+ base + f34->v7.off.transfer_length,
+ length,
+ sizeof(length));
+ if (ret < 0) {
+ dev_err(&f34->fn->dev, "%s: Failed to write transfer length\n",
+ __func__);
+ return ret;
+ }
+
+ ret = rmi_f34v7_write_command(f34, v7_CMD_READ_CONFIG);
+ if (ret < 0) {
+ dev_err(&f34->fn->dev, "%s: Failed to write command\n",
+ __func__);
+ return ret;
+ }
+
+ ret = rmi_f34v7_wait_for_idle(f34, WRITE_WAIT_MS);
+ if (ret < 0) {
+ dev_err(&f34->fn->dev, "%s: Failed to wait for idle status\n",
+ __func__);
+ return ret;
+ }
+
+ ret = rmi_read_block(f34->fn->rmi_dev,
+ base + f34->v7.off.payload,
+ f34->v7.read_config_buf,
+ f34->v7.partition_table_bytes);
+ if (ret < 0) {
+ dev_err(&f34->fn->dev, "%s: Failed to read block data\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void rmi_f34v7_parse_partition_table(struct f34_data *f34,
+ const unsigned char *partition_table,
+ struct block_count *blkcount, struct physical_address *phyaddr)
+{
+ unsigned char ii;
+ unsigned char index;
+ unsigned short partition_length;
+ unsigned short physical_address;
+ struct partition_table *ptable;
+
+ for (ii = 0; ii < f34->v7.partitions; ii++) {
+ index = ii * 8 + 2;
+ ptable = (struct partition_table *)&partition_table[index];
+ partition_length = ptable->partition_length_15_8 << 8 |
+ ptable->partition_length_7_0;
+ physical_address = ptable->start_physical_address_15_8 << 8 |
+ ptable->start_physical_address_7_0;
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
+ "%s: Partition entry %d: %*ph\n",
+ __func__, ii, sizeof(struct partition_table), ptable);
+ switch (ptable->partition_id) {
+ case CORE_CODE_PARTITION:
+ blkcount->ui_firmware = partition_length;
+ phyaddr->ui_firmware = physical_address;
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
+ "%s: Core code block count: %d\n",
+ __func__, blkcount->ui_firmware);
+ break;
+ case CORE_CONFIG_PARTITION:
+ blkcount->ui_config = partition_length;
+ phyaddr->ui_config = physical_address;
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
+ "%s: Core config block count: %d\n",
+ __func__, blkcount->ui_config);
+ break;
+ case DISPLAY_CONFIG_PARTITION:
+ blkcount->dp_config = partition_length;
+ phyaddr->dp_config = physical_address;
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
+ "%s: Display config block count: %d\n",
+ __func__, blkcount->dp_config);
+ break;
+ case FLASH_CONFIG_PARTITION:
+ blkcount->fl_config = partition_length;
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
+ "%s: Flash config block count: %d\n",
+ __func__, blkcount->fl_config);
+ break;
+ case GUEST_CODE_PARTITION:
+ blkcount->guest_code = partition_length;
+ phyaddr->guest_code = physical_address;
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
+ "%s: Guest code block count: %d\n",
+ __func__, blkcount->guest_code);
+ break;
+ case GUEST_SERIALIZATION_PARTITION:
+ blkcount->pm_config = partition_length;
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
+ "%s: Guest serialization block count: %d\n",
+ __func__, blkcount->pm_config);
+ break;
+ case GLOBAL_PARAMETERS_PARTITION:
+ blkcount->bl_config = partition_length;
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
+ "%s: Global parameters block count: %d\n",
+ __func__, blkcount->bl_config);
+ break;
+ case DEVICE_CONFIG_PARTITION:
+ blkcount->lockdown = partition_length;
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
+ "%s: Device config block count: %d\n",
+ __func__, blkcount->lockdown);
+ break;
+ };
+ }
+}
+
+static int rmi_f34v7_read_queries_bl_version(struct f34_data *f34)
+{
+ int ret;
+ unsigned char base;
+ unsigned char offset;
+ struct f34v7_query_0 query_0;
+ struct f34v7_query_1_7 query_1_7;
+
+ base = f34->fn->fd.query_base_addr;
+
+ ret = rmi_read_block(f34->fn->rmi_dev,
+ base,
+ query_0.data,
+ sizeof(query_0.data));
+ if (ret < 0) {
+ dev_err(&f34->fn->dev, "%s: Failed to read query 0\n", __func__);
+ return ret;
+ }
+
+ offset = query_0.subpacket_1_size + 1;
+
+ ret = rmi_read_block(f34->fn->rmi_dev,
+ base + offset,
+ query_1_7.data,
+ sizeof(query_1_7.data));
+ if (ret < 0) {
+ dev_err(&f34->fn->dev, "%s: Failed to read queries 1 to 7\n",
+ __func__);
+ return ret;
+ }
+
+ f34->bootloader_id[0] = query_1_7.bl_minor_revision;
+ f34->bootloader_id[1] = query_1_7.bl_major_revision;
+
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "Bootloader V%d.%d\n",
+ f34->bootloader_id[1], f34->bootloader_id[0]);
+
+ return 0;
+}
+
+static int rmi_f34v7_read_queries(struct f34_data *f34)
+{
+ int ret;
+ unsigned char ii;
+ unsigned char base;
+ unsigned char index;
+ unsigned char offset;
+ unsigned char *ptable;
+ struct f34v7_query_0 query_0;
+ struct f34v7_query_1_7 query_1_7;
+
+ base = f34->fn->fd.query_base_addr;
+
+ ret = rmi_read_block(f34->fn->rmi_dev,
+ base,
+ query_0.data,
+ sizeof(query_0.data));
+ if (ret < 0) {
+ dev_err(&f34->fn->dev, "%s: Failed to read query 0\n", __func__);
+ return ret;
+ }
+
+ offset = query_0.subpacket_1_size + 1;
+
+ ret = rmi_read_block(f34->fn->rmi_dev,
+ base + offset,
+ query_1_7.data,
+ sizeof(query_1_7.data));
+ if (ret < 0) {
+ dev_err(&f34->fn->dev, "%s: Failed to read queries 1 to 7\n",
+ __func__);
+ return ret;
+ }
+
+ f34->bootloader_id[0] = query_1_7.bl_minor_revision;
+ f34->bootloader_id[1] = query_1_7.bl_major_revision;
+
+ f34->v7.block_size = query_1_7.block_size_15_8 << 8 |
+ query_1_7.block_size_7_0;
+
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: f34->v7.block_size = %d\n",
+ __func__, f34->v7.block_size);
+
+ f34->v7.flash_config_length = query_1_7.flash_config_length_15_8 << 8 |
+ query_1_7.flash_config_length_7_0;
+
+ f34->v7.payload_length = query_1_7.payload_length_15_8 << 8 |
+ query_1_7.payload_length_7_0;
+
+ f34->v7.off.flash_status = V7_FLASH_STATUS_OFFSET;
+ f34->v7.off.partition_id = V7_PARTITION_ID_OFFSET;
+ f34->v7.off.block_number = V7_BLOCK_NUMBER_OFFSET;
+ f34->v7.off.transfer_length = V7_TRANSFER_LENGTH_OFFSET;
+ f34->v7.off.flash_cmd = V7_COMMAND_OFFSET;
+ f34->v7.off.payload = V7_PAYLOAD_OFFSET;
+
+ f34->v7.flash_properties.has_disp_config = query_1_7.has_display_config;
+ f34->v7.flash_properties.has_perm_config = query_1_7.has_guest_serialization;
+ f34->v7.flash_properties.has_bl_config = query_1_7.has_global_parameters;
+
+ f34->v7.has_guest_code = query_1_7.has_guest_code;
+ f34->v7.flash_properties.has_config_id = query_0.has_config_id;
+
+ if (f34->v7.flash_properties.has_config_id) {
+ char f34_ctrl[4];
+
+ ret = rmi_read_block(f34->fn->rmi_dev,
+ f34->fn->fd.control_base_addr,
+ f34_ctrl,
+ sizeof(f34_ctrl));
+ if (ret)
+ return ret;
+
+ snprintf(f34->configuration_id, sizeof(f34->configuration_id),
+ "%02x%02x%02x%02x",
+ f34_ctrl[0], f34_ctrl[1], f34_ctrl[2], f34_ctrl[3]);
+
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "Configuration ID: %s\n",
+ f34->configuration_id);
+ }
+
+ index = sizeof(query_1_7.data) - V7_PARTITION_SUPPORT_BYTES;
+
+ f34->v7.partitions = 0;
+ for (offset = 0; offset < V7_PARTITION_SUPPORT_BYTES; offset++) {
+ for (ii = 0; ii < 8; ii++) {
+ if (query_1_7.data[index + offset] & (1 << ii))
+ f34->v7.partitions++;
+ }
+
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
+ "%s: Supported partitions: 0x%02x\n",
+ __func__, query_1_7.data[index + offset]);
+ }
+
+ f34->v7.partition_table_bytes = f34->v7.partitions * 8 + 2;
+
+ f34->v7.read_config_buf = devm_kzalloc(&f34->fn->dev,
+ f34->v7.partition_table_bytes,
+ GFP_KERNEL);
+ if (!f34->v7.read_config_buf) {
+ f34->v7.read_config_buf_size = 0;
+ return -ENOMEM;
+ }
+
+ f34->v7.read_config_buf_size = f34->v7.partition_table_bytes;
+ ptable = f34->v7.read_config_buf;
+
+ ret = rmi_f34v7_read_f34v7_partition_table(f34);
+ if (ret < 0) {
+ dev_err(&f34->fn->dev, "%s: Failed to read partition table\n",
+ __func__);
+ return ret;
+ }
+
+ rmi_f34v7_parse_partition_table(f34, ptable, &f34->v7.blkcount, &f34->v7.phyaddr);
+
+ return 0;
+}
+
+static int rmi_f34v7_check_ui_firmware_size(struct f34_data *f34)
+{
+ unsigned short block_count;
+
+ block_count = f34->v7.img.ui_firmware.size / f34->v7.block_size;
+
+ if (block_count != f34->v7.blkcount.ui_firmware) {
+ dev_err(&f34->fn->dev, "%s: UI firmware size mismatch:"
+ "block_count=%d,f34->v7.blkcount.ui_firmware=%d\n",
+ __func__, block_count, f34->v7.blkcount.ui_firmware);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rmi_f34v7_check_ui_configuration_size(struct f34_data *f34)
+{
+ unsigned short block_count;
+
+ block_count = f34->v7.img.ui_config.size / f34->v7.block_size;
+
+ if (block_count != f34->v7.blkcount.ui_config) {
+ dev_err(&f34->fn->dev, "%s: UI configuration size mismatch\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rmi_f34v7_check_dp_configuration_size(struct f34_data *f34)
+{
+ unsigned short block_count;
+
+ block_count = f34->v7.img.dp_config.size / f34->v7.block_size;
+
+ if (block_count != f34->v7.blkcount.dp_config) {
+ dev_err(&f34->fn->dev, "%s: Display configuration size mismatch\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rmi_f34v7_check_guest_code_size(struct f34_data *f34)
+{
+ unsigned short block_count;
+
+ block_count = f34->v7.img.guest_code.size / f34->v7.block_size;
+ if (block_count != f34->v7.blkcount.guest_code) {
+ dev_err(&f34->fn->dev, "%s: Guest code size mismatch\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rmi_f34v7_check_bl_configuration_size(struct f34_data *f34)
+{
+ unsigned short block_count;
+
+ block_count = f34->v7.img.bl_config.size / f34->v7.block_size;
+
+ if (block_count != f34->v7.blkcount.bl_config) {
+ dev_err(&f34->fn->dev, "%s: Bootloader config size mismatch\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rmi_f34v7_erase_configuration(struct f34_data *f34)
+{
+ int ret;
+
+ dev_info(&f34->fn->dev, "Erasing config...\n");
+
+ switch (f34->v7.config_area) {
+ case v7_UI_CONFIG_AREA:
+ ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_UI_CONFIG);
+ if (ret < 0)
+ return ret;
+ break;
+ case v7_DP_CONFIG_AREA:
+ ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_DISP_CONFIG);
+ if (ret < 0)
+ return ret;
+ break;
+ case v7_BL_CONFIG_AREA:
+ ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_BL_CONFIG);
+ if (ret < 0)
+ return ret;
+ break;
+ }
+
+ ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+static int rmi_f34v7_erase_guest_code(struct f34_data *f34)
+{
+ int ret;
+
+ dev_info(&f34->fn->dev, "Erasing guest code...\n");
+
+ ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_GUEST_CODE);
+ if (ret < 0)
+ return ret;
+
+ ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int rmi_f34v7_erase_all(struct f34_data *f34)
+{
+ int ret;
+
+ dev_info(&f34->fn->dev, "Erasing firmware...\n");
+
+ ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_UI_FIRMWARE);
+ if (ret < 0)
+ return ret;
+
+ ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS);
+ if (ret < 0)
+ return ret;
+
+ f34->v7.config_area = v7_UI_CONFIG_AREA;
+ ret = rmi_f34v7_erase_configuration(f34);
+ if (ret < 0)
+ return ret;
+
+ if (f34->v7.flash_properties.has_disp_config) {
+ f34->v7.config_area = v7_DP_CONFIG_AREA;
+ ret = rmi_f34v7_erase_configuration(f34);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (f34->v7.new_partition_table && f34->v7.has_guest_code) {
+ ret = rmi_f34v7_erase_guest_code(f34);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rmi_f34v7_read_f34v7_blocks(struct f34_data *f34,
+ unsigned short block_cnt,
+ unsigned char command)
+{
+ int ret;
+ unsigned char base;
+ unsigned char length[2];
+ unsigned short transfer;
+ unsigned short max_transfer;
+ unsigned short remaining = block_cnt;
+ unsigned short block_number = 0;
+ unsigned short index = 0;
+
+ base = f34->fn->fd.data_base_addr;
+
+ ret = rmi_f34v7_write_partition_id(f34, command);
+ if (ret < 0)
+ return ret;
+
+ ret = rmi_write_block(f34->fn->rmi_dev,
+ base + f34->v7.off.block_number,
+ (unsigned char *)&block_number,
+ sizeof(block_number));
+ if (ret < 0) {
+ dev_err(&f34->fn->dev, "%s: Failed to write block number\n",
+ __func__);
+ return ret;
+ }
+
+ if (f34->v7.payload_length > (PAGE_SIZE / f34->v7.block_size))
+ max_transfer = PAGE_SIZE / f34->v7.block_size;
+ else
+ max_transfer = f34->v7.payload_length;
+
+ do {
+ if (remaining / max_transfer)
+ transfer = max_transfer;
+ else
+ transfer = remaining;
+
+ length[0] = (unsigned char)(transfer & MASK_8BIT);
+ length[1] = (unsigned char)(transfer >> 8);
+
+ ret = rmi_write_block(f34->fn->rmi_dev,
+ base + f34->v7.off.transfer_length,
+ length,
+ sizeof(length));
+ if (ret < 0) {
+ dev_err(&f34->fn->dev,
+ "%s: Failed to write transfer length "
+ "(%d blocks remaining)\n",
+ __func__, remaining);
+ return ret;
+ }
+
+ ret = rmi_f34v7_write_command(f34, command);
+ if (ret < 0) {
+ dev_err(&f34->fn->dev, "%s: Failed to write command "
+ "(%d blocks remaining)\n",
+ __func__, remaining);
+ return ret;
+ }
+
+ ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS);
+ if (ret < 0) {
+ dev_err(&f34->fn->dev, "%s: Failed to wait for idle status "
+ "(%d blocks remaining)\n",
+ __func__, remaining);
+ return ret;
+ }
+
+ ret = rmi_read_block(f34->fn->rmi_dev,
+ base + f34->v7.off.payload,
+ &f34->v7.read_config_buf[index],
+ transfer * f34->v7.block_size);
+ if (ret < 0) {
+ dev_err(&f34->fn->dev, "%s: Failed to read block data "
+ "(%d blocks remaining)\n",
+ __func__, remaining);
+ return ret;
+ }
+
+ index += (transfer * f34->v7.block_size);
+ remaining -= transfer;
+ } while (remaining);
+
+ return 0;
+}
+
+static int rmi_f34v7_write_f34v7_blocks(struct f34_data *f34,
+ unsigned char *block_ptr,
+ unsigned short block_cnt, unsigned char command)
+{
+ int ret;
+ unsigned char base;
+ unsigned char length[2];
+ unsigned short transfer;
+ unsigned short max_transfer;
+ unsigned short remaining = block_cnt;
+ unsigned short block_number = 0;
+
+ base = f34->fn->fd.data_base_addr;
+
+ ret = rmi_f34v7_write_partition_id(f34, command);
+ if (ret < 0)
+ return ret;
+
+ ret = rmi_write_block(f34->fn->rmi_dev,
+ base + f34->v7.off.block_number,
+ (unsigned char *)&block_number,
+ sizeof(block_number));
+ if (ret < 0) {
+ dev_err(&f34->fn->dev, "%s: Failed to write block number\n",
+ __func__);
+ return ret;
+ }
+
+ if (f34->v7.payload_length > (PAGE_SIZE / f34->v7.block_size))
+ max_transfer = PAGE_SIZE / f34->v7.block_size;
+ else
+ max_transfer = f34->v7.payload_length;
+
+ do {
+ if (remaining / max_transfer)
+ transfer = max_transfer;
+ else
+ transfer = remaining;
+
+ length[0] = (unsigned char)(transfer & MASK_8BIT);
+ length[1] = (unsigned char)(transfer >> 8);
+
+ ret = rmi_write_block(f34->fn->rmi_dev,
+ base + f34->v7.off.transfer_length,
+ length,
+ sizeof(length));
+ if (ret < 0) {
+ dev_err(&f34->fn->dev,
+ "%s: Failed to write transfer length (%d blocks remaining)\n",
+ __func__, remaining);
+ return ret;
+ }
+
+ ret = rmi_f34v7_write_command(f34, command);
+ if (ret < 0) {
+ dev_err(&f34->fn->dev,
+ "%s: Failed to write command (%d blocks remaining)\n",
+ __func__, remaining);
+ return ret;
+ }
+
+ ret = rmi_write_block(f34->fn->rmi_dev,
+ base + f34->v7.off.payload,
+ block_ptr,
+ transfer * f34->v7.block_size);
+ if (ret < 0) {
+ dev_err(&f34->fn->dev,
+ "%s: Failed to write block data (%d blocks remaining)\n",
+ __func__, remaining);
+ return ret;
+ }
+
+ ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS);
+ if (ret < 0) {
+ dev_err(&f34->fn->dev,
+ "%s: Failed to wait for idle status (%d blocks remaining)\n",
+ __func__, remaining);
+ return ret;
+ }
+
+ block_ptr += (transfer * f34->v7.block_size);
+ remaining -= transfer;
+ } while (remaining);
+
+ return 0;
+}
+
+static int rmi_f34v7_write_f34_blocks(struct f34_data *f34,
+ unsigned char *block_ptr,
+ unsigned short block_cnt, unsigned char cmd)
+{
+ int ret;
+
+ ret = rmi_f34v7_write_f34v7_blocks(f34, block_ptr, block_cnt, cmd);
+
+ return ret;
+}
+
+static int rmi_f34v7_write_configuration(struct f34_data *f34)
+{
+ return rmi_f34v7_write_f34_blocks(f34, (unsigned char *)f34->v7.config_data,
+ f34->v7.config_block_count, v7_CMD_WRITE_CONFIG);
+}
+
+static int rmi_f34v7_write_ui_configuration(struct f34_data *f34)
+{
+ f34->v7.config_area = v7_UI_CONFIG_AREA;
+ f34->v7.config_data = f34->v7.img.ui_config.data;
+ f34->v7.config_size = f34->v7.img.ui_config.size;
+ f34->v7.config_block_count = f34->v7.config_size / f34->v7.block_size;
+
+ return rmi_f34v7_write_configuration(f34);
+}
+
+static int rmi_f34v7_write_dp_configuration(struct f34_data *f34)
+{
+ f34->v7.config_area = v7_DP_CONFIG_AREA;
+ f34->v7.config_data = f34->v7.img.dp_config.data;
+ f34->v7.config_size = f34->v7.img.dp_config.size;
+ f34->v7.config_block_count = f34->v7.config_size / f34->v7.block_size;
+
+ return rmi_f34v7_write_configuration(f34);
+}
+
+static int rmi_f34v7_write_guest_code(struct f34_data *f34)
+{
+ unsigned short guest_code_block_count;
+ int ret;
+
+ guest_code_block_count = f34->v7.img.guest_code.size / f34->v7.block_size;
+
+ ret = rmi_f34v7_write_f34_blocks(f34, (unsigned char *)f34->v7.img.guest_code.data,
+ guest_code_block_count, v7_CMD_WRITE_GUEST_CODE);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int rmi_f34v7_write_flash_configuration(struct f34_data *f34)
+{
+ int ret;
+
+ f34->v7.config_area = v7_FLASH_CONFIG_AREA;
+ f34->v7.config_data = f34->v7.img.fl_config.data;
+ f34->v7.config_size = f34->v7.img.fl_config.size;
+ f34->v7.config_block_count = f34->v7.config_size / f34->v7.block_size;
+
+ if (f34->v7.config_block_count != f34->v7.blkcount.fl_config) {
+ dev_err(&f34->fn->dev, "%s: Flash configuration size mismatch\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_FLASH_CONFIG);
+ if (ret < 0)
+ return ret;
+
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
+ "%s: Erase flash configuration command written\n", __func__);
+
+ ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS);
+ if (ret < 0)
+ return ret;
+
+ ret = rmi_f34v7_write_configuration(f34);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int rmi_f34v7_write_partition_table(struct f34_data *f34)
+{
+ unsigned short block_count;
+ int ret;
+
+ block_count = f34->v7.blkcount.bl_config;
+ f34->v7.config_area = v7_BL_CONFIG_AREA;
+ f34->v7.config_size = f34->v7.block_size * block_count;
+ devm_kfree(&f34->fn->dev, f34->v7.read_config_buf);
+ f34->v7.read_config_buf = devm_kzalloc(&f34->fn->dev, f34->v7.config_size,
+ GFP_KERNEL);
+ if (!f34->v7.read_config_buf) {
+ f34->v7.read_config_buf_size = 0;
+ return -ENOMEM;
+ }
+
+ f34->v7.read_config_buf_size = f34->v7.config_size;
+
+ ret = rmi_f34v7_read_f34v7_blocks(f34, block_count, v7_CMD_READ_CONFIG);
+ if (ret < 0)
+ return ret;
+
+ ret = rmi_f34v7_erase_configuration(f34);
+ if (ret < 0)
+ return ret;
+
+ ret = rmi_f34v7_write_flash_configuration(f34);
+ if (ret < 0)
+ return ret;
+
+ f34->v7.config_area = v7_BL_CONFIG_AREA;
+ f34->v7.config_data = f34->v7.read_config_buf;
+ f34->v7.config_size = f34->v7.img.bl_config.size;
+ f34->v7.config_block_count = f34->v7.config_size / f34->v7.block_size;
+
+ ret = rmi_f34v7_write_configuration(f34);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int rmi_f34v7_write_firmware(struct f34_data *f34)
+{
+ unsigned short firmware_block_count;
+
+ firmware_block_count = f34->v7.img.ui_firmware.size / f34->v7.block_size;
+
+ return rmi_f34v7_write_f34_blocks(f34, (unsigned char *)f34->v7.img.ui_firmware.data,
+ firmware_block_count, v7_CMD_WRITE_FW);
+}
+
+static void rmi_f34v7_compare_partition_tables(struct f34_data *f34)
+{
+ if (f34->v7.phyaddr.ui_firmware != f34->v7.img.phyaddr.ui_firmware) {
+ f34->v7.new_partition_table = true;
+ return;
+ }
+
+ if (f34->v7.phyaddr.ui_config != f34->v7.img.phyaddr.ui_config) {
+ f34->v7.new_partition_table = true;
+ return;
+ }
+
+ if (f34->v7.flash_properties.has_disp_config) {
+ if (f34->v7.phyaddr.dp_config != f34->v7.img.phyaddr.dp_config) {
+ f34->v7.new_partition_table = true;
+ return;
+ }
+ }
+
+ if (f34->v7.flash_properties.has_disp_config) {
+ if (f34->v7.phyaddr.dp_config != f34->v7.img.phyaddr.dp_config) {
+ f34->v7.new_partition_table = true;
+ return;
+ }
+ }
+
+ if (f34->v7.has_guest_code) {
+ if (f34->v7.phyaddr.guest_code != f34->v7.img.phyaddr.guest_code) {
+ f34->v7.new_partition_table = true;
+ return;
+ }
+ }
+
+ f34->v7.new_partition_table = false;
+}
+
+static unsigned int le_to_uint(const unsigned char *ptr)
+{
+ return (unsigned int)ptr[0] +
+ (unsigned int)ptr[1] * 0x100 +
+ (unsigned int)ptr[2] * 0x10000 +
+ (unsigned int)ptr[3] * 0x1000000;
+}
+
+static void rmi_f34v7_parse_image_header_10_bl_container(struct f34_data *f34,
+ const unsigned char *image)
+{
+ unsigned char ii;
+ unsigned char num_of_containers;
+ unsigned int addr;
+ unsigned int container_id;
+ unsigned int length;
+ const unsigned char *content;
+ struct container_descriptor *descriptor;
+
+ num_of_containers = (f34->v7.img.bootloader.size - 4) / 4;
+
+ for (ii = 1; ii <= num_of_containers; ii++) {
+ addr = le_to_uint(f34->v7.img.bootloader.data + (ii * 4));
+ descriptor = (struct container_descriptor *)(image + addr);
+ container_id = descriptor->container_id[0] |
+ descriptor->container_id[1] << 8;
+ content = image + le_to_uint(descriptor->content_address);
+ length = le_to_uint(descriptor->content_length);
+ switch (container_id) {
+ case BL_CONFIG_CONTAINER:
+ case GLOBAL_PARAMETERS_CONTAINER:
+ f34->v7.img.bl_config.data = content;
+ f34->v7.img.bl_config.size = length;
+ break;
+ case BL_LOCKDOWN_INFO_CONTAINER:
+ case DEVICE_CONFIG_CONTAINER:
+ f34->v7.img.lockdown.data = content;
+ f34->v7.img.lockdown.size = length;
+ break;
+ default:
+ break;
+ };
+ }
+}
+
+static void rmi_f34v7_parse_image_header_10(struct f34_data *f34)
+{
+ unsigned char ii;
+ unsigned char num_of_containers;
+ unsigned int addr;
+ unsigned int offset;
+ unsigned int container_id;
+ unsigned int length;
+ const unsigned char *image;
+ const unsigned char *content;
+ struct container_descriptor *descriptor;
+ struct image_header_10 *header;
+
+ image = f34->v7.image;
+ header = (struct image_header_10 *)image;
+
+ f34->v7.img.checksum = le_to_uint(header->checksum);
+
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: f34->v7.img.checksum=%d\n",
+ __func__, f34->v7.img.checksum);
+
+ /* address of top level container */
+ offset = le_to_uint(header->top_level_container_start_addr);
+ descriptor = (struct container_descriptor *)(image + offset);
+
+ /* address of top level container content */
+ offset = le_to_uint(descriptor->content_address);
+ num_of_containers = le_to_uint(descriptor->content_length) / 4;
+
+ for (ii = 0; ii < num_of_containers; ii++) {
+ addr = le_to_uint(image + offset);
+ offset += 4;
+ descriptor = (struct container_descriptor *)(image + addr);
+ container_id = descriptor->container_id[0] |
+ descriptor->container_id[1] << 8;
+ content = image + le_to_uint(descriptor->content_address);
+ length = le_to_uint(descriptor->content_length);
+
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
+ "%s: container_id=%d, length=%d\n", __func__,
+ container_id, length);
+
+ switch (container_id) {
+ case UI_CONTAINER:
+ case CORE_CODE_CONTAINER:
+ f34->v7.img.ui_firmware.data = content;
+ f34->v7.img.ui_firmware.size = length;
+ break;
+ case UI_CONFIG_CONTAINER:
+ case CORE_CONFIG_CONTAINER:
+ f34->v7.img.ui_config.data = content;
+ f34->v7.img.ui_config.size = length;
+ break;
+ case BL_CONTAINER:
+ f34->v7.img.bl_version = *content;
+ f34->v7.img.bootloader.data = content;
+ f34->v7.img.bootloader.size = length;
+ rmi_f34v7_parse_image_header_10_bl_container(f34, image);
+ break;
+ case GUEST_CODE_CONTAINER:
+ f34->v7.img.contains_guest_code = true;
+ f34->v7.img.guest_code.data = content;
+ f34->v7.img.guest_code.size = length;
+ break;
+ case DISPLAY_CONFIG_CONTAINER:
+ f34->v7.img.contains_disp_config = true;
+ f34->v7.img.dp_config.data = content;
+ f34->v7.img.dp_config.size = length;
+ break;
+ case FLASH_CONFIG_CONTAINER:
+ f34->v7.img.contains_flash_config = true;
+ f34->v7.img.fl_config.data = content;
+ f34->v7.img.fl_config.size = length;
+ break;
+ case GENERAL_INFORMATION_CONTAINER:
+ f34->v7.img.contains_firmware_id = true;
+ f34->v7.img.firmware_id = le_to_uint(content + 4);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static int rmi_f34v7_parse_image_info(struct f34_data *f34)
+{
+ struct image_header_10 *header;
+
+ header = (struct image_header_10 *)f34->v7.image;
+
+ memset(&f34->v7.img, 0x00, sizeof(f34->v7.img));
+
+ rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
+ "%s: header->major_header_version = %d\n",
+ __func__, header->major_header_version);
+
+ switch (header->major_header_version) {
+ case IMAGE_HEADER_VERSION_10:
+ rmi_f34v7_parse_image_header_10(f34);
+ break;
+ default:
+ dev_err(&f34->fn->dev, "Unsupported image file format %02X\n",
+ header->major_header_version);
+ return -EINVAL;
+ }
+
+ if (!f34->v7.img.contains_flash_config) {
+ dev_err(&f34->fn->dev, "%s: No flash config in fw image\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ rmi_f34v7_parse_partition_table(f34, f34->v7.img.fl_config.data,
+ &f34->v7.img.blkcount, &f34->v7.img.phyaddr);
+
+ rmi_f34v7_compare_partition_tables(f34);
+
+ return 0;
+}
+
+int rmi_f34v7_do_reflash(struct f34_data *f34, const struct firmware *fw)
+{
+ int ret;
+
+ rmi_f34v7_read_queries_bl_version(f34);
+
+ f34->v7.image = fw->data;
+
+ ret = rmi_f34v7_parse_image_info(f34);
+ if (ret < 0)
+ goto fail;
+
+ if (!f34->v7.new_partition_table) {
+ ret = rmi_f34v7_check_ui_firmware_size(f34);
+ if (ret < 0)
+ goto fail;
+
+ ret = rmi_f34v7_check_ui_configuration_size(f34);
+ if (ret < 0)
+ goto fail;
+
+ if (f34->v7.flash_properties.has_disp_config &&
+ f34->v7.img.contains_disp_config) {
+ ret = rmi_f34v7_check_dp_configuration_size(f34);
+ if (ret < 0)
+ goto fail;
+ }
+
+ if (f34->v7.has_guest_code && f34->v7.img.contains_guest_code) {
+ ret = rmi_f34v7_check_guest_code_size(f34);
+ if (ret < 0)
+ goto fail;
+ }
+ } else {
+ ret = rmi_f34v7_check_bl_configuration_size(f34);
+ if (ret < 0)
+ goto fail;
+ }
+
+ ret = rmi_f34v7_erase_all(f34);
+ if (ret < 0)
+ goto fail;
+
+ if (f34->v7.new_partition_table) {
+ ret = rmi_f34v7_write_partition_table(f34);
+ if (ret < 0)
+ goto fail;
+ dev_info(&f34->fn->dev, "%s: Partition table programmed\n",
+ __func__);
+ }
+
+ dev_info(&f34->fn->dev, "Writing firmware (%d bytes)...\n",
+ f34->v7.img.ui_firmware.size);
+
+ ret = rmi_f34v7_write_firmware(f34);
+ if (ret < 0)
+ goto fail;
+
+ dev_info(&f34->fn->dev, "Writing config (%d bytes)...\n",
+ f34->v7.img.ui_config.size);
+
+ f34->v7.config_area = v7_UI_CONFIG_AREA;
+ ret = rmi_f34v7_write_ui_configuration(f34);
+ if (ret < 0)
+ goto fail;
+
+ if (f34->v7.flash_properties.has_disp_config &&
+ f34->v7.img.contains_disp_config) {
+ dev_info(&f34->fn->dev, "Writing display config...\n");
+
+ ret = rmi_f34v7_write_dp_configuration(f34);
+ if (ret < 0)
+ goto fail;
+ }
+
+ if (f34->v7.new_partition_table) {
+ if (f34->v7.has_guest_code && f34->v7.img.contains_guest_code) {
+ dev_info(&f34->fn->dev, "Writing guest code...\n");
+
+ ret = rmi_f34v7_write_guest_code(f34);
+ if (ret < 0)
+ goto fail;
+ }
+ }
+
+fail:
+ return ret;
+}
+
+static int rmi_f34v7_enter_flash_prog(struct f34_data *f34)
+{
+ int ret;
+
+ ret = rmi_f34v7_read_flash_status(f34);
+ if (ret < 0)
+ return ret;
+
+ if (f34->v7.in_bl_mode)
+ return 0;
+
+ ret = rmi_f34v7_write_command(f34, v7_CMD_ENABLE_FLASH_PROG);
+ if (ret < 0)
+ return ret;
+
+ ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS);
+ if (ret < 0)
+ return ret;
+
+ if (!f34->v7.in_bl_mode) {
+ dev_err(&f34->fn->dev, "%s: BL mode not entered\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int rmi_f34v7_start_reflash(struct f34_data *f34, const struct firmware *fw)
+{
+ int ret = 0;
+
+ f34->v7.config_area = v7_UI_CONFIG_AREA;
+ f34->v7.image = fw->data;
+
+ ret = rmi_f34v7_parse_image_info(f34);
+ if (ret < 0)
+ goto exit;
+
+ if (!f34->v7.force_update && f34->v7.new_partition_table) {
+ dev_err(&f34->fn->dev, "%s: Partition table mismatch\n",
+ __func__);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ dev_info(&f34->fn->dev, "Firmware image OK\n");
+
+ ret = rmi_f34v7_read_flash_status(f34);
+ if (ret < 0)
+ goto exit;
+
+ if (f34->v7.in_bl_mode) {
+ dev_info(&f34->fn->dev, "%s: Device in bootloader mode\n",
+ __func__);
+ }
+
+ rmi_f34v7_enter_flash_prog(f34);
+
+ return 0;
+
+exit:
+ return ret;
+}
+
+int rmi_f34v7_probe(struct f34_data *f34)
+{
+ int ret;
+
+ /* Read bootloader version */
+ ret = rmi_read_block(f34->fn->rmi_dev,
+ f34->fn->fd.query_base_addr + BOOTLOADER_ID_OFFSET,
+ f34->bootloader_id,
+ sizeof(f34->bootloader_id));
+ if (ret < 0) {
+ dev_err(&f34->fn->dev, "%s: Failed to read bootloader ID\n",
+ __func__);
+ return ret;
+ }
+
+ if (f34->bootloader_id[1] == '5') {
+ f34->bl_version = BL_V5;
+ } else if (f34->bootloader_id[1] == '6') {
+ f34->bl_version = BL_V6;
+ } else if (f34->bootloader_id[1] == 7) {
+ f34->bl_version = BL_V7;
+ } else {
+ dev_err(&f34->fn->dev, "%s: Unrecognized bootloader version\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ memset(&f34->v7.blkcount, 0x00, sizeof(f34->v7.blkcount));
+ memset(&f34->v7.phyaddr, 0x00, sizeof(f34->v7.phyaddr));
+ rmi_f34v7_read_queries(f34);
+
+ f34->v7.force_update = FORCE_UPDATE;
+ f34->v7.initialized = true;
+ return 0;
+}
diff --git a/include/linux/rmi.h b/include/linux/rmi.h
index a283a67..6baee9f 100644
--- a/include/linux/rmi.h
+++ b/include/linux/rmi.h
@@ -331,7 +331,7 @@ struct rmi_driver_data {
struct rmi_function *f01_container;
struct rmi_function *f34_container;
- bool f01_bootloader_mode;
+ bool bootloader_mode;
u32 attn_count;
int num_of_irq_regs;
--
2.7.4
Signed-off-by: Nick Dyer <[email protected]>
---
drivers/input/rmi4/rmi_driver.c | 2 +-
drivers/input/rmi4/rmi_driver.h | 5 +-
drivers/input/rmi4/rmi_f01.c | 23 ++++++++-
drivers/input/rmi4/rmi_f34.c | 112 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 139 insertions(+), 3 deletions(-)
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index a9e2827..f015b6a 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -303,7 +303,7 @@ static void rmi_driver_set_input_name(struct rmi_device *rmi_dev,
struct input_dev *input)
{
struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
- char *device_name = rmi_f01_get_product_ID(data->f01_container);
+ const char *device_name = rmi_f01_get_product_ID(data->f01_container);
char *name;
name = devm_kasprintf(&rmi_dev->dev, GFP_KERNEL,
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index e627a3a..e22a77f 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -105,7 +105,10 @@ int rmi_init_functions(struct rmi_driver_data *data);
int rmi_initial_reset(struct rmi_device *rmi_dev, void *ctx,
const struct pdt_entry *pdt);
-char *rmi_f01_get_product_ID(struct rmi_function *fn);
+const char *rmi_f01_get_product_ID(struct rmi_function *fn);
+u8 rmi_f01_get_manufacturer_ID(struct rmi_function *fn);
+const char *rmi_f01_get_date_of_manufacture(struct rmi_function *fn);
+u32 rmi_f01_get_firmware_ID(struct rmi_function *fn);
#ifdef CONFIG_RMI4_F34
int rmi_f34_create_sysfs(struct rmi_device *rmi_dev);
diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c
index b64d1cd..8624be8 100644
--- a/drivers/input/rmi4/rmi_f01.c
+++ b/drivers/input/rmi4/rmi_f01.c
@@ -241,13 +241,34 @@ static int rmi_f01_read_properties(struct rmi_device *rmi_dev,
return 0;
}
-char *rmi_f01_get_product_ID(struct rmi_function *fn)
+const char *rmi_f01_get_product_ID(struct rmi_function *fn)
{
struct f01_data *f01 = dev_get_drvdata(&fn->dev);
return f01->properties.product_id;
}
+u8 rmi_f01_get_manufacturer_ID(struct rmi_function *fn)
+{
+ struct f01_data *f01 = dev_get_drvdata(&fn->dev);
+
+ return f01->properties.manufacturer_id;
+}
+
+const char *rmi_f01_get_date_of_manufacture(struct rmi_function *fn)
+{
+ struct f01_data *f01 = dev_get_drvdata(&fn->dev);
+
+ return f01->properties.dom;
+}
+
+u32 rmi_f01_get_firmware_ID(struct rmi_function *fn)
+{
+ struct f01_data *f01 = dev_get_drvdata(&fn->dev);
+
+ return f01->properties.firmware_id;
+}
+
#ifdef CONFIG_OF
static int rmi_f01_of_probe(struct device *dev,
struct rmi_device_platform_data *pdata)
diff --git a/drivers/input/rmi4/rmi_f34.c b/drivers/input/rmi4/rmi_f34.c
index 59adfec..10b9722 100644
--- a/drivers/input/rmi4/rmi_f34.c
+++ b/drivers/input/rmi4/rmi_f34.c
@@ -383,6 +383,112 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
return ret;
}
+static ssize_t rmi_driver_manufacturer_id_show(struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(dev);
+ struct rmi_function *fn = data->f01_container;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n",
+ rmi_f01_get_manufacturer_ID(fn));
+}
+
+static DEVICE_ATTR(manufacturer_id, 0444,
+ rmi_driver_manufacturer_id_show, NULL);
+
+static ssize_t rmi_driver_date_of_manufacture_show(struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(dev);
+ struct rmi_function *fn = data->f01_container;
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n",
+ rmi_f01_get_date_of_manufacture(fn));
+}
+
+static DEVICE_ATTR(date_of_manufacture, 0444,
+ rmi_driver_date_of_manufacture_show, NULL);
+
+static ssize_t rmi_driver_product_id_show(struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(dev);
+ struct rmi_function *fn = data->f01_container;
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n",
+ rmi_f01_get_product_ID(fn));
+}
+
+static DEVICE_ATTR(product_id, 0444,
+ rmi_driver_product_id_show, NULL);
+
+static ssize_t rmi_driver_firmware_id_show(struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(dev);
+ struct rmi_function *fn = data->f01_container;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n",
+ rmi_f01_get_firmware_ID(fn));
+}
+
+static DEVICE_ATTR(firmware_id, 0444,
+ rmi_driver_firmware_id_show, NULL);
+
+static ssize_t rmi_driver_bootloader_id_show(struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(dev);
+ struct rmi_function *fn = data->f34_container;
+ struct f34_data *f34;
+
+ if (fn) {
+ f34 = dev_get_drvdata(&fn->dev);
+
+ if (f34->bl_version == BL_V5)
+ return scnprintf(buf, PAGE_SIZE, "%c%c\n",
+ f34->bootloader_id[0],
+ f34->bootloader_id[1]);
+ else
+ return scnprintf(buf, PAGE_SIZE, "V%d.%d\n",
+ f34->bootloader_id[1],
+ f34->bootloader_id[0]);
+ }
+
+ return 0;
+}
+
+static DEVICE_ATTR(bootloader_id, 0444,
+ rmi_driver_bootloader_id_show, NULL);
+
+static ssize_t rmi_driver_configuration_id_show(struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(dev);
+ struct rmi_function *fn = data->f34_container;
+ struct f34_data *f34;
+
+ if (fn) {
+ f34 = dev_get_drvdata(&fn->dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n", f34->configuration_id);
+ }
+
+ return 0;
+}
+
+static DEVICE_ATTR(configuration_id, 0444,
+ rmi_driver_configuration_id_show, NULL);
+
+static int rmi_firmware_update(struct rmi_driver_data *data,
+ const struct firmware *fw);
+
static ssize_t rmi_driver_update_fw_store(struct device *dev,
struct device_attribute *dattr,
const char *buf, size_t count)
@@ -434,6 +540,12 @@ static DEVICE_ATTR(update_fw_status, 0444,
rmi_driver_update_fw_status_show, NULL);
static struct attribute *rmi_firmware_attrs[] = {
+ &dev_attr_bootloader_id.attr,
+ &dev_attr_configuration_id.attr,
+ &dev_attr_manufacturer_id.attr,
+ &dev_attr_date_of_manufacture.attr,
+ &dev_attr_product_id.attr,
+ &dev_attr_firmware_id.attr,
&dev_attr_update_fw.attr,
&dev_attr_update_fw_status.attr,
NULL
--
2.7.4
The status is the percentage complete, or once complete, zero for
success or a negative return code.
Signed-off-by: Nick Dyer <[email protected]>
---
drivers/input/rmi4/rmi_f34.c | 34 ++++++++++++++++++++++++++++++++++
drivers/input/rmi4/rmi_f34.h | 4 ++++
drivers/input/rmi4/rmi_f34v7.c | 14 ++++++++++++++
3 files changed, 52 insertions(+)
diff --git a/drivers/input/rmi4/rmi_f34.c b/drivers/input/rmi4/rmi_f34.c
index 4f97613..59adfec 100644
--- a/drivers/input/rmi4/rmi_f34.c
+++ b/drivers/input/rmi4/rmi_f34.c
@@ -156,6 +156,9 @@ static int rmi_f34_write_blocks(struct f34_data *f34, const void *data,
i + 1, block_count);
data += f34->v5.block_size;
+ f34->update_progress += f34->v5.block_size;
+ f34->update_status = (f34->update_progress * 100) /
+ f34->update_size;
}
return 0;
@@ -185,6 +188,8 @@ static int rmi_f34_flash_firmware(struct f34_data *f34,
struct rmi_function *fn = f34->fn;
int ret;
+ f34->update_progress = 0;
+ f34->update_size = syn_fw->image_size + syn_fw->config_size;
if (syn_fw->image_size) {
dev_info(&fn->dev, "Erasing firmware...\n");
ret = rmi_f34_command(f34, F34_ERASE_ALL,
@@ -276,12 +281,24 @@ int rmi_f34_update_firmware(struct f34_data *f34, const struct firmware *fw)
ret = rmi_f34_flash_firmware(f34, syn_fw);
+ f34->update_status = ret;
mutex_unlock(&f34->v5.flash_mutex);
out:
return ret;
}
+int rmi_f34_status(struct rmi_function *fn)
+{
+ struct f34_data *f34 = dev_get_drvdata(&fn->dev);
+
+ /*
+ * The status is the percentage complete, or once complete,
+ * zero for success or a negative return code.
+ */
+ return f34->update_status;
+}
+
static int rmi_firmware_update(struct rmi_driver_data *data,
const struct firmware *fw)
{
@@ -400,8 +417,25 @@ static ssize_t rmi_driver_update_fw_store(struct device *dev,
static DEVICE_ATTR(update_fw, 0200, NULL, rmi_driver_update_fw_store);
+static ssize_t rmi_driver_update_fw_status_show(struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(dev);
+ int update_status = 0;
+
+ if (data->f34_container)
+ update_status = rmi_f34_status(data->f34_container);
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", update_status);
+}
+
+static DEVICE_ATTR(update_fw_status, 0444,
+ rmi_driver_update_fw_status_show, NULL);
+
static struct attribute *rmi_firmware_attrs[] = {
&dev_attr_update_fw.attr,
+ &dev_attr_update_fw_status.attr,
NULL
};
diff --git a/drivers/input/rmi4/rmi_f34.h b/drivers/input/rmi4/rmi_f34.h
index 9ab8ece..726d5ed 100644
--- a/drivers/input/rmi4/rmi_f34.h
+++ b/drivers/input/rmi4/rmi_f34.h
@@ -424,6 +424,10 @@ struct f34_data {
unsigned char bootloader_id[5];
unsigned char configuration_id[9];
+ int update_status;
+ int update_progress;
+ int update_size;
+
union {
struct f34v5_data v5;
struct f34v7_data v7;
diff --git a/drivers/input/rmi4/rmi_f34v7.c b/drivers/input/rmi4/rmi_f34v7.c
index 839f308..af00a3f 100644
--- a/drivers/input/rmi4/rmi_f34v7.c
+++ b/drivers/input/rmi4/rmi_f34v7.c
@@ -918,6 +918,11 @@ static int rmi_f34v7_write_f34v7_blocks(struct f34_data *f34,
block_ptr += (transfer * f34->v7.block_size);
remaining -= transfer;
+
+ if (command == v7_CMD_WRITE_FW)
+ f34->update_status = 80 - 70*remaining/block_cnt;
+ else if (command == v7_CMD_WRITE_CONFIG)
+ f34->update_status = 90 - 10*remaining/block_cnt;
} while (remaining);
return 0;
@@ -1273,6 +1278,8 @@ int rmi_f34v7_do_reflash(struct f34_data *f34, const struct firmware *fw)
if (ret < 0)
goto fail;
+ f34->update_status = 5;
+
if (!f34->v7.new_partition_table) {
ret = rmi_f34v7_check_ui_firmware_size(f34);
if (ret < 0)
@@ -1312,6 +1319,7 @@ int rmi_f34v7_do_reflash(struct f34_data *f34, const struct firmware *fw)
__func__);
}
+ f34->update_status = 10;
dev_info(&f34->fn->dev, "Writing firmware (%d bytes)...\n",
f34->v7.img.ui_firmware.size);
@@ -1336,6 +1344,8 @@ int rmi_f34v7_do_reflash(struct f34_data *f34, const struct firmware *fw)
goto fail;
}
+ f34->update_status = 95;
+
if (f34->v7.new_partition_table) {
if (f34->v7.has_guest_code && f34->v7.img.contains_guest_code) {
dev_info(&f34->fn->dev, "Writing guest code...\n");
@@ -1346,7 +1356,11 @@ int rmi_f34v7_do_reflash(struct f34_data *f34, const struct firmware *fw)
}
}
+ f34->update_status = 0;
+ return 0;
+
fail:
+ f34->update_status = ret;
return ret;
}
--
2.7.4
On Tue 18 Oct 16:26 PDT 2016, Nick Dyer wrote:
> Port firmware update code from Samsung Galaxy S7 driver into
> mainline framework.
>
> This patch has been tested on Synaptics S7813.
Please split this patch into two; one for the variable renames and
enablement and one for the introduction of the v7 function - or perhaps
fold the prior part into the f34 introduction patch.
Is there no way to better utilize the f34 support introduced in the
earlier patch? Just feels odd that f34 version 1 - 6 can be done in 486
lines of code and v7 require 1451. (I have not yet looked at the details
of the patch)
Regards,
Bjorn
>
> Signed-off-by: Nick Dyer <[email protected]>
> ---
> drivers/input/rmi4/Makefile | 2 +-
> drivers/input/rmi4/rmi_driver.c | 56 +-
> drivers/input/rmi4/rmi_f34.c | 146 ++--
> drivers/input/rmi4/rmi_f34.h | 379 +++++++++-
> drivers/input/rmi4/rmi_f34v7.c | 1451 +++++++++++++++++++++++++++++++++++++++
> include/linux/rmi.h | 2 +-
> 6 files changed, 1940 insertions(+), 96 deletions(-)
> create mode 100644 drivers/input/rmi4/rmi_f34v7.c
>
> diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile
> index 5f165ad..a74b6d5 100644
> --- a/drivers/input/rmi4/Makefile
> +++ b/drivers/input/rmi4/Makefile
> @@ -7,7 +7,7 @@ rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.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_core-$(CONFIG_RMI4_F34) += rmi_f34.o rmi_f34v7.o
> rmi_core-$(CONFIG_RMI4_F54) += rmi_f54.o
>
> # Transports
> diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
> index 78fdcef..a9e2827 100644
> --- a/drivers/input/rmi4/rmi_driver.c
> +++ b/drivers/input/rmi4/rmi_driver.c
> @@ -473,7 +473,7 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
> return retval;
> }
>
> - return (data->f01_bootloader_mode || addr == pdt_start) ?
> + return (data->bootloader_mode || addr == pdt_start) ?
> RMI_SCAN_DONE : RMI_SCAN_CONTINUE;
> }
>
> @@ -681,41 +681,49 @@ bool rmi_register_desc_has_subpacket(const struct rmi_register_desc_item *item,
> subpacket) == subpacket;
> }
>
> -/* Indicates that flash programming is enabled (bootloader mode). */
> -#define RMI_F01_STATUS_BOOTLOADER(status) (!!((status) & 0x40))
> -
> -/*
> - * Given the PDT entry for F01, read the device status register to determine
> - * if we're stuck in bootloader mode or not.
> - *
> - */
> static int rmi_check_bootloader_mode(struct rmi_device *rmi_dev,
> const struct pdt_entry *pdt)
> {
> - int error;
> - u8 device_status;
> + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
> + int ret;
> + u8 status;
>
> - error = rmi_read(rmi_dev, pdt->data_base_addr + pdt->page_start,
> - &device_status);
> - if (error) {
> - dev_err(&rmi_dev->dev,
> - "Failed to read device status: %d.\n", error);
> - return error;
> + if (pdt->function_number == 0x34 && pdt->function_version > 1) {
> + ret = rmi_read(rmi_dev, pdt->data_base_addr, &status);
> + if (ret) {
> + dev_err(&rmi_dev->dev,
> + "Failed to read F34 status: %d.\n", ret);
> + return ret;
> + }
> +
> + if (status & BIT(7))
> + data->bootloader_mode = true;
> + } else if (pdt->function_number == 0x01) {
> + ret = rmi_read(rmi_dev, pdt->data_base_addr, &status);
> + if (ret) {
> + dev_err(&rmi_dev->dev,
> + "Failed to read F01 status: %d.\n", ret);
> + return ret;
> + }
> +
> + if (status & BIT(6))
> + data->bootloader_mode = true;
> }
>
> - return RMI_F01_STATUS_BOOTLOADER(device_status);
> + return 0;
> }
>
> static int rmi_count_irqs(struct rmi_device *rmi_dev,
> void *ctx, const struct pdt_entry *pdt)
> {
> - struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
> int *irq_count = ctx;
> + int ret;
>
> *irq_count += pdt->interrupt_source_count;
> - if (pdt->function_number == 0x01)
> - data->f01_bootloader_mode =
> - rmi_check_bootloader_mode(rmi_dev, pdt);
> +
> + ret = rmi_check_bootloader_mode(rmi_dev, pdt);
> + if (ret < 0)
> + return ret;
>
> return RMI_SCAN_CONTINUE;
> }
> @@ -884,13 +892,15 @@ int rmi_probe_interrupts(struct rmi_driver_data *data)
> */
> rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Counting IRQs.\n", __func__);
> irq_count = 0;
> + data->bootloader_mode = false;
> +
> retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_count_irqs);
> if (retval < 0) {
> dev_err(dev, "IRQ counting failed with code %d.\n", retval);
> return retval;
> }
>
> - if (data->f01_bootloader_mode)
> + if (data->bootloader_mode)
> dev_warn(&rmi_dev->dev, "Device in bootloader mode.\n");
>
> data->irq_count = irq_count;
> diff --git a/drivers/input/rmi4/rmi_f34.c b/drivers/input/rmi4/rmi_f34.c
> index 9e4cf34..4f97613 100644
> --- a/drivers/input/rmi4/rmi_f34.c
> +++ b/drivers/input/rmi4/rmi_f34.c
> @@ -12,6 +12,7 @@
> #include <linux/firmware.h>
> #include <asm/unaligned.h>
> #include <asm/unaligned.h>
> +#include <linux/bitops.h>
>
> #include "rmi_driver.h"
> #include "rmi_f34.h"
> @@ -58,9 +59,9 @@ static int rmi_f34_command(struct f34_data *f34, u8 command,
> return ret;
> }
>
> - init_completion(&f34->cmd_done);
> + init_completion(&f34->v5.cmd_done);
>
> - ret = rmi_read(rmi_dev, f34->ctrl_address, &f34->status);
> + ret = rmi_read(rmi_dev, f34->v5.ctrl_address, &f34->v5.status);
> if (ret) {
> dev_err(&f34->fn->dev,
> "%s: Failed to read cmd register: %d (command %#02x)\n",
> @@ -68,9 +69,9 @@ static int rmi_f34_command(struct f34_data *f34, u8 command,
> return ret;
> }
>
> - f34->status |= command & 0x0f;
> + f34->v5.status |= command & 0x0f;
>
> - ret = rmi_write(rmi_dev, f34->ctrl_address, f34->status);
> + ret = rmi_write(rmi_dev, f34->v5.ctrl_address, f34->v5.status);
> if (ret < 0) {
> dev_err(&f34->fn->dev,
> "Failed to write F34 command %#02x: %d\n",
> @@ -78,10 +79,10 @@ static int rmi_f34_command(struct f34_data *f34, u8 command,
> return ret;
> }
>
> - if (!wait_for_completion_timeout(&f34->cmd_done,
> + if (!wait_for_completion_timeout(&f34->v5.cmd_done,
> msecs_to_jiffies(timeout))) {
>
> - ret = rmi_read(rmi_dev, f34->ctrl_address, &f34->status);
> + ret = rmi_read(rmi_dev, f34->v5.ctrl_address, &f34->v5.status);
> if (ret) {
> dev_err(&f34->fn->dev,
> "%s: cmd %#02x timed out: %d\n",
> @@ -89,10 +90,10 @@ static int rmi_f34_command(struct f34_data *f34, u8 command,
> return ret;
> }
>
> - if (f34->status & 0x7f) {
> + if (f34->v5.status & 0x7f) {
> dev_err(&f34->fn->dev,
> "%s: cmd %#02x timed out, status: %#02x\n",
> - __func__, command, f34->status);
> + __func__, command, f34->v5.status);
> return -ETIMEDOUT;
> }
> }
> @@ -105,12 +106,15 @@ static int rmi_f34_attention(struct rmi_function *fn, unsigned long *irq_bits)
> struct f34_data *f34 = dev_get_drvdata(&fn->dev);
> int ret;
>
> - ret = rmi_read(f34->fn->rmi_dev, f34->ctrl_address, &f34->status);
> + if (f34->bl_version != BL_V5)
> + return 0;
> +
> + ret = rmi_read(f34->fn->rmi_dev, f34->v5.ctrl_address, &f34->v5.status);
> rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: status: %#02x, ret: %d\n",
> - __func__, f34->status, ret);
> + __func__, f34->v5.status, ret);
>
> - if (!ret && !(f34->status & 0x7f))
> - complete(&f34->cmd_done);
> + if (!ret && !(f34->v5.status & 0x7f))
> + complete(&f34->v5.cmd_done);
>
> return 0;
> }
> @@ -133,7 +137,7 @@ static int rmi_f34_write_blocks(struct f34_data *f34, const void *data,
> }
>
> for (i = 0; i < block_count; i++) {
> - ret = rmi_write_block(rmi_dev, address, data, f34->block_size);
> + ret = rmi_write_block(rmi_dev, address, data, f34->v5.block_size);
> if (ret) {
> dev_err(&fn->dev,
> "failed to write block #%d: %d\n", i, ret);
> @@ -151,7 +155,7 @@ static int rmi_f34_write_blocks(struct f34_data *f34, const void *data,
> rmi_dbg(RMI_DEBUG_FN, &fn->dev, "wrote block %d of %d\n",
> i + 1, block_count);
>
> - data += f34->block_size;
> + data += f34->v5.block_size;
> }
>
> return 0;
> @@ -159,26 +163,24 @@ static int rmi_f34_write_blocks(struct f34_data *f34, const void *data,
>
> static int rmi_f34_write_firmware(struct f34_data *f34, const void *data)
> {
> - return rmi_f34_write_blocks(f34, data, f34->fw_blocks,
> + return rmi_f34_write_blocks(f34, data, f34->v5.fw_blocks,
> F34_WRITE_FW_BLOCK);
> }
>
> static int rmi_f34_write_config(struct f34_data *f34, const void *data)
> {
> - return rmi_f34_write_blocks(f34, data, f34->config_blocks,
> + return rmi_f34_write_blocks(f34, data, f34->v5.config_blocks,
> F34_WRITE_CONFIG_BLOCK);
> }
>
> -int rmi_f34_enable_flash(struct rmi_function *fn)
> +int rmi_f34_enable_flash(struct f34_data *f34)
> {
> - struct f34_data *f34 = dev_get_drvdata(&fn->dev);
> -
> return rmi_f34_command(f34, F34_ENABLE_FLASH_PROG,
> F34_ENABLE_WAIT_MS, true);
> }
>
> static int rmi_f34_flash_firmware(struct f34_data *f34,
> - const struct rmi_f34_firmware *syn_fw)
> + const struct rmi_f34_firmware *syn_fw)
> {
> struct rmi_function *fn = f34->fn;
> int ret;
> @@ -221,9 +223,8 @@ static int rmi_f34_flash_firmware(struct f34_data *f34,
> return 0;
> }
>
> -int rmi_f34_update_firmware(struct rmi_function *fn, const struct firmware *fw)
> +int rmi_f34_update_firmware(struct f34_data *f34, const struct firmware *fw)
> {
> - struct f34_data *f34 = dev_get_drvdata(&fn->dev);
> const struct rmi_f34_firmware *syn_fw;
> int ret;
>
> @@ -231,72 +232,61 @@ int rmi_f34_update_firmware(struct rmi_function *fn, const struct firmware *fw)
> BUILD_BUG_ON(offsetof(struct rmi_f34_firmware, data) !=
> F34_FW_IMAGE_OFFSET);
>
> - rmi_dbg(RMI_DEBUG_FN, &fn->dev,
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
> "FW size:%d, checksum:%08x, image_size:%d, config_size:%d\n",
> (int)fw->size,
> le32_to_cpu(syn_fw->checksum),
> le32_to_cpu(syn_fw->image_size),
> le32_to_cpu(syn_fw->config_size));
>
> - rmi_dbg(RMI_DEBUG_FN, &fn->dev,
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
> "FW bootloader_id:%02x, product_id:%.*s, info: %02x%02x\n",
> syn_fw->bootloader_version,
> (int)sizeof(syn_fw->product_id), syn_fw->product_id,
> syn_fw->product_info[0], syn_fw->product_info[1]);
>
> if (syn_fw->image_size &&
> - syn_fw->image_size != f34->fw_blocks * f34->block_size) {
> - dev_err(&fn->dev,
> + syn_fw->image_size != f34->v5.fw_blocks * f34->v5.block_size) {
> + dev_err(&f34->fn->dev,
> "Bad firmware image: fw size %d, expected %d\n",
> syn_fw->image_size,
> - f34->fw_blocks * f34->block_size);
> + f34->v5.fw_blocks * f34->v5.block_size);
> ret = -EILSEQ;
> goto out;
> }
>
> if (syn_fw->config_size &&
> - syn_fw->config_size != f34->config_blocks * f34->block_size) {
> - dev_err(&fn->dev,
> + syn_fw->config_size != f34->v5.config_blocks * f34->v5.block_size) {
> + dev_err(&f34->fn->dev,
> "Bad firmware image: config size %d, expected %d\n",
> syn_fw->config_size,
> - f34->config_blocks * f34->block_size);
> + f34->v5.config_blocks * f34->v5.block_size);
> ret = -EILSEQ;
> goto out;
> }
>
> if (syn_fw->image_size && !syn_fw->config_size) {
> - dev_err(&fn->dev, "Bad firmware image: no config data\n");
> + dev_err(&f34->fn->dev, "Bad firmware image: no config data\n");
> ret = -EILSEQ;
> goto out;
> }
>
> dev_info(&f34->fn->dev, "Firmware image OK\n");
> - mutex_lock(&f34->flash_mutex);
> + mutex_lock(&f34->v5.flash_mutex);
>
> ret = rmi_f34_flash_firmware(f34, syn_fw);
>
> - mutex_unlock(&f34->flash_mutex);
> + mutex_unlock(&f34->v5.flash_mutex);
>
> out:
> return ret;
> }
>
> -int rmi_f34_check_supported(struct rmi_function *fn)
> -{
> - u8 version = fn->fd.function_version;
> -
> - /* Only version 0 currently supported */
> - if (version == 0)
> - return 0;
> -
> - dev_warn(&fn->dev, "F34 V%d not supported!\n", version);
> - return -ENODEV;
> -}
> -
> static int rmi_firmware_update(struct rmi_driver_data *data,
> const struct firmware *fw)
> {
> struct device *dev = &data->rmi_dev->dev;
> + struct f34_data *f34;
> int ret;
>
> if (!data->f34_container) {
> @@ -304,13 +294,23 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
> return -EINVAL;
> }
>
> - ret = rmi_f34_check_supported(data->f34_container);
> - if (ret)
> - return ret;
> + f34 = dev_get_drvdata(&data->f34_container->dev);
> +
> + if (f34->bl_version == BL_V7) {
> + if (data->pdt_props & HAS_BSR) {
> + dev_err(dev, "%s: LTS not supported\n", __func__);
> + return -ENODEV;
> + }
> + } else if (data->f34_container->fd.function_version != 0) {
> + dev_err(dev, "%s: Version not supported\n", __func__);
> + return -ENODEV;
> + }
>
> /* Enter flash mode */
> - rmi_dbg(RMI_DEBUG_FN, dev, "Enabling flash\n");
> - ret = rmi_f34_enable_flash(data->f34_container);
> + if (f34->bl_version == BL_V7)
> + ret = rmi_f34v7_start_reflash(f34, fw);
> + else
> + ret = rmi_f34_enable_flash(f34);
> if (ret)
> return ret;
>
> @@ -325,14 +325,21 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
> if (ret)
> return ret;
>
> - if (!data->f01_bootloader_mode || !data->f34_container) {
> + if (!data->bootloader_mode || !data->f34_container) {
> dev_warn(dev, "%s: No F34 present or not in bootloader!\n",
> - __func__);
> + __func__);
> return -EINVAL;
> }
>
> + f34 = dev_get_drvdata(&data->f34_container->dev);
> +
> /* Perform firmware update */
> - ret = rmi_f34_update_firmware(data->f34_container, fw);
> + if (f34->bl_version == BL_V7)
> + ret = rmi_f34v7_do_reflash(f34, fw);
> + else
> + ret = rmi_f34_update_firmware(f34, fw);
> +
> + dev_info(&f34->fn->dev, "Firmware update complete, status:%d\n", ret);
>
> /* Re-probe */
> rmi_dbg(RMI_DEBUG_FN, dev, "Re-probing device\n");
> @@ -407,12 +414,9 @@ static int rmi_f34_probe(struct rmi_function *fn)
> struct f34_data *f34;
> unsigned char f34_queries[9];
> bool has_config_id;
> + u8 version = fn->fd.function_version;
> int ret;
>
> - ret = rmi_f34_check_supported(fn);
> - if (ret)
> - return ret;
> -
> f34 = devm_kzalloc(&fn->dev, sizeof(struct f34_data), GFP_KERNEL);
> if (!f34)
> return -ENOMEM;
> @@ -420,31 +424,37 @@ static int rmi_f34_probe(struct rmi_function *fn)
> f34->fn = fn;
> dev_set_drvdata(&fn->dev, f34);
>
> - mutex_init(&f34->flash_mutex);
> - init_completion(&f34->cmd_done);
> + /* v5 code only supported version 0, try V7 probe */
> + if (version > 0)
> + return rmi_f34v7_probe(f34);
> +
> + f34->bl_version = BL_V5;
>
> ret = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr,
> f34_queries, sizeof(f34_queries));
> if (ret) {
> dev_err(&fn->dev, "%s: Failed to query properties\n",
> - __func__);
> + __func__);
> return ret;
> }
>
> snprintf(f34->bootloader_id, sizeof(f34->bootloader_id),
> "%c%c", f34_queries[0], f34_queries[1]);
>
> - f34->block_size = get_unaligned_le16(&f34_queries[3]);
> - f34->fw_blocks = get_unaligned_le16(&f34_queries[5]);
> - f34->config_blocks = get_unaligned_le16(&f34_queries[7]);
> - f34->ctrl_address = fn->fd.data_base_addr + F34_BLOCK_DATA_OFFSET +
> - f34->block_size;
> + mutex_init(&f34->v5.flash_mutex);
> + init_completion(&f34->v5.cmd_done);
> +
> + f34->v5.block_size = get_unaligned_le16(&f34_queries[3]);
> + f34->v5.fw_blocks = get_unaligned_le16(&f34_queries[5]);
> + f34->v5.config_blocks = get_unaligned_le16(&f34_queries[7]);
> + f34->v5.ctrl_address = fn->fd.data_base_addr + F34_BLOCK_DATA_OFFSET +
> + f34->v5.block_size;
> has_config_id = f34_queries[2] & (1 << 2);
>
> rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Bootloader ID: %s\n", f34->bootloader_id);
> - rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Block size: %d\n", f34->block_size);
> - rmi_dbg(RMI_DEBUG_FN, &fn->dev, "FW blocks: %d\n", f34->fw_blocks);
> - rmi_dbg(RMI_DEBUG_FN, &fn->dev, "CFG blocks: %d\n", f34->config_blocks);
> + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Block size: %d\n", f34->v5.block_size);
> + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "FW blocks: %d\n", f34->v5.fw_blocks);
> + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "CFG blocks: %d\n", f34->v5.config_blocks);
>
> if (has_config_id) {
> ret = rmi_read_block(fn->rmi_dev, fn->fd.control_base_addr,
> diff --git a/drivers/input/rmi4/rmi_f34.h b/drivers/input/rmi4/rmi_f34.h
> index 0c19a58..9ab8ece 100644
> --- a/drivers/input/rmi4/rmi_f34.h
> +++ b/drivers/input/rmi4/rmi_f34.h
> @@ -33,6 +33,317 @@
>
> #define F34_BOOTLOADER_ID_LEN 2
>
> +#define V7_FLASH_STATUS_OFFSET 0
> +#define V7_PARTITION_ID_OFFSET 1
> +#define V7_BLOCK_NUMBER_OFFSET 2
> +#define V7_TRANSFER_LENGTH_OFFSET 3
> +#define V7_COMMAND_OFFSET 4
> +#define V7_PAYLOAD_OFFSET 5
> +#define BOOTLOADER_ID_OFFSET 1
> +
> +#define V7_PARTITION_SUPPORT_BYTES 4
> +
> +#define SLEEP_MODE_NORMAL (0x00)
> +
> +#define IMAGE_HEADER_VERSION_10 0x10
> +
> +#define MAX_IMAGE_NAME_LEN 256
> +#define SYNAPTICS_RMI4_PRODUCT_ID_SIZE 10
> +#define PRODUCT_ID_SIZE 10
> +
> +#define MASK_8BIT 0xFF
> +#define MASK_5BIT 0x1F
> +
> +#define ENABLE_WAIT_MS (1 * 1000)
> +#define WRITE_WAIT_MS (3 * 1000)
> +#define ERASE_WAIT_MS (5 * 1000)
> +
> +#define MIN_SLEEP_TIME_US 50
> +#define MAX_SLEEP_TIME_US 100
> +
> +#define FORCE_UPDATE false
> +
> +#define HAS_BSR BIT(5)
> +
> +enum rmi_f34_bl_version {
> + BL_V5 = 5,
> + BL_V6 = 6,
> + BL_V7 = 7,
> +};
> +
> +enum rmi_f34v7_flash_command2 {
> + CMD_V7_IDLE = 0x00,
> + CMD_V7_ENTER_BL,
> + CMD_V7_READ,
> + CMD_V7_WRITE,
> + CMD_V7_ERASE,
> + CMD_V7_ERASE_AP,
> + CMD_V7_SENSOR_ID,
> +};
> +
> +enum rmi_f34v7_flash_command {
> + v7_CMD_IDLE = 0,
> + v7_CMD_WRITE_FW,
> + v7_CMD_WRITE_CONFIG,
> + v7_CMD_WRITE_LOCKDOWN,
> + v7_CMD_WRITE_GUEST_CODE,
> + v7_CMD_READ_CONFIG,
> + v7_CMD_ERASE_ALL,
> + v7_CMD_ERASE_UI_FIRMWARE,
> + v7_CMD_ERASE_UI_CONFIG,
> + v7_CMD_ERASE_BL_CONFIG,
> + v7_CMD_ERASE_DISP_CONFIG,
> + v7_CMD_ERASE_FLASH_CONFIG,
> + v7_CMD_ERASE_GUEST_CODE,
> + v7_CMD_ENABLE_FLASH_PROG,
> +};
> +
> +enum rmi_f34v7_config_area {
> + v7_UI_CONFIG_AREA = 0,
> + v7_PM_CONFIG_AREA,
> + v7_BL_CONFIG_AREA,
> + v7_DP_CONFIG_AREA,
> + v7_FLASH_CONFIG_AREA,
> +};
> +
> +enum rmi_f34v7_partition_id {
> + BOOTLOADER_PARTITION = 0x01,
> + DEVICE_CONFIG_PARTITION,
> + FLASH_CONFIG_PARTITION,
> + MANUFACTURING_BLOCK_PARTITION,
> + GUEST_SERIALIZATION_PARTITION,
> + GLOBAL_PARAMETERS_PARTITION,
> + CORE_CODE_PARTITION,
> + CORE_CONFIG_PARTITION,
> + GUEST_CODE_PARTITION,
> + DISPLAY_CONFIG_PARTITION,
> +};
> +
> +struct f34v7_query_0 {
> + union {
> + struct {
> + unsigned char subpacket_1_size:3;
> + unsigned char has_config_id:1;
> + unsigned char f34_query0_b4:1;
> + unsigned char has_thqa:1;
> + unsigned char f34_query0_b6__7:2;
> + } __packed;
> + unsigned char data[1];
> + };
> +};
> +
> +struct synaptics_rmi4_f34_query_01 {
> + union {
> + struct {
> + unsigned char reg_map:1;
> + unsigned char unlocked:1;
> + unsigned char has_config_id:1;
> + unsigned char has_perm_config:1;
> + unsigned char has_bl_config:1;
> + unsigned char has_disp_config:1;
> + unsigned char has_ctrl1:1;
> + unsigned char has_flash_query4:1;
> + } __packed;
> + unsigned char data[1];
> + };
> +};
> +
> +struct f34v7_query_1_7 {
> + union {
> + struct {
> + /* query 1 */
> + unsigned char bl_minor_revision;
> + unsigned char bl_major_revision;
> +
> + /* query 2 */
> + unsigned char bl_fw_id_7_0;
> + unsigned char bl_fw_id_15_8;
> + unsigned char bl_fw_id_23_16;
> + unsigned char bl_fw_id_31_24;
> +
> + /* query 3 */
> + unsigned char minimum_write_size;
> + unsigned char block_size_7_0;
> + unsigned char block_size_15_8;
> + unsigned char flash_page_size_7_0;
> + unsigned char flash_page_size_15_8;
> +
> + /* query 4 */
> + unsigned char adjustable_partition_area_size_7_0;
> + unsigned char adjustable_partition_area_size_15_8;
> +
> + /* query 5 */
> + unsigned char flash_config_length_7_0;
> + unsigned char flash_config_length_15_8;
> +
> + /* query 6 */
> + unsigned char payload_length_7_0;
> + unsigned char payload_length_15_8;
> +
> + /* query 7 */
> + unsigned char f34_query7_b0:1;
> + unsigned char has_bootloader:1;
> + unsigned char has_device_config:1;
> + unsigned char has_flash_config:1;
> + unsigned char has_manufacturing_block:1;
> + unsigned char has_guest_serialization:1;
> + unsigned char has_global_parameters:1;
> + unsigned char has_core_code:1;
> + unsigned char has_core_config:1;
> + unsigned char has_guest_code:1;
> + unsigned char has_display_config:1;
> + unsigned char f34_query7_b11__15:5;
> + unsigned char f34_query7_b16__23;
> + unsigned char f34_query7_b24__31;
> + } __packed;
> + unsigned char data[21];
> + };
> +};
> +
> +struct f34v7_data_1_5 {
> + union {
> + struct {
> + unsigned char partition_id:5;
> + unsigned char f34_data1_b5__7:3;
> + unsigned char block_offset_7_0;
> + unsigned char block_offset_15_8;
> + unsigned char transfer_length_7_0;
> + unsigned char transfer_length_15_8;
> + unsigned char command;
> + unsigned char payload_0;
> + unsigned char payload_1;
> + } __packed;
> + unsigned char data[8];
> + };
> +};
> +
> +struct block_data {
> + const unsigned char *data;
> + int size;
> +};
> +
> +struct partition_table {
> + unsigned char partition_id:5;
> + unsigned char byte_0_reserved:3;
> + unsigned char byte_1_reserved;
> + unsigned char partition_length_7_0;
> + unsigned char partition_length_15_8;
> + unsigned char start_physical_address_7_0;
> + unsigned char start_physical_address_15_8;
> + unsigned char partition_properties_7_0;
> + unsigned char partition_properties_15_8;
> +} __packed;
> +
> +struct physical_address {
> + unsigned short ui_firmware;
> + unsigned short ui_config;
> + unsigned short dp_config;
> + unsigned short guest_code;
> +};
> +
> +struct container_descriptor {
> + unsigned char content_checksum[4];
> + unsigned char container_id[2];
> + unsigned char minor_version;
> + unsigned char major_version;
> + unsigned char reserved_08;
> + unsigned char reserved_09;
> + unsigned char reserved_0a;
> + unsigned char reserved_0b;
> + unsigned char container_option_flags[4];
> + unsigned char content_options_length[4];
> + unsigned char content_options_address[4];
> + unsigned char content_length[4];
> + unsigned char content_address[4];
> +};
> +
> +enum container_id {
> + TOP_LEVEL_CONTAINER = 0,
> + UI_CONTAINER,
> + UI_CONFIG_CONTAINER,
> + BL_CONTAINER,
> + BL_IMAGE_CONTAINER,
> + BL_CONFIG_CONTAINER,
> + BL_LOCKDOWN_INFO_CONTAINER,
> + PERMANENT_CONFIG_CONTAINER,
> + GUEST_CODE_CONTAINER,
> + BL_PROTOCOL_DESCRIPTOR_CONTAINER,
> + UI_PROTOCOL_DESCRIPTOR_CONTAINER,
> + RMI_SELF_DISCOVERY_CONTAINER,
> + RMI_PAGE_CONTENT_CONTAINER,
> + GENERAL_INFORMATION_CONTAINER,
> + DEVICE_CONFIG_CONTAINER,
> + FLASH_CONFIG_CONTAINER,
> + GUEST_SERIALIZATION_CONTAINER,
> + GLOBAL_PARAMETERS_CONTAINER,
> + CORE_CODE_CONTAINER,
> + CORE_CONFIG_CONTAINER,
> + DISPLAY_CONFIG_CONTAINER,
> +};
> +
> +struct block_count {
> + unsigned short ui_firmware;
> + unsigned short ui_config;
> + unsigned short dp_config;
> + unsigned short fl_config;
> + unsigned short pm_config;
> + unsigned short bl_config;
> + unsigned short lockdown;
> + unsigned short guest_code;
> +};
> +
> +struct image_header_10 {
> + unsigned char checksum[4];
> + unsigned char reserved_04;
> + unsigned char reserved_05;
> + unsigned char minor_header_version;
> + unsigned char major_header_version;
> + unsigned char reserved_08;
> + unsigned char reserved_09;
> + unsigned char reserved_0a;
> + unsigned char reserved_0b;
> + unsigned char top_level_container_start_addr[4];
> +};
> +
> +struct image_metadata {
> + bool contains_firmware_id;
> + bool contains_bootloader;
> + bool contains_disp_config;
> + bool contains_guest_code;
> + bool contains_flash_config;
> + unsigned int firmware_id;
> + unsigned int checksum;
> + unsigned int bootloader_size;
> + unsigned int disp_config_offset;
> + unsigned char bl_version;
> + unsigned char product_id[PRODUCT_ID_SIZE + 1];
> + unsigned char cstmr_product_id[PRODUCT_ID_SIZE + 1];
> + struct block_data bootloader;
> + struct block_data ui_firmware;
> + struct block_data ui_config;
> + struct block_data dp_config;
> + struct block_data fl_config;
> + struct block_data bl_config;
> + struct block_data guest_code;
> + struct block_data lockdown;
> + struct block_count blkcount;
> + struct physical_address phyaddr;
> +};
> +
> +struct register_offset {
> + unsigned char properties;
> + unsigned char properties_2;
> + unsigned char block_size;
> + unsigned char block_count;
> + unsigned char gc_block_count;
> + unsigned char flash_status;
> + unsigned char partition_id;
> + unsigned char block_number;
> + unsigned char transfer_length;
> + unsigned char flash_cmd;
> + unsigned char payload;
> +};
> +
> struct rmi_f34_firmware {
> __le32 checksum;
> u8 pad1[3];
> @@ -45,9 +356,7 @@ struct rmi_f34_firmware {
> u8 data[];
> };
>
> -struct f34_data {
> - struct rmi_function *fn;
> -
> +struct f34v5_data {
> u16 block_size;
> u16 fw_blocks;
> u16 config_blocks;
> @@ -56,9 +365,73 @@ struct f34_data {
>
> struct completion cmd_done;
> struct mutex flash_mutex;
> +};
> +
> +struct f34v7_data {
> + bool initialized;
> + bool has_perm_config;
> + bool has_bl_config;
> + bool has_disp_config;
> + bool has_guest_code;
> + bool force_update;
> + unsigned char *read_config_buf;
> + unsigned char command;
> + unsigned char flash_status;
> + unsigned char productinfo1;
> + unsigned char productinfo2;
> + unsigned char properties_off;
> + unsigned char blk_size_off;
> + unsigned char blk_count_off;
> + unsigned char blk_data_off;
> + unsigned char properties2_off;
> + unsigned char guest_blk_count_off;
> + unsigned char flash_cmd_off;
> + unsigned char flash_status_off;
> + unsigned short block_size;
> + unsigned short fw_block_count;
> + unsigned short config_block_count;
> + unsigned short perm_config_block_count;
> + unsigned short bl_config_block_count;
> + unsigned short disp_config_block_count;
> + unsigned short guest_code_block_count;
> + unsigned short config_size;
> + unsigned short config_area;
> + char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1];
>
> + struct synaptics_rmi4_f34_query_01 flash_properties;
> + struct workqueue_struct *fwu_workqueue;
> + struct delayed_work fwu_work;
> +
> + unsigned short flash_config_length;
> + unsigned short payload_length;
> + struct register_offset off;
> + unsigned char partitions;
> + unsigned short partition_table_bytes;
> + unsigned short read_config_buf_size;
> + struct block_count blkcount;
> + struct physical_address phyaddr;
> + struct image_metadata img;
> + bool new_partition_table;
> + const unsigned char *config_data;
> + const unsigned char *image;
> + bool in_bl_mode;
> +};
> +
> +struct f34_data {
> + struct rmi_function *fn;
> +
> + enum rmi_f34_bl_version bl_version;
> unsigned char bootloader_id[5];
> unsigned char configuration_id[9];
> +
> + union {
> + struct f34v5_data v5;
> + struct f34v7_data v7;
> + };
> };
>
> +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);
> +
> #endif /* _RMI_F34_H */
> diff --git a/drivers/input/rmi4/rmi_f34v7.c b/drivers/input/rmi4/rmi_f34v7.c
> new file mode 100644
> index 0000000..839f308
> --- /dev/null
> +++ b/drivers/input/rmi4/rmi_f34v7.c
> @@ -0,0 +1,1451 @@
> +/*
> + * Copyright (c) 2016, Zodiac Inflight Innovations
> + * Copyright (c) 2007-2016, Synaptics Incorporated
> + * Copyright (C) 2012 Alexandra Chin <[email protected]>
> + * Copyright (C) 2012 Scott Lin <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/rmi.h>
> +#include <linux/firmware.h>
> +#include <asm/unaligned.h>
> +#include <linux/delay.h>
> +#include <linux/slab.h>
> +
> +#include "rmi_driver.h"
> +#include "rmi_f34.h"
> +
> +static int rmi_f34v7_read_flash_status(struct f34_data *f34)
> +{
> + unsigned char status;
> + unsigned char command;
> + int ret;
> +
> + ret = rmi_read_block(f34->fn->rmi_dev,
> + f34->fn->fd.data_base_addr + f34->v7.off.flash_status,
> + &status,
> + sizeof(status));
> + if (ret < 0) {
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
> + "%s: Failed to read flash status\n", __func__);
> + return ret;
> + }
> +
> + f34->v7.in_bl_mode = status >> 7;
> +
> + f34->v7.flash_status = status & MASK_5BIT;
> +
> + if (f34->v7.flash_status != 0x00) {
> + dev_err(&f34->fn->dev, "%s: status=%d, command=0x%02x\n",
> + __func__, f34->v7.flash_status, f34->v7.command);
> + }
> +
> + ret = rmi_read_block(f34->fn->rmi_dev,
> + f34->fn->fd.data_base_addr + f34->v7.off.flash_cmd,
> + &command,
> + sizeof(command));
> + if (ret < 0) {
> + dev_err(&f34->fn->dev, "%s: Failed to read flash command\n",
> + __func__);
> + return ret;
> + }
> +
> + f34->v7.command = command;
> +
> + return 0;
> +}
> +
> +static int rmi_f34v7_wait_for_idle(struct f34_data *f34, int timeout_ms)
> +{
> + int count = 0;
> + int timeout_count = ((timeout_ms * 1000) / MAX_SLEEP_TIME_US) + 1;
> +
> + do {
> + usleep_range(MIN_SLEEP_TIME_US, MAX_SLEEP_TIME_US);
> +
> + count++;
> +
> + rmi_f34v7_read_flash_status(f34);
> +
> + if ((f34->v7.command == v7_CMD_IDLE) && (f34->v7.flash_status == 0x00)) {
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
> + "Idle status detected\n");
> + return 0;
> + }
> + } while (count < timeout_count);
> +
> + dev_err(&f34->fn->dev, "%s: Timed out waiting for idle status\n", __func__);
> +
> + return -ETIMEDOUT;
> +}
> +
> +static int rmi_f34v7_write_command_single_transaction(struct f34_data *f34,
> + unsigned char cmd)
> +{
> + int ret;
> + unsigned char base;
> + struct f34v7_data_1_5 data_1_5;
> +
> + base = f34->fn->fd.data_base_addr;
> +
> + memset(data_1_5.data, 0x00, sizeof(data_1_5.data));
> +
> + switch (cmd) {
> + case v7_CMD_ERASE_ALL:
> + data_1_5.partition_id = CORE_CODE_PARTITION;
> + data_1_5.command = CMD_V7_ERASE_AP;
> + break;
> + case v7_CMD_ERASE_UI_FIRMWARE:
> + data_1_5.partition_id = CORE_CODE_PARTITION;
> + data_1_5.command = CMD_V7_ERASE;
> + break;
> + case v7_CMD_ERASE_BL_CONFIG:
> + data_1_5.partition_id = GLOBAL_PARAMETERS_PARTITION;
> + data_1_5.command = CMD_V7_ERASE;
> + break;
> + case v7_CMD_ERASE_UI_CONFIG:
> + data_1_5.partition_id = CORE_CONFIG_PARTITION;
> + data_1_5.command = CMD_V7_ERASE;
> + break;
> + case v7_CMD_ERASE_DISP_CONFIG:
> + data_1_5.partition_id = DISPLAY_CONFIG_PARTITION;
> + data_1_5.command = CMD_V7_ERASE;
> + break;
> + case v7_CMD_ERASE_FLASH_CONFIG:
> + data_1_5.partition_id = FLASH_CONFIG_PARTITION;
> + data_1_5.command = CMD_V7_ERASE;
> + break;
> + case v7_CMD_ERASE_GUEST_CODE:
> + data_1_5.partition_id = GUEST_CODE_PARTITION;
> + data_1_5.command = CMD_V7_ERASE;
> + break;
> + case v7_CMD_ENABLE_FLASH_PROG:
> + data_1_5.partition_id = BOOTLOADER_PARTITION;
> + data_1_5.command = CMD_V7_ENTER_BL;
> + break;
> + };
> +
> + data_1_5.payload_0 = f34->bootloader_id[0];
> + data_1_5.payload_1 = f34->bootloader_id[1];
> +
> + ret = rmi_write_block(f34->fn->rmi_dev,
> + base + f34->v7.off.partition_id,
> + data_1_5.data,
> + sizeof(data_1_5.data));
> + if (ret < 0) {
> + dev_err(&f34->fn->dev, "%s: Failed to write single transaction command\n",
> + __func__);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int rmi_f34v7_write_command(struct f34_data *f34, unsigned char cmd)
> +{
> + int ret;
> + unsigned char base;
> + unsigned char command;
> +
> + base = f34->fn->fd.data_base_addr;
> +
> + switch (cmd) {
> + case v7_CMD_WRITE_FW:
> + case v7_CMD_WRITE_CONFIG:
> + case v7_CMD_WRITE_GUEST_CODE:
> + command = CMD_V7_WRITE;
> + break;
> + case v7_CMD_READ_CONFIG:
> + command = CMD_V7_READ;
> + break;
> + case v7_CMD_ERASE_ALL:
> + command = CMD_V7_ERASE_AP;
> + break;
> + case v7_CMD_ERASE_UI_FIRMWARE:
> + case v7_CMD_ERASE_BL_CONFIG:
> + case v7_CMD_ERASE_UI_CONFIG:
> + case v7_CMD_ERASE_DISP_CONFIG:
> + case v7_CMD_ERASE_FLASH_CONFIG:
> + case v7_CMD_ERASE_GUEST_CODE:
> + command = CMD_V7_ERASE;
> + break;
> + case v7_CMD_ENABLE_FLASH_PROG:
> + command = CMD_V7_ENTER_BL;
> + break;
> + default:
> + dev_err(&f34->fn->dev, "%s: Invalid command 0x%02x\n",
> + __func__, cmd);
> + return -EINVAL;
> + };
> +
> + f34->v7.command = command;
> +
> + switch (cmd) {
> + case v7_CMD_ERASE_ALL:
> + case v7_CMD_ERASE_UI_FIRMWARE:
> + case v7_CMD_ERASE_BL_CONFIG:
> + case v7_CMD_ERASE_UI_CONFIG:
> + case v7_CMD_ERASE_DISP_CONFIG:
> + case v7_CMD_ERASE_FLASH_CONFIG:
> + case v7_CMD_ERASE_GUEST_CODE:
> + case v7_CMD_ENABLE_FLASH_PROG:
> + ret = rmi_f34v7_write_command_single_transaction(f34, cmd);
> + if (ret < 0)
> + return ret;
> + else
> + return 0;
> + default:
> + break;
> + };
> +
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: writing cmd %02X\n",
> + __func__, command);
> +
> + ret = rmi_write_block(f34->fn->rmi_dev,
> + base + f34->v7.off.flash_cmd,
> + &command,
> + sizeof(command));
> + if (ret < 0) {
> + dev_err(&f34->fn->dev, "%s: Failed to write flash command\n",
> + __func__);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int rmi_f34v7_write_partition_id(struct f34_data *f34,
> + unsigned char cmd)
> +{
> + int ret;
> + unsigned char base;
> + unsigned char partition;
> +
> + base = f34->fn->fd.data_base_addr;
> +
> + switch (cmd) {
> + case v7_CMD_WRITE_FW:
> + partition = CORE_CODE_PARTITION;
> + break;
> + case v7_CMD_WRITE_CONFIG:
> + case v7_CMD_READ_CONFIG:
> + if (f34->v7.config_area == v7_UI_CONFIG_AREA)
> + partition = CORE_CONFIG_PARTITION;
> + else if (f34->v7.config_area == v7_DP_CONFIG_AREA)
> + partition = DISPLAY_CONFIG_PARTITION;
> + else if (f34->v7.config_area == v7_PM_CONFIG_AREA)
> + partition = GUEST_SERIALIZATION_PARTITION;
> + else if (f34->v7.config_area == v7_BL_CONFIG_AREA)
> + partition = GLOBAL_PARAMETERS_PARTITION;
> + else if (f34->v7.config_area == v7_FLASH_CONFIG_AREA)
> + partition = FLASH_CONFIG_PARTITION;
> + break;
> + case v7_CMD_WRITE_GUEST_CODE:
> + partition = GUEST_CODE_PARTITION;
> + break;
> + case v7_CMD_ERASE_ALL:
> + partition = CORE_CODE_PARTITION;
> + break;
> + case v7_CMD_ERASE_BL_CONFIG:
> + partition = GLOBAL_PARAMETERS_PARTITION;
> + break;
> + case v7_CMD_ERASE_UI_CONFIG:
> + partition = CORE_CONFIG_PARTITION;
> + break;
> + case v7_CMD_ERASE_DISP_CONFIG:
> + partition = DISPLAY_CONFIG_PARTITION;
> + break;
> + case v7_CMD_ERASE_FLASH_CONFIG:
> + partition = FLASH_CONFIG_PARTITION;
> + break;
> + case v7_CMD_ERASE_GUEST_CODE:
> + partition = GUEST_CODE_PARTITION;
> + break;
> + case v7_CMD_ENABLE_FLASH_PROG:
> + partition = BOOTLOADER_PARTITION;
> + break;
> + default:
> + dev_err(&f34->fn->dev, "%s: Invalid command 0x%02x\n",
> + __func__, cmd);
> + return -EINVAL;
> + };
> +
> + ret = rmi_write_block(f34->fn->rmi_dev,
> + base + f34->v7.off.partition_id,
> + &partition,
> + sizeof(partition));
> + if (ret < 0) {
> + dev_err(&f34->fn->dev, "%s: Failed to write partition ID\n",
> + __func__);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int rmi_f34v7_read_f34v7_partition_table(struct f34_data *f34)
> +{
> + int ret;
> + unsigned char base;
> + unsigned char length[2];
> + unsigned short block_number = 0;
> +
> + base = f34->fn->fd.data_base_addr;
> +
> + f34->v7.config_area = v7_FLASH_CONFIG_AREA;
> +
> + ret = rmi_f34v7_write_partition_id(f34, v7_CMD_READ_CONFIG);
> + if (ret < 0)
> + return ret;
> +
> + ret = rmi_write_block(f34->fn->rmi_dev,
> + base + f34->v7.off.block_number,
> + (unsigned char *)&block_number,
> + sizeof(block_number));
> + if (ret < 0) {
> + dev_err(&f34->fn->dev, "%s: Failed to write block number\n",
> + __func__);
> + return ret;
> + }
> +
> + length[0] = (unsigned char)(f34->v7.flash_config_length & MASK_8BIT);
> + length[1] = (unsigned char)(f34->v7.flash_config_length >> 8);
> +
> + ret = rmi_write_block(f34->fn->rmi_dev,
> + base + f34->v7.off.transfer_length,
> + length,
> + sizeof(length));
> + if (ret < 0) {
> + dev_err(&f34->fn->dev, "%s: Failed to write transfer length\n",
> + __func__);
> + return ret;
> + }
> +
> + ret = rmi_f34v7_write_command(f34, v7_CMD_READ_CONFIG);
> + if (ret < 0) {
> + dev_err(&f34->fn->dev, "%s: Failed to write command\n",
> + __func__);
> + return ret;
> + }
> +
> + ret = rmi_f34v7_wait_for_idle(f34, WRITE_WAIT_MS);
> + if (ret < 0) {
> + dev_err(&f34->fn->dev, "%s: Failed to wait for idle status\n",
> + __func__);
> + return ret;
> + }
> +
> + ret = rmi_read_block(f34->fn->rmi_dev,
> + base + f34->v7.off.payload,
> + f34->v7.read_config_buf,
> + f34->v7.partition_table_bytes);
> + if (ret < 0) {
> + dev_err(&f34->fn->dev, "%s: Failed to read block data\n",
> + __func__);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void rmi_f34v7_parse_partition_table(struct f34_data *f34,
> + const unsigned char *partition_table,
> + struct block_count *blkcount, struct physical_address *phyaddr)
> +{
> + unsigned char ii;
> + unsigned char index;
> + unsigned short partition_length;
> + unsigned short physical_address;
> + struct partition_table *ptable;
> +
> + for (ii = 0; ii < f34->v7.partitions; ii++) {
> + index = ii * 8 + 2;
> + ptable = (struct partition_table *)&partition_table[index];
> + partition_length = ptable->partition_length_15_8 << 8 |
> + ptable->partition_length_7_0;
> + physical_address = ptable->start_physical_address_15_8 << 8 |
> + ptable->start_physical_address_7_0;
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
> + "%s: Partition entry %d: %*ph\n",
> + __func__, ii, sizeof(struct partition_table), ptable);
> + switch (ptable->partition_id) {
> + case CORE_CODE_PARTITION:
> + blkcount->ui_firmware = partition_length;
> + phyaddr->ui_firmware = physical_address;
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
> + "%s: Core code block count: %d\n",
> + __func__, blkcount->ui_firmware);
> + break;
> + case CORE_CONFIG_PARTITION:
> + blkcount->ui_config = partition_length;
> + phyaddr->ui_config = physical_address;
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
> + "%s: Core config block count: %d\n",
> + __func__, blkcount->ui_config);
> + break;
> + case DISPLAY_CONFIG_PARTITION:
> + blkcount->dp_config = partition_length;
> + phyaddr->dp_config = physical_address;
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
> + "%s: Display config block count: %d\n",
> + __func__, blkcount->dp_config);
> + break;
> + case FLASH_CONFIG_PARTITION:
> + blkcount->fl_config = partition_length;
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
> + "%s: Flash config block count: %d\n",
> + __func__, blkcount->fl_config);
> + break;
> + case GUEST_CODE_PARTITION:
> + blkcount->guest_code = partition_length;
> + phyaddr->guest_code = physical_address;
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
> + "%s: Guest code block count: %d\n",
> + __func__, blkcount->guest_code);
> + break;
> + case GUEST_SERIALIZATION_PARTITION:
> + blkcount->pm_config = partition_length;
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
> + "%s: Guest serialization block count: %d\n",
> + __func__, blkcount->pm_config);
> + break;
> + case GLOBAL_PARAMETERS_PARTITION:
> + blkcount->bl_config = partition_length;
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
> + "%s: Global parameters block count: %d\n",
> + __func__, blkcount->bl_config);
> + break;
> + case DEVICE_CONFIG_PARTITION:
> + blkcount->lockdown = partition_length;
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
> + "%s: Device config block count: %d\n",
> + __func__, blkcount->lockdown);
> + break;
> + };
> + }
> +}
> +
> +static int rmi_f34v7_read_queries_bl_version(struct f34_data *f34)
> +{
> + int ret;
> + unsigned char base;
> + unsigned char offset;
> + struct f34v7_query_0 query_0;
> + struct f34v7_query_1_7 query_1_7;
> +
> + base = f34->fn->fd.query_base_addr;
> +
> + ret = rmi_read_block(f34->fn->rmi_dev,
> + base,
> + query_0.data,
> + sizeof(query_0.data));
> + if (ret < 0) {
> + dev_err(&f34->fn->dev, "%s: Failed to read query 0\n", __func__);
> + return ret;
> + }
> +
> + offset = query_0.subpacket_1_size + 1;
> +
> + ret = rmi_read_block(f34->fn->rmi_dev,
> + base + offset,
> + query_1_7.data,
> + sizeof(query_1_7.data));
> + if (ret < 0) {
> + dev_err(&f34->fn->dev, "%s: Failed to read queries 1 to 7\n",
> + __func__);
> + return ret;
> + }
> +
> + f34->bootloader_id[0] = query_1_7.bl_minor_revision;
> + f34->bootloader_id[1] = query_1_7.bl_major_revision;
> +
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "Bootloader V%d.%d\n",
> + f34->bootloader_id[1], f34->bootloader_id[0]);
> +
> + return 0;
> +}
> +
> +static int rmi_f34v7_read_queries(struct f34_data *f34)
> +{
> + int ret;
> + unsigned char ii;
> + unsigned char base;
> + unsigned char index;
> + unsigned char offset;
> + unsigned char *ptable;
> + struct f34v7_query_0 query_0;
> + struct f34v7_query_1_7 query_1_7;
> +
> + base = f34->fn->fd.query_base_addr;
> +
> + ret = rmi_read_block(f34->fn->rmi_dev,
> + base,
> + query_0.data,
> + sizeof(query_0.data));
> + if (ret < 0) {
> + dev_err(&f34->fn->dev, "%s: Failed to read query 0\n", __func__);
> + return ret;
> + }
> +
> + offset = query_0.subpacket_1_size + 1;
> +
> + ret = rmi_read_block(f34->fn->rmi_dev,
> + base + offset,
> + query_1_7.data,
> + sizeof(query_1_7.data));
> + if (ret < 0) {
> + dev_err(&f34->fn->dev, "%s: Failed to read queries 1 to 7\n",
> + __func__);
> + return ret;
> + }
> +
> + f34->bootloader_id[0] = query_1_7.bl_minor_revision;
> + f34->bootloader_id[1] = query_1_7.bl_major_revision;
> +
> + f34->v7.block_size = query_1_7.block_size_15_8 << 8 |
> + query_1_7.block_size_7_0;
> +
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: f34->v7.block_size = %d\n",
> + __func__, f34->v7.block_size);
> +
> + f34->v7.flash_config_length = query_1_7.flash_config_length_15_8 << 8 |
> + query_1_7.flash_config_length_7_0;
> +
> + f34->v7.payload_length = query_1_7.payload_length_15_8 << 8 |
> + query_1_7.payload_length_7_0;
> +
> + f34->v7.off.flash_status = V7_FLASH_STATUS_OFFSET;
> + f34->v7.off.partition_id = V7_PARTITION_ID_OFFSET;
> + f34->v7.off.block_number = V7_BLOCK_NUMBER_OFFSET;
> + f34->v7.off.transfer_length = V7_TRANSFER_LENGTH_OFFSET;
> + f34->v7.off.flash_cmd = V7_COMMAND_OFFSET;
> + f34->v7.off.payload = V7_PAYLOAD_OFFSET;
> +
> + f34->v7.flash_properties.has_disp_config = query_1_7.has_display_config;
> + f34->v7.flash_properties.has_perm_config = query_1_7.has_guest_serialization;
> + f34->v7.flash_properties.has_bl_config = query_1_7.has_global_parameters;
> +
> + f34->v7.has_guest_code = query_1_7.has_guest_code;
> + f34->v7.flash_properties.has_config_id = query_0.has_config_id;
> +
> + if (f34->v7.flash_properties.has_config_id) {
> + char f34_ctrl[4];
> +
> + ret = rmi_read_block(f34->fn->rmi_dev,
> + f34->fn->fd.control_base_addr,
> + f34_ctrl,
> + sizeof(f34_ctrl));
> + if (ret)
> + return ret;
> +
> + snprintf(f34->configuration_id, sizeof(f34->configuration_id),
> + "%02x%02x%02x%02x",
> + f34_ctrl[0], f34_ctrl[1], f34_ctrl[2], f34_ctrl[3]);
> +
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "Configuration ID: %s\n",
> + f34->configuration_id);
> + }
> +
> + index = sizeof(query_1_7.data) - V7_PARTITION_SUPPORT_BYTES;
> +
> + f34->v7.partitions = 0;
> + for (offset = 0; offset < V7_PARTITION_SUPPORT_BYTES; offset++) {
> + for (ii = 0; ii < 8; ii++) {
> + if (query_1_7.data[index + offset] & (1 << ii))
> + f34->v7.partitions++;
> + }
> +
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
> + "%s: Supported partitions: 0x%02x\n",
> + __func__, query_1_7.data[index + offset]);
> + }
> +
> + f34->v7.partition_table_bytes = f34->v7.partitions * 8 + 2;
> +
> + f34->v7.read_config_buf = devm_kzalloc(&f34->fn->dev,
> + f34->v7.partition_table_bytes,
> + GFP_KERNEL);
> + if (!f34->v7.read_config_buf) {
> + f34->v7.read_config_buf_size = 0;
> + return -ENOMEM;
> + }
> +
> + f34->v7.read_config_buf_size = f34->v7.partition_table_bytes;
> + ptable = f34->v7.read_config_buf;
> +
> + ret = rmi_f34v7_read_f34v7_partition_table(f34);
> + if (ret < 0) {
> + dev_err(&f34->fn->dev, "%s: Failed to read partition table\n",
> + __func__);
> + return ret;
> + }
> +
> + rmi_f34v7_parse_partition_table(f34, ptable, &f34->v7.blkcount, &f34->v7.phyaddr);
> +
> + return 0;
> +}
> +
> +static int rmi_f34v7_check_ui_firmware_size(struct f34_data *f34)
> +{
> + unsigned short block_count;
> +
> + block_count = f34->v7.img.ui_firmware.size / f34->v7.block_size;
> +
> + if (block_count != f34->v7.blkcount.ui_firmware) {
> + dev_err(&f34->fn->dev, "%s: UI firmware size mismatch:"
> + "block_count=%d,f34->v7.blkcount.ui_firmware=%d\n",
> + __func__, block_count, f34->v7.blkcount.ui_firmware);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int rmi_f34v7_check_ui_configuration_size(struct f34_data *f34)
> +{
> + unsigned short block_count;
> +
> + block_count = f34->v7.img.ui_config.size / f34->v7.block_size;
> +
> + if (block_count != f34->v7.blkcount.ui_config) {
> + dev_err(&f34->fn->dev, "%s: UI configuration size mismatch\n",
> + __func__);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int rmi_f34v7_check_dp_configuration_size(struct f34_data *f34)
> +{
> + unsigned short block_count;
> +
> + block_count = f34->v7.img.dp_config.size / f34->v7.block_size;
> +
> + if (block_count != f34->v7.blkcount.dp_config) {
> + dev_err(&f34->fn->dev, "%s: Display configuration size mismatch\n",
> + __func__);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int rmi_f34v7_check_guest_code_size(struct f34_data *f34)
> +{
> + unsigned short block_count;
> +
> + block_count = f34->v7.img.guest_code.size / f34->v7.block_size;
> + if (block_count != f34->v7.blkcount.guest_code) {
> + dev_err(&f34->fn->dev, "%s: Guest code size mismatch\n",
> + __func__);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int rmi_f34v7_check_bl_configuration_size(struct f34_data *f34)
> +{
> + unsigned short block_count;
> +
> + block_count = f34->v7.img.bl_config.size / f34->v7.block_size;
> +
> + if (block_count != f34->v7.blkcount.bl_config) {
> + dev_err(&f34->fn->dev, "%s: Bootloader config size mismatch\n",
> + __func__);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int rmi_f34v7_erase_configuration(struct f34_data *f34)
> +{
> + int ret;
> +
> + dev_info(&f34->fn->dev, "Erasing config...\n");
> +
> + switch (f34->v7.config_area) {
> + case v7_UI_CONFIG_AREA:
> + ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_UI_CONFIG);
> + if (ret < 0)
> + return ret;
> + break;
> + case v7_DP_CONFIG_AREA:
> + ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_DISP_CONFIG);
> + if (ret < 0)
> + return ret;
> + break;
> + case v7_BL_CONFIG_AREA:
> + ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_BL_CONFIG);
> + if (ret < 0)
> + return ret;
> + break;
> + }
> +
> + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS);
> + if (ret < 0)
> + return ret;
> +
> + return ret;
> +}
> +
> +static int rmi_f34v7_erase_guest_code(struct f34_data *f34)
> +{
> + int ret;
> +
> + dev_info(&f34->fn->dev, "Erasing guest code...\n");
> +
> + ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_GUEST_CODE);
> + if (ret < 0)
> + return ret;
> +
> + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS);
> + if (ret < 0)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int rmi_f34v7_erase_all(struct f34_data *f34)
> +{
> + int ret;
> +
> + dev_info(&f34->fn->dev, "Erasing firmware...\n");
> +
> + ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_UI_FIRMWARE);
> + if (ret < 0)
> + return ret;
> +
> + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS);
> + if (ret < 0)
> + return ret;
> +
> + f34->v7.config_area = v7_UI_CONFIG_AREA;
> + ret = rmi_f34v7_erase_configuration(f34);
> + if (ret < 0)
> + return ret;
> +
> + if (f34->v7.flash_properties.has_disp_config) {
> + f34->v7.config_area = v7_DP_CONFIG_AREA;
> + ret = rmi_f34v7_erase_configuration(f34);
> + if (ret < 0)
> + return ret;
> + }
> +
> + if (f34->v7.new_partition_table && f34->v7.has_guest_code) {
> + ret = rmi_f34v7_erase_guest_code(f34);
> + if (ret < 0)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int rmi_f34v7_read_f34v7_blocks(struct f34_data *f34,
> + unsigned short block_cnt,
> + unsigned char command)
> +{
> + int ret;
> + unsigned char base;
> + unsigned char length[2];
> + unsigned short transfer;
> + unsigned short max_transfer;
> + unsigned short remaining = block_cnt;
> + unsigned short block_number = 0;
> + unsigned short index = 0;
> +
> + base = f34->fn->fd.data_base_addr;
> +
> + ret = rmi_f34v7_write_partition_id(f34, command);
> + if (ret < 0)
> + return ret;
> +
> + ret = rmi_write_block(f34->fn->rmi_dev,
> + base + f34->v7.off.block_number,
> + (unsigned char *)&block_number,
> + sizeof(block_number));
> + if (ret < 0) {
> + dev_err(&f34->fn->dev, "%s: Failed to write block number\n",
> + __func__);
> + return ret;
> + }
> +
> + if (f34->v7.payload_length > (PAGE_SIZE / f34->v7.block_size))
> + max_transfer = PAGE_SIZE / f34->v7.block_size;
> + else
> + max_transfer = f34->v7.payload_length;
> +
> + do {
> + if (remaining / max_transfer)
> + transfer = max_transfer;
> + else
> + transfer = remaining;
> +
> + length[0] = (unsigned char)(transfer & MASK_8BIT);
> + length[1] = (unsigned char)(transfer >> 8);
> +
> + ret = rmi_write_block(f34->fn->rmi_dev,
> + base + f34->v7.off.transfer_length,
> + length,
> + sizeof(length));
> + if (ret < 0) {
> + dev_err(&f34->fn->dev,
> + "%s: Failed to write transfer length "
> + "(%d blocks remaining)\n",
> + __func__, remaining);
> + return ret;
> + }
> +
> + ret = rmi_f34v7_write_command(f34, command);
> + if (ret < 0) {
> + dev_err(&f34->fn->dev, "%s: Failed to write command "
> + "(%d blocks remaining)\n",
> + __func__, remaining);
> + return ret;
> + }
> +
> + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS);
> + if (ret < 0) {
> + dev_err(&f34->fn->dev, "%s: Failed to wait for idle status "
> + "(%d blocks remaining)\n",
> + __func__, remaining);
> + return ret;
> + }
> +
> + ret = rmi_read_block(f34->fn->rmi_dev,
> + base + f34->v7.off.payload,
> + &f34->v7.read_config_buf[index],
> + transfer * f34->v7.block_size);
> + if (ret < 0) {
> + dev_err(&f34->fn->dev, "%s: Failed to read block data "
> + "(%d blocks remaining)\n",
> + __func__, remaining);
> + return ret;
> + }
> +
> + index += (transfer * f34->v7.block_size);
> + remaining -= transfer;
> + } while (remaining);
> +
> + return 0;
> +}
> +
> +static int rmi_f34v7_write_f34v7_blocks(struct f34_data *f34,
> + unsigned char *block_ptr,
> + unsigned short block_cnt, unsigned char command)
> +{
> + int ret;
> + unsigned char base;
> + unsigned char length[2];
> + unsigned short transfer;
> + unsigned short max_transfer;
> + unsigned short remaining = block_cnt;
> + unsigned short block_number = 0;
> +
> + base = f34->fn->fd.data_base_addr;
> +
> + ret = rmi_f34v7_write_partition_id(f34, command);
> + if (ret < 0)
> + return ret;
> +
> + ret = rmi_write_block(f34->fn->rmi_dev,
> + base + f34->v7.off.block_number,
> + (unsigned char *)&block_number,
> + sizeof(block_number));
> + if (ret < 0) {
> + dev_err(&f34->fn->dev, "%s: Failed to write block number\n",
> + __func__);
> + return ret;
> + }
> +
> + if (f34->v7.payload_length > (PAGE_SIZE / f34->v7.block_size))
> + max_transfer = PAGE_SIZE / f34->v7.block_size;
> + else
> + max_transfer = f34->v7.payload_length;
> +
> + do {
> + if (remaining / max_transfer)
> + transfer = max_transfer;
> + else
> + transfer = remaining;
> +
> + length[0] = (unsigned char)(transfer & MASK_8BIT);
> + length[1] = (unsigned char)(transfer >> 8);
> +
> + ret = rmi_write_block(f34->fn->rmi_dev,
> + base + f34->v7.off.transfer_length,
> + length,
> + sizeof(length));
> + if (ret < 0) {
> + dev_err(&f34->fn->dev,
> + "%s: Failed to write transfer length (%d blocks remaining)\n",
> + __func__, remaining);
> + return ret;
> + }
> +
> + ret = rmi_f34v7_write_command(f34, command);
> + if (ret < 0) {
> + dev_err(&f34->fn->dev,
> + "%s: Failed to write command (%d blocks remaining)\n",
> + __func__, remaining);
> + return ret;
> + }
> +
> + ret = rmi_write_block(f34->fn->rmi_dev,
> + base + f34->v7.off.payload,
> + block_ptr,
> + transfer * f34->v7.block_size);
> + if (ret < 0) {
> + dev_err(&f34->fn->dev,
> + "%s: Failed to write block data (%d blocks remaining)\n",
> + __func__, remaining);
> + return ret;
> + }
> +
> + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS);
> + if (ret < 0) {
> + dev_err(&f34->fn->dev,
> + "%s: Failed to wait for idle status (%d blocks remaining)\n",
> + __func__, remaining);
> + return ret;
> + }
> +
> + block_ptr += (transfer * f34->v7.block_size);
> + remaining -= transfer;
> + } while (remaining);
> +
> + return 0;
> +}
> +
> +static int rmi_f34v7_write_f34_blocks(struct f34_data *f34,
> + unsigned char *block_ptr,
> + unsigned short block_cnt, unsigned char cmd)
> +{
> + int ret;
> +
> + ret = rmi_f34v7_write_f34v7_blocks(f34, block_ptr, block_cnt, cmd);
> +
> + return ret;
> +}
> +
> +static int rmi_f34v7_write_configuration(struct f34_data *f34)
> +{
> + return rmi_f34v7_write_f34_blocks(f34, (unsigned char *)f34->v7.config_data,
> + f34->v7.config_block_count, v7_CMD_WRITE_CONFIG);
> +}
> +
> +static int rmi_f34v7_write_ui_configuration(struct f34_data *f34)
> +{
> + f34->v7.config_area = v7_UI_CONFIG_AREA;
> + f34->v7.config_data = f34->v7.img.ui_config.data;
> + f34->v7.config_size = f34->v7.img.ui_config.size;
> + f34->v7.config_block_count = f34->v7.config_size / f34->v7.block_size;
> +
> + return rmi_f34v7_write_configuration(f34);
> +}
> +
> +static int rmi_f34v7_write_dp_configuration(struct f34_data *f34)
> +{
> + f34->v7.config_area = v7_DP_CONFIG_AREA;
> + f34->v7.config_data = f34->v7.img.dp_config.data;
> + f34->v7.config_size = f34->v7.img.dp_config.size;
> + f34->v7.config_block_count = f34->v7.config_size / f34->v7.block_size;
> +
> + return rmi_f34v7_write_configuration(f34);
> +}
> +
> +static int rmi_f34v7_write_guest_code(struct f34_data *f34)
> +{
> + unsigned short guest_code_block_count;
> + int ret;
> +
> + guest_code_block_count = f34->v7.img.guest_code.size / f34->v7.block_size;
> +
> + ret = rmi_f34v7_write_f34_blocks(f34, (unsigned char *)f34->v7.img.guest_code.data,
> + guest_code_block_count, v7_CMD_WRITE_GUEST_CODE);
> + if (ret < 0)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int rmi_f34v7_write_flash_configuration(struct f34_data *f34)
> +{
> + int ret;
> +
> + f34->v7.config_area = v7_FLASH_CONFIG_AREA;
> + f34->v7.config_data = f34->v7.img.fl_config.data;
> + f34->v7.config_size = f34->v7.img.fl_config.size;
> + f34->v7.config_block_count = f34->v7.config_size / f34->v7.block_size;
> +
> + if (f34->v7.config_block_count != f34->v7.blkcount.fl_config) {
> + dev_err(&f34->fn->dev, "%s: Flash configuration size mismatch\n",
> + __func__);
> + return -EINVAL;
> + }
> +
> + ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_FLASH_CONFIG);
> + if (ret < 0)
> + return ret;
> +
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
> + "%s: Erase flash configuration command written\n", __func__);
> +
> + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS);
> + if (ret < 0)
> + return ret;
> +
> + ret = rmi_f34v7_write_configuration(f34);
> + if (ret < 0)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int rmi_f34v7_write_partition_table(struct f34_data *f34)
> +{
> + unsigned short block_count;
> + int ret;
> +
> + block_count = f34->v7.blkcount.bl_config;
> + f34->v7.config_area = v7_BL_CONFIG_AREA;
> + f34->v7.config_size = f34->v7.block_size * block_count;
> + devm_kfree(&f34->fn->dev, f34->v7.read_config_buf);
> + f34->v7.read_config_buf = devm_kzalloc(&f34->fn->dev, f34->v7.config_size,
> + GFP_KERNEL);
> + if (!f34->v7.read_config_buf) {
> + f34->v7.read_config_buf_size = 0;
> + return -ENOMEM;
> + }
> +
> + f34->v7.read_config_buf_size = f34->v7.config_size;
> +
> + ret = rmi_f34v7_read_f34v7_blocks(f34, block_count, v7_CMD_READ_CONFIG);
> + if (ret < 0)
> + return ret;
> +
> + ret = rmi_f34v7_erase_configuration(f34);
> + if (ret < 0)
> + return ret;
> +
> + ret = rmi_f34v7_write_flash_configuration(f34);
> + if (ret < 0)
> + return ret;
> +
> + f34->v7.config_area = v7_BL_CONFIG_AREA;
> + f34->v7.config_data = f34->v7.read_config_buf;
> + f34->v7.config_size = f34->v7.img.bl_config.size;
> + f34->v7.config_block_count = f34->v7.config_size / f34->v7.block_size;
> +
> + ret = rmi_f34v7_write_configuration(f34);
> + if (ret < 0)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int rmi_f34v7_write_firmware(struct f34_data *f34)
> +{
> + unsigned short firmware_block_count;
> +
> + firmware_block_count = f34->v7.img.ui_firmware.size / f34->v7.block_size;
> +
> + return rmi_f34v7_write_f34_blocks(f34, (unsigned char *)f34->v7.img.ui_firmware.data,
> + firmware_block_count, v7_CMD_WRITE_FW);
> +}
> +
> +static void rmi_f34v7_compare_partition_tables(struct f34_data *f34)
> +{
> + if (f34->v7.phyaddr.ui_firmware != f34->v7.img.phyaddr.ui_firmware) {
> + f34->v7.new_partition_table = true;
> + return;
> + }
> +
> + if (f34->v7.phyaddr.ui_config != f34->v7.img.phyaddr.ui_config) {
> + f34->v7.new_partition_table = true;
> + return;
> + }
> +
> + if (f34->v7.flash_properties.has_disp_config) {
> + if (f34->v7.phyaddr.dp_config != f34->v7.img.phyaddr.dp_config) {
> + f34->v7.new_partition_table = true;
> + return;
> + }
> + }
> +
> + if (f34->v7.flash_properties.has_disp_config) {
> + if (f34->v7.phyaddr.dp_config != f34->v7.img.phyaddr.dp_config) {
> + f34->v7.new_partition_table = true;
> + return;
> + }
> + }
> +
> + if (f34->v7.has_guest_code) {
> + if (f34->v7.phyaddr.guest_code != f34->v7.img.phyaddr.guest_code) {
> + f34->v7.new_partition_table = true;
> + return;
> + }
> + }
> +
> + f34->v7.new_partition_table = false;
> +}
> +
> +static unsigned int le_to_uint(const unsigned char *ptr)
> +{
> + return (unsigned int)ptr[0] +
> + (unsigned int)ptr[1] * 0x100 +
> + (unsigned int)ptr[2] * 0x10000 +
> + (unsigned int)ptr[3] * 0x1000000;
> +}
> +
> +static void rmi_f34v7_parse_image_header_10_bl_container(struct f34_data *f34,
> + const unsigned char *image)
> +{
> + unsigned char ii;
> + unsigned char num_of_containers;
> + unsigned int addr;
> + unsigned int container_id;
> + unsigned int length;
> + const unsigned char *content;
> + struct container_descriptor *descriptor;
> +
> + num_of_containers = (f34->v7.img.bootloader.size - 4) / 4;
> +
> + for (ii = 1; ii <= num_of_containers; ii++) {
> + addr = le_to_uint(f34->v7.img.bootloader.data + (ii * 4));
> + descriptor = (struct container_descriptor *)(image + addr);
> + container_id = descriptor->container_id[0] |
> + descriptor->container_id[1] << 8;
> + content = image + le_to_uint(descriptor->content_address);
> + length = le_to_uint(descriptor->content_length);
> + switch (container_id) {
> + case BL_CONFIG_CONTAINER:
> + case GLOBAL_PARAMETERS_CONTAINER:
> + f34->v7.img.bl_config.data = content;
> + f34->v7.img.bl_config.size = length;
> + break;
> + case BL_LOCKDOWN_INFO_CONTAINER:
> + case DEVICE_CONFIG_CONTAINER:
> + f34->v7.img.lockdown.data = content;
> + f34->v7.img.lockdown.size = length;
> + break;
> + default:
> + break;
> + };
> + }
> +}
> +
> +static void rmi_f34v7_parse_image_header_10(struct f34_data *f34)
> +{
> + unsigned char ii;
> + unsigned char num_of_containers;
> + unsigned int addr;
> + unsigned int offset;
> + unsigned int container_id;
> + unsigned int length;
> + const unsigned char *image;
> + const unsigned char *content;
> + struct container_descriptor *descriptor;
> + struct image_header_10 *header;
> +
> + image = f34->v7.image;
> + header = (struct image_header_10 *)image;
> +
> + f34->v7.img.checksum = le_to_uint(header->checksum);
> +
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: f34->v7.img.checksum=%d\n",
> + __func__, f34->v7.img.checksum);
> +
> + /* address of top level container */
> + offset = le_to_uint(header->top_level_container_start_addr);
> + descriptor = (struct container_descriptor *)(image + offset);
> +
> + /* address of top level container content */
> + offset = le_to_uint(descriptor->content_address);
> + num_of_containers = le_to_uint(descriptor->content_length) / 4;
> +
> + for (ii = 0; ii < num_of_containers; ii++) {
> + addr = le_to_uint(image + offset);
> + offset += 4;
> + descriptor = (struct container_descriptor *)(image + addr);
> + container_id = descriptor->container_id[0] |
> + descriptor->container_id[1] << 8;
> + content = image + le_to_uint(descriptor->content_address);
> + length = le_to_uint(descriptor->content_length);
> +
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
> + "%s: container_id=%d, length=%d\n", __func__,
> + container_id, length);
> +
> + switch (container_id) {
> + case UI_CONTAINER:
> + case CORE_CODE_CONTAINER:
> + f34->v7.img.ui_firmware.data = content;
> + f34->v7.img.ui_firmware.size = length;
> + break;
> + case UI_CONFIG_CONTAINER:
> + case CORE_CONFIG_CONTAINER:
> + f34->v7.img.ui_config.data = content;
> + f34->v7.img.ui_config.size = length;
> + break;
> + case BL_CONTAINER:
> + f34->v7.img.bl_version = *content;
> + f34->v7.img.bootloader.data = content;
> + f34->v7.img.bootloader.size = length;
> + rmi_f34v7_parse_image_header_10_bl_container(f34, image);
> + break;
> + case GUEST_CODE_CONTAINER:
> + f34->v7.img.contains_guest_code = true;
> + f34->v7.img.guest_code.data = content;
> + f34->v7.img.guest_code.size = length;
> + break;
> + case DISPLAY_CONFIG_CONTAINER:
> + f34->v7.img.contains_disp_config = true;
> + f34->v7.img.dp_config.data = content;
> + f34->v7.img.dp_config.size = length;
> + break;
> + case FLASH_CONFIG_CONTAINER:
> + f34->v7.img.contains_flash_config = true;
> + f34->v7.img.fl_config.data = content;
> + f34->v7.img.fl_config.size = length;
> + break;
> + case GENERAL_INFORMATION_CONTAINER:
> + f34->v7.img.contains_firmware_id = true;
> + f34->v7.img.firmware_id = le_to_uint(content + 4);
> + break;
> + default:
> + break;
> + }
> + }
> +}
> +
> +static int rmi_f34v7_parse_image_info(struct f34_data *f34)
> +{
> + struct image_header_10 *header;
> +
> + header = (struct image_header_10 *)f34->v7.image;
> +
> + memset(&f34->v7.img, 0x00, sizeof(f34->v7.img));
> +
> + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
> + "%s: header->major_header_version = %d\n",
> + __func__, header->major_header_version);
> +
> + switch (header->major_header_version) {
> + case IMAGE_HEADER_VERSION_10:
> + rmi_f34v7_parse_image_header_10(f34);
> + break;
> + default:
> + dev_err(&f34->fn->dev, "Unsupported image file format %02X\n",
> + header->major_header_version);
> + return -EINVAL;
> + }
> +
> + if (!f34->v7.img.contains_flash_config) {
> + dev_err(&f34->fn->dev, "%s: No flash config in fw image\n",
> + __func__);
> + return -EINVAL;
> + }
> +
> + rmi_f34v7_parse_partition_table(f34, f34->v7.img.fl_config.data,
> + &f34->v7.img.blkcount, &f34->v7.img.phyaddr);
> +
> + rmi_f34v7_compare_partition_tables(f34);
> +
> + return 0;
> +}
> +
> +int rmi_f34v7_do_reflash(struct f34_data *f34, const struct firmware *fw)
> +{
> + int ret;
> +
> + rmi_f34v7_read_queries_bl_version(f34);
> +
> + f34->v7.image = fw->data;
> +
> + ret = rmi_f34v7_parse_image_info(f34);
> + if (ret < 0)
> + goto fail;
> +
> + if (!f34->v7.new_partition_table) {
> + ret = rmi_f34v7_check_ui_firmware_size(f34);
> + if (ret < 0)
> + goto fail;
> +
> + ret = rmi_f34v7_check_ui_configuration_size(f34);
> + if (ret < 0)
> + goto fail;
> +
> + if (f34->v7.flash_properties.has_disp_config &&
> + f34->v7.img.contains_disp_config) {
> + ret = rmi_f34v7_check_dp_configuration_size(f34);
> + if (ret < 0)
> + goto fail;
> + }
> +
> + if (f34->v7.has_guest_code && f34->v7.img.contains_guest_code) {
> + ret = rmi_f34v7_check_guest_code_size(f34);
> + if (ret < 0)
> + goto fail;
> + }
> + } else {
> + ret = rmi_f34v7_check_bl_configuration_size(f34);
> + if (ret < 0)
> + goto fail;
> + }
> +
> + ret = rmi_f34v7_erase_all(f34);
> + if (ret < 0)
> + goto fail;
> +
> + if (f34->v7.new_partition_table) {
> + ret = rmi_f34v7_write_partition_table(f34);
> + if (ret < 0)
> + goto fail;
> + dev_info(&f34->fn->dev, "%s: Partition table programmed\n",
> + __func__);
> + }
> +
> + dev_info(&f34->fn->dev, "Writing firmware (%d bytes)...\n",
> + f34->v7.img.ui_firmware.size);
> +
> + ret = rmi_f34v7_write_firmware(f34);
> + if (ret < 0)
> + goto fail;
> +
> + dev_info(&f34->fn->dev, "Writing config (%d bytes)...\n",
> + f34->v7.img.ui_config.size);
> +
> + f34->v7.config_area = v7_UI_CONFIG_AREA;
> + ret = rmi_f34v7_write_ui_configuration(f34);
> + if (ret < 0)
> + goto fail;
> +
> + if (f34->v7.flash_properties.has_disp_config &&
> + f34->v7.img.contains_disp_config) {
> + dev_info(&f34->fn->dev, "Writing display config...\n");
> +
> + ret = rmi_f34v7_write_dp_configuration(f34);
> + if (ret < 0)
> + goto fail;
> + }
> +
> + if (f34->v7.new_partition_table) {
> + if (f34->v7.has_guest_code && f34->v7.img.contains_guest_code) {
> + dev_info(&f34->fn->dev, "Writing guest code...\n");
> +
> + ret = rmi_f34v7_write_guest_code(f34);
> + if (ret < 0)
> + goto fail;
> + }
> + }
> +
> +fail:
> + return ret;
> +}
> +
> +static int rmi_f34v7_enter_flash_prog(struct f34_data *f34)
> +{
> + int ret;
> +
> + ret = rmi_f34v7_read_flash_status(f34);
> + if (ret < 0)
> + return ret;
> +
> + if (f34->v7.in_bl_mode)
> + return 0;
> +
> + ret = rmi_f34v7_write_command(f34, v7_CMD_ENABLE_FLASH_PROG);
> + if (ret < 0)
> + return ret;
> +
> + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS);
> + if (ret < 0)
> + return ret;
> +
> + if (!f34->v7.in_bl_mode) {
> + dev_err(&f34->fn->dev, "%s: BL mode not entered\n", __func__);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +int rmi_f34v7_start_reflash(struct f34_data *f34, const struct firmware *fw)
> +{
> + int ret = 0;
> +
> + f34->v7.config_area = v7_UI_CONFIG_AREA;
> + f34->v7.image = fw->data;
> +
> + ret = rmi_f34v7_parse_image_info(f34);
> + if (ret < 0)
> + goto exit;
> +
> + if (!f34->v7.force_update && f34->v7.new_partition_table) {
> + dev_err(&f34->fn->dev, "%s: Partition table mismatch\n",
> + __func__);
> + ret = -EINVAL;
> + goto exit;
> + }
> +
> + dev_info(&f34->fn->dev, "Firmware image OK\n");
> +
> + ret = rmi_f34v7_read_flash_status(f34);
> + if (ret < 0)
> + goto exit;
> +
> + if (f34->v7.in_bl_mode) {
> + dev_info(&f34->fn->dev, "%s: Device in bootloader mode\n",
> + __func__);
> + }
> +
> + rmi_f34v7_enter_flash_prog(f34);
> +
> + return 0;
> +
> +exit:
> + return ret;
> +}
> +
> +int rmi_f34v7_probe(struct f34_data *f34)
> +{
> + int ret;
> +
> + /* Read bootloader version */
> + ret = rmi_read_block(f34->fn->rmi_dev,
> + f34->fn->fd.query_base_addr + BOOTLOADER_ID_OFFSET,
> + f34->bootloader_id,
> + sizeof(f34->bootloader_id));
> + if (ret < 0) {
> + dev_err(&f34->fn->dev, "%s: Failed to read bootloader ID\n",
> + __func__);
> + return ret;
> + }
> +
> + if (f34->bootloader_id[1] == '5') {
> + f34->bl_version = BL_V5;
> + } else if (f34->bootloader_id[1] == '6') {
> + f34->bl_version = BL_V6;
> + } else if (f34->bootloader_id[1] == 7) {
> + f34->bl_version = BL_V7;
> + } else {
> + dev_err(&f34->fn->dev, "%s: Unrecognized bootloader version\n",
> + __func__);
> + return -EINVAL;
> + }
> +
> + memset(&f34->v7.blkcount, 0x00, sizeof(f34->v7.blkcount));
> + memset(&f34->v7.phyaddr, 0x00, sizeof(f34->v7.phyaddr));
> + rmi_f34v7_read_queries(f34);
> +
> + f34->v7.force_update = FORCE_UPDATE;
> + f34->v7.initialized = true;
> + return 0;
> +}
> diff --git a/include/linux/rmi.h b/include/linux/rmi.h
> index a283a67..6baee9f 100644
> --- a/include/linux/rmi.h
> +++ b/include/linux/rmi.h
> @@ -331,7 +331,7 @@ struct rmi_driver_data {
>
> struct rmi_function *f01_container;
> struct rmi_function *f34_container;
> - bool f01_bootloader_mode;
> + bool bootloader_mode;
>
> u32 attn_count;
> int num_of_irq_regs;
> --
> 2.7.4
>