Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5191d88a authored by Nick Dyer's avatar Nick Dyer Committed by Dmitry Torokhov
Browse files

Input: synaptics-rmi4 - add support for F34 V7 bootloader



Port firmware update code from Samsung Galaxy S7 driver into
mainline framework.

This patch has been tested on Synaptics S7813.

Signed-off-by: default avatarNick Dyer <nick@shmanahar.org>
Tested-by: default avatarChris Healy <cphealy@gmail.com>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 5d244f7e
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -8,7 +8,7 @@ rmi_core-$(CONFIG_RMI4_F03) += rmi_f03.o
rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o
rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o
rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o
rmi_core-$(CONFIG_RMI4_F34) += rmi_f34.o
rmi_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

+33 −23
Original line number Diff line number Diff line
@@ -544,7 +544,7 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
	else
		*empty_pages = 0;

	return (data->f01_bootloader_mode || *empty_pages >= 2) ?
	return (data->bootloader_mode || *empty_pages >= 2) ?
					RMI_SCAN_DONE : RMI_SCAN_CONTINUE;
}

@@ -749,41 +749,49 @@ bool rmi_register_desc_has_subpacket(const struct rmi_register_desc_item *item,
				subpacket) == subpacket;
}

/* Indicates that flash programming is enabled (bootloader mode). */
#define RMI_F01_STATUS_BOOTLOADER(status)	(!!((status) & 0x40))

/*
 * Given the PDT entry for F01, read the device status register to determine
 * if we're stuck in bootloader mode or not.
 *
 */
static int rmi_check_bootloader_mode(struct rmi_device *rmi_dev,
				     const struct pdt_entry *pdt)
{
	int error;
	u8 device_status;
	struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
	int ret;
	u8 status;

	error = rmi_read(rmi_dev, pdt->data_base_addr + pdt->page_start,
			 &device_status);
	if (error) {
	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 device status: %d.\n", error);
		return error;
				"Failed to read F34 status: %d.\n", ret);
			return ret;
		}

	return RMI_F01_STATUS_BOOTLOADER(device_status);
		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 0;
}

static int rmi_count_irqs(struct rmi_device *rmi_dev,
			 void *ctx, const struct pdt_entry *pdt)
{
	struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
	int *irq_count = ctx;
	int ret;

	*irq_count += pdt->interrupt_source_count;
	if (pdt->function_number == 0x01)
		data->f01_bootloader_mode =
			rmi_check_bootloader_mode(rmi_dev, pdt);

	ret = rmi_check_bootloader_mode(rmi_dev, pdt);
	if (ret < 0)
		return ret;

	return RMI_SCAN_CONTINUE;
}
@@ -1024,13 +1032,15 @@ int rmi_probe_interrupts(struct rmi_driver_data *data)
	 */
	rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Counting IRQs.\n", __func__);
	irq_count = 0;
	data->bootloader_mode = false;

	retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_count_irqs);
	if (retval < 0) {
		dev_err(dev, "IRQ counting failed with code %d.\n", retval);
		return retval;
	}

	if (data->f01_bootloader_mode)
	if (data->bootloader_mode)
		dev_warn(&rmi_dev->dev, "Device in bootloader mode.\n");

	data->irq_count = irq_count;
+33 −7
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
#include <linux/firmware.h>
#include <asm/unaligned.h>
#include <asm/unaligned.h>
#include <linux/bitops.h>

#include "rmi_driver.h"
#include "rmi_f34.h"
@@ -105,6 +106,9 @@ 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);
@@ -292,16 +296,23 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
		return -EINVAL;
	}

	/* Only version 0 currently supported */
	if (data->f34_container->fd.function_version != 0) {
	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;
	}

	f34 = dev_get_drvdata(&data->f34_container->dev);

	/* 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;
@@ -319,7 +330,7 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
	if (ret)
		return ret;

	if (!data->f01_bootloader_mode || !data->f34_container) {
	if (!data->bootloader_mode || !data->f34_container) {
		dev_warn(dev, "%s: No F34 present or not in bootloader!\n",
				__func__);
		return -EINVAL;
@@ -330,6 +341,9 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
	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);
@@ -363,6 +377,9 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
	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)
@@ -411,6 +428,7 @@ 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);
@@ -420,6 +438,14 @@ static int rmi_f34_probe(struct rmi_function *fn)
	f34->fn = fn;
	dev_set_drvdata(&fn->dev, f34);

	/* v5 code only supported version 0, try V7 probe */
	if (version > 0)
		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) {
+248 −2
Original line number Diff line number Diff line
@@ -33,6 +33,216 @@

#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];
@@ -56,13 +266,49 @@ struct f34v5_data {
	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[9];
	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 */
+1372 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading