18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Abilis Systems Single DVB-T Receiver 48c2ecf20Sopenharmony_ci * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com> 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/kernel.h> 88c2ecf20Sopenharmony_ci#include <linux/errno.h> 98c2ecf20Sopenharmony_ci#include <linux/ctype.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/firmware.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "as102_drv.h" 148c2ecf20Sopenharmony_ci#include "as102_fw.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic const char as102_st_fw1[] = "as102_data1_st.hex"; 178c2ecf20Sopenharmony_cistatic const char as102_st_fw2[] = "as102_data2_st.hex"; 188c2ecf20Sopenharmony_cistatic const char as102_dt_fw1[] = "as102_data1_dt.hex"; 198c2ecf20Sopenharmony_cistatic const char as102_dt_fw2[] = "as102_data2_dt.hex"; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic unsigned char atohx(unsigned char *dst, char *src) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci unsigned char value = 0; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci char msb = tolower(*src) - '0'; 268c2ecf20Sopenharmony_ci char lsb = tolower(*(src + 1)) - '0'; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci if (msb > 9) 298c2ecf20Sopenharmony_ci msb -= 7; 308c2ecf20Sopenharmony_ci if (lsb > 9) 318c2ecf20Sopenharmony_ci lsb -= 7; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci *dst = value = ((msb & 0xF) << 4) | (lsb & 0xF); 348c2ecf20Sopenharmony_ci return value; 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* 388c2ecf20Sopenharmony_ci * Parse INTEL HEX firmware file to extract address and data. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_cistatic int parse_hex_line(unsigned char *fw_data, unsigned char *addr, 418c2ecf20Sopenharmony_ci unsigned char *data, int *dataLength, 428c2ecf20Sopenharmony_ci unsigned char *addr_has_changed) { 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci int count = 0; 458c2ecf20Sopenharmony_ci unsigned char *src, dst; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (*fw_data++ != ':') { 488c2ecf20Sopenharmony_ci pr_err("invalid firmware file\n"); 498c2ecf20Sopenharmony_ci return -EFAULT; 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci /* locate end of line */ 538c2ecf20Sopenharmony_ci for (src = fw_data; *src != '\n'; src += 2) { 548c2ecf20Sopenharmony_ci atohx(&dst, src); 558c2ecf20Sopenharmony_ci /* parse line to split addr / data */ 568c2ecf20Sopenharmony_ci switch (count) { 578c2ecf20Sopenharmony_ci case 0: 588c2ecf20Sopenharmony_ci *dataLength = dst; 598c2ecf20Sopenharmony_ci break; 608c2ecf20Sopenharmony_ci case 1: 618c2ecf20Sopenharmony_ci addr[2] = dst; 628c2ecf20Sopenharmony_ci break; 638c2ecf20Sopenharmony_ci case 2: 648c2ecf20Sopenharmony_ci addr[3] = dst; 658c2ecf20Sopenharmony_ci break; 668c2ecf20Sopenharmony_ci case 3: 678c2ecf20Sopenharmony_ci /* check if data is an address */ 688c2ecf20Sopenharmony_ci if (dst == 0x04) 698c2ecf20Sopenharmony_ci *addr_has_changed = 1; 708c2ecf20Sopenharmony_ci else 718c2ecf20Sopenharmony_ci *addr_has_changed = 0; 728c2ecf20Sopenharmony_ci break; 738c2ecf20Sopenharmony_ci case 4: 748c2ecf20Sopenharmony_ci case 5: 758c2ecf20Sopenharmony_ci if (*addr_has_changed) 768c2ecf20Sopenharmony_ci addr[(count - 4)] = dst; 778c2ecf20Sopenharmony_ci else 788c2ecf20Sopenharmony_ci data[(count - 4)] = dst; 798c2ecf20Sopenharmony_ci break; 808c2ecf20Sopenharmony_ci default: 818c2ecf20Sopenharmony_ci data[(count - 4)] = dst; 828c2ecf20Sopenharmony_ci break; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci count++; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* return read value + ':' + '\n' */ 888c2ecf20Sopenharmony_ci return (count * 2) + 2; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int as102_firmware_upload(struct as10x_bus_adapter_t *bus_adap, 928c2ecf20Sopenharmony_ci unsigned char *cmd, 938c2ecf20Sopenharmony_ci const struct firmware *firmware) { 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci struct as10x_fw_pkt_t *fw_pkt; 968c2ecf20Sopenharmony_ci int total_read_bytes = 0, errno = 0; 978c2ecf20Sopenharmony_ci unsigned char addr_has_changed = 0; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci fw_pkt = kmalloc(sizeof(*fw_pkt), GFP_KERNEL); 1008c2ecf20Sopenharmony_ci if (!fw_pkt) 1018c2ecf20Sopenharmony_ci return -ENOMEM; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci for (total_read_bytes = 0; total_read_bytes < firmware->size; ) { 1058c2ecf20Sopenharmony_ci int read_bytes = 0, data_len = 0; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* parse intel hex line */ 1088c2ecf20Sopenharmony_ci read_bytes = parse_hex_line( 1098c2ecf20Sopenharmony_ci (u8 *) (firmware->data + total_read_bytes), 1108c2ecf20Sopenharmony_ci fw_pkt->raw.address, 1118c2ecf20Sopenharmony_ci fw_pkt->raw.data, 1128c2ecf20Sopenharmony_ci &data_len, 1138c2ecf20Sopenharmony_ci &addr_has_changed); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (read_bytes <= 0) 1168c2ecf20Sopenharmony_ci goto error; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* detect the end of file */ 1198c2ecf20Sopenharmony_ci total_read_bytes += read_bytes; 1208c2ecf20Sopenharmony_ci if (total_read_bytes == firmware->size) { 1218c2ecf20Sopenharmony_ci fw_pkt->u.request[0] = 0x00; 1228c2ecf20Sopenharmony_ci fw_pkt->u.request[1] = 0x03; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* send EOF command */ 1258c2ecf20Sopenharmony_ci errno = bus_adap->ops->upload_fw_pkt(bus_adap, 1268c2ecf20Sopenharmony_ci (uint8_t *) 1278c2ecf20Sopenharmony_ci fw_pkt, 2, 0); 1288c2ecf20Sopenharmony_ci if (errno < 0) 1298c2ecf20Sopenharmony_ci goto error; 1308c2ecf20Sopenharmony_ci } else { 1318c2ecf20Sopenharmony_ci if (!addr_has_changed) { 1328c2ecf20Sopenharmony_ci /* prepare command to send */ 1338c2ecf20Sopenharmony_ci fw_pkt->u.request[0] = 0x00; 1348c2ecf20Sopenharmony_ci fw_pkt->u.request[1] = 0x01; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci data_len += sizeof(fw_pkt->u.request); 1378c2ecf20Sopenharmony_ci data_len += sizeof(fw_pkt->raw.address); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* send cmd to device */ 1408c2ecf20Sopenharmony_ci errno = bus_adap->ops->upload_fw_pkt(bus_adap, 1418c2ecf20Sopenharmony_ci (uint8_t *) 1428c2ecf20Sopenharmony_ci fw_pkt, 1438c2ecf20Sopenharmony_ci data_len, 1448c2ecf20Sopenharmony_ci 0); 1458c2ecf20Sopenharmony_ci if (errno < 0) 1468c2ecf20Sopenharmony_ci goto error; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_cierror: 1518c2ecf20Sopenharmony_ci kfree(fw_pkt); 1528c2ecf20Sopenharmony_ci return (errno == 0) ? total_read_bytes : errno; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ciint as102_fw_upload(struct as10x_bus_adapter_t *bus_adap) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci int errno = -EFAULT; 1588c2ecf20Sopenharmony_ci const struct firmware *firmware = NULL; 1598c2ecf20Sopenharmony_ci unsigned char *cmd_buf = NULL; 1608c2ecf20Sopenharmony_ci const char *fw1, *fw2; 1618c2ecf20Sopenharmony_ci struct usb_device *dev = bus_adap->usb_dev; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* select fw file to upload */ 1648c2ecf20Sopenharmony_ci if (dual_tuner) { 1658c2ecf20Sopenharmony_ci fw1 = as102_dt_fw1; 1668c2ecf20Sopenharmony_ci fw2 = as102_dt_fw2; 1678c2ecf20Sopenharmony_ci } else { 1688c2ecf20Sopenharmony_ci fw1 = as102_st_fw1; 1698c2ecf20Sopenharmony_ci fw2 = as102_st_fw2; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* allocate buffer to store firmware upload command and data */ 1738c2ecf20Sopenharmony_ci cmd_buf = kzalloc(MAX_FW_PKT_SIZE, GFP_KERNEL); 1748c2ecf20Sopenharmony_ci if (cmd_buf == NULL) { 1758c2ecf20Sopenharmony_ci errno = -ENOMEM; 1768c2ecf20Sopenharmony_ci goto error; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* request kernel to locate firmware file: part1 */ 1808c2ecf20Sopenharmony_ci errno = request_firmware(&firmware, fw1, &dev->dev); 1818c2ecf20Sopenharmony_ci if (errno < 0) { 1828c2ecf20Sopenharmony_ci pr_err("%s: unable to locate firmware file: %s\n", 1838c2ecf20Sopenharmony_ci DRIVER_NAME, fw1); 1848c2ecf20Sopenharmony_ci goto error; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* initiate firmware upload */ 1888c2ecf20Sopenharmony_ci errno = as102_firmware_upload(bus_adap, cmd_buf, firmware); 1898c2ecf20Sopenharmony_ci if (errno < 0) { 1908c2ecf20Sopenharmony_ci pr_err("%s: error during firmware upload part1\n", 1918c2ecf20Sopenharmony_ci DRIVER_NAME); 1928c2ecf20Sopenharmony_ci goto error; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci pr_info("%s: firmware: %s loaded with success\n", 1968c2ecf20Sopenharmony_ci DRIVER_NAME, fw1); 1978c2ecf20Sopenharmony_ci release_firmware(firmware); 1988c2ecf20Sopenharmony_ci firmware = NULL; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* wait for boot to complete */ 2018c2ecf20Sopenharmony_ci mdelay(100); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* request kernel to locate firmware file: part2 */ 2048c2ecf20Sopenharmony_ci errno = request_firmware(&firmware, fw2, &dev->dev); 2058c2ecf20Sopenharmony_ci if (errno < 0) { 2068c2ecf20Sopenharmony_ci pr_err("%s: unable to locate firmware file: %s\n", 2078c2ecf20Sopenharmony_ci DRIVER_NAME, fw2); 2088c2ecf20Sopenharmony_ci goto error; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* initiate firmware upload */ 2128c2ecf20Sopenharmony_ci errno = as102_firmware_upload(bus_adap, cmd_buf, firmware); 2138c2ecf20Sopenharmony_ci if (errno < 0) { 2148c2ecf20Sopenharmony_ci pr_err("%s: error during firmware upload part2\n", 2158c2ecf20Sopenharmony_ci DRIVER_NAME); 2168c2ecf20Sopenharmony_ci goto error; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci pr_info("%s: firmware: %s loaded with success\n", 2208c2ecf20Sopenharmony_ci DRIVER_NAME, fw2); 2218c2ecf20Sopenharmony_cierror: 2228c2ecf20Sopenharmony_ci kfree(cmd_buf); 2238c2ecf20Sopenharmony_ci release_firmware(firmware); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return errno; 2268c2ecf20Sopenharmony_ci} 227