diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-12-18 03:45:00 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-12-18 03:45:00 +0300 |
commit | af79ce47efabba36d1db0902d46a80de7f251411 (patch) | |
tree | 571106db25ed2da54891995aed38407f85e6ac4b /drivers/input/rmi4 | |
parent | c07dee7348e2451bcf2f178bf0e7830268e2c31a (diff) | |
parent | f26e8817b235d8764363bffcc9cbfc61867371f2 (diff) | |
download | linux-af79ce47efabba36d1db0902d46a80de7f251411.tar.xz |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input subsystem updates from Dmitry Torokhov:
- updated support for Synaptics RMI4 devices, including support for
SMBus controllers, firmware update support, sensor tuning, and PS/2
guest support
- ALPS driver now supports tracksticks on SS5 controllers
- i8042 now uses chassis info to skip selftest on Asus laptops as list
of individual models became too unwieldy
- miscellaneous fixes to other drivers
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (67 commits)
Input: imx6ul_tsc - generalize the averaging property
Input: drv260x - use generic device properties
Input: drv260x - use temporary for &client->dev
Input: drv260x - fix input device's parent assignment
Input: synaptics-rmi4 - add support for F34 V7 bootloader
Input: drv260x - fix initializing overdrive voltage
Input: ALPS - fix protcol -> protocol
Input: i8042 - comment #else/#endif of CONFIG_PNP
Input: lpc32xx-keys - fix invalid error handling of a requested irq
Input: synaptics-rmi4 - fix debug for sensor clip
Input: synaptics-rmi4 - store the attn data in the driver
Input: synaptics-rmi4 - allow to add attention data
Input: synaptics-rmi4 - f03 - grab data passed by transport device
Input: synaptics-rmi4 - add support for F03
Input: imx6ul_tsc - convert int to u32
Input: imx6ul_tsc - add mask when set REG_ADC_CFG
Input: synaptics-rmi4 - have only one struct platform data
Input: synaptics-rmi4 - remove EXPORT_SYMBOL_GPL for internal functions
Input: synaptics-rmi4 - remove mutex calls while updating the firmware
Input: drv2667 - fix misuse of regmap_update_bits
...
Diffstat (limited to 'drivers/input/rmi4')
-rw-r--r-- | drivers/input/rmi4/Kconfig | 42 | ||||
-rw-r--r-- | drivers/input/rmi4/Makefile | 4 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_2d_sensor.c | 10 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_2d_sensor.h | 2 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_bus.c | 12 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_bus.h | 12 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_driver.c | 420 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_driver.h | 31 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_f01.c | 12 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_f03.c | 250 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_f11.c | 98 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_f12.c | 146 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_f30.c | 22 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_f34.c | 516 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_f34.h | 314 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_f34v7.c | 1372 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_f54.c | 19 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_f55.c | 131 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_i2c.c | 74 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_smbus.c | 447 | ||||
-rw-r--r-- | drivers/input/rmi4/rmi_spi.c | 72 |
21 files changed, 3686 insertions, 320 deletions
diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index 4c8a55857e00..30cc627a4f45 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -27,6 +27,27 @@ config RMI4_SPI If unsure, say N. +config RMI4_SMB + tristate "RMI4 SMB Support" + depends on RMI4_CORE && I2C + help + Say Y here if you want to support RMI4 devices connected to an SMB + bus. + + If unsure, say N. + + To compile this driver as a module, choose M here: the module will be + called rmi_smbus. + +config RMI4_F03 + bool "RMI4 Function 03 (PS2 Guest)" + depends on RMI4_CORE && SERIO + help + Say Y here if you want to add support for RMI4 function 03. + + Function 03 provides PS2 guest support for RMI4 devices. This + includes support for TrackPoints on TouchPads. + config RMI4_2D_SENSOR bool depends on RMI4_CORE @@ -62,13 +83,34 @@ 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 depends on VIDEO_V4L2=y || (RMI4_CORE=m && VIDEO_V4L2=m) select VIDEOBUF2_VMALLOC + select RMI4_F55 help Say Y here if you want to add support for RMI4 function 54 Function 54 provides access to various diagnostic features in certain RMI4 touch sensors. + +config RMI4_F55 + bool "RMI4 Function 55 (Sensor tuning)" + depends on RMI4_CORE + help + Say Y here if you want to add support for RMI4 function 55 + + Function 55 provides access to the RMI4 touch sensor tuning + mechanism. diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile index 0bafc8502c4b..9aaac3dd8613 100644 --- a/drivers/input/rmi4/Makefile +++ b/drivers/input/rmi4/Makefile @@ -4,11 +4,15 @@ rmi_core-y := rmi_bus.o rmi_driver.o rmi_f01.o rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o # Function drivers +rmi_core-$(CONFIG_RMI4_F03) += rmi_f03.o rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o +rmi_core-$(CONFIG_RMI4_F34) += rmi_f34.o rmi_f34v7.o rmi_core-$(CONFIG_RMI4_F54) += rmi_f54.o +rmi_core-$(CONFIG_RMI4_F55) += rmi_f55.o # Transports obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o obj-$(CONFIG_RMI4_SPI) += rmi_spi.o +obj-$(CONFIG_RMI4_SMB) += rmi_smbus.o diff --git a/drivers/input/rmi4/rmi_2d_sensor.c b/drivers/input/rmi4/rmi_2d_sensor.c index e97bd7fabccc..07007ff8e29f 100644 --- a/drivers/input/rmi4/rmi_2d_sensor.c +++ b/drivers/input/rmi4/rmi_2d_sensor.c @@ -177,10 +177,12 @@ static void rmi_2d_sensor_set_input_params(struct rmi_2d_sensor *sensor) sensor->dmax = DMAX * res_x; } - input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xff, 0, 0); - input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 0x0f, 0, 0); - input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 0x0f, 0, 0); - input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0); + input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xff, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 0x0f, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 0x0f, 0, 0); + input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0); + input_set_abs_params(input, ABS_MT_TOOL_TYPE, + 0, MT_TOOL_MAX, 0, 0); if (sensor->sensor_type == rmi_sensor_touchpad) input_flags = INPUT_MT_POINTER; diff --git a/drivers/input/rmi4/rmi_2d_sensor.h b/drivers/input/rmi4/rmi_2d_sensor.h index 77fcdfef003c..c871bef4dac0 100644 --- a/drivers/input/rmi4/rmi_2d_sensor.h +++ b/drivers/input/rmi4/rmi_2d_sensor.h @@ -67,6 +67,8 @@ struct rmi_2d_sensor { u8 report_rel; u8 x_mm; u8 y_mm; + enum rmi_reg_state dribble; + enum rmi_reg_state palm_detect; }; int rmi_2d_sensor_of_probe(struct device *dev, diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index ef8c747c35e7..1c40d94ca506 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -230,6 +230,9 @@ err_put_device: 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); @@ -302,6 +305,9 @@ struct bus_type rmi_bus_type = { static struct rmi_function_handler *fn_handlers[] = { &rmi_f01_handler, +#ifdef CONFIG_RMI4_F03 + &rmi_f03_handler, +#endif #ifdef CONFIG_RMI4_F11 &rmi_f11_handler, #endif @@ -311,9 +317,15 @@ 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 +#ifdef CONFIG_RMI4_F55 + &rmi_f55_handler, +#endif }; static void __rmi_unregister_function_handlers(int start_idx) diff --git a/drivers/input/rmi4/rmi_bus.h b/drivers/input/rmi4/rmi_bus.h index 899579830536..b7625a9ac66a 100644 --- a/drivers/input/rmi4/rmi_bus.h +++ b/drivers/input/rmi4/rmi_bus.h @@ -105,6 +105,18 @@ rmi_get_platform_data(struct rmi_device *d) bool rmi_is_physical_device(struct device *dev); /** + * rmi_reset - reset a RMI4 device + * @d: Pointer to an RMI device + * + * Calls for a reset of each function implemented by a specific device. + * Returns 0 on success or a negative error code. + */ +static inline int rmi_reset(struct rmi_device *d) +{ + return d->driver->reset_handler(d); +} + +/** * rmi_read - read a single byte * @d: Pointer to an RMI device * @addr: The address to read from diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 4a88312fbd25..11447ab1055c 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -17,6 +17,7 @@ #include <linux/bitmap.h> #include <linux/delay.h> #include <linux/fs.h> +#include <linux/irq.h> #include <linux/pm.h> #include <linux/slab.h> #include <linux/of.h> @@ -33,12 +34,22 @@ #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"); + + devm_kfree(&rmi_dev->dev, data->irq_memory); + data->irq_memory = NULL; + data->irq_status = NULL; + data->fn_irq_bits = NULL; + data->current_irq_mask = NULL; + data->new_irq_mask = NULL; + data->f01_container = NULL; + data->f34_container = NULL; /* Doing it in the reverse order so F01 will be removed last */ list_for_each_entry_safe_reverse(fn, tmp, @@ -133,7 +144,7 @@ static void process_one_interrupt(struct rmi_driver_data *data, } } -int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) +static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) { struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); struct device *dev = &rmi_dev->dev; @@ -143,7 +154,7 @@ int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) if (!data) return 0; - if (!rmi_dev->xport->attn_data) { + if (!data->attn_data.data) { error = rmi_read_block(rmi_dev, data->f01_container->fd.data_base_addr + 1, data->irq_status, data->num_of_irq_regs); @@ -178,7 +189,81 @@ int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) return 0; } -EXPORT_SYMBOL_GPL(rmi_process_interrupt_requests); + +void rmi_set_attn_data(struct rmi_device *rmi_dev, unsigned long irq_status, + void *data, size_t size) +{ + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); + struct rmi4_attn_data attn_data; + void *fifo_data; + + if (!drvdata->enabled) + return; + + fifo_data = kmemdup(data, size, GFP_ATOMIC); + if (!fifo_data) + return; + + attn_data.irq_status = irq_status; + attn_data.size = size; + attn_data.data = fifo_data; + + kfifo_put(&drvdata->attn_fifo, attn_data); +} +EXPORT_SYMBOL_GPL(rmi_set_attn_data); + +static irqreturn_t rmi_irq_fn(int irq, void *dev_id) +{ + struct rmi_device *rmi_dev = dev_id; + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); + struct rmi4_attn_data attn_data = {0}; + int ret, count; + + count = kfifo_get(&drvdata->attn_fifo, &attn_data); + if (count) { + *(drvdata->irq_status) = attn_data.irq_status; + drvdata->attn_data = attn_data; + } + + ret = rmi_process_interrupt_requests(rmi_dev); + if (ret) + rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev, + "Failed to process interrupt request: %d\n", ret); + + if (count) + kfree(attn_data.data); + + if (!kfifo_is_empty(&drvdata->attn_fifo)) + return rmi_irq_fn(irq, dev_id); + + return IRQ_HANDLED; +} + +static int rmi_irq_init(struct rmi_device *rmi_dev) +{ + struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + int irq_flags = irq_get_trigger_type(pdata->irq); + int ret; + + if (!irq_flags) + irq_flags = IRQF_TRIGGER_LOW; + + ret = devm_request_threaded_irq(&rmi_dev->dev, pdata->irq, NULL, + rmi_irq_fn, irq_flags | IRQF_ONESHOT, + dev_name(rmi_dev->xport->dev), + rmi_dev); + if (ret < 0) { + dev_err(&rmi_dev->dev, "Failed to register interrupt %d\n", + pdata->irq); + + return ret; + } + + data->enabled = true; + + return 0; +} static int suspend_one_function(struct rmi_function *fn) { @@ -248,7 +333,7 @@ static int rmi_resume_functions(struct rmi_device *rmi_dev) return 0; } -static int enable_sensor(struct rmi_device *rmi_dev) +int rmi_enable_sensor(struct rmi_device *rmi_dev) { int retval = 0; @@ -379,8 +464,8 @@ static int rmi_driver_reset_handler(struct rmi_device *rmi_dev) return 0; } -int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry, - u16 pdt_address) +static int rmi_read_pdt_entry(struct rmi_device *rmi_dev, + struct pdt_entry *entry, u16 pdt_address) { u8 buf[RMI_PDT_ENTRY_SIZE]; int error; @@ -403,7 +488,6 @@ int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry, return 0; } -EXPORT_SYMBOL_GPL(rmi_read_pdt_entry); static void rmi_driver_copy_pdt_to_fd(const struct pdt_entry *pdt, struct rmi_function_descriptor *fd) @@ -422,6 +506,7 @@ static void rmi_driver_copy_pdt_to_fd(const struct pdt_entry *pdt, static int rmi_scan_pdt_page(struct rmi_device *rmi_dev, int page, + int *empty_pages, void *ctx, int (*callback)(struct rmi_device *rmi_dev, void *ctx, @@ -449,20 +534,30 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev, return retval; } - return (data->f01_bootloader_mode || addr == pdt_start) ? + /* + * Count number of empty PDT pages. If a gap of two pages + * or more is found, stop scanning. + */ + if (addr == pdt_start) + ++*empty_pages; + else + *empty_pages = 0; + + return (data->bootloader_mode || *empty_pages >= 2) ? 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 empty_pages = 0; int retval = RMI_SCAN_DONE; for (page = 0; page <= RMI4_MAX_PAGE; page++) { - retval = rmi_scan_pdt_page(rmi_dev, page, ctx, callback); + retval = rmi_scan_pdt_page(rmi_dev, page, &empty_pages, + ctx, callback); if (retval != RMI_SCAN_CONTINUE) break; } @@ -600,7 +695,6 @@ free_struct_buff: kfree(struct_buf); return ret; } -EXPORT_SYMBOL_GPL(rmi_read_register_desc); const struct rmi_register_desc_item *rmi_get_register_desc_item( struct rmi_register_descriptor *rdesc, u16 reg) @@ -616,7 +710,6 @@ const struct rmi_register_desc_item *rmi_get_register_desc_item( return NULL; } -EXPORT_SYMBOL_GPL(rmi_get_register_desc_item); size_t rmi_register_desc_calc_size(struct rmi_register_descriptor *rdesc) { @@ -630,7 +723,6 @@ size_t rmi_register_desc_calc_size(struct rmi_register_descriptor *rdesc) } return size; } -EXPORT_SYMBOL_GPL(rmi_register_desc_calc_size); /* Compute the register offset relative to the base address */ int rmi_register_desc_calc_reg_offset( @@ -648,7 +740,6 @@ int rmi_register_desc_calc_reg_offset( } return -1; } -EXPORT_SYMBOL_GPL(rmi_register_desc_calc_reg_offset); bool rmi_register_desc_has_subpacket(const struct rmi_register_desc_item *item, u8 subpacket) @@ -657,51 +748,55 @@ 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); - if (data->f01_bootloader_mode) - dev_warn(&rmi_dev->dev, - "WARNING: RMI4 device is in bootloader mode!\n"); - } + + ret = rmi_check_bootloader_mode(rmi_dev, pdt); + if (ret < 0) + return ret; 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; @@ -720,6 +815,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, @@ -776,6 +872,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); @@ -786,23 +884,95 @@ err_put_fn: return error; } -int rmi_driver_suspend(struct rmi_device *rmi_dev) +void rmi_enable_irq(struct rmi_device *rmi_dev, bool clear_wake) { - int retval = 0; + struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + int irq = pdata->irq; + int irq_flags; + int retval; + + mutex_lock(&data->enabled_mutex); + + if (data->enabled) + goto out; + + enable_irq(irq); + data->enabled = true; + if (clear_wake && device_may_wakeup(rmi_dev->xport->dev)) { + retval = disable_irq_wake(irq); + if (!retval) + dev_warn(&rmi_dev->dev, + "Failed to disable irq for wake: %d\n", + retval); + } + + /* + * Call rmi_process_interrupt_requests() after enabling irq, + * otherwise we may lose interrupt on edge-triggered systems. + */ + irq_flags = irq_get_trigger_type(pdata->irq); + if (irq_flags & IRQ_TYPE_EDGE_BOTH) + rmi_process_interrupt_requests(rmi_dev); + +out: + mutex_unlock(&data->enabled_mutex); +} + +void rmi_disable_irq(struct rmi_device *rmi_dev, bool enable_wake) +{ + struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct rmi4_attn_data attn_data = {0}; + int irq = pdata->irq; + int retval, count; + + mutex_lock(&data->enabled_mutex); + + if (!data->enabled) + goto out; + + data->enabled = false; + disable_irq(irq); + if (enable_wake && device_may_wakeup(rmi_dev->xport->dev)) { + retval = enable_irq_wake(irq); + if (!retval) + dev_warn(&rmi_dev->dev, + "Failed to enable irq for wake: %d\n", + retval); + } + + /* make sure the fifo is clean */ + while (!kfifo_is_empty(&data->attn_fifo)) { + count = kfifo_get(&data->attn_fifo, &attn_data); + if (count) + kfree(attn_data.data); + } + +out: + mutex_unlock(&data->enabled_mutex); +} + +int rmi_driver_suspend(struct rmi_device *rmi_dev, bool enable_wake) +{ + int retval; retval = rmi_suspend_functions(rmi_dev); if (retval) dev_warn(&rmi_dev->dev, "Failed to suspend functions: %d\n", retval); + rmi_disable_irq(rmi_dev, enable_wake); return retval; } EXPORT_SYMBOL_GPL(rmi_driver_suspend); -int rmi_driver_resume(struct rmi_device *rmi_dev) +int rmi_driver_resume(struct rmi_device *rmi_dev, bool clear_wake) { int retval; + rmi_enable_irq(rmi_dev, clear_wake); + retval = rmi_resume_functions(rmi_dev); if (retval) dev_warn(&rmi_dev->dev, "Failed to suspend functions: %d\n", @@ -816,6 +986,9 @@ static int rmi_driver_remove(struct device *dev) { struct rmi_device *rmi_dev = to_rmi_device(dev); + rmi_disable_irq(rmi_dev, false); + + rmi_f34_remove_sysfs(rmi_dev); rmi_free_function_list(rmi_dev); return 0; @@ -842,15 +1015,95 @@ static inline int rmi_driver_of_probe(struct device *dev, } #endif +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; + 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; + 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->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; + + size = BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long); + data->irq_memory = devm_kzalloc(dev, size * 4, GFP_KERNEL); + if (!data->irq_memory) { + dev_err(dev, "Failed to allocate memory for irq masks.\n"); + return retval; + } + + data->irq_status = data->irq_memory + size * 0; + data->fn_irq_bits = data->irq_memory + size * 1; + data->current_irq_mask = data->irq_memory + size * 2; + data->new_irq_mask = data->irq_memory + size * 3; + + return retval; +} + +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 +1169,12 @@ 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); + mutex_init(&data->enabled_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 +1191,20 @@ 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_init_functions(data); + if (retval) + goto err; - 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_f34_create_sysfs(rmi_dev); + if (retval) + goto err; if (data->input) { rmi_driver_set_input_name(rmi_dev, data->input); @@ -1003,9 +1217,13 @@ static int rmi_driver_probe(struct device *dev) } } + retval = rmi_irq_init(rmi_dev); + if (retval < 0) + goto err_destroy_functions; + 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 8dfbebe9bf86..24f8f764d171 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -51,9 +51,6 @@ struct pdt_entry { u8 function_number; }; -int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry, - u16 pdt_address); - #define RMI_REG_DESC_PRESENSE_BITS (32 * BITS_PER_BYTE) #define RMI_REG_DESC_SUBPACKET_BITS (37 * BITS_PER_BYTE) @@ -95,12 +92,40 @@ 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); +void rmi_enable_irq(struct rmi_device *rmi_dev, bool clear_wake); +void rmi_disable_irq(struct rmi_device *rmi_dev, bool enable_wake); +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_f03_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; +extern struct rmi_function_handler rmi_f55_handler; #endif diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c index b5d2dfc23bad..18baf4ceb940 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 */ @@ -326,12 +328,12 @@ static int rmi_f01_probe(struct rmi_function *fn) } switch (pdata->power_management.nosleep) { - case RMI_F01_NOSLEEP_DEFAULT: + case RMI_REG_STATE_DEFAULT: break; - case RMI_F01_NOSLEEP_OFF: + case RMI_REG_STATE_OFF: f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_NOSLEEP_BIT; break; - case RMI_F01_NOSLEEP_ON: + case RMI_REG_STATE_ON: f01->device_control.ctrl0 |= RMI_F01_CTRL0_NOSLEEP_BIT; break; } @@ -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_f03.c b/drivers/input/rmi4/rmi_f03.c new file mode 100644 index 000000000000..8a7ca3e2f95e --- /dev/null +++ b/drivers/input/rmi4/rmi_f03.c @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2015-2016 Red Hat + * Copyright (C) 2015 Lyude Paul <thatslyude@gmail.com> + * + * 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/slab.h> +#include <linux/serio.h> +#include <linux/notifier.h> +#include "rmi_driver.h" + +#define RMI_F03_RX_DATA_OFB 0x01 +#define RMI_F03_OB_SIZE 2 + +#define RMI_F03_OB_OFFSET 2 +#define RMI_F03_OB_DATA_OFFSET 1 +#define RMI_F03_OB_FLAG_TIMEOUT BIT(6) +#define RMI_F03_OB_FLAG_PARITY BIT(7) + +#define RMI_F03_DEVICE_COUNT 0x07 +#define RMI_F03_BYTES_PER_DEVICE 0x07 +#define RMI_F03_BYTES_PER_DEVICE_SHIFT 4 +#define RMI_F03_QUEUE_LENGTH 0x0F + +struct f03_data { + struct rmi_function *fn; + + struct serio *serio; + + u8 device_count; + u8 rx_queue_length; +}; + +static int rmi_f03_pt_write(struct serio *id, unsigned char val) +{ + struct f03_data *f03 = id->port_data; + int error; + + rmi_dbg(RMI_DEBUG_FN, &f03->fn->dev, + "%s: Wrote %.2hhx to PS/2 passthrough address", + __func__, val); + + error = rmi_write(f03->fn->rmi_dev, f03->fn->fd.data_base_addr, val); + if (error) { + dev_err(&f03->fn->dev, + "%s: Failed to write to F03 TX register (%d).\n", + __func__, error); + return error; + } + + return 0; +} + +static int rmi_f03_initialize(struct f03_data *f03) +{ + struct rmi_function *fn = f03->fn; + struct device *dev = &fn->dev; + int error; + u8 bytes_per_device; + u8 query1; + u8 query2[RMI_F03_DEVICE_COUNT * RMI_F03_BYTES_PER_DEVICE]; + size_t query2_len; + + error = rmi_read(fn->rmi_dev, fn->fd.query_base_addr, &query1); + if (error) { + dev_err(dev, "Failed to read query register (%d).\n", error); + return error; + } + + f03->device_count = query1 & RMI_F03_DEVICE_COUNT; + bytes_per_device = (query1 >> RMI_F03_BYTES_PER_DEVICE_SHIFT) & + RMI_F03_BYTES_PER_DEVICE; + + query2_len = f03->device_count * bytes_per_device; + + /* + * The first generation of image sensors don't have a second part to + * their f03 query, as such we have to set some of these values manually + */ + if (query2_len < 1) { + f03->device_count = 1; + f03->rx_queue_length = 7; + } else { + error = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr + 1, + query2, query2_len); + if (error) { + dev_err(dev, + "Failed to read second set of query registers (%d).\n", + error); + return error; + } + + f03->rx_queue_length = query2[0] & RMI_F03_QUEUE_LENGTH; + } + + return 0; +} + +static int rmi_f03_register_pt(struct f03_data *f03) +{ + struct serio *serio; + + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!serio) + return -ENOMEM; + + serio->id.type = SERIO_8042; + serio->write = rmi_f03_pt_write; + serio->port_data = f03; + + strlcpy(serio->name, "Synaptics RMI4 PS/2 pass-through", + sizeof(serio->name)); + strlcpy(serio->phys, "synaptics-rmi4-pt/serio1", + sizeof(serio->phys)); + serio->dev.parent = &f03->fn->dev; + + f03->serio = serio; + + serio_register_port(serio); + + return 0; +} + +static int rmi_f03_probe(struct rmi_function *fn) +{ + struct device *dev = &fn->dev; + struct f03_data *f03; + int error; + + f03 = devm_kzalloc(dev, sizeof(struct f03_data), GFP_KERNEL); + if (!f03) + return -ENOMEM; + + f03->fn = fn; + + error = rmi_f03_initialize(f03); + if (error < 0) + return error; + + if (f03->device_count != 1) + dev_warn(dev, "found %d devices on PS/2 passthrough", + f03->device_count); + + dev_set_drvdata(dev, f03); + + error = rmi_f03_register_pt(f03); + if (error) + return error; + + return 0; +} + +static int rmi_f03_config(struct rmi_function *fn) +{ + fn->rmi_dev->driver->set_irq_bits(fn->rmi_dev, fn->irq_mask); + + return 0; +} + +static int rmi_f03_attention(struct rmi_function *fn, unsigned long *irq_bits) +{ + struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); + struct f03_data *f03 = dev_get_drvdata(&fn->dev); + u16 data_addr = fn->fd.data_base_addr; + const u8 ob_len = f03->rx_queue_length * RMI_F03_OB_SIZE; + u8 obs[RMI_F03_QUEUE_LENGTH * RMI_F03_OB_SIZE]; + u8 ob_status; + u8 ob_data; + unsigned int serio_flags; + int i; + int error; + + if (!rmi_dev) + return -ENODEV; + + if (drvdata->attn_data.data) { + /* First grab the data passed by the transport device */ + if (drvdata->attn_data.size < ob_len) { + dev_warn(&fn->dev, "F03 interrupted, but data is missing!\n"); + return 0; + } + + memcpy(obs, drvdata->attn_data.data, ob_len); + + drvdata->attn_data.data += ob_len; + drvdata->attn_data.size -= ob_len; + } else { + /* Grab all of the data registers, and check them for data */ + error = rmi_read_block(fn->rmi_dev, data_addr + RMI_F03_OB_OFFSET, + &obs, ob_len); + if (error) { + dev_err(&fn->dev, + "%s: Failed to read F03 output buffers: %d\n", + __func__, error); + serio_interrupt(f03->serio, 0, SERIO_TIMEOUT); + return error; + } + } + + for (i = 0; i < ob_len; i += RMI_F03_OB_SIZE) { + ob_status = obs[i]; + ob_data = obs[i + RMI_F03_OB_DATA_OFFSET]; + serio_flags = 0; + + if (!(ob_status & RMI_F03_RX_DATA_OFB)) + continue; + + if (ob_status & RMI_F03_OB_FLAG_TIMEOUT) + serio_flags |= SERIO_TIMEOUT; + if (ob_status & RMI_F03_OB_FLAG_PARITY) + serio_flags |= SERIO_PARITY; + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, + "%s: Received %.2hhx from PS2 guest T: %c P: %c\n", + __func__, ob_data, + serio_flags & SERIO_TIMEOUT ? 'Y' : 'N', + serio_flags & SERIO_PARITY ? 'Y' : 'N'); + + serio_interrupt(f03->serio, ob_data, serio_flags); + } + + return 0; +} + +static void rmi_f03_remove(struct rmi_function *fn) +{ + struct f03_data *f03 = dev_get_drvdata(&fn->dev); + + serio_unregister_port(f03->serio); +} + +struct rmi_function_handler rmi_f03_handler = { + .driver = { + .name = "rmi4_f03", + }, + .func = 0x03, + .probe = rmi_f03_probe, + .config = rmi_f03_config, + .attention = rmi_f03_attention, + .remove = rmi_f03_remove, +}; + +MODULE_AUTHOR("Lyude Paul <thatslyude@gmail.com>"); +MODULE_DESCRIPTION("RMI F03 module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c index f798f427a46f..bc5e37f30ac1 100644 --- a/drivers/input/rmi4/rmi_f11.c +++ b/drivers/input/rmi4/rmi_f11.c @@ -571,31 +571,48 @@ static inline u8 rmi_f11_parse_finger_state(const u8 *f_state, u8 n_finger) static void rmi_f11_finger_handler(struct f11_data *f11, struct rmi_2d_sensor *sensor, - unsigned long *irq_bits, int num_irq_regs) + unsigned long *irq_bits, int num_irq_regs, + int size) { const u8 *f_state = f11->data.f_state; u8 finger_state; u8 i; + int abs_fingers; + int rel_fingers; + int abs_size = sensor->nbr_fingers * RMI_F11_ABS_BYTES; int abs_bits = bitmap_and(f11->result_bits, irq_bits, f11->abs_mask, num_irq_regs * 8); int rel_bits = bitmap_and(f11->result_bits, irq_bits, f11->rel_mask, num_irq_regs * 8); - for (i = 0; i < sensor->nbr_fingers; i++) { - /* Possible of having 4 fingers per f_statet register */ - finger_state = rmi_f11_parse_finger_state(f_state, i); - if (finger_state == F11_RESERVED) { - pr_err("Invalid finger state[%d]: 0x%02x", i, - finger_state); - continue; - } + if (abs_bits) { + if (abs_size > size) + abs_fingers = size / RMI_F11_ABS_BYTES; + else + abs_fingers = sensor->nbr_fingers; + + for (i = 0; i < abs_fingers; i++) { + /* Possible of having 4 fingers per f_state register */ + finger_state = rmi_f11_parse_finger_state(f_state, i); + if (finger_state == F11_RESERVED) { + pr_err("Invalid finger state[%d]: 0x%02x", i, + finger_state); + continue; + } - if (abs_bits) rmi_f11_abs_pos_process(f11, sensor, &sensor->objs[i], finger_state, i); + } + } + + if (rel_bits) { + if ((abs_size + sensor->nbr_fingers * RMI_F11_REL_BYTES) > size) + rel_fingers = (size - abs_size) / RMI_F11_REL_BYTES; + else + rel_fingers = sensor->nbr_fingers; - if (rel_bits) + for (i = 0; i < rel_fingers; i++) rmi_f11_rel_pos_report(f11, i); } @@ -611,7 +628,7 @@ static void rmi_f11_finger_handler(struct f11_data *f11, sensor->nbr_fingers, sensor->dmax); - for (i = 0; i < sensor->nbr_fingers; i++) { + for (i = 0; i < abs_fingers; i++) { finger_state = rmi_f11_parse_finger_state(f_state, i); if (finger_state == F11_RESERVED) /* no need to send twice the error */ @@ -1062,8 +1079,8 @@ static int rmi_f11_initialize(struct rmi_function *fn) rc = rmi_2d_sensor_of_probe(&fn->dev, &f11->sensor_pdata); if (rc) return rc; - } else if (pdata->sensor_pdata) { - f11->sensor_pdata = *pdata->sensor_pdata; + } else { + f11->sensor_pdata = pdata->sensor_pdata; } f11->rezero_wait_ms = f11->sensor_pdata.rezero_wait; @@ -1124,6 +1141,8 @@ static int rmi_f11_initialize(struct rmi_function *fn) sensor->topbuttonpad = f11->sensor_pdata.topbuttonpad; sensor->kernel_tracking = f11->sensor_pdata.kernel_tracking; sensor->dmax = f11->sensor_pdata.dmax; + sensor->dribble = f11->sensor_pdata.dribble; + sensor->palm_detect = f11->sensor_pdata.palm_detect; if (f11->sens_query.has_physical_props) { sensor->x_mm = f11->sens_query.x_sensor_size_mm; @@ -1191,11 +1210,33 @@ static int rmi_f11_initialize(struct rmi_function *fn) ctrl->ctrl0_11[RMI_F11_DELTA_Y_THRESHOLD] = sensor->axis_align.delta_y_threshold; - if (f11->sens_query.has_dribble) - ctrl->ctrl0_11[0] = ctrl->ctrl0_11[0] & ~BIT(6); + if (f11->sens_query.has_dribble) { + switch (sensor->dribble) { + case RMI_REG_STATE_OFF: + ctrl->ctrl0_11[0] &= ~BIT(6); + break; + case RMI_REG_STATE_ON: + ctrl->ctrl0_11[0] |= BIT(6); + break; + case RMI_REG_STATE_DEFAULT: + default: + break; + } + } - if (f11->sens_query.has_palm_det) - ctrl->ctrl0_11[11] = ctrl->ctrl0_11[11] & ~BIT(0); + if (f11->sens_query.has_palm_det) { + switch (sensor->palm_detect) { + case RMI_REG_STATE_OFF: + ctrl->ctrl0_11[11] &= ~BIT(0); + break; + case RMI_REG_STATE_ON: + ctrl->ctrl0_11[11] |= BIT(0); + break; + case RMI_REG_STATE_DEFAULT: + default: + break; + } + } rc = f11_write_control_regs(fn, &f11->sens_query, &f11->dev_controls, fn->fd.query_base_addr); @@ -1241,12 +1282,21 @@ static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits) struct f11_data *f11 = dev_get_drvdata(&fn->dev); u16 data_base_addr = fn->fd.data_base_addr; int error; + int valid_bytes = f11->sensor.pkt_size; - if (rmi_dev->xport->attn_data) { - memcpy(f11->sensor.data_pkt, rmi_dev->xport->attn_data, - f11->sensor.attn_size); - rmi_dev->xport->attn_data += f11->sensor.attn_size; - rmi_dev->xport->attn_size -= f11->sensor.attn_size; + if (drvdata->attn_data.data) { + /* + * The valid data in the attention report is less then + * expected. Only process the complete fingers. + */ + if (f11->sensor.attn_size > drvdata->attn_data.size) + valid_bytes = drvdata->attn_data.size; + else + valid_bytes = f11->sensor.attn_size; + memcpy(f11->sensor.data_pkt, drvdata->attn_data.data, + valid_bytes); + drvdata->attn_data.data += f11->sensor.attn_size; + drvdata->attn_data.size -= f11->sensor.attn_size; } else { error = rmi_read_block(rmi_dev, data_base_addr, f11->sensor.data_pkt, @@ -1256,7 +1306,7 @@ static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits) } rmi_f11_finger_handler(f11, &f11->sensor, irq_bits, - drvdata->num_of_irq_regs); + drvdata->num_of_irq_regs, valid_bytes); return 0; } diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c index 332c02f0b107..07aff4356fe0 100644 --- a/drivers/input/rmi4/rmi_f12.c +++ b/drivers/input/rmi4/rmi_f12.c @@ -26,9 +26,12 @@ enum rmi_f12_object_type { RMI_F12_OBJECT_SMALL_OBJECT = 0x0D, }; +#define F12_DATA1_BYTES_PER_OBJ 8 + struct f12_data { struct rmi_2d_sensor sensor; struct rmi_2d_sensor_platform_data sensor_pdata; + bool has_dribble; u16 data_addr; @@ -68,10 +71,6 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12) u8 buf[15]; int pitch_x = 0; int pitch_y = 0; - int clip_x_low = 0; - int clip_x_high = 0; - int clip_y_low = 0; - int clip_y_high = 0; int rx_receivers = 0; int tx_receivers = 0; int sensor_flags = 0; @@ -124,7 +123,9 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12) } rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: x low: %d x high: %d y low: %d y high: %d\n", - __func__, clip_x_low, clip_x_high, clip_y_low, clip_y_high); + __func__, + sensor->axis_align.clip_x_low, sensor->axis_align.clip_x_high, + sensor->axis_align.clip_y_low, sensor->axis_align.clip_y_high); if (rmi_register_desc_has_subpacket(item, 3)) { rx_receivers = buf[offset]; @@ -146,12 +147,16 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12) return 0; } -static void rmi_f12_process_objects(struct f12_data *f12, u8 *data1) +static void rmi_f12_process_objects(struct f12_data *f12, u8 *data1, int size) { int i; struct rmi_2d_sensor *sensor = &f12->sensor; + int objects = f12->data1->num_subpackets; + + if ((f12->data1->num_subpackets * F12_DATA1_BYTES_PER_OBJ) > size) + objects = size / F12_DATA1_BYTES_PER_OBJ; - for (i = 0; i < f12->data1->num_subpackets; i++) { + for (i = 0; i < objects; i++) { struct rmi_2d_sensor_abs_object *obj = &sensor->objs[i]; obj->type = RMI_2D_OBJECT_NONE; @@ -182,7 +187,7 @@ static void rmi_f12_process_objects(struct f12_data *f12, u8 *data1) rmi_2d_sensor_abs_process(sensor, obj, i); - data1 += 8; + data1 += F12_DATA1_BYTES_PER_OBJ; } if (sensor->kernel_tracking) @@ -192,7 +197,7 @@ static void rmi_f12_process_objects(struct f12_data *f12, u8 *data1) sensor->nbr_fingers, sensor->dmax); - for (i = 0; i < sensor->nbr_fingers; i++) + for (i = 0; i < objects; i++) rmi_2d_sensor_abs_report(sensor, &sensor->objs[i], i); } @@ -201,14 +206,20 @@ static int rmi_f12_attention(struct rmi_function *fn, { int retval; struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); struct f12_data *f12 = dev_get_drvdata(&fn->dev); struct rmi_2d_sensor *sensor = &f12->sensor; - - if (rmi_dev->xport->attn_data) { - memcpy(sensor->data_pkt, rmi_dev->xport->attn_data, - sensor->attn_size); - rmi_dev->xport->attn_data += sensor->attn_size; - rmi_dev->xport->attn_size -= sensor->attn_size; + int valid_bytes = sensor->pkt_size; + + if (drvdata->attn_data.data) { + if (sensor->attn_size > drvdata->attn_data.size) + valid_bytes = drvdata->attn_data.size; + else + valid_bytes = sensor->attn_size; + memcpy(sensor->data_pkt, drvdata->attn_data.data, + valid_bytes); + drvdata->attn_data.data += sensor->attn_size; + drvdata->attn_data.size -= sensor->attn_size; } else { retval = rmi_read_block(rmi_dev, f12->data_addr, sensor->data_pkt, sensor->pkt_size); @@ -221,19 +232,83 @@ static int rmi_f12_attention(struct rmi_function *fn, if (f12->data1) rmi_f12_process_objects(f12, - &sensor->data_pkt[f12->data1_offset]); + &sensor->data_pkt[f12->data1_offset], valid_bytes); input_mt_sync_frame(sensor->input); return 0; } +static int rmi_f12_write_control_regs(struct rmi_function *fn) +{ + int ret; + const struct rmi_register_desc_item *item; + struct rmi_device *rmi_dev = fn->rmi_dev; + struct f12_data *f12 = dev_get_drvdata(&fn->dev); + int control_size; + char buf[3]; + u16 control_offset = 0; + u8 subpacket_offset = 0; + + if (f12->has_dribble + && (f12->sensor.dribble != RMI_REG_STATE_DEFAULT)) { + item = rmi_get_register_desc_item(&f12->control_reg_desc, 20); + if (item) { + control_offset = rmi_register_desc_calc_reg_offset( + &f12->control_reg_desc, 20); + + /* + * The byte containing the EnableDribble bit will be + * in either byte 0 or byte 2 of control 20. Depending + * on the existence of subpacket 0. If control 20 is + * larger then 3 bytes, just read the first 3. + */ + control_size = min(item->reg_size, 3UL); + + ret = rmi_read_block(rmi_dev, fn->fd.control_base_addr + + control_offset, buf, control_size); + if (ret) + return ret; + + if (rmi_register_desc_has_subpacket(item, 0)) + subpacket_offset += 1; + + switch (f12->sensor.dribble) { + case RMI_REG_STATE_OFF: + buf[subpacket_offset] &= ~BIT(2); + break; + case RMI_REG_STATE_ON: + buf[subpacket_offset] |= BIT(2); + break; + case RMI_REG_STATE_DEFAULT: + default: + break; + } + + ret = rmi_write_block(rmi_dev, + fn->fd.control_base_addr + control_offset, + buf, control_size); + if (ret) + return ret; + } + } + + return 0; + +} + static int rmi_f12_config(struct rmi_function *fn) { struct rmi_driver *drv = fn->rmi_dev->driver; + int ret; drv->set_irq_bits(fn->rmi_dev, fn->irq_mask); + ret = rmi_f12_write_control_regs(fn); + if (ret) + dev_warn(&fn->dev, + "Failed to write F12 control registers: %d\n", ret); + return 0; } @@ -247,7 +322,7 @@ static int rmi_f12_probe(struct rmi_function *fn) const struct rmi_register_desc_item *item; struct rmi_2d_sensor *sensor; struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); - struct rmi_transport_dev *xport = rmi_dev->xport; + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); u16 data_offset = 0; rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s\n", __func__); @@ -260,7 +335,7 @@ static int rmi_f12_probe(struct rmi_function *fn) } ++query_addr; - if (!(buf & 0x1)) { + if (!(buf & BIT(0))) { dev_err(&fn->dev, "Behavior of F12 without register descriptors is undefined.\n"); return -ENODEV; @@ -270,12 +345,14 @@ static int rmi_f12_probe(struct rmi_function *fn) if (!f12) return -ENOMEM; + f12->has_dribble = !!(buf & BIT(3)); + if (fn->dev.of_node) { ret = rmi_2d_sensor_of_probe(&fn->dev, &f12->sensor_pdata); if (ret) return ret; - } else if (pdata->sensor_pdata) { - f12->sensor_pdata = *pdata->sensor_pdata; + } else { + f12->sensor_pdata = pdata->sensor_pdata; } ret = rmi_read_register_desc(rmi_dev, query_addr, @@ -318,6 +395,7 @@ static int rmi_f12_probe(struct rmi_function *fn) sensor->x_mm = f12->sensor_pdata.x_mm; sensor->y_mm = f12->sensor_pdata.y_mm; + sensor->dribble = f12->sensor_pdata.dribble; if (sensor->sensor_type == rmi_sensor_default) sensor->sensor_type = @@ -343,7 +421,7 @@ static int rmi_f12_probe(struct rmi_function *fn) * HID attention reports. */ item = rmi_get_register_desc_item(&f12->data_reg_desc, 0); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 1); @@ -357,15 +435,15 @@ static int rmi_f12_probe(struct rmi_function *fn) } item = rmi_get_register_desc_item(&f12->data_reg_desc, 2); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 3); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 4); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 5); @@ -377,22 +455,22 @@ static int rmi_f12_probe(struct rmi_function *fn) } item = rmi_get_register_desc_item(&f12->data_reg_desc, 6); - if (item && !xport->attn_data) { + if (item && !drvdata->attn_data.data) { f12->data6 = item; f12->data6_offset = data_offset; data_offset += item->reg_size; } item = rmi_get_register_desc_item(&f12->data_reg_desc, 7); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 8); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 9); - if (item && !xport->attn_data) { + if (item && !drvdata->attn_data.data) { f12->data9 = item; f12->data9_offset = data_offset; data_offset += item->reg_size; @@ -401,27 +479,27 @@ static int rmi_f12_probe(struct rmi_function *fn) } item = rmi_get_register_desc_item(&f12->data_reg_desc, 10); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 11); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 12); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 13); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 14); - if (item && !xport->attn_data) + if (item && !drvdata->attn_data.data) data_offset += item->reg_size; item = rmi_get_register_desc_item(&f12->data_reg_desc, 15); - if (item && !xport->attn_data) { + if (item && !drvdata->attn_data.data) { f12->data15 = item; f12->data15_offset = data_offset; data_offset += item->reg_size; diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c index 760aff1bc420..f4b491e3e0fd 100644 --- a/drivers/input/rmi4/rmi_f30.c +++ b/drivers/input/rmi4/rmi_f30.c @@ -99,6 +99,7 @@ static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits) { struct f30_data *f30 = dev_get_drvdata(&fn->dev); struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); int retval; int gpiled = 0; int value = 0; @@ -109,11 +110,15 @@ static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits) return 0; /* Read the gpi led data. */ - if (rmi_dev->xport->attn_data) { - memcpy(f30->data_regs, rmi_dev->xport->attn_data, + if (drvdata->attn_data.data) { + if (drvdata->attn_data.size < f30->register_count) { + dev_warn(&fn->dev, "F30 interrupted, but data is missing\n"); + return 0; + } + memcpy(f30->data_regs, drvdata->attn_data.data, f30->register_count); - rmi_dev->xport->attn_data += f30->register_count; - rmi_dev->xport->attn_size -= f30->register_count; + drvdata->attn_data.data += f30->register_count; + drvdata->attn_data.size -= f30->register_count; } else { retval = rmi_read_block(rmi_dev, fn->fd.data_base_addr, f30->data_regs, f30->register_count); @@ -192,7 +197,7 @@ static int rmi_f30_config(struct rmi_function *fn) rmi_get_platform_data(fn->rmi_dev); int error; - if (pdata->f30_data && pdata->f30_data->disable) { + if (pdata->f30_data.disable) { drv->clear_irq_bits(fn->rmi_dev, fn->irq_mask); } else { /* Write Control Register values back to device */ @@ -351,7 +356,7 @@ static inline int rmi_f30_initialize(struct rmi_function *fn) f30->gpioled_key_map = (u16 *)map_memory; pdata = rmi_get_platform_data(rmi_dev); - if (pdata && f30->has_gpio) { + if (f30->has_gpio) { button = BTN_LEFT; for (i = 0; i < f30->gpioled_count; i++) { if (rmi_f30_is_valid_button(i, f30->ctrl)) { @@ -362,8 +367,7 @@ static inline int rmi_f30_initialize(struct rmi_function *fn) * f30->has_mech_mouse_btns, but I am * not sure, so use only the pdata info */ - if (pdata->f30_data && - pdata->f30_data->buttonpad) + if (pdata->f30_data.buttonpad) break; } } @@ -378,7 +382,7 @@ static int rmi_f30_probe(struct rmi_function *fn) const struct rmi_device_platform_data *pdata = rmi_get_platform_data(fn->rmi_dev); - if (pdata->f30_data && pdata->f30_data->disable) + if (pdata->f30_data.disable) return 0; rc = rmi_f30_initialize(fn); diff --git a/drivers/input/rmi4/rmi_f34.c b/drivers/input/rmi4/rmi_f34.c new file mode 100644 index 000000000000..9774dfbab9bb --- /dev/null +++ b/drivers/input/rmi4/rmi_f34.c @@ -0,0 +1,516 @@ +/* + * 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 <linux/bitops.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->v5.cmd_done); + + 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", + __func__, ret, command); + return ret; + } + + f34->v5.status |= command & 0x0f; + + 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", + command, ret); + return ret; + } + + if (!wait_for_completion_timeout(&f34->v5.cmd_done, + msecs_to_jiffies(timeout))) { + + 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", + __func__, command, ret); + return ret; + } + + if (f34->v5.status & 0x7f) { + dev_err(&f34->fn->dev, + "%s: cmd %#02x timed out, status: %#02x\n", + __func__, command, f34->v5.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; + + if (f34->bl_version != 5) + 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->v5.status, ret); + + if (!ret && !(f34->v5.status & 0x7f)) + complete(&f34->v5.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->v5.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->v5.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->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->v5.config_blocks, + F34_WRITE_CONFIG_BLOCK); +} + +int rmi_f34_enable_flash(struct f34_data *f34) +{ + 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 f34_data *f34, const struct firmware *fw) +{ + 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, &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, &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->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->v5.fw_blocks * f34->v5.block_size); + ret = -EILSEQ; + goto out; + } + + if (syn_fw->config_size && + 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->v5.config_blocks * f34->v5.block_size); + ret = -EILSEQ; + goto out; + } + + if (syn_fw->image_size && !syn_fw->config_size) { + 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->v5.flash_mutex); + + ret = rmi_f34_flash_firmware(f34, syn_fw); + + mutex_unlock(&f34->v5.flash_mutex); + +out: + return ret; +} + +static int rmi_firmware_update(struct rmi_driver_data *data, + const struct firmware *fw) +{ + struct rmi_device *rmi_dev = data->rmi_dev; + struct device *dev = &rmi_dev->dev; + struct f34_data *f34; + int ret; + + if (!data->f34_container) { + dev_warn(dev, "%s: No F34 present!\n", __func__); + return -EINVAL; + } + + f34 = dev_get_drvdata(&data->f34_container->dev); + + if (f34->bl_version == 7) { + if (data->pdt_props & HAS_BSR) { + dev_err(dev, "%s: LTS not supported\n", __func__); + return -ENODEV; + } + } else if (f34->bl_version != 5) { + dev_warn(dev, "F34 V%d not supported!\n", + data->f34_container->fd.function_version); + return -ENODEV; + } + + /* Enter flash mode */ + if (f34->bl_version == 7) + ret = rmi_f34v7_start_reflash(f34, fw); + else + ret = rmi_f34_enable_flash(f34); + if (ret) + return ret; + + rmi_disable_irq(rmi_dev, false); + + /* Tear down functions and re-probe */ + rmi_free_function_list(rmi_dev); + + ret = rmi_probe_interrupts(data); + if (ret) + return ret; + + ret = rmi_init_functions(data); + if (ret) + return ret; + + if (!data->bootloader_mode || !data->f34_container) { + dev_warn(dev, "%s: No F34 present or not in bootloader!\n", + __func__); + return -EINVAL; + } + + rmi_enable_irq(rmi_dev, false); + + f34 = dev_get_drvdata(&data->f34_container->dev); + + /* Perform firmware update */ + if (f34->bl_version == 7) + 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); + + rmi_disable_irq(rmi_dev, false); + + /* Re-probe */ + rmi_dbg(RMI_DEBUG_FN, dev, "Re-probing device\n"); + rmi_free_function_list(rmi_dev); + + ret = rmi_scan_pdt(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; + + rmi_enable_irq(rmi_dev, false); + + if (data->f01_container->dev.driver) + /* Driver already bound, so enable ATTN now. */ + return rmi_enable_sensor(rmi_dev); + + rmi_dbg(RMI_DEBUG_FN, dev, "%s complete\n", __func__); + + return ret; +} + +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) +{ + 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; + u8 version = fn->fd.function_version; + int 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); + + /* v5 code only supported version 0, try V7 probe */ + if (version > 0) + return rmi_f34v7_probe(f34); + else if (version != 0) + return -ENODEV; + + f34->bl_version = 5; + + 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]); + + 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->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, + 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 000000000000..2c21056dc375 --- /dev/null +++ b/drivers/input/rmi4/rmi_f34.h @@ -0,0 +1,314 @@ +/* + * 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 + +/* F34 V7 defines */ +#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 V7_BOOTLOADER_ID_OFFSET 1 + +#define IMAGE_HEADER_VERSION_10 0x10 + +#define CONFIG_ID_SIZE 32 +#define PRODUCT_ID_SIZE 10 + +#define ENABLE_WAIT_MS (1 * 1000) +#define WRITE_WAIT_MS (3 * 1000) + +#define MIN_SLEEP_TIME_US 50 +#define MAX_SLEEP_TIME_US 100 + +#define HAS_BSR BIT(5) +#define HAS_CONFIG_ID BIT(3) +#define HAS_GUEST_CODE BIT(6) +#define HAS_DISP_CFG BIT(5) + +/* F34 V7 commands */ +#define CMD_V7_IDLE 0 +#define CMD_V7_ENTER_BL 1 +#define CMD_V7_READ 2 +#define CMD_V7_WRITE 3 +#define CMD_V7_ERASE 4 +#define CMD_V7_ERASE_AP 5 +#define CMD_V7_SENSOR_ID 6 + +#define v7_CMD_IDLE 0 +#define v7_CMD_WRITE_FW 1 +#define v7_CMD_WRITE_CONFIG 2 +#define v7_CMD_WRITE_LOCKDOWN 3 +#define v7_CMD_WRITE_GUEST_CODE 4 +#define v7_CMD_READ_CONFIG 5 +#define v7_CMD_ERASE_ALL 6 +#define v7_CMD_ERASE_UI_FIRMWARE 7 +#define v7_CMD_ERASE_UI_CONFIG 8 +#define v7_CMD_ERASE_BL_CONFIG 9 +#define v7_CMD_ERASE_DISP_CONFIG 10 +#define v7_CMD_ERASE_FLASH_CONFIG 11 +#define v7_CMD_ERASE_GUEST_CODE 12 +#define v7_CMD_ENABLE_FLASH_PROG 13 + +#define v7_UI_CONFIG_AREA 0 +#define v7_PM_CONFIG_AREA 1 +#define v7_BL_CONFIG_AREA 2 +#define v7_DP_CONFIG_AREA 3 +#define v7_FLASH_CONFIG_AREA 4 + +/* F34 V7 partition IDs */ +#define BOOTLOADER_PARTITION 1 +#define DEVICE_CONFIG_PARTITION 2 +#define FLASH_CONFIG_PARTITION 3 +#define MANUFACTURING_BLOCK_PARTITION 4 +#define GUEST_SERIALIZATION_PARTITION 5 +#define GLOBAL_PARAMETERS_PARTITION 6 +#define CORE_CODE_PARTITION 7 +#define CORE_CONFIG_PARTITION 8 +#define GUEST_CODE_PARTITION 9 +#define DISPLAY_CONFIG_PARTITION 10 + +/* F34 V7 container IDs */ +#define TOP_LEVEL_CONTAINER 0 +#define UI_CONTAINER 1 +#define UI_CONFIG_CONTAINER 2 +#define BL_CONTAINER 3 +#define BL_IMAGE_CONTAINER 4 +#define BL_CONFIG_CONTAINER 5 +#define BL_LOCKDOWN_INFO_CONTAINER 6 +#define PERMANENT_CONFIG_CONTAINER 7 +#define GUEST_CODE_CONTAINER 8 +#define BL_PROTOCOL_DESCRIPTOR_CONTAINER 9 +#define UI_PROTOCOL_DESCRIPTOR_CONTAINER 10 +#define RMI_SELF_DISCOVERY_CONTAINER 11 +#define RMI_PAGE_CONTENT_CONTAINER 12 +#define GENERAL_INFORMATION_CONTAINER 13 +#define DEVICE_CONFIG_CONTAINER 14 +#define FLASH_CONFIG_CONTAINER 15 +#define GUEST_SERIALIZATION_CONTAINER 16 +#define GLOBAL_PARAMETERS_CONTAINER 17 +#define CORE_CODE_CONTAINER 18 +#define CORE_CONFIG_CONTAINER 19 +#define DISPLAY_CONFIG_CONTAINER 20 + +struct f34v7_query_1_7 { + u8 bl_minor_revision; /* query 1 */ + u8 bl_major_revision; + __le32 bl_fw_id; /* query 2 */ + u8 minimum_write_size; /* query 3 */ + __le16 block_size; + __le16 flash_page_size; + __le16 adjustable_partition_area_size; /* query 4 */ + __le16 flash_config_length; /* query 5 */ + __le16 payload_length; /* query 6 */ + u8 partition_support[4]; /* query 7 */ +} __packed; + +struct f34v7_data_1_5 { + u8 partition_id; + __le16 block_offset; + __le16 transfer_length; + u8 command; + u8 payload[2]; +} __packed; + +struct block_data { + const void *data; + int size; +}; + +struct partition_table { + u8 partition_id; + u8 byte_1_reserved; + __le16 partition_length; + __le16 start_physical_address; + __le16 partition_properties; +} __packed; + +struct physical_address { + u16 ui_firmware; + u16 ui_config; + u16 dp_config; + u16 guest_code; +}; + +struct container_descriptor { + __le32 content_checksum; + __le16 container_id; + u8 minor_version; + u8 major_version; + u8 reserved_08; + u8 reserved_09; + u8 reserved_0a; + u8 reserved_0b; + u8 container_option_flags[4]; + __le32 content_options_length; + __le32 content_options_address; + __le32 content_length; + __le32 content_address; +} __packed; + +struct block_count { + u16 ui_firmware; + u16 ui_config; + u16 dp_config; + u16 fl_config; + u16 pm_config; + u16 bl_config; + u16 lockdown; + u16 guest_code; +}; + +struct image_header_10 { + __le32 checksum; + u8 reserved_04; + u8 reserved_05; + u8 minor_header_version; + u8 major_header_version; + u8 reserved_08; + u8 reserved_09; + u8 reserved_0a; + u8 reserved_0b; + __le32 top_level_container_start_addr; +}; + +struct image_metadata { + bool contains_firmware_id; + bool contains_bootloader; + bool contains_display_cfg; + bool contains_guest_code; + bool contains_flash_config; + unsigned int firmware_id; + unsigned int checksum; + unsigned int bootloader_size; + unsigned int display_cfg_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 { + u8 properties; + u8 properties_2; + u8 block_size; + u8 block_count; + u8 gc_block_count; + u8 flash_status; + u8 partition_id; + u8 block_number; + u8 transfer_length; + u8 flash_cmd; + u8 payload; +}; + +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 f34v5_data { + u16 block_size; + u16 fw_blocks; + u16 config_blocks; + u16 ctrl_address; + u8 status; + + struct completion cmd_done; + struct mutex flash_mutex; +}; + +struct f34v7_data { + bool has_display_cfg; + bool has_guest_code; + bool force_update; + bool in_bl_mode; + u8 *read_config_buf; + size_t read_config_buf_size; + u8 command; + u8 flash_status; + u16 block_size; + u16 config_block_count; + u16 config_size; + u16 config_area; + u16 flash_config_length; + u16 payload_length; + u8 partitions; + u16 partition_table_bytes; + bool new_partition_table; + + struct register_offset off; + struct block_count blkcount; + struct physical_address phyaddr; + struct image_metadata img; + + const void *config_data; + const void *image; +}; + +struct f34_data { + struct rmi_function *fn; + + u8 bl_version; + unsigned char bootloader_id[5]; + unsigned char configuration_id[CONFIG_ID_SIZE*2 + 1]; + + 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 000000000000..ca31f9539d9b --- /dev/null +++ b/drivers/input/rmi4/rmi_f34v7.c @@ -0,0 +1,1372 @@ +/* + * Copyright (c) 2016, Zodiac Inflight Innovations + * Copyright (c) 2007-2016, Synaptics Incorporated + * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com> + * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com> + * + * 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) +{ + u8 status; + u8 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 & 0x1f; + + 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, + u8 cmd) +{ + int ret; + u8 base; + struct f34v7_data_1_5 data_1_5; + + base = f34->fn->fd.data_base_addr; + + memset(&data_1_5, 0, sizeof(data_1_5)); + + 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, sizeof(data_1_5)); + 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, u8 cmd) +{ + int ret; + u8 base; + u8 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, u8 cmd) +{ + int ret; + u8 base; + u8 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; + u8 base; + __le16 length; + u16 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, + &block_number, sizeof(block_number)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to write block number\n", + __func__); + return ret; + } + + put_unaligned_le16(f34->v7.flash_config_length, &length); + + 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 void *partition_table, + struct block_count *blkcount, + struct physical_address *phyaddr) +{ + int i; + int index; + u16 partition_length; + u16 physical_address; + const struct partition_table *ptable; + + for (i = 0; i < f34->v7.partitions; i++) { + index = i * 8 + 2; + ptable = partition_table + index; + partition_length = le16_to_cpu(ptable->partition_length); + physical_address = le16_to_cpu(ptable->start_physical_address); + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, + "%s: Partition entry %d: %*ph\n", + __func__, i, sizeof(struct partition_table), ptable); + switch (ptable->partition_id & 0x1f) { + 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; + u8 base; + int offset; + u8 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, + sizeof(query_0)); + if (ret < 0) { + dev_err(&f34->fn->dev, + "%s: Failed to read query 0\n", __func__); + return ret; + } + + offset = (query_0 & 0x7) + 1; + + ret = rmi_read_block(f34->fn->rmi_dev, + base + offset, + &query_1_7, + sizeof(query_1_7)); + 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; + int i, j; + u8 base; + int offset; + u8 *ptable; + u8 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, + sizeof(query_0)); + if (ret < 0) { + dev_err(&f34->fn->dev, + "%s: Failed to read query 0\n", __func__); + return ret; + } + + offset = (query_0 & 0x07) + 1; + + ret = rmi_read_block(f34->fn->rmi_dev, + base + offset, + &query_1_7, + sizeof(query_1_7)); + 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 = le16_to_cpu(query_1_7.block_size); + f34->v7.flash_config_length = + le16_to_cpu(query_1_7.flash_config_length); + f34->v7.payload_length = le16_to_cpu(query_1_7.payload_length); + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: f34->v7.block_size = %d\n", + __func__, f34->v7.block_size); + + 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.has_display_cfg = query_1_7.partition_support[1] & HAS_DISP_CFG; + f34->v7.has_guest_code = + query_1_7.partition_support[1] & HAS_GUEST_CODE; + + if (query_0 & HAS_CONFIG_ID) { + char f34_ctrl[CONFIG_ID_SIZE]; + int i = 0; + u8 *p = f34->configuration_id; + *p = '\0'; + + ret = rmi_read_block(f34->fn->rmi_dev, + f34->fn->fd.control_base_addr, + f34_ctrl, + sizeof(f34_ctrl)); + if (ret) + return ret; + + /* Eat leading zeros */ + while (i < sizeof(f34_ctrl) && !f34_ctrl[i]) + i++; + + for (; i < sizeof(f34_ctrl); i++) + p += snprintf(p, f34->configuration_id + + sizeof(f34->configuration_id) - p, + "%02X", f34_ctrl[i]); + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "Configuration ID: %s\n", + f34->configuration_id); + } + + f34->v7.partitions = 0; + for (i = 0; i < sizeof(query_1_7.partition_support); i++) + for (j = 0; j < 8; j++) + if (query_1_7.partition_support[i] & (1 << j)) + f34->v7.partitions++; + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: Supported partitions: %*ph\n", + __func__, sizeof(query_1_7.partition_support), + query_1_7.partition_support); + + + 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) +{ + u16 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, + "UI firmware size mismatch: %d != %d\n", + block_count, f34->v7.blkcount.ui_firmware); + return -EINVAL; + } + + return 0; +} + +static int rmi_f34v7_check_ui_config_size(struct f34_data *f34) +{ + u16 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, "UI config size mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int rmi_f34v7_check_dp_config_size(struct f34_data *f34) +{ + u16 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, "Display config size mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int rmi_f34v7_check_guest_code_size(struct f34_data *f34) +{ + u16 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, "Guest code size mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int rmi_f34v7_check_bl_config_size(struct f34_data *f34) +{ + u16 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, "Bootloader config size mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int rmi_f34v7_erase_config(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_config(f34); + if (ret < 0) + return ret; + + if (f34->v7.has_display_cfg) { + f34->v7.config_area = v7_DP_CONFIG_AREA; + ret = rmi_f34v7_erase_config(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, u16 block_cnt, + u8 command) +{ + int ret; + u8 base; + __le16 length; + u16 transfer; + u16 max_transfer; + u16 remaining = block_cnt; + u16 block_number = 0; + u16 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, + &block_number, sizeof(block_number)); + if (ret < 0) { + dev_err(&f34->fn->dev, "%s: Failed to write block number\n", + __func__); + return ret; + } + + max_transfer = min(f34->v7.payload_length, + (u16)(PAGE_SIZE / f34->v7.block_size)); + + do { + transfer = min(remaining, max_transfer); + put_unaligned_le16(transfer, &length); + + 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: Write transfer length fail (%d remaining)\n", + __func__, remaining); + return ret; + } + + ret = rmi_f34v7_write_command(f34, command); + if (ret < 0) + return ret; + + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS); + if (ret < 0) { + dev_err(&f34->fn->dev, + "%s: Wait for idle failed (%d blks 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: Read block failed (%d blks 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, + const void *block_ptr, u16 block_cnt, + u8 command) +{ + int ret; + u8 base; + __le16 length; + u16 transfer; + u16 max_transfer; + u16 remaining = block_cnt; + u16 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, + &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 { + transfer = min(remaining, max_transfer); + put_unaligned_le16(transfer, &length); + + 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: Write transfer length fail (%d remaining)\n", + __func__, remaining); + return ret; + } + + ret = rmi_f34v7_write_command(f34, command); + if (ret < 0) + 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 writing data (%d blks 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 wait for idle (%d blks remaining)\n", + __func__, remaining); + return ret; + } + + block_ptr += (transfer * f34->v7.block_size); + remaining -= transfer; + } while (remaining); + + return 0; +} + +static int rmi_f34v7_write_config(struct f34_data *f34) +{ + return rmi_f34v7_write_f34v7_blocks(f34, f34->v7.config_data, + f34->v7.config_block_count, + v7_CMD_WRITE_CONFIG); +} + +static int rmi_f34v7_write_ui_config(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_config(f34); +} + +static int rmi_f34v7_write_dp_config(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_config(f34); +} + +static int rmi_f34v7_write_guest_code(struct f34_data *f34) +{ + return rmi_f34v7_write_f34v7_blocks(f34, f34->v7.img.guest_code.data, + f34->v7.img.guest_code.size / + f34->v7.block_size, + v7_CMD_WRITE_GUEST_CODE); +} + +static int rmi_f34v7_write_flash_config(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 config 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 config command written\n", __func__); + + ret = rmi_f34v7_wait_for_idle(f34, ENABLE_WAIT_MS); + if (ret < 0) + return ret; + + ret = rmi_f34v7_write_config(f34); + if (ret < 0) + return ret; + + return 0; +} + +static int rmi_f34v7_write_partition_table(struct f34_data *f34) +{ + u16 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_config(f34); + if (ret < 0) + return ret; + + ret = rmi_f34v7_write_flash_config(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_config(f34); + if (ret < 0) + return ret; + + return 0; +} + +static int rmi_f34v7_write_firmware(struct f34_data *f34) +{ + u16 blk_count; + + blk_count = f34->v7.img.ui_firmware.size / f34->v7.block_size; + + return rmi_f34v7_write_f34v7_blocks(f34, f34->v7.img.ui_firmware.data, + blk_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.has_display_cfg && + f34->v7.phyaddr.dp_config != f34->v7.img.phyaddr.dp_config) { + f34->v7.new_partition_table = true; + return; + } + + if (f34->v7.has_guest_code && + 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 void rmi_f34v7_parse_img_header_10_bl_container(struct f34_data *f34, + const void *image) +{ + int i; + int num_of_containers; + unsigned int addr; + unsigned int container_id; + unsigned int length; + const void *content; + const struct container_descriptor *descriptor; + + num_of_containers = f34->v7.img.bootloader.size / 4 - 1; + + for (i = 1; i <= num_of_containers; i++) { + addr = get_unaligned_le32(f34->v7.img.bootloader.data + i * 4); + descriptor = image + addr; + container_id = le16_to_cpu(descriptor->container_id); + content = image + le32_to_cpu(descriptor->content_address); + length = le32_to_cpu(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 int i; + unsigned int num_of_containers; + unsigned int addr; + unsigned int offset; + unsigned int container_id; + unsigned int length; + const void *image = f34->v7.image; + const u8 *content; + const struct container_descriptor *descriptor; + const struct image_header_10 *header = image; + + f34->v7.img.checksum = le32_to_cpu(header->checksum); + + rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: f34->v7.img.checksum=%X\n", + __func__, f34->v7.img.checksum); + + /* address of top level container */ + offset = le32_to_cpu(header->top_level_container_start_addr); + descriptor = image + offset; + + /* address of top level container content */ + offset = le32_to_cpu(descriptor->content_address); + num_of_containers = le32_to_cpu(descriptor->content_length) / 4; + + for (i = 0; i < num_of_containers; i++) { + addr = get_unaligned_le32(image + offset); + offset += 4; + descriptor = image + addr; + container_id = le16_to_cpu(descriptor->container_id); + content = image + le32_to_cpu(descriptor->content_address); + length = le32_to_cpu(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_img_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_display_cfg = 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 = + get_unaligned_le32(content + 4); + break; + default: + break; + } + } +} + +static int rmi_f34v7_parse_image_info(struct f34_data *f34) +{ + const struct image_header_10 *header = 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_config_size(f34); + if (ret < 0) + goto fail; + + if (f34->v7.has_display_cfg && + f34->v7.img.contains_display_cfg) { + ret = rmi_f34v7_check_dp_config_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_config_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_config(f34); + if (ret < 0) + goto fail; + + if (f34->v7.has_display_cfg && f34->v7.img.contains_display_cfg) { + dev_info(&f34->fn->dev, "Writing display config...\n"); + + ret = rmi_f34v7_write_dp_config(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 + V7_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 = 5; + } else if (f34->bootloader_id[1] == '6') { + f34->bl_version = 6; + } else if (f34->bootloader_id[1] == 7) { + f34->bl_version = 7; + } 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 = false; + return 0; +} diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c index cf805b960866..dea63e2db3e6 100644 --- a/drivers/input/rmi4/rmi_f54.c +++ b/drivers/input/rmi4/rmi_f54.c @@ -200,7 +200,7 @@ static int rmi_f54_request_report(struct rmi_function *fn, u8 report_type) error = rmi_write(rmi_dev, fn->fd.command_base_addr, F54_GET_REPORT); if (error < 0) - return error; + goto unlock; init_completion(&f54->cmd_done); @@ -209,15 +209,18 @@ static int rmi_f54_request_report(struct rmi_function *fn, u8 report_type) queue_delayed_work(f54->workqueue, &f54->work, 0); +unlock: mutex_unlock(&f54->data_mutex); - return 0; + return error; } static size_t rmi_f54_get_report_size(struct f54_data *f54) { - u8 rx = f54->num_rx_electrodes ? : f54->num_rx_electrodes; - u8 tx = f54->num_tx_electrodes ? : f54->num_tx_electrodes; + struct rmi_device *rmi_dev = f54->fn->rmi_dev; + struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev); + u8 rx = drv_data->num_rx_electrodes ? : f54->num_rx_electrodes; + u8 tx = drv_data->num_tx_electrodes ? : f54->num_tx_electrodes; size_t size; switch (rmi_f54_get_reptype(f54, f54->input)) { @@ -401,6 +404,10 @@ static int rmi_f54_vidioc_enum_input(struct file *file, void *priv, static int rmi_f54_set_input(struct f54_data *f54, unsigned int i) { + struct rmi_device *rmi_dev = f54->fn->rmi_dev; + struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev); + u8 rx = drv_data->num_rx_electrodes ? : f54->num_rx_electrodes; + u8 tx = drv_data->num_tx_electrodes ? : f54->num_tx_electrodes; struct v4l2_pix_format *f = &f54->format; enum rmi_f54_report_type reptype; int ret; @@ -415,8 +422,8 @@ static int rmi_f54_set_input(struct f54_data *f54, unsigned int i) f54->input = i; - f->width = f54->num_rx_electrodes; - f->height = f54->num_tx_electrodes; + f->width = rx; + f->height = tx; f->field = V4L2_FIELD_NONE; f->colorspace = V4L2_COLORSPACE_RAW; f->bytesperline = f->width * sizeof(u16); diff --git a/drivers/input/rmi4/rmi_f55.c b/drivers/input/rmi4/rmi_f55.c new file mode 100644 index 000000000000..37390ca6a924 --- /dev/null +++ b/drivers/input/rmi4/rmi_f55.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2012-2015 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/bitops.h> +#include <linux/kernel.h> +#include <linux/rmi.h> +#include <linux/slab.h> +#include "rmi_driver.h" + +#define F55_NAME "rmi4_f55" + +/* F55 data offsets */ +#define F55_NUM_RX_OFFSET 0 +#define F55_NUM_TX_OFFSET 1 +#define F55_PHYS_CHAR_OFFSET 2 + +/* Only read required query registers */ +#define F55_QUERY_LEN 3 + +/* F55 capabilities */ +#define F55_CAP_SENSOR_ASSIGN BIT(0) + +struct f55_data { + struct rmi_function *fn; + + u8 qry[F55_QUERY_LEN]; + u8 num_rx_electrodes; + u8 cfg_num_rx_electrodes; + u8 num_tx_electrodes; + u8 cfg_num_tx_electrodes; +}; + +static int rmi_f55_detect(struct rmi_function *fn) +{ + struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev); + struct f55_data *f55; + int error; + + f55 = dev_get_drvdata(&fn->dev); + + error = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr, + &f55->qry, sizeof(f55->qry)); + if (error) { + dev_err(&fn->dev, "%s: Failed to query F55 properties\n", + __func__); + return error; + } + + f55->num_rx_electrodes = f55->qry[F55_NUM_RX_OFFSET]; + f55->num_tx_electrodes = f55->qry[F55_NUM_TX_OFFSET]; + + f55->cfg_num_rx_electrodes = f55->num_rx_electrodes; + f55->cfg_num_tx_electrodes = f55->num_rx_electrodes; + + drv_data->num_rx_electrodes = f55->cfg_num_rx_electrodes; + drv_data->num_tx_electrodes = f55->cfg_num_rx_electrodes; + + if (f55->qry[F55_PHYS_CHAR_OFFSET] & F55_CAP_SENSOR_ASSIGN) { + int i, total; + u8 buf[256]; + + /* + * Calculate the number of enabled receive and transmit + * electrodes by reading F55:Ctrl1 (sensor receiver assignment) + * and F55:Ctrl2 (sensor transmitter assignment). The number of + * enabled electrodes is the sum of all field entries with a + * value other than 0xff. + */ + error = rmi_read_block(fn->rmi_dev, + fn->fd.control_base_addr + 1, + buf, f55->num_rx_electrodes); + if (!error) { + total = 0; + for (i = 0; i < f55->num_rx_electrodes; i++) { + if (buf[i] != 0xff) + total++; + } + f55->cfg_num_rx_electrodes = total; + drv_data->num_rx_electrodes = total; + } + + error = rmi_read_block(fn->rmi_dev, + fn->fd.control_base_addr + 2, + buf, f55->num_tx_electrodes); + if (!error) { + total = 0; + for (i = 0; i < f55->num_tx_electrodes; i++) { + if (buf[i] != 0xff) + total++; + } + f55->cfg_num_tx_electrodes = total; + drv_data->num_tx_electrodes = total; + } + } + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F55 num_rx_electrodes: %d (raw %d)\n", + f55->cfg_num_rx_electrodes, f55->num_rx_electrodes); + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F55 num_tx_electrodes: %d (raw %d)\n", + f55->cfg_num_tx_electrodes, f55->num_tx_electrodes); + + return 0; +} + +static int rmi_f55_probe(struct rmi_function *fn) +{ + struct f55_data *f55; + + f55 = devm_kzalloc(&fn->dev, sizeof(struct f55_data), GFP_KERNEL); + if (!f55) + return -ENOMEM; + + f55->fn = fn; + dev_set_drvdata(&fn->dev, f55); + + return rmi_f55_detect(fn); +} + +struct rmi_function_handler rmi_f55_handler = { + .driver = { + .name = F55_NAME, + }, + .func = 0x55, + .probe = rmi_f55_probe, +}; diff --git a/drivers/input/rmi4/rmi_i2c.c b/drivers/input/rmi4/rmi_i2c.c index 1ebc2c1debae..082306d7c207 100644 --- a/drivers/input/rmi4/rmi_i2c.c +++ b/drivers/input/rmi4/rmi_i2c.c @@ -9,7 +9,6 @@ #include <linux/i2c.h> #include <linux/rmi.h> -#include <linux/irq.h> #include <linux/of.h> #include <linux/delay.h> #include <linux/regulator/consumer.h> @@ -35,8 +34,6 @@ struct rmi_i2c_xport { struct mutex page_mutex; int page; - int irq; - u8 *tx_buf; size_t tx_buf_size; @@ -177,42 +174,6 @@ static const struct rmi_transport_ops rmi_i2c_ops = { .read_block = rmi_i2c_read_block, }; -static irqreturn_t rmi_i2c_irq(int irq, void *dev_id) -{ - struct rmi_i2c_xport *rmi_i2c = dev_id; - struct rmi_device *rmi_dev = rmi_i2c->xport.rmi_dev; - int ret; - - ret = rmi_process_interrupt_requests(rmi_dev); - if (ret) - rmi_dbg(RMI_DEBUG_XPORT, &rmi_dev->dev, - "Failed to process interrupt request: %d\n", ret); - - return IRQ_HANDLED; -} - -static int rmi_i2c_init_irq(struct i2c_client *client) -{ - struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); - int irq_flags = irqd_get_trigger_type(irq_get_irq_data(rmi_i2c->irq)); - int ret; - - if (!irq_flags) - irq_flags = IRQF_TRIGGER_LOW; - - ret = devm_request_threaded_irq(&client->dev, rmi_i2c->irq, NULL, - rmi_i2c_irq, irq_flags | IRQF_ONESHOT, client->name, - rmi_i2c); - if (ret < 0) { - dev_warn(&client->dev, "Failed to register interrupt %d\n", - rmi_i2c->irq); - - return ret; - } - - return 0; -} - #ifdef CONFIG_OF static const struct of_device_id rmi_i2c_of_match[] = { { .compatible = "syna,rmi4-i2c" }, @@ -255,8 +216,7 @@ static int rmi_i2c_probe(struct i2c_client *client, if (!client->dev.of_node && client_pdata) *pdata = *client_pdata; - if (client->irq > 0) - rmi_i2c->irq = client->irq; + pdata->irq = client->irq; rmi_dbg(RMI_DEBUG_XPORT, &client->dev, "Probing %s.\n", dev_name(&client->dev)); @@ -321,10 +281,6 @@ static int rmi_i2c_probe(struct i2c_client *client, if (retval) return retval; - retval = rmi_i2c_init_irq(client); - if (retval < 0) - return retval; - dev_info(&client->dev, "registered rmi i2c driver at %#04x.\n", client->addr); return 0; @@ -337,18 +293,10 @@ static int rmi_i2c_suspend(struct device *dev) struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); int ret; - ret = rmi_driver_suspend(rmi_i2c->xport.rmi_dev); + ret = rmi_driver_suspend(rmi_i2c->xport.rmi_dev, true); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); - disable_irq(rmi_i2c->irq); - if (device_may_wakeup(&client->dev)) { - ret = enable_irq_wake(rmi_i2c->irq); - if (!ret) - dev_warn(dev, "Failed to enable irq for wake: %d\n", - ret); - } - regulator_bulk_disable(ARRAY_SIZE(rmi_i2c->supplies), rmi_i2c->supplies); @@ -368,15 +316,7 @@ static int rmi_i2c_resume(struct device *dev) msleep(rmi_i2c->startup_delay); - enable_irq(rmi_i2c->irq); - if (device_may_wakeup(&client->dev)) { - ret = disable_irq_wake(rmi_i2c->irq); - if (!ret) - dev_warn(dev, "Failed to disable irq for wake: %d\n", - ret); - } - - ret = rmi_driver_resume(rmi_i2c->xport.rmi_dev); + ret = rmi_driver_resume(rmi_i2c->xport.rmi_dev, true); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); @@ -391,12 +331,10 @@ static int rmi_i2c_runtime_suspend(struct device *dev) struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); int ret; - ret = rmi_driver_suspend(rmi_i2c->xport.rmi_dev); + ret = rmi_driver_suspend(rmi_i2c->xport.rmi_dev, false); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); - disable_irq(rmi_i2c->irq); - regulator_bulk_disable(ARRAY_SIZE(rmi_i2c->supplies), rmi_i2c->supplies); @@ -416,9 +354,7 @@ static int rmi_i2c_runtime_resume(struct device *dev) msleep(rmi_i2c->startup_delay); - enable_irq(rmi_i2c->irq); - - ret = rmi_driver_resume(rmi_i2c->xport.rmi_dev); + ret = rmi_driver_resume(rmi_i2c->xport.rmi_dev, false); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); diff --git a/drivers/input/rmi4/rmi_smbus.c b/drivers/input/rmi4/rmi_smbus.c new file mode 100644 index 000000000000..76752555d809 --- /dev/null +++ b/drivers/input/rmi4/rmi_smbus.c @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2015 - 2016 Red Hat, Inc + * Copyright (c) 2011, 2012 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * 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/delay.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/kconfig.h> +#include <linux/lockdep.h> +#include <linux/module.h> +#include <linux/pm.h> +#include <linux/rmi.h> +#include <linux/slab.h> +#include "rmi_driver.h" + +#define SMB_PROTOCOL_VERSION_ADDRESS 0xfd +#define SMB_MAX_COUNT 32 +#define RMI_SMB2_MAP_SIZE 8 /* 8 entry of 4 bytes each */ +#define RMI_SMB2_MAP_FLAGS_WE 0x01 + +struct mapping_table_entry { + __le16 rmiaddr; + u8 readcount; + u8 flags; +}; + +struct rmi_smb_xport { + struct rmi_transport_dev xport; + struct i2c_client *client; + + struct mutex page_mutex; + int page; + u8 table_index; + struct mutex mappingtable_mutex; + struct mapping_table_entry mapping_table[RMI_SMB2_MAP_SIZE]; +}; + +static int rmi_smb_get_version(struct rmi_smb_xport *rmi_smb) +{ + struct i2c_client *client = rmi_smb->client; + int retval; + + /* Check if for SMBus new version device by reading version byte. */ + retval = i2c_smbus_read_byte_data(client, SMB_PROTOCOL_VERSION_ADDRESS); + if (retval < 0) { + dev_err(&client->dev, "failed to get SMBus version number!\n"); + return retval; + } + return retval + 1; +} + +/* SMB block write - wrapper over ic2_smb_write_block */ +static int smb_block_write(struct rmi_transport_dev *xport, + u8 commandcode, const void *buf, size_t len) +{ + struct rmi_smb_xport *rmi_smb = + container_of(xport, struct rmi_smb_xport, xport); + struct i2c_client *client = rmi_smb->client; + int retval; + + retval = i2c_smbus_write_block_data(client, commandcode, len, buf); + + rmi_dbg(RMI_DEBUG_XPORT, &client->dev, + "wrote %zd bytes at %#04x: %d (%*ph)\n", + len, commandcode, retval, (int)len, buf); + + return retval; +} + +/* + * The function to get command code for smbus operations and keeps + * records to the driver mapping table + */ +static int rmi_smb_get_command_code(struct rmi_transport_dev *xport, + u16 rmiaddr, int bytecount, bool isread, u8 *commandcode) +{ + struct rmi_smb_xport *rmi_smb = + container_of(xport, struct rmi_smb_xport, xport); + int i; + int retval; + struct mapping_table_entry mapping_data[1]; + + mutex_lock(&rmi_smb->mappingtable_mutex); + for (i = 0; i < RMI_SMB2_MAP_SIZE; i++) { + if (rmi_smb->mapping_table[i].rmiaddr == rmiaddr) { + if (isread) { + if (rmi_smb->mapping_table[i].readcount + == bytecount) { + *commandcode = i; + retval = 0; + goto exit; + } + } else { + if (rmi_smb->mapping_table[i].flags & + RMI_SMB2_MAP_FLAGS_WE) { + *commandcode = i; + retval = 0; + goto exit; + } + } + } + } + i = rmi_smb->table_index; + rmi_smb->table_index = (i + 1) % RMI_SMB2_MAP_SIZE; + + /* constructs mapping table data entry. 4 bytes each entry */ + memset(mapping_data, 0, sizeof(mapping_data)); + + mapping_data[0].rmiaddr = cpu_to_le16(rmiaddr); + mapping_data[0].readcount = bytecount; + mapping_data[0].flags = !isread ? RMI_SMB2_MAP_FLAGS_WE : 0; + + retval = smb_block_write(xport, i + 0x80, mapping_data, + sizeof(mapping_data)); + + if (retval < 0) { + /* + * if not written to device mapping table + * clear the driver mapping table records + */ + rmi_smb->mapping_table[i].rmiaddr = 0x0000; + rmi_smb->mapping_table[i].readcount = 0; + rmi_smb->mapping_table[i].flags = 0; + goto exit; + } + /* save to the driver level mapping table */ + rmi_smb->mapping_table[i].rmiaddr = rmiaddr; + rmi_smb->mapping_table[i].readcount = bytecount; + rmi_smb->mapping_table[i].flags = !isread ? RMI_SMB2_MAP_FLAGS_WE : 0; + *commandcode = i; + +exit: + mutex_unlock(&rmi_smb->mappingtable_mutex); + + return retval; +} + +static int rmi_smb_write_block(struct rmi_transport_dev *xport, u16 rmiaddr, + const void *databuff, size_t len) +{ + int retval = 0; + u8 commandcode; + struct rmi_smb_xport *rmi_smb = + container_of(xport, struct rmi_smb_xport, xport); + int cur_len = (int)len; + + mutex_lock(&rmi_smb->page_mutex); + + while (cur_len > 0) { + /* + * break into 32 bytes chunks to write get command code + */ + int block_len = min_t(int, len, SMB_MAX_COUNT); + + retval = rmi_smb_get_command_code(xport, rmiaddr, block_len, + false, &commandcode); + if (retval < 0) + goto exit; + + retval = smb_block_write(xport, commandcode, + databuff, block_len); + if (retval < 0) + goto exit; + + /* prepare to write next block of bytes */ + cur_len -= SMB_MAX_COUNT; + databuff += SMB_MAX_COUNT; + rmiaddr += SMB_MAX_COUNT; + } +exit: + mutex_unlock(&rmi_smb->page_mutex); + return retval; +} + +/* SMB block read - wrapper over ic2_smb_read_block */ +static int smb_block_read(struct rmi_transport_dev *xport, + u8 commandcode, void *buf, size_t len) +{ + struct rmi_smb_xport *rmi_smb = + container_of(xport, struct rmi_smb_xport, xport); + struct i2c_client *client = rmi_smb->client; + int retval; + + retval = i2c_smbus_read_block_data(client, commandcode, buf); + if (retval < 0) + return retval; + + return retval; +} + +static int rmi_smb_read_block(struct rmi_transport_dev *xport, u16 rmiaddr, + void *databuff, size_t len) +{ + struct rmi_smb_xport *rmi_smb = + container_of(xport, struct rmi_smb_xport, xport); + int retval; + u8 commandcode; + int cur_len = (int)len; + + mutex_lock(&rmi_smb->page_mutex); + memset(databuff, 0, len); + + while (cur_len > 0) { + /* break into 32 bytes chunks to write get command code */ + int block_len = min_t(int, cur_len, SMB_MAX_COUNT); + + retval = rmi_smb_get_command_code(xport, rmiaddr, block_len, + true, &commandcode); + if (retval < 0) + goto exit; + + retval = smb_block_read(xport, commandcode, + databuff, block_len); + if (retval < 0) + goto exit; + + /* prepare to read next block of bytes */ + cur_len -= SMB_MAX_COUNT; + databuff += SMB_MAX_COUNT; + rmiaddr += SMB_MAX_COUNT; + } + + retval = 0; + +exit: + mutex_unlock(&rmi_smb->page_mutex); + return retval; +} + +static void rmi_smb_clear_state(struct rmi_smb_xport *rmi_smb) +{ + /* the mapping table has been flushed, discard the current one */ + mutex_lock(&rmi_smb->mappingtable_mutex); + memset(rmi_smb->mapping_table, 0, sizeof(rmi_smb->mapping_table)); + mutex_unlock(&rmi_smb->mappingtable_mutex); +} + +static int rmi_smb_enable_smbus_mode(struct rmi_smb_xport *rmi_smb) +{ + int retval; + + /* we need to get the smbus version to activate the touchpad */ + retval = rmi_smb_get_version(rmi_smb); + if (retval < 0) + return retval; + + return 0; +} + +static int rmi_smb_reset(struct rmi_transport_dev *xport, u16 reset_addr) +{ + struct rmi_smb_xport *rmi_smb = + container_of(xport, struct rmi_smb_xport, xport); + + rmi_smb_clear_state(rmi_smb); + + /* + * we do not call the actual reset command, it has to be handled in + * PS/2 or there will be races between PS/2 and SMBus. + * PS/2 should ensure that a psmouse_reset is called before + * intializing the device and after it has been removed to be in a known + * state. + */ + return rmi_smb_enable_smbus_mode(rmi_smb); +} + +static const struct rmi_transport_ops rmi_smb_ops = { + .write_block = rmi_smb_write_block, + .read_block = rmi_smb_read_block, + .reset = rmi_smb_reset, +}; + +static int rmi_smb_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct rmi_device_platform_data *pdata = dev_get_platdata(&client->dev); + struct rmi_smb_xport *rmi_smb; + int retval; + int smbus_version; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BLOCK_DATA | + I2C_FUNC_SMBUS_HOST_NOTIFY)) { + dev_err(&client->dev, + "adapter does not support required functionality.\n"); + return -ENODEV; + } + + if (client->irq <= 0) { + dev_err(&client->dev, "no IRQ provided, giving up.\n"); + return client->irq ? client->irq : -ENODEV; + } + + rmi_smb = devm_kzalloc(&client->dev, sizeof(struct rmi_smb_xport), + GFP_KERNEL); + if (!rmi_smb) + return -ENOMEM; + + if (!pdata) { + dev_err(&client->dev, "no platform data, aborting\n"); + return -ENOMEM; + } + + rmi_dbg(RMI_DEBUG_XPORT, &client->dev, "Probing %s.\n", + dev_name(&client->dev)); + + rmi_smb->client = client; + mutex_init(&rmi_smb->page_mutex); + mutex_init(&rmi_smb->mappingtable_mutex); + + rmi_smb->xport.dev = &client->dev; + rmi_smb->xport.pdata = *pdata; + rmi_smb->xport.pdata.irq = client->irq; + rmi_smb->xport.proto_name = "smb2"; + rmi_smb->xport.ops = &rmi_smb_ops; + + retval = rmi_smb_get_version(rmi_smb); + if (retval < 0) + return retval; + + smbus_version = retval; + rmi_dbg(RMI_DEBUG_XPORT, &client->dev, "Smbus version is %d", + smbus_version); + + if (smbus_version != 2) { + dev_err(&client->dev, "Unrecognized SMB version %d.\n", + smbus_version); + return -ENODEV; + } + + i2c_set_clientdata(client, rmi_smb); + + retval = rmi_register_transport_device(&rmi_smb->xport); + if (retval) { + dev_err(&client->dev, "Failed to register transport driver at 0x%.2X.\n", + client->addr); + i2c_set_clientdata(client, NULL); + return retval; + } + + dev_info(&client->dev, "registered rmi smb driver at %#04x.\n", + client->addr); + return 0; + +} + +static int rmi_smb_remove(struct i2c_client *client) +{ + struct rmi_smb_xport *rmi_smb = i2c_get_clientdata(client); + + rmi_unregister_transport_device(&rmi_smb->xport); + + return 0; +} + +static int __maybe_unused rmi_smb_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rmi_smb_xport *rmi_smb = i2c_get_clientdata(client); + int ret; + + ret = rmi_driver_suspend(rmi_smb->xport.rmi_dev, true); + if (ret) + dev_warn(dev, "Failed to suspend device: %d\n", ret); + + return ret; +} + +static int __maybe_unused rmi_smb_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rmi_smb_xport *rmi_smb = i2c_get_clientdata(client); + int ret; + + ret = rmi_driver_suspend(rmi_smb->xport.rmi_dev, false); + if (ret) + dev_warn(dev, "Failed to suspend device: %d\n", ret); + + return ret; +} + +static int __maybe_unused rmi_smb_resume(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct rmi_smb_xport *rmi_smb = i2c_get_clientdata(client); + struct rmi_device *rmi_dev = rmi_smb->xport.rmi_dev; + int ret; + + rmi_smb_reset(&rmi_smb->xport, 0); + + rmi_reset(rmi_dev); + + ret = rmi_driver_resume(rmi_smb->xport.rmi_dev, true); + if (ret) + dev_warn(dev, "Failed to resume device: %d\n", ret); + + return 0; +} + +static int __maybe_unused rmi_smb_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rmi_smb_xport *rmi_smb = i2c_get_clientdata(client); + int ret; + + ret = rmi_driver_resume(rmi_smb->xport.rmi_dev, false); + if (ret) + dev_warn(dev, "Failed to resume device: %d\n", ret); + + return 0; +} + +static const struct dev_pm_ops rmi_smb_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rmi_smb_suspend, rmi_smb_resume) + SET_RUNTIME_PM_OPS(rmi_smb_runtime_suspend, rmi_smb_runtime_resume, + NULL) +}; + +static const struct i2c_device_id rmi_id[] = { + { "rmi4_smbus", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rmi_id); + +static struct i2c_driver rmi_smb_driver = { + .driver = { + .name = "rmi4_smbus", + .pm = &rmi_smb_pm, + }, + .id_table = rmi_id, + .probe = rmi_smb_probe, + .remove = rmi_smb_remove, +}; + +module_i2c_driver(rmi_smb_driver); + +MODULE_AUTHOR("Andrew Duggan <aduggan@synaptics.com>"); +MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@redhat.com>"); +MODULE_DESCRIPTION("RMI4 SMBus driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/rmi4/rmi_spi.c b/drivers/input/rmi4/rmi_spi.c index 4ebef607e214..69548d7d1f10 100644 --- a/drivers/input/rmi4/rmi_spi.c +++ b/drivers/input/rmi4/rmi_spi.c @@ -12,7 +12,6 @@ #include <linux/rmi.h> #include <linux/slab.h> #include <linux/spi/spi.h> -#include <linux/irq.h> #include <linux/of.h> #include "rmi_driver.h" @@ -44,8 +43,6 @@ struct rmi_spi_xport { struct mutex page_mutex; int page; - int irq; - u8 *rx_buf; u8 *tx_buf; int xfer_buf_size; @@ -326,41 +323,6 @@ static const struct rmi_transport_ops rmi_spi_ops = { .read_block = rmi_spi_read_block, }; -static irqreturn_t rmi_spi_irq(int irq, void *dev_id) -{ - struct rmi_spi_xport *rmi_spi = dev_id; - struct rmi_device *rmi_dev = rmi_spi->xport.rmi_dev; - int ret; - - ret = rmi_process_interrupt_requests(rmi_dev); - if (ret) - rmi_dbg(RMI_DEBUG_XPORT, &rmi_dev->dev, - "Failed to process interrupt request: %d\n", ret); - - return IRQ_HANDLED; -} - -static int rmi_spi_init_irq(struct spi_device *spi) -{ - struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); - int irq_flags = irqd_get_trigger_type(irq_get_irq_data(rmi_spi->irq)); - int ret; - - if (!irq_flags) - irq_flags = IRQF_TRIGGER_LOW; - - ret = devm_request_threaded_irq(&spi->dev, rmi_spi->irq, NULL, - rmi_spi_irq, irq_flags | IRQF_ONESHOT, - dev_name(&spi->dev), rmi_spi); - if (ret < 0) { - dev_warn(&spi->dev, "Failed to register interrupt %d\n", - rmi_spi->irq); - return ret; - } - - return 0; -} - #ifdef CONFIG_OF static int rmi_spi_of_probe(struct spi_device *spi, struct rmi_device_platform_data *pdata) @@ -440,8 +402,7 @@ static int rmi_spi_probe(struct spi_device *spi) return retval; } - if (spi->irq > 0) - rmi_spi->irq = spi->irq; + pdata->irq = spi->irq; rmi_spi->spi = spi; mutex_init(&rmi_spi->page_mutex); @@ -477,10 +438,6 @@ static int rmi_spi_probe(struct spi_device *spi) if (retval) return retval; - retval = rmi_spi_init_irq(spi); - if (retval < 0) - return retval; - dev_info(&spi->dev, "registered RMI SPI driver\n"); return 0; } @@ -492,17 +449,10 @@ static int rmi_spi_suspend(struct device *dev) struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); int ret; - ret = rmi_driver_suspend(rmi_spi->xport.rmi_dev); + ret = rmi_driver_suspend(rmi_spi->xport.rmi_dev, true); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); - disable_irq(rmi_spi->irq); - if (device_may_wakeup(&spi->dev)) { - ret = enable_irq_wake(rmi_spi->irq); - if (!ret) - dev_warn(dev, "Failed to enable irq for wake: %d\n", - ret); - } return ret; } @@ -512,15 +462,7 @@ static int rmi_spi_resume(struct device *dev) struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); int ret; - enable_irq(rmi_spi->irq); - if (device_may_wakeup(&spi->dev)) { - ret = disable_irq_wake(rmi_spi->irq); - if (!ret) - dev_warn(dev, "Failed to disable irq for wake: %d\n", - ret); - } - - ret = rmi_driver_resume(rmi_spi->xport.rmi_dev); + ret = rmi_driver_resume(rmi_spi->xport.rmi_dev, true); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); @@ -535,12 +477,10 @@ static int rmi_spi_runtime_suspend(struct device *dev) struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); int ret; - ret = rmi_driver_suspend(rmi_spi->xport.rmi_dev); + ret = rmi_driver_suspend(rmi_spi->xport.rmi_dev, false); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); - disable_irq(rmi_spi->irq); - return 0; } @@ -550,9 +490,7 @@ static int rmi_spi_runtime_resume(struct device *dev) struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); int ret; - enable_irq(rmi_spi->irq); - - ret = rmi_driver_resume(rmi_spi->xport.rmi_dev); + ret = rmi_driver_resume(rmi_spi->xport.rmi_dev, false); if (ret) dev_warn(dev, "Failed to resume device: %d\n", ret); |