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