162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Abilis Systems Single DVB-T Receiver 462306a36Sopenharmony_ci * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com> 562306a36Sopenharmony_ci * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/errno.h> 962306a36Sopenharmony_ci#include <linux/ctype.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/firmware.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "as102_drv.h" 1462306a36Sopenharmony_ci#include "as102_fw.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic const char as102_st_fw1[] = "as102_data1_st.hex"; 1762306a36Sopenharmony_cistatic const char as102_st_fw2[] = "as102_data2_st.hex"; 1862306a36Sopenharmony_cistatic const char as102_dt_fw1[] = "as102_data1_dt.hex"; 1962306a36Sopenharmony_cistatic const char as102_dt_fw2[] = "as102_data2_dt.hex"; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic unsigned char atohx(unsigned char *dst, char *src) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci unsigned char value = 0; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci char msb = tolower(*src) - '0'; 2662306a36Sopenharmony_ci char lsb = tolower(*(src + 1)) - '0'; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci if (msb > 9) 2962306a36Sopenharmony_ci msb -= 7; 3062306a36Sopenharmony_ci if (lsb > 9) 3162306a36Sopenharmony_ci lsb -= 7; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci *dst = value = ((msb & 0xF) << 4) | (lsb & 0xF); 3462306a36Sopenharmony_ci return value; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* 3862306a36Sopenharmony_ci * Parse INTEL HEX firmware file to extract address and data. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_cistatic int parse_hex_line(unsigned char *fw_data, unsigned char *addr, 4162306a36Sopenharmony_ci unsigned char *data, int *dataLength, 4262306a36Sopenharmony_ci unsigned char *addr_has_changed) { 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci int count = 0; 4562306a36Sopenharmony_ci unsigned char *src, dst; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if (*fw_data++ != ':') { 4862306a36Sopenharmony_ci pr_err("invalid firmware file\n"); 4962306a36Sopenharmony_ci return -EFAULT; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* locate end of line */ 5362306a36Sopenharmony_ci for (src = fw_data; *src != '\n'; src += 2) { 5462306a36Sopenharmony_ci atohx(&dst, src); 5562306a36Sopenharmony_ci /* parse line to split addr / data */ 5662306a36Sopenharmony_ci switch (count) { 5762306a36Sopenharmony_ci case 0: 5862306a36Sopenharmony_ci *dataLength = dst; 5962306a36Sopenharmony_ci break; 6062306a36Sopenharmony_ci case 1: 6162306a36Sopenharmony_ci addr[2] = dst; 6262306a36Sopenharmony_ci break; 6362306a36Sopenharmony_ci case 2: 6462306a36Sopenharmony_ci addr[3] = dst; 6562306a36Sopenharmony_ci break; 6662306a36Sopenharmony_ci case 3: 6762306a36Sopenharmony_ci /* check if data is an address */ 6862306a36Sopenharmony_ci if (dst == 0x04) 6962306a36Sopenharmony_ci *addr_has_changed = 1; 7062306a36Sopenharmony_ci else 7162306a36Sopenharmony_ci *addr_has_changed = 0; 7262306a36Sopenharmony_ci break; 7362306a36Sopenharmony_ci case 4: 7462306a36Sopenharmony_ci case 5: 7562306a36Sopenharmony_ci if (*addr_has_changed) 7662306a36Sopenharmony_ci addr[(count - 4)] = dst; 7762306a36Sopenharmony_ci else 7862306a36Sopenharmony_ci data[(count - 4)] = dst; 7962306a36Sopenharmony_ci break; 8062306a36Sopenharmony_ci default: 8162306a36Sopenharmony_ci data[(count - 4)] = dst; 8262306a36Sopenharmony_ci break; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci count++; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* return read value + ':' + '\n' */ 8862306a36Sopenharmony_ci return (count * 2) + 2; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic int as102_firmware_upload(struct as10x_bus_adapter_t *bus_adap, 9262306a36Sopenharmony_ci unsigned char *cmd, 9362306a36Sopenharmony_ci const struct firmware *firmware) { 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci struct as10x_fw_pkt_t *fw_pkt; 9662306a36Sopenharmony_ci int total_read_bytes = 0, errno = 0; 9762306a36Sopenharmony_ci unsigned char addr_has_changed = 0; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci fw_pkt = kmalloc(sizeof(*fw_pkt), GFP_KERNEL); 10062306a36Sopenharmony_ci if (!fw_pkt) 10162306a36Sopenharmony_ci return -ENOMEM; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci for (total_read_bytes = 0; total_read_bytes < firmware->size; ) { 10562306a36Sopenharmony_ci int read_bytes = 0, data_len = 0; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* parse intel hex line */ 10862306a36Sopenharmony_ci read_bytes = parse_hex_line( 10962306a36Sopenharmony_ci (u8 *) (firmware->data + total_read_bytes), 11062306a36Sopenharmony_ci fw_pkt->raw.address, 11162306a36Sopenharmony_ci fw_pkt->raw.data, 11262306a36Sopenharmony_ci &data_len, 11362306a36Sopenharmony_ci &addr_has_changed); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (read_bytes <= 0) 11662306a36Sopenharmony_ci goto error; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* detect the end of file */ 11962306a36Sopenharmony_ci total_read_bytes += read_bytes; 12062306a36Sopenharmony_ci if (total_read_bytes == firmware->size) { 12162306a36Sopenharmony_ci fw_pkt->u.request[0] = 0x00; 12262306a36Sopenharmony_ci fw_pkt->u.request[1] = 0x03; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* send EOF command */ 12562306a36Sopenharmony_ci errno = bus_adap->ops->upload_fw_pkt(bus_adap, 12662306a36Sopenharmony_ci (uint8_t *) 12762306a36Sopenharmony_ci fw_pkt, 2, 0); 12862306a36Sopenharmony_ci if (errno < 0) 12962306a36Sopenharmony_ci goto error; 13062306a36Sopenharmony_ci } else { 13162306a36Sopenharmony_ci if (!addr_has_changed) { 13262306a36Sopenharmony_ci /* prepare command to send */ 13362306a36Sopenharmony_ci fw_pkt->u.request[0] = 0x00; 13462306a36Sopenharmony_ci fw_pkt->u.request[1] = 0x01; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci data_len += sizeof(fw_pkt->u.request); 13762306a36Sopenharmony_ci data_len += sizeof(fw_pkt->raw.address); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* send cmd to device */ 14062306a36Sopenharmony_ci errno = bus_adap->ops->upload_fw_pkt(bus_adap, 14162306a36Sopenharmony_ci (uint8_t *) 14262306a36Sopenharmony_ci fw_pkt, 14362306a36Sopenharmony_ci data_len, 14462306a36Sopenharmony_ci 0); 14562306a36Sopenharmony_ci if (errno < 0) 14662306a36Sopenharmony_ci goto error; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_cierror: 15162306a36Sopenharmony_ci kfree(fw_pkt); 15262306a36Sopenharmony_ci return (errno == 0) ? total_read_bytes : errno; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ciint as102_fw_upload(struct as10x_bus_adapter_t *bus_adap) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci int errno = -EFAULT; 15862306a36Sopenharmony_ci const struct firmware *firmware = NULL; 15962306a36Sopenharmony_ci unsigned char *cmd_buf = NULL; 16062306a36Sopenharmony_ci const char *fw1, *fw2; 16162306a36Sopenharmony_ci struct usb_device *dev = bus_adap->usb_dev; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* select fw file to upload */ 16462306a36Sopenharmony_ci if (dual_tuner) { 16562306a36Sopenharmony_ci fw1 = as102_dt_fw1; 16662306a36Sopenharmony_ci fw2 = as102_dt_fw2; 16762306a36Sopenharmony_ci } else { 16862306a36Sopenharmony_ci fw1 = as102_st_fw1; 16962306a36Sopenharmony_ci fw2 = as102_st_fw2; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* allocate buffer to store firmware upload command and data */ 17362306a36Sopenharmony_ci cmd_buf = kzalloc(MAX_FW_PKT_SIZE, GFP_KERNEL); 17462306a36Sopenharmony_ci if (cmd_buf == NULL) { 17562306a36Sopenharmony_ci errno = -ENOMEM; 17662306a36Sopenharmony_ci goto error; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* request kernel to locate firmware file: part1 */ 18062306a36Sopenharmony_ci errno = request_firmware(&firmware, fw1, &dev->dev); 18162306a36Sopenharmony_ci if (errno < 0) { 18262306a36Sopenharmony_ci pr_err("%s: unable to locate firmware file: %s\n", 18362306a36Sopenharmony_ci DRIVER_NAME, fw1); 18462306a36Sopenharmony_ci goto error; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* initiate firmware upload */ 18862306a36Sopenharmony_ci errno = as102_firmware_upload(bus_adap, cmd_buf, firmware); 18962306a36Sopenharmony_ci if (errno < 0) { 19062306a36Sopenharmony_ci pr_err("%s: error during firmware upload part1\n", 19162306a36Sopenharmony_ci DRIVER_NAME); 19262306a36Sopenharmony_ci goto error; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci pr_info("%s: firmware: %s loaded with success\n", 19662306a36Sopenharmony_ci DRIVER_NAME, fw1); 19762306a36Sopenharmony_ci release_firmware(firmware); 19862306a36Sopenharmony_ci firmware = NULL; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* wait for boot to complete */ 20162306a36Sopenharmony_ci mdelay(100); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* request kernel to locate firmware file: part2 */ 20462306a36Sopenharmony_ci errno = request_firmware(&firmware, fw2, &dev->dev); 20562306a36Sopenharmony_ci if (errno < 0) { 20662306a36Sopenharmony_ci pr_err("%s: unable to locate firmware file: %s\n", 20762306a36Sopenharmony_ci DRIVER_NAME, fw2); 20862306a36Sopenharmony_ci goto error; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* initiate firmware upload */ 21262306a36Sopenharmony_ci errno = as102_firmware_upload(bus_adap, cmd_buf, firmware); 21362306a36Sopenharmony_ci if (errno < 0) { 21462306a36Sopenharmony_ci pr_err("%s: error during firmware upload part2\n", 21562306a36Sopenharmony_ci DRIVER_NAME); 21662306a36Sopenharmony_ci goto error; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci pr_info("%s: firmware: %s loaded with success\n", 22062306a36Sopenharmony_ci DRIVER_NAME, fw2); 22162306a36Sopenharmony_cierror: 22262306a36Sopenharmony_ci kfree(cmd_buf); 22362306a36Sopenharmony_ci release_firmware(firmware); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return errno; 22662306a36Sopenharmony_ci} 227