xref: /linux/drivers/media/usb/as102/as102_fw.c (revision 58e16d792a6a8c6b750f637a4649967fcac853dc)
1*3e0a4e85SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
241b44e04SPierrick Hascoet /*
341b44e04SPierrick Hascoet  * Abilis Systems Single DVB-T Receiver
441b44e04SPierrick Hascoet  * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
5e54d081dSDevin Heitmueller  * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
641b44e04SPierrick Hascoet  */
741b44e04SPierrick Hascoet #include <linux/kernel.h>
841b44e04SPierrick Hascoet #include <linux/errno.h>
941b44e04SPierrick Hascoet #include <linux/ctype.h>
1041b44e04SPierrick Hascoet #include <linux/delay.h>
1141b44e04SPierrick Hascoet #include <linux/firmware.h>
1241b44e04SPierrick Hascoet 
1341b44e04SPierrick Hascoet #include "as102_drv.h"
1441b44e04SPierrick Hascoet #include "as102_fw.h"
1541b44e04SPierrick Hascoet 
16f1ff7c49SMauro Dreissig static const char as102_st_fw1[] = "as102_data1_st.hex";
17f1ff7c49SMauro Dreissig static const char as102_st_fw2[] = "as102_data2_st.hex";
18f1ff7c49SMauro Dreissig static const char as102_dt_fw1[] = "as102_data1_dt.hex";
19f1ff7c49SMauro Dreissig static const char as102_dt_fw2[] = "as102_data2_dt.hex";
2041b44e04SPierrick Hascoet 
atohx(unsigned char * dst,char * src)21e54d081dSDevin Heitmueller static unsigned char atohx(unsigned char *dst, char *src)
22e54d081dSDevin Heitmueller {
2341b44e04SPierrick Hascoet 	unsigned char value = 0;
2441b44e04SPierrick Hascoet 
2541b44e04SPierrick Hascoet 	char msb = tolower(*src) - '0';
2641b44e04SPierrick Hascoet 	char lsb = tolower(*(src + 1)) - '0';
2741b44e04SPierrick Hascoet 
2841b44e04SPierrick Hascoet 	if (msb > 9)
2941b44e04SPierrick Hascoet 		msb -= 7;
3041b44e04SPierrick Hascoet 	if (lsb > 9)
3141b44e04SPierrick Hascoet 		lsb -= 7;
3241b44e04SPierrick Hascoet 
3341b44e04SPierrick Hascoet 	*dst = value = ((msb & 0xF) << 4) | (lsb & 0xF);
3441b44e04SPierrick Hascoet 	return value;
3541b44e04SPierrick Hascoet }
3641b44e04SPierrick Hascoet 
3741b44e04SPierrick Hascoet /*
3841b44e04SPierrick Hascoet  * Parse INTEL HEX firmware file to extract address and data.
3941b44e04SPierrick Hascoet  */
parse_hex_line(unsigned char * fw_data,unsigned char * addr,unsigned char * data,int * dataLength,unsigned char * addr_has_changed)4041b44e04SPierrick Hascoet static int parse_hex_line(unsigned char *fw_data, unsigned char *addr,
4141b44e04SPierrick Hascoet 			  unsigned char *data, int *dataLength,
4241b44e04SPierrick Hascoet 			  unsigned char *addr_has_changed) {
4341b44e04SPierrick Hascoet 
4441b44e04SPierrick Hascoet 	int count = 0;
4541b44e04SPierrick Hascoet 	unsigned char *src, dst;
4641b44e04SPierrick Hascoet 
4741b44e04SPierrick Hascoet 	if (*fw_data++ != ':') {
487d462fe5SSylwester Nawrocki 		pr_err("invalid firmware file\n");
4941b44e04SPierrick Hascoet 		return -EFAULT;
5041b44e04SPierrick Hascoet 	}
5141b44e04SPierrick Hascoet 
5241b44e04SPierrick Hascoet 	/* locate end of line */
5341b44e04SPierrick Hascoet 	for (src = fw_data; *src != '\n'; src += 2) {
5441b44e04SPierrick Hascoet 		atohx(&dst, src);
5541b44e04SPierrick Hascoet 		/* parse line to split addr / data */
5641b44e04SPierrick Hascoet 		switch (count) {
5741b44e04SPierrick Hascoet 		case 0:
5841b44e04SPierrick Hascoet 			*dataLength = dst;
5941b44e04SPierrick Hascoet 			break;
6041b44e04SPierrick Hascoet 		case 1:
6141b44e04SPierrick Hascoet 			addr[2] = dst;
6241b44e04SPierrick Hascoet 			break;
6341b44e04SPierrick Hascoet 		case 2:
6441b44e04SPierrick Hascoet 			addr[3] = dst;
6541b44e04SPierrick Hascoet 			break;
6641b44e04SPierrick Hascoet 		case 3:
6741b44e04SPierrick Hascoet 			/* check if data is an address */
6841b44e04SPierrick Hascoet 			if (dst == 0x04)
6941b44e04SPierrick Hascoet 				*addr_has_changed = 1;
7041b44e04SPierrick Hascoet 			else
7141b44e04SPierrick Hascoet 				*addr_has_changed = 0;
7241b44e04SPierrick Hascoet 			break;
7341b44e04SPierrick Hascoet 		case  4:
7441b44e04SPierrick Hascoet 		case  5:
75e54d081dSDevin Heitmueller 			if (*addr_has_changed)
7641b44e04SPierrick Hascoet 				addr[(count - 4)] = dst;
77e54d081dSDevin Heitmueller 			else
7841b44e04SPierrick Hascoet 				data[(count - 4)] = dst;
7941b44e04SPierrick Hascoet 			break;
8041b44e04SPierrick Hascoet 		default:
8141b44e04SPierrick Hascoet 			data[(count - 4)] = dst;
8241b44e04SPierrick Hascoet 			break;
8341b44e04SPierrick Hascoet 		}
8441b44e04SPierrick Hascoet 		count++;
8541b44e04SPierrick Hascoet 	}
8641b44e04SPierrick Hascoet 
8741b44e04SPierrick Hascoet 	/* return read value + ':' + '\n' */
88e54d081dSDevin Heitmueller 	return (count * 2) + 2;
8941b44e04SPierrick Hascoet }
9041b44e04SPierrick Hascoet 
as102_firmware_upload(struct as10x_bus_adapter_t * bus_adap,unsigned char * cmd,const struct firmware * firmware)9134490a0aSSylwester Nawrocki static int as102_firmware_upload(struct as10x_bus_adapter_t *bus_adap,
9241b44e04SPierrick Hascoet 				 unsigned char *cmd,
9341b44e04SPierrick Hascoet 				 const struct firmware *firmware) {
9441b44e04SPierrick Hascoet 
95b3120d2cSMichele Baldessari 	struct as10x_fw_pkt_t *fw_pkt;
9641b44e04SPierrick Hascoet 	int total_read_bytes = 0, errno = 0;
9741b44e04SPierrick Hascoet 	unsigned char addr_has_changed = 0;
9841b44e04SPierrick Hascoet 
99b3120d2cSMichele Baldessari 	fw_pkt = kmalloc(sizeof(*fw_pkt), GFP_KERNEL);
100b3120d2cSMichele Baldessari 	if (!fw_pkt)
101b3120d2cSMichele Baldessari 		return -ENOMEM;
102b3120d2cSMichele Baldessari 
103b3120d2cSMichele Baldessari 
10441b44e04SPierrick Hascoet 	for (total_read_bytes = 0; total_read_bytes < firmware->size; ) {
10541b44e04SPierrick Hascoet 		int read_bytes = 0, data_len = 0;
10641b44e04SPierrick Hascoet 
10741b44e04SPierrick Hascoet 		/* parse intel hex line */
10841b44e04SPierrick Hascoet 		read_bytes = parse_hex_line(
10941b44e04SPierrick Hascoet 				(u8 *) (firmware->data + total_read_bytes),
110b3120d2cSMichele Baldessari 				fw_pkt->raw.address,
111b3120d2cSMichele Baldessari 				fw_pkt->raw.data,
11241b44e04SPierrick Hascoet 				&data_len,
11341b44e04SPierrick Hascoet 				&addr_has_changed);
11441b44e04SPierrick Hascoet 
115e54d081dSDevin Heitmueller 		if (read_bytes <= 0)
11641b44e04SPierrick Hascoet 			goto error;
11741b44e04SPierrick Hascoet 
11841b44e04SPierrick Hascoet 		/* detect the end of file */
119e54d081dSDevin Heitmueller 		total_read_bytes += read_bytes;
120e54d081dSDevin Heitmueller 		if (total_read_bytes == firmware->size) {
121b3120d2cSMichele Baldessari 			fw_pkt->u.request[0] = 0x00;
122b3120d2cSMichele Baldessari 			fw_pkt->u.request[1] = 0x03;
12341b44e04SPierrick Hascoet 
12441b44e04SPierrick Hascoet 			/* send EOF command */
125e54d081dSDevin Heitmueller 			errno = bus_adap->ops->upload_fw_pkt(bus_adap,
126e54d081dSDevin Heitmueller 							     (uint8_t *)
127b3120d2cSMichele Baldessari 							     fw_pkt, 2, 0);
128e54d081dSDevin Heitmueller 			if (errno < 0)
12941b44e04SPierrick Hascoet 				goto error;
13041b44e04SPierrick Hascoet 		} else {
13141b44e04SPierrick Hascoet 			if (!addr_has_changed) {
13241b44e04SPierrick Hascoet 				/* prepare command to send */
133b3120d2cSMichele Baldessari 				fw_pkt->u.request[0] = 0x00;
134b3120d2cSMichele Baldessari 				fw_pkt->u.request[1] = 0x01;
13541b44e04SPierrick Hascoet 
136b3120d2cSMichele Baldessari 				data_len += sizeof(fw_pkt->u.request);
137b3120d2cSMichele Baldessari 				data_len += sizeof(fw_pkt->raw.address);
13841b44e04SPierrick Hascoet 
13941b44e04SPierrick Hascoet 				/* send cmd to device */
140e54d081dSDevin Heitmueller 				errno = bus_adap->ops->upload_fw_pkt(bus_adap,
141e54d081dSDevin Heitmueller 								     (uint8_t *)
142b3120d2cSMichele Baldessari 								     fw_pkt,
143e54d081dSDevin Heitmueller 								     data_len,
144e54d081dSDevin Heitmueller 								     0);
145e54d081dSDevin Heitmueller 				if (errno < 0)
14641b44e04SPierrick Hascoet 					goto error;
14741b44e04SPierrick Hascoet 			}
14841b44e04SPierrick Hascoet 		}
14941b44e04SPierrick Hascoet 	}
15041b44e04SPierrick Hascoet error:
151b3120d2cSMichele Baldessari 	kfree(fw_pkt);
15241b44e04SPierrick Hascoet 	return (errno == 0) ? total_read_bytes : errno;
15341b44e04SPierrick Hascoet }
15441b44e04SPierrick Hascoet 
as102_fw_upload(struct as10x_bus_adapter_t * bus_adap)15534490a0aSSylwester Nawrocki int as102_fw_upload(struct as10x_bus_adapter_t *bus_adap)
156e54d081dSDevin Heitmueller {
15741b44e04SPierrick Hascoet 	int errno = -EFAULT;
1584ae2e594SJesper Juhl 	const struct firmware *firmware = NULL;
15941b44e04SPierrick Hascoet 	unsigned char *cmd_buf = NULL;
160f1ff7c49SMauro Dreissig 	const char *fw1, *fw2;
16141b44e04SPierrick Hascoet 	struct usb_device *dev = bus_adap->usb_dev;
16282aae98dSSylwester Nawrocki 
16341b44e04SPierrick Hascoet 	/* select fw file to upload */
16441b44e04SPierrick Hascoet 	if (dual_tuner) {
16541b44e04SPierrick Hascoet 		fw1 = as102_dt_fw1;
16641b44e04SPierrick Hascoet 		fw2 = as102_dt_fw2;
16741b44e04SPierrick Hascoet 	} else {
16841b44e04SPierrick Hascoet 		fw1 = as102_st_fw1;
16941b44e04SPierrick Hascoet 		fw2 = as102_st_fw2;
17041b44e04SPierrick Hascoet 	}
17141b44e04SPierrick Hascoet 
17241b44e04SPierrick Hascoet 	/* allocate buffer to store firmware upload command and data */
173e54d081dSDevin Heitmueller 	cmd_buf = kzalloc(MAX_FW_PKT_SIZE, GFP_KERNEL);
174e54d081dSDevin Heitmueller 	if (cmd_buf == NULL) {
17541b44e04SPierrick Hascoet 		errno = -ENOMEM;
17641b44e04SPierrick Hascoet 		goto error;
17741b44e04SPierrick Hascoet 	}
17841b44e04SPierrick Hascoet 
17941b44e04SPierrick Hascoet 	/* request kernel to locate firmware file: part1 */
180e54d081dSDevin Heitmueller 	errno = request_firmware(&firmware, fw1, &dev->dev);
181e54d081dSDevin Heitmueller 	if (errno < 0) {
1827d462fe5SSylwester Nawrocki 		pr_err("%s: unable to locate firmware file: %s\n",
18341b44e04SPierrick Hascoet 		       DRIVER_NAME, fw1);
18441b44e04SPierrick Hascoet 		goto error;
18541b44e04SPierrick Hascoet 	}
18641b44e04SPierrick Hascoet 
18741b44e04SPierrick Hascoet 	/* initiate firmware upload */
188e54d081dSDevin Heitmueller 	errno = as102_firmware_upload(bus_adap, cmd_buf, firmware);
189e54d081dSDevin Heitmueller 	if (errno < 0) {
1907d462fe5SSylwester Nawrocki 		pr_err("%s: error during firmware upload part1\n",
19141b44e04SPierrick Hascoet 		       DRIVER_NAME);
19241b44e04SPierrick Hascoet 		goto error;
19341b44e04SPierrick Hascoet 	}
19441b44e04SPierrick Hascoet 
1957d462fe5SSylwester Nawrocki 	pr_info("%s: firmware: %s loaded with success\n",
19641b44e04SPierrick Hascoet 		DRIVER_NAME, fw1);
19741b44e04SPierrick Hascoet 	release_firmware(firmware);
198b7718522SChristian Engelmayer 	firmware = NULL;
19941b44e04SPierrick Hascoet 
20041b44e04SPierrick Hascoet 	/* wait for boot to complete */
20141b44e04SPierrick Hascoet 	mdelay(100);
20241b44e04SPierrick Hascoet 
20341b44e04SPierrick Hascoet 	/* request kernel to locate firmware file: part2 */
204e54d081dSDevin Heitmueller 	errno = request_firmware(&firmware, fw2, &dev->dev);
205e54d081dSDevin Heitmueller 	if (errno < 0) {
2067d462fe5SSylwester Nawrocki 		pr_err("%s: unable to locate firmware file: %s\n",
20741b44e04SPierrick Hascoet 		       DRIVER_NAME, fw2);
20841b44e04SPierrick Hascoet 		goto error;
20941b44e04SPierrick Hascoet 	}
21041b44e04SPierrick Hascoet 
21141b44e04SPierrick Hascoet 	/* initiate firmware upload */
212e54d081dSDevin Heitmueller 	errno = as102_firmware_upload(bus_adap, cmd_buf, firmware);
213e54d081dSDevin Heitmueller 	if (errno < 0) {
2147d462fe5SSylwester Nawrocki 		pr_err("%s: error during firmware upload part2\n",
21541b44e04SPierrick Hascoet 		       DRIVER_NAME);
21641b44e04SPierrick Hascoet 		goto error;
21741b44e04SPierrick Hascoet 	}
21841b44e04SPierrick Hascoet 
2197d462fe5SSylwester Nawrocki 	pr_info("%s: firmware: %s loaded with success\n",
22041b44e04SPierrick Hascoet 		DRIVER_NAME, fw2);
22141b44e04SPierrick Hascoet error:
22241b44e04SPierrick Hascoet 	kfree(cmd_buf);
22341b44e04SPierrick Hascoet 	release_firmware(firmware);
22457753767SSylwester Nawrocki 
22541b44e04SPierrick Hascoet 	return errno;
22641b44e04SPierrick Hascoet }
227