xref: /openbmc/linux/drivers/media/usb/as102/as102_fw.c (revision 58e16d792a6a8c6b750f637a4649967fcac853dc)
1*3e0a4e85SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2d6317c68SMauro Carvalho Chehab /*
3d6317c68SMauro Carvalho Chehab  * Abilis Systems Single DVB-T Receiver
4d6317c68SMauro Carvalho Chehab  * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
5d6317c68SMauro Carvalho Chehab  * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
6d6317c68SMauro Carvalho Chehab  */
7d6317c68SMauro Carvalho Chehab #include <linux/kernel.h>
8d6317c68SMauro Carvalho Chehab #include <linux/errno.h>
9d6317c68SMauro Carvalho Chehab #include <linux/ctype.h>
10d6317c68SMauro Carvalho Chehab #include <linux/delay.h>
11d6317c68SMauro Carvalho Chehab #include <linux/firmware.h>
12d6317c68SMauro Carvalho Chehab 
13d6317c68SMauro Carvalho Chehab #include "as102_drv.h"
14d6317c68SMauro Carvalho Chehab #include "as102_fw.h"
15d6317c68SMauro Carvalho Chehab 
16d6317c68SMauro Carvalho Chehab static const char as102_st_fw1[] = "as102_data1_st.hex";
17d6317c68SMauro Carvalho Chehab static const char as102_st_fw2[] = "as102_data2_st.hex";
18d6317c68SMauro Carvalho Chehab static const char as102_dt_fw1[] = "as102_data1_dt.hex";
19d6317c68SMauro Carvalho Chehab static const char as102_dt_fw2[] = "as102_data2_dt.hex";
20d6317c68SMauro Carvalho Chehab 
atohx(unsigned char * dst,char * src)21d6317c68SMauro Carvalho Chehab static unsigned char atohx(unsigned char *dst, char *src)
22d6317c68SMauro Carvalho Chehab {
23d6317c68SMauro Carvalho Chehab 	unsigned char value = 0;
24d6317c68SMauro Carvalho Chehab 
25d6317c68SMauro Carvalho Chehab 	char msb = tolower(*src) - '0';
26d6317c68SMauro Carvalho Chehab 	char lsb = tolower(*(src + 1)) - '0';
27d6317c68SMauro Carvalho Chehab 
28d6317c68SMauro Carvalho Chehab 	if (msb > 9)
29d6317c68SMauro Carvalho Chehab 		msb -= 7;
30d6317c68SMauro Carvalho Chehab 	if (lsb > 9)
31d6317c68SMauro Carvalho Chehab 		lsb -= 7;
32d6317c68SMauro Carvalho Chehab 
33d6317c68SMauro Carvalho Chehab 	*dst = value = ((msb & 0xF) << 4) | (lsb & 0xF);
34d6317c68SMauro Carvalho Chehab 	return value;
35d6317c68SMauro Carvalho Chehab }
36d6317c68SMauro Carvalho Chehab 
37d6317c68SMauro Carvalho Chehab /*
38d6317c68SMauro Carvalho Chehab  * Parse INTEL HEX firmware file to extract address and data.
39d6317c68SMauro Carvalho Chehab  */
parse_hex_line(unsigned char * fw_data,unsigned char * addr,unsigned char * data,int * dataLength,unsigned char * addr_has_changed)40d6317c68SMauro Carvalho Chehab static int parse_hex_line(unsigned char *fw_data, unsigned char *addr,
41d6317c68SMauro Carvalho Chehab 			  unsigned char *data, int *dataLength,
42d6317c68SMauro Carvalho Chehab 			  unsigned char *addr_has_changed) {
43d6317c68SMauro Carvalho Chehab 
44d6317c68SMauro Carvalho Chehab 	int count = 0;
45d6317c68SMauro Carvalho Chehab 	unsigned char *src, dst;
46d6317c68SMauro Carvalho Chehab 
47d6317c68SMauro Carvalho Chehab 	if (*fw_data++ != ':') {
48d6317c68SMauro Carvalho Chehab 		pr_err("invalid firmware file\n");
49d6317c68SMauro Carvalho Chehab 		return -EFAULT;
50d6317c68SMauro Carvalho Chehab 	}
51d6317c68SMauro Carvalho Chehab 
52d6317c68SMauro Carvalho Chehab 	/* locate end of line */
53d6317c68SMauro Carvalho Chehab 	for (src = fw_data; *src != '\n'; src += 2) {
54d6317c68SMauro Carvalho Chehab 		atohx(&dst, src);
55d6317c68SMauro Carvalho Chehab 		/* parse line to split addr / data */
56d6317c68SMauro Carvalho Chehab 		switch (count) {
57d6317c68SMauro Carvalho Chehab 		case 0:
58d6317c68SMauro Carvalho Chehab 			*dataLength = dst;
59d6317c68SMauro Carvalho Chehab 			break;
60d6317c68SMauro Carvalho Chehab 		case 1:
61d6317c68SMauro Carvalho Chehab 			addr[2] = dst;
62d6317c68SMauro Carvalho Chehab 			break;
63d6317c68SMauro Carvalho Chehab 		case 2:
64d6317c68SMauro Carvalho Chehab 			addr[3] = dst;
65d6317c68SMauro Carvalho Chehab 			break;
66d6317c68SMauro Carvalho Chehab 		case 3:
67d6317c68SMauro Carvalho Chehab 			/* check if data is an address */
68d6317c68SMauro Carvalho Chehab 			if (dst == 0x04)
69d6317c68SMauro Carvalho Chehab 				*addr_has_changed = 1;
70d6317c68SMauro Carvalho Chehab 			else
71d6317c68SMauro Carvalho Chehab 				*addr_has_changed = 0;
72d6317c68SMauro Carvalho Chehab 			break;
73d6317c68SMauro Carvalho Chehab 		case  4:
74d6317c68SMauro Carvalho Chehab 		case  5:
75d6317c68SMauro Carvalho Chehab 			if (*addr_has_changed)
76d6317c68SMauro Carvalho Chehab 				addr[(count - 4)] = dst;
77d6317c68SMauro Carvalho Chehab 			else
78d6317c68SMauro Carvalho Chehab 				data[(count - 4)] = dst;
79d6317c68SMauro Carvalho Chehab 			break;
80d6317c68SMauro Carvalho Chehab 		default:
81d6317c68SMauro Carvalho Chehab 			data[(count - 4)] = dst;
82d6317c68SMauro Carvalho Chehab 			break;
83d6317c68SMauro Carvalho Chehab 		}
84d6317c68SMauro Carvalho Chehab 		count++;
85d6317c68SMauro Carvalho Chehab 	}
86d6317c68SMauro Carvalho Chehab 
87d6317c68SMauro Carvalho Chehab 	/* return read value + ':' + '\n' */
88d6317c68SMauro Carvalho Chehab 	return (count * 2) + 2;
89d6317c68SMauro Carvalho Chehab }
90d6317c68SMauro Carvalho Chehab 
as102_firmware_upload(struct as10x_bus_adapter_t * bus_adap,unsigned char * cmd,const struct firmware * firmware)91d6317c68SMauro Carvalho Chehab static int as102_firmware_upload(struct as10x_bus_adapter_t *bus_adap,
92d6317c68SMauro Carvalho Chehab 				 unsigned char *cmd,
93d6317c68SMauro Carvalho Chehab 				 const struct firmware *firmware) {
94d6317c68SMauro Carvalho Chehab 
95b3120d2cSMichele Baldessari 	struct as10x_fw_pkt_t *fw_pkt;
96d6317c68SMauro Carvalho Chehab 	int total_read_bytes = 0, errno = 0;
97d6317c68SMauro Carvalho Chehab 	unsigned char addr_has_changed = 0;
98d6317c68SMauro Carvalho Chehab 
99b3120d2cSMichele Baldessari 	fw_pkt = kmalloc(sizeof(*fw_pkt), GFP_KERNEL);
100b3120d2cSMichele Baldessari 	if (!fw_pkt)
101b3120d2cSMichele Baldessari 		return -ENOMEM;
102b3120d2cSMichele Baldessari 
103b3120d2cSMichele Baldessari 
104d6317c68SMauro Carvalho Chehab 	for (total_read_bytes = 0; total_read_bytes < firmware->size; ) {
105d6317c68SMauro Carvalho Chehab 		int read_bytes = 0, data_len = 0;
106d6317c68SMauro Carvalho Chehab 
107d6317c68SMauro Carvalho Chehab 		/* parse intel hex line */
108d6317c68SMauro Carvalho Chehab 		read_bytes = parse_hex_line(
109d6317c68SMauro Carvalho Chehab 				(u8 *) (firmware->data + total_read_bytes),
110b3120d2cSMichele Baldessari 				fw_pkt->raw.address,
111b3120d2cSMichele Baldessari 				fw_pkt->raw.data,
112d6317c68SMauro Carvalho Chehab 				&data_len,
113d6317c68SMauro Carvalho Chehab 				&addr_has_changed);
114d6317c68SMauro Carvalho Chehab 
115d6317c68SMauro Carvalho Chehab 		if (read_bytes <= 0)
116d6317c68SMauro Carvalho Chehab 			goto error;
117d6317c68SMauro Carvalho Chehab 
118d6317c68SMauro Carvalho Chehab 		/* detect the end of file */
119d6317c68SMauro Carvalho Chehab 		total_read_bytes += read_bytes;
120d6317c68SMauro Carvalho Chehab 		if (total_read_bytes == firmware->size) {
121b3120d2cSMichele Baldessari 			fw_pkt->u.request[0] = 0x00;
122b3120d2cSMichele Baldessari 			fw_pkt->u.request[1] = 0x03;
123d6317c68SMauro Carvalho Chehab 
124d6317c68SMauro Carvalho Chehab 			/* send EOF command */
125d6317c68SMauro Carvalho Chehab 			errno = bus_adap->ops->upload_fw_pkt(bus_adap,
126d6317c68SMauro Carvalho Chehab 							     (uint8_t *)
127b3120d2cSMichele Baldessari 							     fw_pkt, 2, 0);
128d6317c68SMauro Carvalho Chehab 			if (errno < 0)
129d6317c68SMauro Carvalho Chehab 				goto error;
130d6317c68SMauro Carvalho Chehab 		} else {
131d6317c68SMauro Carvalho Chehab 			if (!addr_has_changed) {
132d6317c68SMauro Carvalho Chehab 				/* prepare command to send */
133b3120d2cSMichele Baldessari 				fw_pkt->u.request[0] = 0x00;
134b3120d2cSMichele Baldessari 				fw_pkt->u.request[1] = 0x01;
135d6317c68SMauro Carvalho Chehab 
136b3120d2cSMichele Baldessari 				data_len += sizeof(fw_pkt->u.request);
137b3120d2cSMichele Baldessari 				data_len += sizeof(fw_pkt->raw.address);
138d6317c68SMauro Carvalho Chehab 
139d6317c68SMauro Carvalho Chehab 				/* send cmd to device */
140d6317c68SMauro Carvalho Chehab 				errno = bus_adap->ops->upload_fw_pkt(bus_adap,
141d6317c68SMauro Carvalho Chehab 								     (uint8_t *)
142b3120d2cSMichele Baldessari 								     fw_pkt,
143d6317c68SMauro Carvalho Chehab 								     data_len,
144d6317c68SMauro Carvalho Chehab 								     0);
145d6317c68SMauro Carvalho Chehab 				if (errno < 0)
146d6317c68SMauro Carvalho Chehab 					goto error;
147d6317c68SMauro Carvalho Chehab 			}
148d6317c68SMauro Carvalho Chehab 		}
149d6317c68SMauro Carvalho Chehab 	}
150d6317c68SMauro Carvalho Chehab error:
151b3120d2cSMichele Baldessari 	kfree(fw_pkt);
152d6317c68SMauro Carvalho Chehab 	return (errno == 0) ? total_read_bytes : errno;
153d6317c68SMauro Carvalho Chehab }
154d6317c68SMauro Carvalho Chehab 
as102_fw_upload(struct as10x_bus_adapter_t * bus_adap)155d6317c68SMauro Carvalho Chehab int as102_fw_upload(struct as10x_bus_adapter_t *bus_adap)
156d6317c68SMauro Carvalho Chehab {
157d6317c68SMauro Carvalho Chehab 	int errno = -EFAULT;
158d6317c68SMauro Carvalho Chehab 	const struct firmware *firmware = NULL;
159d6317c68SMauro Carvalho Chehab 	unsigned char *cmd_buf = NULL;
160d6317c68SMauro Carvalho Chehab 	const char *fw1, *fw2;
161d6317c68SMauro Carvalho Chehab 	struct usb_device *dev = bus_adap->usb_dev;
162d6317c68SMauro Carvalho Chehab 
163d6317c68SMauro Carvalho Chehab 	/* select fw file to upload */
164d6317c68SMauro Carvalho Chehab 	if (dual_tuner) {
165d6317c68SMauro Carvalho Chehab 		fw1 = as102_dt_fw1;
166d6317c68SMauro Carvalho Chehab 		fw2 = as102_dt_fw2;
167d6317c68SMauro Carvalho Chehab 	} else {
168d6317c68SMauro Carvalho Chehab 		fw1 = as102_st_fw1;
169d6317c68SMauro Carvalho Chehab 		fw2 = as102_st_fw2;
170d6317c68SMauro Carvalho Chehab 	}
171d6317c68SMauro Carvalho Chehab 
172d6317c68SMauro Carvalho Chehab 	/* allocate buffer to store firmware upload command and data */
173d6317c68SMauro Carvalho Chehab 	cmd_buf = kzalloc(MAX_FW_PKT_SIZE, GFP_KERNEL);
174d6317c68SMauro Carvalho Chehab 	if (cmd_buf == NULL) {
175d6317c68SMauro Carvalho Chehab 		errno = -ENOMEM;
176d6317c68SMauro Carvalho Chehab 		goto error;
177d6317c68SMauro Carvalho Chehab 	}
178d6317c68SMauro Carvalho Chehab 
179d6317c68SMauro Carvalho Chehab 	/* request kernel to locate firmware file: part1 */
180d6317c68SMauro Carvalho Chehab 	errno = request_firmware(&firmware, fw1, &dev->dev);
181d6317c68SMauro Carvalho Chehab 	if (errno < 0) {
182d6317c68SMauro Carvalho Chehab 		pr_err("%s: unable to locate firmware file: %s\n",
183d6317c68SMauro Carvalho Chehab 		       DRIVER_NAME, fw1);
184d6317c68SMauro Carvalho Chehab 		goto error;
185d6317c68SMauro Carvalho Chehab 	}
186d6317c68SMauro Carvalho Chehab 
187d6317c68SMauro Carvalho Chehab 	/* initiate firmware upload */
188d6317c68SMauro Carvalho Chehab 	errno = as102_firmware_upload(bus_adap, cmd_buf, firmware);
189d6317c68SMauro Carvalho Chehab 	if (errno < 0) {
190d6317c68SMauro Carvalho Chehab 		pr_err("%s: error during firmware upload part1\n",
191d6317c68SMauro Carvalho Chehab 		       DRIVER_NAME);
192d6317c68SMauro Carvalho Chehab 		goto error;
193d6317c68SMauro Carvalho Chehab 	}
194d6317c68SMauro Carvalho Chehab 
195d6317c68SMauro Carvalho Chehab 	pr_info("%s: firmware: %s loaded with success\n",
196d6317c68SMauro Carvalho Chehab 		DRIVER_NAME, fw1);
197d6317c68SMauro Carvalho Chehab 	release_firmware(firmware);
198b7718522SChristian Engelmayer 	firmware = NULL;
199d6317c68SMauro Carvalho Chehab 
200d6317c68SMauro Carvalho Chehab 	/* wait for boot to complete */
201d6317c68SMauro Carvalho Chehab 	mdelay(100);
202d6317c68SMauro Carvalho Chehab 
203d6317c68SMauro Carvalho Chehab 	/* request kernel to locate firmware file: part2 */
204d6317c68SMauro Carvalho Chehab 	errno = request_firmware(&firmware, fw2, &dev->dev);
205d6317c68SMauro Carvalho Chehab 	if (errno < 0) {
206d6317c68SMauro Carvalho Chehab 		pr_err("%s: unable to locate firmware file: %s\n",
207d6317c68SMauro Carvalho Chehab 		       DRIVER_NAME, fw2);
208d6317c68SMauro Carvalho Chehab 		goto error;
209d6317c68SMauro Carvalho Chehab 	}
210d6317c68SMauro Carvalho Chehab 
211d6317c68SMauro Carvalho Chehab 	/* initiate firmware upload */
212d6317c68SMauro Carvalho Chehab 	errno = as102_firmware_upload(bus_adap, cmd_buf, firmware);
213d6317c68SMauro Carvalho Chehab 	if (errno < 0) {
214d6317c68SMauro Carvalho Chehab 		pr_err("%s: error during firmware upload part2\n",
215d6317c68SMauro Carvalho Chehab 		       DRIVER_NAME);
216d6317c68SMauro Carvalho Chehab 		goto error;
217d6317c68SMauro Carvalho Chehab 	}
218d6317c68SMauro Carvalho Chehab 
219d6317c68SMauro Carvalho Chehab 	pr_info("%s: firmware: %s loaded with success\n",
220d6317c68SMauro Carvalho Chehab 		DRIVER_NAME, fw2);
221d6317c68SMauro Carvalho Chehab error:
222d6317c68SMauro Carvalho Chehab 	kfree(cmd_buf);
223d6317c68SMauro Carvalho Chehab 	release_firmware(firmware);
224d6317c68SMauro Carvalho Chehab 
225d6317c68SMauro Carvalho Chehab 	return errno;
226d6317c68SMauro Carvalho Chehab }
227