18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for the NXP SAA7164 PCIe bridge 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/firmware.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "saa7164.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define SAA7164_REV2_FIRMWARE "NXP7164-2010-03-10.1.fw" 148c2ecf20Sopenharmony_ci#define SAA7164_REV2_FIRMWARE_SIZE 4019072 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define SAA7164_REV3_FIRMWARE "NXP7164-2010-03-10.1.fw" 178c2ecf20Sopenharmony_ci#define SAA7164_REV3_FIRMWARE_SIZE 4019072 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistruct fw_header { 208c2ecf20Sopenharmony_ci u32 firmwaresize; 218c2ecf20Sopenharmony_ci u32 bslsize; 228c2ecf20Sopenharmony_ci u32 reserved; 238c2ecf20Sopenharmony_ci u32 version; 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic int saa7164_dl_wait_ack(struct saa7164_dev *dev, u32 reg) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci u32 timeout = SAA_DEVICE_TIMEOUT; 298c2ecf20Sopenharmony_ci while ((saa7164_readl(reg) & 0x01) == 0) { 308c2ecf20Sopenharmony_ci timeout -= 10; 318c2ecf20Sopenharmony_ci if (timeout == 0) { 328c2ecf20Sopenharmony_ci printk(KERN_ERR "%s() timeout (no d/l ack)\n", 338c2ecf20Sopenharmony_ci __func__); 348c2ecf20Sopenharmony_ci return -EBUSY; 358c2ecf20Sopenharmony_ci } 368c2ecf20Sopenharmony_ci msleep(100); 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci return 0; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int saa7164_dl_wait_clr(struct saa7164_dev *dev, u32 reg) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci u32 timeout = SAA_DEVICE_TIMEOUT; 458c2ecf20Sopenharmony_ci while (saa7164_readl(reg) & 0x01) { 468c2ecf20Sopenharmony_ci timeout -= 10; 478c2ecf20Sopenharmony_ci if (timeout == 0) { 488c2ecf20Sopenharmony_ci printk(KERN_ERR "%s() timeout (no d/l clr)\n", 498c2ecf20Sopenharmony_ci __func__); 508c2ecf20Sopenharmony_ci return -EBUSY; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci msleep(100); 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* TODO: move dlflags into dev-> and change to write/readl/b */ 598c2ecf20Sopenharmony_ci/* TODO: Excessive levels of debug */ 608c2ecf20Sopenharmony_cistatic int saa7164_downloadimage(struct saa7164_dev *dev, u8 *src, u32 srcsize, 618c2ecf20Sopenharmony_ci u32 dlflags, u8 __iomem *dst, u32 dstsize) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci u32 reg, timeout, offset; 648c2ecf20Sopenharmony_ci u8 *srcbuf = NULL; 658c2ecf20Sopenharmony_ci int ret; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci u32 dlflag = dlflags; 688c2ecf20Sopenharmony_ci u32 dlflag_ack = dlflag + 4; 698c2ecf20Sopenharmony_ci u32 drflag = dlflag_ack + 4; 708c2ecf20Sopenharmony_ci u32 drflag_ack = drflag + 4; 718c2ecf20Sopenharmony_ci u32 bleflag = drflag_ack + 4; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, 748c2ecf20Sopenharmony_ci "%s(image=%p, size=%d, flags=0x%x, dst=%p, dstsize=0x%x)\n", 758c2ecf20Sopenharmony_ci __func__, src, srcsize, dlflags, dst, dstsize); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if ((src == NULL) || (dst == NULL)) { 788c2ecf20Sopenharmony_ci ret = -EIO; 798c2ecf20Sopenharmony_ci goto out; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci srcbuf = kzalloc(4 * 1048576, GFP_KERNEL); 838c2ecf20Sopenharmony_ci if (NULL == srcbuf) { 848c2ecf20Sopenharmony_ci ret = -ENOMEM; 858c2ecf20Sopenharmony_ci goto out; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (srcsize > (4*1048576)) { 898c2ecf20Sopenharmony_ci ret = -ENOMEM; 908c2ecf20Sopenharmony_ci goto out; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci memcpy(srcbuf, src, srcsize); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "%s() dlflag = 0x%x\n", __func__, dlflag); 968c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "%s() dlflag_ack = 0x%x\n", __func__, dlflag_ack); 978c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "%s() drflag = 0x%x\n", __func__, drflag); 988c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "%s() drflag_ack = 0x%x\n", __func__, drflag_ack); 998c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "%s() bleflag = 0x%x\n", __func__, bleflag); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci reg = saa7164_readl(dlflag); 1028c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "%s() dlflag (0x%x)= 0x%x\n", __func__, dlflag, reg); 1038c2ecf20Sopenharmony_ci if (reg == 1) 1048c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, 1058c2ecf20Sopenharmony_ci "%s() Download flag already set, please reboot\n", 1068c2ecf20Sopenharmony_ci __func__); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* Indicate download start */ 1098c2ecf20Sopenharmony_ci saa7164_writel(dlflag, 1); 1108c2ecf20Sopenharmony_ci ret = saa7164_dl_wait_ack(dev, dlflag_ack); 1118c2ecf20Sopenharmony_ci if (ret < 0) 1128c2ecf20Sopenharmony_ci goto out; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* Ack download start, then wait for wait */ 1158c2ecf20Sopenharmony_ci saa7164_writel(dlflag, 0); 1168c2ecf20Sopenharmony_ci ret = saa7164_dl_wait_clr(dev, dlflag_ack); 1178c2ecf20Sopenharmony_ci if (ret < 0) 1188c2ecf20Sopenharmony_ci goto out; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* Deal with the raw firmware, in the appropriate chunk size */ 1218c2ecf20Sopenharmony_ci for (offset = 0; srcsize > dstsize; 1228c2ecf20Sopenharmony_ci srcsize -= dstsize, offset += dstsize) { 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "%s() memcpy %d\n", __func__, dstsize); 1258c2ecf20Sopenharmony_ci memcpy_toio(dst, srcbuf + offset, dstsize); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* Flag the data as ready */ 1288c2ecf20Sopenharmony_ci saa7164_writel(drflag, 1); 1298c2ecf20Sopenharmony_ci ret = saa7164_dl_wait_ack(dev, drflag_ack); 1308c2ecf20Sopenharmony_ci if (ret < 0) 1318c2ecf20Sopenharmony_ci goto out; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* Wait for indication data was received */ 1348c2ecf20Sopenharmony_ci saa7164_writel(drflag, 0); 1358c2ecf20Sopenharmony_ci ret = saa7164_dl_wait_clr(dev, drflag_ack); 1368c2ecf20Sopenharmony_ci if (ret < 0) 1378c2ecf20Sopenharmony_ci goto out; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "%s() memcpy(l) %d\n", __func__, dstsize); 1428c2ecf20Sopenharmony_ci /* Write last block to the device */ 1438c2ecf20Sopenharmony_ci memcpy_toio(dst, srcbuf+offset, srcsize); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* Flag the data as ready */ 1468c2ecf20Sopenharmony_ci saa7164_writel(drflag, 1); 1478c2ecf20Sopenharmony_ci ret = saa7164_dl_wait_ack(dev, drflag_ack); 1488c2ecf20Sopenharmony_ci if (ret < 0) 1498c2ecf20Sopenharmony_ci goto out; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci saa7164_writel(drflag, 0); 1528c2ecf20Sopenharmony_ci timeout = 0; 1538c2ecf20Sopenharmony_ci while (saa7164_readl(bleflag) != SAA_DEVICE_IMAGE_BOOTING) { 1548c2ecf20Sopenharmony_ci if (saa7164_readl(bleflag) & SAA_DEVICE_IMAGE_CORRUPT) { 1558c2ecf20Sopenharmony_ci printk(KERN_ERR "%s() image corrupt\n", __func__); 1568c2ecf20Sopenharmony_ci ret = -EBUSY; 1578c2ecf20Sopenharmony_ci goto out; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (saa7164_readl(bleflag) & SAA_DEVICE_MEMORY_CORRUPT) { 1618c2ecf20Sopenharmony_ci printk(KERN_ERR "%s() device memory corrupt\n", 1628c2ecf20Sopenharmony_ci __func__); 1638c2ecf20Sopenharmony_ci ret = -EBUSY; 1648c2ecf20Sopenharmony_ci goto out; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci msleep(10); /* Checkpatch throws a < 20ms warning */ 1688c2ecf20Sopenharmony_ci if (timeout++ > 60) 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci printk(KERN_INFO "%s() Image downloaded, booting...\n", __func__); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci ret = saa7164_dl_wait_clr(dev, drflag_ack); 1758c2ecf20Sopenharmony_ci if (ret < 0) 1768c2ecf20Sopenharmony_ci goto out; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci printk(KERN_INFO "%s() Image booted successfully.\n", __func__); 1798c2ecf20Sopenharmony_ci ret = 0; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ciout: 1828c2ecf20Sopenharmony_ci kfree(srcbuf); 1838c2ecf20Sopenharmony_ci return ret; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/* TODO: Excessive debug */ 1878c2ecf20Sopenharmony_ci/* Load the firmware. Optionally it can be in ROM or newer versions 1888c2ecf20Sopenharmony_ci * can be on disk, saving the expense of the ROM hardware. */ 1898c2ecf20Sopenharmony_ciint saa7164_downloadfirmware(struct saa7164_dev *dev) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci /* u32 second_timeout = 60 * SAA_DEVICE_TIMEOUT; */ 1928c2ecf20Sopenharmony_ci u32 tmp, filesize, version, err_flags, first_timeout, fwlength; 1938c2ecf20Sopenharmony_ci u32 second_timeout, updatebootloader = 1, bootloadersize = 0; 1948c2ecf20Sopenharmony_ci const struct firmware *fw = NULL; 1958c2ecf20Sopenharmony_ci struct fw_header *hdr, *boothdr = NULL, *fwhdr; 1968c2ecf20Sopenharmony_ci u32 bootloaderversion = 0, fwloadersize; 1978c2ecf20Sopenharmony_ci u8 *bootloaderoffset = NULL, *fwloaderoffset; 1988c2ecf20Sopenharmony_ci char *fwname; 1998c2ecf20Sopenharmony_ci int ret; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "%s()\n", __func__); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (saa7164_boards[dev->board].chiprev == SAA7164_CHIP_REV2) { 2048c2ecf20Sopenharmony_ci fwname = SAA7164_REV2_FIRMWARE; 2058c2ecf20Sopenharmony_ci fwlength = SAA7164_REV2_FIRMWARE_SIZE; 2068c2ecf20Sopenharmony_ci } else { 2078c2ecf20Sopenharmony_ci fwname = SAA7164_REV3_FIRMWARE; 2088c2ecf20Sopenharmony_ci fwlength = SAA7164_REV3_FIRMWARE_SIZE; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci version = saa7164_getcurrentfirmwareversion(dev); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (version == 0x00) { 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci second_timeout = 100; 2168c2ecf20Sopenharmony_ci first_timeout = 100; 2178c2ecf20Sopenharmony_ci err_flags = saa7164_readl(SAA_BOOTLOADERERROR_FLAGS); 2188c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "%s() err_flags = %x\n", 2198c2ecf20Sopenharmony_ci __func__, err_flags); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci while (err_flags != SAA_DEVICE_IMAGE_BOOTING) { 2228c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "%s() err_flags = %x\n", 2238c2ecf20Sopenharmony_ci __func__, err_flags); 2248c2ecf20Sopenharmony_ci msleep(10); /* Checkpatch throws a < 20ms warning */ 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (err_flags & SAA_DEVICE_IMAGE_CORRUPT) { 2278c2ecf20Sopenharmony_ci printk(KERN_ERR "%s() firmware corrupt\n", 2288c2ecf20Sopenharmony_ci __func__); 2298c2ecf20Sopenharmony_ci break; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci if (err_flags & SAA_DEVICE_MEMORY_CORRUPT) { 2328c2ecf20Sopenharmony_ci printk(KERN_ERR "%s() device memory corrupt\n", 2338c2ecf20Sopenharmony_ci __func__); 2348c2ecf20Sopenharmony_ci break; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci if (err_flags & SAA_DEVICE_NO_IMAGE) { 2378c2ecf20Sopenharmony_ci printk(KERN_ERR "%s() no first image\n", 2388c2ecf20Sopenharmony_ci __func__); 2398c2ecf20Sopenharmony_ci break; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci if (err_flags & SAA_DEVICE_IMAGE_SEARCHING) { 2428c2ecf20Sopenharmony_ci first_timeout -= 10; 2438c2ecf20Sopenharmony_ci if (first_timeout == 0) { 2448c2ecf20Sopenharmony_ci printk(KERN_ERR 2458c2ecf20Sopenharmony_ci "%s() no first image\n", 2468c2ecf20Sopenharmony_ci __func__); 2478c2ecf20Sopenharmony_ci break; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci } else if (err_flags & SAA_DEVICE_IMAGE_LOADING) { 2508c2ecf20Sopenharmony_ci second_timeout -= 10; 2518c2ecf20Sopenharmony_ci if (second_timeout == 0) { 2528c2ecf20Sopenharmony_ci printk(KERN_ERR 2538c2ecf20Sopenharmony_ci "%s() FW load time exceeded\n", 2548c2ecf20Sopenharmony_ci __func__); 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci } else { 2588c2ecf20Sopenharmony_ci second_timeout -= 10; 2598c2ecf20Sopenharmony_ci if (second_timeout == 0) { 2608c2ecf20Sopenharmony_ci printk(KERN_ERR 2618c2ecf20Sopenharmony_ci "%s() Unknown bootloader flags 0x%x\n", 2628c2ecf20Sopenharmony_ci __func__, err_flags); 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci err_flags = saa7164_readl(SAA_BOOTLOADERERROR_FLAGS); 2688c2ecf20Sopenharmony_ci } /* While != Booting */ 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (err_flags == SAA_DEVICE_IMAGE_BOOTING) { 2718c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "%s() Loader 1 has loaded.\n", 2728c2ecf20Sopenharmony_ci __func__); 2738c2ecf20Sopenharmony_ci first_timeout = SAA_DEVICE_TIMEOUT; 2748c2ecf20Sopenharmony_ci second_timeout = 60 * SAA_DEVICE_TIMEOUT; 2758c2ecf20Sopenharmony_ci second_timeout = 100; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci err_flags = saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS); 2788c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "%s() err_flags2 = %x\n", 2798c2ecf20Sopenharmony_ci __func__, err_flags); 2808c2ecf20Sopenharmony_ci while (err_flags != SAA_DEVICE_IMAGE_BOOTING) { 2818c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "%s() err_flags2 = %x\n", 2828c2ecf20Sopenharmony_ci __func__, err_flags); 2838c2ecf20Sopenharmony_ci msleep(10); /* Checkpatch throws a < 20ms warning */ 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (err_flags & SAA_DEVICE_IMAGE_CORRUPT) { 2868c2ecf20Sopenharmony_ci printk(KERN_ERR 2878c2ecf20Sopenharmony_ci "%s() firmware corrupt\n", 2888c2ecf20Sopenharmony_ci __func__); 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci if (err_flags & SAA_DEVICE_MEMORY_CORRUPT) { 2928c2ecf20Sopenharmony_ci printk(KERN_ERR 2938c2ecf20Sopenharmony_ci "%s() device memory corrupt\n", 2948c2ecf20Sopenharmony_ci __func__); 2958c2ecf20Sopenharmony_ci break; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci if (err_flags & SAA_DEVICE_NO_IMAGE) { 2988c2ecf20Sopenharmony_ci printk(KERN_ERR "%s() no second image\n", 2998c2ecf20Sopenharmony_ci __func__); 3008c2ecf20Sopenharmony_ci break; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci if (err_flags & SAA_DEVICE_IMAGE_SEARCHING) { 3038c2ecf20Sopenharmony_ci first_timeout -= 10; 3048c2ecf20Sopenharmony_ci if (first_timeout == 0) { 3058c2ecf20Sopenharmony_ci printk(KERN_ERR 3068c2ecf20Sopenharmony_ci "%s() no second image\n", 3078c2ecf20Sopenharmony_ci __func__); 3088c2ecf20Sopenharmony_ci break; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci } else if (err_flags & 3118c2ecf20Sopenharmony_ci SAA_DEVICE_IMAGE_LOADING) { 3128c2ecf20Sopenharmony_ci second_timeout -= 10; 3138c2ecf20Sopenharmony_ci if (second_timeout == 0) { 3148c2ecf20Sopenharmony_ci printk(KERN_ERR 3158c2ecf20Sopenharmony_ci "%s() FW load time exceeded\n", 3168c2ecf20Sopenharmony_ci __func__); 3178c2ecf20Sopenharmony_ci break; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci } else { 3208c2ecf20Sopenharmony_ci second_timeout -= 10; 3218c2ecf20Sopenharmony_ci if (second_timeout == 0) { 3228c2ecf20Sopenharmony_ci printk(KERN_ERR 3238c2ecf20Sopenharmony_ci "%s() Unknown bootloader flags 0x%x\n", 3248c2ecf20Sopenharmony_ci __func__, err_flags); 3258c2ecf20Sopenharmony_ci break; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci err_flags = 3308c2ecf20Sopenharmony_ci saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS); 3318c2ecf20Sopenharmony_ci } /* err_flags != SAA_DEVICE_IMAGE_BOOTING */ 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "%s() Loader flags 1:0x%x 2:0x%x.\n", 3348c2ecf20Sopenharmony_ci __func__, 3358c2ecf20Sopenharmony_ci saa7164_readl(SAA_BOOTLOADERERROR_FLAGS), 3368c2ecf20Sopenharmony_ci saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS)); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci } /* err_flags == SAA_DEVICE_IMAGE_BOOTING */ 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* It's possible for both firmwares to have booted, 3418c2ecf20Sopenharmony_ci * but that doesn't mean they've finished booting yet. 3428c2ecf20Sopenharmony_ci */ 3438c2ecf20Sopenharmony_ci if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) == 3448c2ecf20Sopenharmony_ci SAA_DEVICE_IMAGE_BOOTING) && 3458c2ecf20Sopenharmony_ci (saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS) == 3468c2ecf20Sopenharmony_ci SAA_DEVICE_IMAGE_BOOTING)) { 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "%s() Loader 2 has loaded.\n", 3508c2ecf20Sopenharmony_ci __func__); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci first_timeout = SAA_DEVICE_TIMEOUT; 3538c2ecf20Sopenharmony_ci while (first_timeout) { 3548c2ecf20Sopenharmony_ci msleep(10); /* Checkpatch throws a < 20ms warning */ 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci version = 3578c2ecf20Sopenharmony_ci saa7164_getcurrentfirmwareversion(dev); 3588c2ecf20Sopenharmony_ci if (version) { 3598c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, 3608c2ecf20Sopenharmony_ci "%s() All f/w loaded successfully\n", 3618c2ecf20Sopenharmony_ci __func__); 3628c2ecf20Sopenharmony_ci break; 3638c2ecf20Sopenharmony_ci } else { 3648c2ecf20Sopenharmony_ci first_timeout -= 10; 3658c2ecf20Sopenharmony_ci if (first_timeout == 0) { 3668c2ecf20Sopenharmony_ci printk(KERN_ERR 3678c2ecf20Sopenharmony_ci "%s() FW did not boot\n", 3688c2ecf20Sopenharmony_ci __func__); 3698c2ecf20Sopenharmony_ci break; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci version = saa7164_getcurrentfirmwareversion(dev); 3758c2ecf20Sopenharmony_ci } /* version == 0 */ 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* Has the firmware really booted? */ 3788c2ecf20Sopenharmony_ci if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) == 3798c2ecf20Sopenharmony_ci SAA_DEVICE_IMAGE_BOOTING) && 3808c2ecf20Sopenharmony_ci (saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS) == 3818c2ecf20Sopenharmony_ci SAA_DEVICE_IMAGE_BOOTING) && (version == 0)) { 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci printk(KERN_ERR 3848c2ecf20Sopenharmony_ci "%s() The firmware hung, probably bad firmware\n", 3858c2ecf20Sopenharmony_ci __func__); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* Tell the second stage loader we have a deadlock */ 3888c2ecf20Sopenharmony_ci saa7164_writel(SAA_DEVICE_DEADLOCK_DETECTED_OFFSET, 3898c2ecf20Sopenharmony_ci SAA_DEVICE_DEADLOCK_DETECTED); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci saa7164_getfirmwarestatus(dev); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci return -ENOMEM; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "Device has Firmware Version %d.%d.%d.%d\n", 3978c2ecf20Sopenharmony_ci (version & 0x0000fc00) >> 10, 3988c2ecf20Sopenharmony_ci (version & 0x000003e0) >> 5, 3998c2ecf20Sopenharmony_ci (version & 0x0000001f), 4008c2ecf20Sopenharmony_ci (version & 0xffff0000) >> 16); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* Load the firmware from the disk if required */ 4038c2ecf20Sopenharmony_ci if (version == 0) { 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci printk(KERN_INFO "%s() Waiting for firmware upload (%s)\n", 4068c2ecf20Sopenharmony_ci __func__, fwname); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci ret = request_firmware(&fw, fwname, &dev->pci->dev); 4098c2ecf20Sopenharmony_ci if (ret) { 4108c2ecf20Sopenharmony_ci printk(KERN_ERR "%s() Upload failed. (file not found?)\n", 4118c2ecf20Sopenharmony_ci __func__); 4128c2ecf20Sopenharmony_ci return -ENOMEM; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci printk(KERN_INFO "%s() firmware read %zu bytes.\n", 4168c2ecf20Sopenharmony_ci __func__, fw->size); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (fw->size != fwlength) { 4198c2ecf20Sopenharmony_ci printk(KERN_ERR "saa7164: firmware incorrect size %zu != %u\n", 4208c2ecf20Sopenharmony_ci fw->size, fwlength); 4218c2ecf20Sopenharmony_ci ret = -ENOMEM; 4228c2ecf20Sopenharmony_ci goto out; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci printk(KERN_INFO "%s() firmware loaded.\n", __func__); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci hdr = (struct fw_header *)fw->data; 4288c2ecf20Sopenharmony_ci printk(KERN_INFO "Firmware file header part 1:\n"); 4298c2ecf20Sopenharmony_ci printk(KERN_INFO " .FirmwareSize = 0x%x\n", hdr->firmwaresize); 4308c2ecf20Sopenharmony_ci printk(KERN_INFO " .BSLSize = 0x%x\n", hdr->bslsize); 4318c2ecf20Sopenharmony_ci printk(KERN_INFO " .Reserved = 0x%x\n", hdr->reserved); 4328c2ecf20Sopenharmony_ci printk(KERN_INFO " .Version = 0x%x\n", hdr->version); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* Retrieve bootloader if reqd */ 4358c2ecf20Sopenharmony_ci if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0)) 4368c2ecf20Sopenharmony_ci /* Second bootloader in the firmware file */ 4378c2ecf20Sopenharmony_ci filesize = hdr->reserved * 16; 4388c2ecf20Sopenharmony_ci else 4398c2ecf20Sopenharmony_ci filesize = (hdr->firmwaresize + hdr->bslsize) * 4408c2ecf20Sopenharmony_ci 16 + sizeof(struct fw_header); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci printk(KERN_INFO "%s() SecBootLoader.FileSize = %d\n", 4438c2ecf20Sopenharmony_ci __func__, filesize); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* Get bootloader (if reqd) and firmware header */ 4468c2ecf20Sopenharmony_ci if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0)) { 4478c2ecf20Sopenharmony_ci /* Second boot loader is required */ 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* Get the loader header */ 4508c2ecf20Sopenharmony_ci boothdr = (struct fw_header *)(fw->data + 4518c2ecf20Sopenharmony_ci sizeof(struct fw_header)); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci bootloaderversion = 4548c2ecf20Sopenharmony_ci saa7164_readl(SAA_DEVICE_2ND_VERSION); 4558c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "Onboard BootLoader:\n"); 4568c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "->Flag 0x%x\n", 4578c2ecf20Sopenharmony_ci saa7164_readl(SAA_BOOTLOADERERROR_FLAGS)); 4588c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "->Ack 0x%x\n", 4598c2ecf20Sopenharmony_ci saa7164_readl(SAA_DATAREADY_FLAG_ACK)); 4608c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "->FW Version 0x%x\n", version); 4618c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "->Loader Version 0x%x\n", 4628c2ecf20Sopenharmony_ci bootloaderversion); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) == 4658c2ecf20Sopenharmony_ci 0x03) && (saa7164_readl(SAA_DATAREADY_FLAG_ACK) 4668c2ecf20Sopenharmony_ci == 0x00) && (version == 0x00)) { 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "BootLoader version in rom %d.%d.%d.%d\n", 4698c2ecf20Sopenharmony_ci (bootloaderversion & 0x0000fc00) >> 10, 4708c2ecf20Sopenharmony_ci (bootloaderversion & 0x000003e0) >> 5, 4718c2ecf20Sopenharmony_ci (bootloaderversion & 0x0000001f), 4728c2ecf20Sopenharmony_ci (bootloaderversion & 0xffff0000) >> 16 4738c2ecf20Sopenharmony_ci ); 4748c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "BootLoader version in file %d.%d.%d.%d\n", 4758c2ecf20Sopenharmony_ci (boothdr->version & 0x0000fc00) >> 10, 4768c2ecf20Sopenharmony_ci (boothdr->version & 0x000003e0) >> 5, 4778c2ecf20Sopenharmony_ci (boothdr->version & 0x0000001f), 4788c2ecf20Sopenharmony_ci (boothdr->version & 0xffff0000) >> 16 4798c2ecf20Sopenharmony_ci ); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (bootloaderversion == boothdr->version) 4828c2ecf20Sopenharmony_ci updatebootloader = 0; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* Calculate offset to firmware header */ 4868c2ecf20Sopenharmony_ci tmp = (boothdr->firmwaresize + boothdr->bslsize) * 16 + 4878c2ecf20Sopenharmony_ci (sizeof(struct fw_header) + 4888c2ecf20Sopenharmony_ci sizeof(struct fw_header)); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci fwhdr = (struct fw_header *)(fw->data+tmp); 4918c2ecf20Sopenharmony_ci } else { 4928c2ecf20Sopenharmony_ci /* No second boot loader */ 4938c2ecf20Sopenharmony_ci fwhdr = hdr; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "Firmware version in file %d.%d.%d.%d\n", 4978c2ecf20Sopenharmony_ci (fwhdr->version & 0x0000fc00) >> 10, 4988c2ecf20Sopenharmony_ci (fwhdr->version & 0x000003e0) >> 5, 4998c2ecf20Sopenharmony_ci (fwhdr->version & 0x0000001f), 5008c2ecf20Sopenharmony_ci (fwhdr->version & 0xffff0000) >> 16 5018c2ecf20Sopenharmony_ci ); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci if (version == fwhdr->version) { 5048c2ecf20Sopenharmony_ci /* No download, firmware already on board */ 5058c2ecf20Sopenharmony_ci ret = 0; 5068c2ecf20Sopenharmony_ci goto out; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0)) { 5108c2ecf20Sopenharmony_ci if (updatebootloader) { 5118c2ecf20Sopenharmony_ci /* Get ready to upload the bootloader */ 5128c2ecf20Sopenharmony_ci bootloadersize = (boothdr->firmwaresize + 5138c2ecf20Sopenharmony_ci boothdr->bslsize) * 16 + 5148c2ecf20Sopenharmony_ci sizeof(struct fw_header); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci bootloaderoffset = (u8 *)(fw->data + 5178c2ecf20Sopenharmony_ci sizeof(struct fw_header)); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, "bootloader d/l starts.\n"); 5208c2ecf20Sopenharmony_ci printk(KERN_INFO "%s() FirmwareSize = 0x%x\n", 5218c2ecf20Sopenharmony_ci __func__, boothdr->firmwaresize); 5228c2ecf20Sopenharmony_ci printk(KERN_INFO "%s() BSLSize = 0x%x\n", 5238c2ecf20Sopenharmony_ci __func__, boothdr->bslsize); 5248c2ecf20Sopenharmony_ci printk(KERN_INFO "%s() Reserved = 0x%x\n", 5258c2ecf20Sopenharmony_ci __func__, boothdr->reserved); 5268c2ecf20Sopenharmony_ci printk(KERN_INFO "%s() Version = 0x%x\n", 5278c2ecf20Sopenharmony_ci __func__, boothdr->version); 5288c2ecf20Sopenharmony_ci ret = saa7164_downloadimage( 5298c2ecf20Sopenharmony_ci dev, 5308c2ecf20Sopenharmony_ci bootloaderoffset, 5318c2ecf20Sopenharmony_ci bootloadersize, 5328c2ecf20Sopenharmony_ci SAA_DOWNLOAD_FLAGS, 5338c2ecf20Sopenharmony_ci dev->bmmio + SAA_DEVICE_DOWNLOAD_OFFSET, 5348c2ecf20Sopenharmony_ci SAA_DEVICE_BUFFERBLOCKSIZE); 5358c2ecf20Sopenharmony_ci if (ret < 0) { 5368c2ecf20Sopenharmony_ci printk(KERN_ERR 5378c2ecf20Sopenharmony_ci "bootloader d/l has failed\n"); 5388c2ecf20Sopenharmony_ci goto out; 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci dprintk(DBGLVL_FW, 5418c2ecf20Sopenharmony_ci "bootloader download complete.\n"); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci printk(KERN_ERR "starting firmware download(2)\n"); 5468c2ecf20Sopenharmony_ci bootloadersize = (boothdr->firmwaresize + 5478c2ecf20Sopenharmony_ci boothdr->bslsize) * 16 + 5488c2ecf20Sopenharmony_ci sizeof(struct fw_header); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci bootloaderoffset = 5518c2ecf20Sopenharmony_ci (u8 *)(fw->data + sizeof(struct fw_header)); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci fwloaderoffset = bootloaderoffset + bootloadersize; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* TODO: fix this bounds overrun here with old f/ws */ 5568c2ecf20Sopenharmony_ci fwloadersize = (fwhdr->firmwaresize + fwhdr->bslsize) * 5578c2ecf20Sopenharmony_ci 16 + sizeof(struct fw_header); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci ret = saa7164_downloadimage( 5608c2ecf20Sopenharmony_ci dev, 5618c2ecf20Sopenharmony_ci fwloaderoffset, 5628c2ecf20Sopenharmony_ci fwloadersize, 5638c2ecf20Sopenharmony_ci SAA_DEVICE_2ND_DOWNLOADFLAG_OFFSET, 5648c2ecf20Sopenharmony_ci dev->bmmio + SAA_DEVICE_2ND_DOWNLOAD_OFFSET, 5658c2ecf20Sopenharmony_ci SAA_DEVICE_2ND_BUFFERBLOCKSIZE); 5668c2ecf20Sopenharmony_ci if (ret < 0) { 5678c2ecf20Sopenharmony_ci printk(KERN_ERR "firmware download failed\n"); 5688c2ecf20Sopenharmony_ci goto out; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci printk(KERN_ERR "firmware download complete.\n"); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci } else { 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* No bootloader update reqd, download firmware only */ 5758c2ecf20Sopenharmony_ci printk(KERN_ERR "starting firmware download(3)\n"); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci ret = saa7164_downloadimage( 5788c2ecf20Sopenharmony_ci dev, 5798c2ecf20Sopenharmony_ci (u8 *)fw->data, 5808c2ecf20Sopenharmony_ci fw->size, 5818c2ecf20Sopenharmony_ci SAA_DOWNLOAD_FLAGS, 5828c2ecf20Sopenharmony_ci dev->bmmio + SAA_DEVICE_DOWNLOAD_OFFSET, 5838c2ecf20Sopenharmony_ci SAA_DEVICE_BUFFERBLOCKSIZE); 5848c2ecf20Sopenharmony_ci if (ret < 0) { 5858c2ecf20Sopenharmony_ci printk(KERN_ERR "firmware download failed\n"); 5868c2ecf20Sopenharmony_ci goto out; 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci printk(KERN_ERR "firmware download complete.\n"); 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci dev->firmwareloaded = 1; 5938c2ecf20Sopenharmony_ci ret = 0; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ciout: 5968c2ecf20Sopenharmony_ci release_firmware(fw); 5978c2ecf20Sopenharmony_ci return ret; 5988c2ecf20Sopenharmony_ci} 599