blob: 1075fb1df0d9c04a967634e56eb2e5a581d3421b [file] [log] [blame]
Pierrick Hascoet41b44e02011-10-31 12:24:39 -03001/*
2 * Abilis Systems Single DVB-T Receiver
3 * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
Devin Heitmuellere54d0812011-10-31 12:24:41 -03004 * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
Pierrick Hascoet41b44e02011-10-31 12:24:39 -03005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20#include <linux/kernel.h>
21#include <linux/errno.h>
22#include <linux/ctype.h>
23#include <linux/delay.h>
24#include <linux/firmware.h>
25
26#include "as102_drv.h"
27#include "as102_fw.h"
28
Pierrick Hascoet41b44e02011-10-31 12:24:39 -030029char as102_st_fw1[] = "as102_data1_st.hex";
30char as102_st_fw2[] = "as102_data2_st.hex";
31char as102_dt_fw1[] = "as102_data1_dt.hex";
32char as102_dt_fw2[] = "as102_data2_dt.hex";
33
Devin Heitmuellere54d0812011-10-31 12:24:41 -030034static unsigned char atohx(unsigned char *dst, char *src)
35{
Pierrick Hascoet41b44e02011-10-31 12:24:39 -030036 unsigned char value = 0;
37
38 char msb = tolower(*src) - '0';
Devin Heitmuellere54d0812011-10-31 12:24:41 -030039 char lsb = tolower(*(src + 1)) - '0';
Pierrick Hascoet41b44e02011-10-31 12:24:39 -030040
Devin Heitmuellere54d0812011-10-31 12:24:41 -030041 if (msb > 9)
Pierrick Hascoet41b44e02011-10-31 12:24:39 -030042 msb -= 7;
Devin Heitmuellere54d0812011-10-31 12:24:41 -030043 if (lsb > 9)
Pierrick Hascoet41b44e02011-10-31 12:24:39 -030044 lsb -= 7;
45
46 *dst = value = ((msb & 0xF) << 4) | (lsb & 0xF);
47 return value;
48}
49
50/*
51 * Parse INTEL HEX firmware file to extract address and data.
52 */
53static int parse_hex_line(unsigned char *fw_data, unsigned char *addr,
54 unsigned char *data, int *dataLength,
55 unsigned char *addr_has_changed) {
56
57 int count = 0;
58 unsigned char *src, dst;
59
60 if (*fw_data++ != ':') {
Sylwester Nawrocki7d462fe2011-11-06 16:31:45 -030061 pr_err("invalid firmware file\n");
Pierrick Hascoet41b44e02011-10-31 12:24:39 -030062 return -EFAULT;
63 }
64
65 /* locate end of line */
Devin Heitmuellere54d0812011-10-31 12:24:41 -030066 for (src = fw_data; *src != '\n'; src += 2) {
Pierrick Hascoet41b44e02011-10-31 12:24:39 -030067 atohx(&dst, src);
68 /* parse line to split addr / data */
69 switch (count) {
Devin Heitmuellere54d0812011-10-31 12:24:41 -030070 case 0:
71 *dataLength = dst;
72 break;
73 case 1:
74 addr[2] = dst;
75 break;
76 case 2:
77 addr[3] = dst;
78 break;
79 case 3:
80 /* check if data is an address */
81 if (dst == 0x04)
82 *addr_has_changed = 1;
83 else
84 *addr_has_changed = 0;
85 break;
86 case 4:
87 case 5:
88 if (*addr_has_changed)
89 addr[(count - 4)] = dst;
90 else
Pierrick Hascoet41b44e02011-10-31 12:24:39 -030091 data[(count - 4)] = dst;
Devin Heitmuellere54d0812011-10-31 12:24:41 -030092 break;
93 default:
94 data[(count - 4)] = dst;
95 break;
Pierrick Hascoet41b44e02011-10-31 12:24:39 -030096 }
97 count++;
98 }
99
100 /* return read value + ':' + '\n' */
Devin Heitmuellere54d0812011-10-31 12:24:41 -0300101 return (count * 2) + 2;
Pierrick Hascoet41b44e02011-10-31 12:24:39 -0300102}
103
Sylwester Nawrocki34490a02011-11-06 16:31:50 -0300104static int as102_firmware_upload(struct as10x_bus_adapter_t *bus_adap,
Pierrick Hascoet41b44e02011-10-31 12:24:39 -0300105 unsigned char *cmd,
106 const struct firmware *firmware) {
107
108 struct as10x_fw_pkt_t fw_pkt;
109 int total_read_bytes = 0, errno = 0;
110 unsigned char addr_has_changed = 0;
111
112 ENTER();
113
114 for (total_read_bytes = 0; total_read_bytes < firmware->size; ) {
115 int read_bytes = 0, data_len = 0;
116
117 /* parse intel hex line */
118 read_bytes = parse_hex_line(
119 (u8 *) (firmware->data + total_read_bytes),
120 fw_pkt.raw.address,
121 fw_pkt.raw.data,
122 &data_len,
123 &addr_has_changed);
124
Devin Heitmuellere54d0812011-10-31 12:24:41 -0300125 if (read_bytes <= 0)
Pierrick Hascoet41b44e02011-10-31 12:24:39 -0300126 goto error;
Pierrick Hascoet41b44e02011-10-31 12:24:39 -0300127
128 /* detect the end of file */
Devin Heitmuellere54d0812011-10-31 12:24:41 -0300129 total_read_bytes += read_bytes;
130 if (total_read_bytes == firmware->size) {
Pierrick Hascoet41b44e02011-10-31 12:24:39 -0300131 fw_pkt.u.request[0] = 0x00;
132 fw_pkt.u.request[1] = 0x03;
133
134 /* send EOF command */
Devin Heitmuellere54d0812011-10-31 12:24:41 -0300135 errno = bus_adap->ops->upload_fw_pkt(bus_adap,
136 (uint8_t *)
137 &fw_pkt, 2, 0);
138 if (errno < 0)
Pierrick Hascoet41b44e02011-10-31 12:24:39 -0300139 goto error;
140 } else {
141 if (!addr_has_changed) {
142 /* prepare command to send */
143 fw_pkt.u.request[0] = 0x00;
144 fw_pkt.u.request[1] = 0x01;
145
146 data_len += sizeof(fw_pkt.u.request);
147 data_len += sizeof(fw_pkt.raw.address);
148
149 /* send cmd to device */
Devin Heitmuellere54d0812011-10-31 12:24:41 -0300150 errno = bus_adap->ops->upload_fw_pkt(bus_adap,
151 (uint8_t *)
152 &fw_pkt,
153 data_len,
154 0);
155 if (errno < 0)
Pierrick Hascoet41b44e02011-10-31 12:24:39 -0300156 goto error;
157 }
158 }
159 }
160error:
161 LEAVE();
162 return (errno == 0) ? total_read_bytes : errno;
163}
164
Sylwester Nawrocki34490a02011-11-06 16:31:50 -0300165int as102_fw_upload(struct as10x_bus_adapter_t *bus_adap)
Devin Heitmuellere54d0812011-10-31 12:24:41 -0300166{
Pierrick Hascoet41b44e02011-10-31 12:24:39 -0300167 int errno = -EFAULT;
Jesper Juhl401c90e2012-03-24 23:39:18 +0100168 const struct firmware *firmware = NULL;
Pierrick Hascoet41b44e02011-10-31 12:24:39 -0300169 unsigned char *cmd_buf = NULL;
170 char *fw1, *fw2;
Pierrick Hascoet41b44e02011-10-31 12:24:39 -0300171 struct usb_device *dev = bus_adap->usb_dev;
Sylwester Nawrocki82aae982011-11-06 16:31:40 -0300172
Pierrick Hascoet41b44e02011-10-31 12:24:39 -0300173 ENTER();
174
175 /* select fw file to upload */
176 if (dual_tuner) {
177 fw1 = as102_dt_fw1;
178 fw2 = as102_dt_fw2;
179 } else {
180 fw1 = as102_st_fw1;
181 fw2 = as102_st_fw2;
182 }
183
Pierrick Hascoet41b44e02011-10-31 12:24:39 -0300184 /* allocate buffer to store firmware upload command and data */
Devin Heitmuellere54d0812011-10-31 12:24:41 -0300185 cmd_buf = kzalloc(MAX_FW_PKT_SIZE, GFP_KERNEL);
186 if (cmd_buf == NULL) {
Pierrick Hascoet41b44e02011-10-31 12:24:39 -0300187 errno = -ENOMEM;
188 goto error;
189 }
190
191 /* request kernel to locate firmware file: part1 */
Devin Heitmuellere54d0812011-10-31 12:24:41 -0300192 errno = request_firmware(&firmware, fw1, &dev->dev);
193 if (errno < 0) {
Sylwester Nawrocki7d462fe2011-11-06 16:31:45 -0300194 pr_err("%s: unable to locate firmware file: %s\n",
195 DRIVER_NAME, fw1);
Pierrick Hascoet41b44e02011-10-31 12:24:39 -0300196 goto error;
197 }
198
199 /* initiate firmware upload */
Devin Heitmuellere54d0812011-10-31 12:24:41 -0300200 errno = as102_firmware_upload(bus_adap, cmd_buf, firmware);
201 if (errno < 0) {
Sylwester Nawrocki7d462fe2011-11-06 16:31:45 -0300202 pr_err("%s: error during firmware upload part1\n",
203 DRIVER_NAME);
Pierrick Hascoet41b44e02011-10-31 12:24:39 -0300204 goto error;
205 }
206
Sylwester Nawrocki7d462fe2011-11-06 16:31:45 -0300207 pr_info("%s: firmware: %s loaded with success\n",
208 DRIVER_NAME, fw1);
Pierrick Hascoet41b44e02011-10-31 12:24:39 -0300209 release_firmware(firmware);
210
211 /* wait for boot to complete */
212 mdelay(100);
213
214 /* request kernel to locate firmware file: part2 */
Devin Heitmuellere54d0812011-10-31 12:24:41 -0300215 errno = request_firmware(&firmware, fw2, &dev->dev);
216 if (errno < 0) {
Sylwester Nawrocki7d462fe2011-11-06 16:31:45 -0300217 pr_err("%s: unable to locate firmware file: %s\n",
218 DRIVER_NAME, fw2);
Pierrick Hascoet41b44e02011-10-31 12:24:39 -0300219 goto error;
220 }
221
222 /* initiate firmware upload */
Devin Heitmuellere54d0812011-10-31 12:24:41 -0300223 errno = as102_firmware_upload(bus_adap, cmd_buf, firmware);
224 if (errno < 0) {
Sylwester Nawrocki7d462fe2011-11-06 16:31:45 -0300225 pr_err("%s: error during firmware upload part2\n",
226 DRIVER_NAME);
Pierrick Hascoet41b44e02011-10-31 12:24:39 -0300227 goto error;
228 }
229
Sylwester Nawrocki7d462fe2011-11-06 16:31:45 -0300230 pr_info("%s: firmware: %s loaded with success\n",
231 DRIVER_NAME, fw2);
Pierrick Hascoet41b44e02011-10-31 12:24:39 -0300232error:
233 /* free data buffer */
234 kfree(cmd_buf);
235 /* release firmware if needed */
236 if (firmware != NULL)
237 release_firmware(firmware);
Sylwester Nawrocki57753762011-11-06 16:31:41 -0300238
Pierrick Hascoet41b44e02011-10-31 12:24:39 -0300239 LEAVE();
240 return errno;
241}