162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Driver for the NXP SAA7164 PCIe bridge
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/firmware.h>
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "saa7164.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define SAA7164_REV2_FIRMWARE		"NXP7164-2010-03-10.1.fw"
1462306a36Sopenharmony_ci#define SAA7164_REV2_FIRMWARE_SIZE	4019072
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define SAA7164_REV3_FIRMWARE		"NXP7164-2010-03-10.1.fw"
1762306a36Sopenharmony_ci#define SAA7164_REV3_FIRMWARE_SIZE	4019072
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistruct fw_header {
2062306a36Sopenharmony_ci	u32	firmwaresize;
2162306a36Sopenharmony_ci	u32	bslsize;
2262306a36Sopenharmony_ci	u32	reserved;
2362306a36Sopenharmony_ci	u32	version;
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic int saa7164_dl_wait_ack(struct saa7164_dev *dev, u32 reg)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	u32 timeout = SAA_DEVICE_TIMEOUT;
2962306a36Sopenharmony_ci	while ((saa7164_readl(reg) & 0x01) == 0) {
3062306a36Sopenharmony_ci		timeout -= 10;
3162306a36Sopenharmony_ci		if (timeout == 0) {
3262306a36Sopenharmony_ci			printk(KERN_ERR "%s() timeout (no d/l ack)\n",
3362306a36Sopenharmony_ci				__func__);
3462306a36Sopenharmony_ci			return -EBUSY;
3562306a36Sopenharmony_ci		}
3662306a36Sopenharmony_ci		msleep(100);
3762306a36Sopenharmony_ci	}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	return 0;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic int saa7164_dl_wait_clr(struct saa7164_dev *dev, u32 reg)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	u32 timeout = SAA_DEVICE_TIMEOUT;
4562306a36Sopenharmony_ci	while (saa7164_readl(reg) & 0x01) {
4662306a36Sopenharmony_ci		timeout -= 10;
4762306a36Sopenharmony_ci		if (timeout == 0) {
4862306a36Sopenharmony_ci			printk(KERN_ERR "%s() timeout (no d/l clr)\n",
4962306a36Sopenharmony_ci				__func__);
5062306a36Sopenharmony_ci			return -EBUSY;
5162306a36Sopenharmony_ci		}
5262306a36Sopenharmony_ci		msleep(100);
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return 0;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* TODO: move dlflags into dev-> and change to write/readl/b */
5962306a36Sopenharmony_ci/* TODO: Excessive levels of debug */
6062306a36Sopenharmony_cistatic int saa7164_downloadimage(struct saa7164_dev *dev, u8 *src, u32 srcsize,
6162306a36Sopenharmony_ci				 u32 dlflags, u8 __iomem *dst, u32 dstsize)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	u32 reg, timeout, offset;
6462306a36Sopenharmony_ci	u8 *srcbuf = NULL;
6562306a36Sopenharmony_ci	int ret;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	u32 dlflag = dlflags;
6862306a36Sopenharmony_ci	u32 dlflag_ack = dlflag + 4;
6962306a36Sopenharmony_ci	u32 drflag = dlflag_ack + 4;
7062306a36Sopenharmony_ci	u32 drflag_ack = drflag + 4;
7162306a36Sopenharmony_ci	u32 bleflag = drflag_ack + 4;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	dprintk(DBGLVL_FW,
7462306a36Sopenharmony_ci		"%s(image=%p, size=%d, flags=0x%x, dst=%p, dstsize=0x%x)\n",
7562306a36Sopenharmony_ci		__func__, src, srcsize, dlflags, dst, dstsize);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if ((src == NULL) || (dst == NULL)) {
7862306a36Sopenharmony_ci		ret = -EIO;
7962306a36Sopenharmony_ci		goto out;
8062306a36Sopenharmony_ci	}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	srcbuf = kzalloc(4 * 1048576, GFP_KERNEL);
8362306a36Sopenharmony_ci	if (NULL == srcbuf) {
8462306a36Sopenharmony_ci		ret = -ENOMEM;
8562306a36Sopenharmony_ci		goto out;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (srcsize > (4*1048576)) {
8962306a36Sopenharmony_ci		ret = -ENOMEM;
9062306a36Sopenharmony_ci		goto out;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	memcpy(srcbuf, src, srcsize);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	dprintk(DBGLVL_FW, "%s() dlflag = 0x%x\n", __func__, dlflag);
9662306a36Sopenharmony_ci	dprintk(DBGLVL_FW, "%s() dlflag_ack = 0x%x\n", __func__, dlflag_ack);
9762306a36Sopenharmony_ci	dprintk(DBGLVL_FW, "%s() drflag = 0x%x\n", __func__, drflag);
9862306a36Sopenharmony_ci	dprintk(DBGLVL_FW, "%s() drflag_ack = 0x%x\n", __func__, drflag_ack);
9962306a36Sopenharmony_ci	dprintk(DBGLVL_FW, "%s() bleflag = 0x%x\n", __func__, bleflag);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	reg = saa7164_readl(dlflag);
10262306a36Sopenharmony_ci	dprintk(DBGLVL_FW, "%s() dlflag (0x%x)= 0x%x\n", __func__, dlflag, reg);
10362306a36Sopenharmony_ci	if (reg == 1)
10462306a36Sopenharmony_ci		dprintk(DBGLVL_FW,
10562306a36Sopenharmony_ci			"%s() Download flag already set, please reboot\n",
10662306a36Sopenharmony_ci			__func__);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/* Indicate download start */
10962306a36Sopenharmony_ci	saa7164_writel(dlflag, 1);
11062306a36Sopenharmony_ci	ret = saa7164_dl_wait_ack(dev, dlflag_ack);
11162306a36Sopenharmony_ci	if (ret < 0)
11262306a36Sopenharmony_ci		goto out;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* Ack download start, then wait for wait */
11562306a36Sopenharmony_ci	saa7164_writel(dlflag, 0);
11662306a36Sopenharmony_ci	ret = saa7164_dl_wait_clr(dev, dlflag_ack);
11762306a36Sopenharmony_ci	if (ret < 0)
11862306a36Sopenharmony_ci		goto out;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* Deal with the raw firmware, in the appropriate chunk size */
12162306a36Sopenharmony_ci	for (offset = 0; srcsize > dstsize;
12262306a36Sopenharmony_ci		srcsize -= dstsize, offset += dstsize) {
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		dprintk(DBGLVL_FW, "%s() memcpy %d\n", __func__, dstsize);
12562306a36Sopenharmony_ci		memcpy_toio(dst, srcbuf + offset, dstsize);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci		/* Flag the data as ready */
12862306a36Sopenharmony_ci		saa7164_writel(drflag, 1);
12962306a36Sopenharmony_ci		ret = saa7164_dl_wait_ack(dev, drflag_ack);
13062306a36Sopenharmony_ci		if (ret < 0)
13162306a36Sopenharmony_ci			goto out;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci		/* Wait for indication data was received */
13462306a36Sopenharmony_ci		saa7164_writel(drflag, 0);
13562306a36Sopenharmony_ci		ret = saa7164_dl_wait_clr(dev, drflag_ack);
13662306a36Sopenharmony_ci		if (ret < 0)
13762306a36Sopenharmony_ci			goto out;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	dprintk(DBGLVL_FW, "%s() memcpy(l) %d\n", __func__, dstsize);
14262306a36Sopenharmony_ci	/* Write last block to the device */
14362306a36Sopenharmony_ci	memcpy_toio(dst, srcbuf+offset, srcsize);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/* Flag the data as ready */
14662306a36Sopenharmony_ci	saa7164_writel(drflag, 1);
14762306a36Sopenharmony_ci	ret = saa7164_dl_wait_ack(dev, drflag_ack);
14862306a36Sopenharmony_ci	if (ret < 0)
14962306a36Sopenharmony_ci		goto out;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	saa7164_writel(drflag, 0);
15262306a36Sopenharmony_ci	timeout = 0;
15362306a36Sopenharmony_ci	while (saa7164_readl(bleflag) != SAA_DEVICE_IMAGE_BOOTING) {
15462306a36Sopenharmony_ci		if (saa7164_readl(bleflag) & SAA_DEVICE_IMAGE_CORRUPT) {
15562306a36Sopenharmony_ci			printk(KERN_ERR "%s() image corrupt\n", __func__);
15662306a36Sopenharmony_ci			ret = -EBUSY;
15762306a36Sopenharmony_ci			goto out;
15862306a36Sopenharmony_ci		}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci		if (saa7164_readl(bleflag) & SAA_DEVICE_MEMORY_CORRUPT) {
16162306a36Sopenharmony_ci			printk(KERN_ERR "%s() device memory corrupt\n",
16262306a36Sopenharmony_ci				__func__);
16362306a36Sopenharmony_ci			ret = -EBUSY;
16462306a36Sopenharmony_ci			goto out;
16562306a36Sopenharmony_ci		}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci		msleep(10); /* Checkpatch throws a < 20ms warning */
16862306a36Sopenharmony_ci		if (timeout++ > 60)
16962306a36Sopenharmony_ci			break;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	printk(KERN_INFO "%s() Image downloaded, booting...\n", __func__);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	ret = saa7164_dl_wait_clr(dev, drflag_ack);
17562306a36Sopenharmony_ci	if (ret < 0)
17662306a36Sopenharmony_ci		goto out;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	printk(KERN_INFO "%s() Image booted successfully.\n", __func__);
17962306a36Sopenharmony_ci	ret = 0;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ciout:
18262306a36Sopenharmony_ci	kfree(srcbuf);
18362306a36Sopenharmony_ci	return ret;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/* TODO: Excessive debug */
18762306a36Sopenharmony_ci/* Load the firmware. Optionally it can be in ROM or newer versions
18862306a36Sopenharmony_ci * can be on disk, saving the expense of the ROM hardware. */
18962306a36Sopenharmony_ciint saa7164_downloadfirmware(struct saa7164_dev *dev)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	/* u32 second_timeout = 60 * SAA_DEVICE_TIMEOUT; */
19262306a36Sopenharmony_ci	u32 tmp, filesize, version, err_flags, first_timeout, fwlength;
19362306a36Sopenharmony_ci	u32 second_timeout, updatebootloader = 1, bootloadersize = 0;
19462306a36Sopenharmony_ci	const struct firmware *fw = NULL;
19562306a36Sopenharmony_ci	struct fw_header *hdr, *boothdr = NULL, *fwhdr;
19662306a36Sopenharmony_ci	u32 bootloaderversion = 0, fwloadersize;
19762306a36Sopenharmony_ci	u8 *bootloaderoffset = NULL, *fwloaderoffset;
19862306a36Sopenharmony_ci	char *fwname;
19962306a36Sopenharmony_ci	int ret;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	dprintk(DBGLVL_FW, "%s()\n", __func__);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (saa7164_boards[dev->board].chiprev == SAA7164_CHIP_REV2) {
20462306a36Sopenharmony_ci		fwname = SAA7164_REV2_FIRMWARE;
20562306a36Sopenharmony_ci		fwlength = SAA7164_REV2_FIRMWARE_SIZE;
20662306a36Sopenharmony_ci	} else {
20762306a36Sopenharmony_ci		fwname = SAA7164_REV3_FIRMWARE;
20862306a36Sopenharmony_ci		fwlength = SAA7164_REV3_FIRMWARE_SIZE;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	version = saa7164_getcurrentfirmwareversion(dev);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (version == 0x00) {
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci		second_timeout = 100;
21662306a36Sopenharmony_ci		first_timeout = 100;
21762306a36Sopenharmony_ci		err_flags = saa7164_readl(SAA_BOOTLOADERERROR_FLAGS);
21862306a36Sopenharmony_ci		dprintk(DBGLVL_FW, "%s() err_flags = %x\n",
21962306a36Sopenharmony_ci			__func__, err_flags);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		while (err_flags != SAA_DEVICE_IMAGE_BOOTING) {
22262306a36Sopenharmony_ci			dprintk(DBGLVL_FW, "%s() err_flags = %x\n",
22362306a36Sopenharmony_ci				__func__, err_flags);
22462306a36Sopenharmony_ci			msleep(10); /* Checkpatch throws a < 20ms warning */
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci			if (err_flags & SAA_DEVICE_IMAGE_CORRUPT) {
22762306a36Sopenharmony_ci				printk(KERN_ERR "%s() firmware corrupt\n",
22862306a36Sopenharmony_ci					__func__);
22962306a36Sopenharmony_ci				break;
23062306a36Sopenharmony_ci			}
23162306a36Sopenharmony_ci			if (err_flags & SAA_DEVICE_MEMORY_CORRUPT) {
23262306a36Sopenharmony_ci				printk(KERN_ERR "%s() device memory corrupt\n",
23362306a36Sopenharmony_ci					__func__);
23462306a36Sopenharmony_ci				break;
23562306a36Sopenharmony_ci			}
23662306a36Sopenharmony_ci			if (err_flags & SAA_DEVICE_NO_IMAGE) {
23762306a36Sopenharmony_ci				printk(KERN_ERR "%s() no first image\n",
23862306a36Sopenharmony_ci				__func__);
23962306a36Sopenharmony_ci				break;
24062306a36Sopenharmony_ci			}
24162306a36Sopenharmony_ci			if (err_flags & SAA_DEVICE_IMAGE_SEARCHING) {
24262306a36Sopenharmony_ci				first_timeout -= 10;
24362306a36Sopenharmony_ci				if (first_timeout == 0) {
24462306a36Sopenharmony_ci					printk(KERN_ERR
24562306a36Sopenharmony_ci						"%s() no first image\n",
24662306a36Sopenharmony_ci						__func__);
24762306a36Sopenharmony_ci					break;
24862306a36Sopenharmony_ci				}
24962306a36Sopenharmony_ci			} else if (err_flags & SAA_DEVICE_IMAGE_LOADING) {
25062306a36Sopenharmony_ci				second_timeout -= 10;
25162306a36Sopenharmony_ci				if (second_timeout == 0) {
25262306a36Sopenharmony_ci					printk(KERN_ERR
25362306a36Sopenharmony_ci					"%s() FW load time exceeded\n",
25462306a36Sopenharmony_ci						__func__);
25562306a36Sopenharmony_ci					break;
25662306a36Sopenharmony_ci				}
25762306a36Sopenharmony_ci			} else {
25862306a36Sopenharmony_ci				second_timeout -= 10;
25962306a36Sopenharmony_ci				if (second_timeout == 0) {
26062306a36Sopenharmony_ci					printk(KERN_ERR
26162306a36Sopenharmony_ci					"%s() Unknown bootloader flags 0x%x\n",
26262306a36Sopenharmony_ci						__func__, err_flags);
26362306a36Sopenharmony_ci					break;
26462306a36Sopenharmony_ci				}
26562306a36Sopenharmony_ci			}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci			err_flags = saa7164_readl(SAA_BOOTLOADERERROR_FLAGS);
26862306a36Sopenharmony_ci		} /* While != Booting */
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		if (err_flags == SAA_DEVICE_IMAGE_BOOTING) {
27162306a36Sopenharmony_ci			dprintk(DBGLVL_FW, "%s() Loader 1 has loaded.\n",
27262306a36Sopenharmony_ci				__func__);
27362306a36Sopenharmony_ci			first_timeout = SAA_DEVICE_TIMEOUT;
27462306a36Sopenharmony_ci			second_timeout = 100;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci			err_flags = saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS);
27762306a36Sopenharmony_ci			dprintk(DBGLVL_FW, "%s() err_flags2 = %x\n",
27862306a36Sopenharmony_ci				__func__, err_flags);
27962306a36Sopenharmony_ci			while (err_flags != SAA_DEVICE_IMAGE_BOOTING) {
28062306a36Sopenharmony_ci				dprintk(DBGLVL_FW, "%s() err_flags2 = %x\n",
28162306a36Sopenharmony_ci					__func__, err_flags);
28262306a36Sopenharmony_ci				msleep(10); /* Checkpatch throws a < 20ms warning */
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci				if (err_flags & SAA_DEVICE_IMAGE_CORRUPT) {
28562306a36Sopenharmony_ci					printk(KERN_ERR
28662306a36Sopenharmony_ci						"%s() firmware corrupt\n",
28762306a36Sopenharmony_ci						__func__);
28862306a36Sopenharmony_ci					break;
28962306a36Sopenharmony_ci				}
29062306a36Sopenharmony_ci				if (err_flags & SAA_DEVICE_MEMORY_CORRUPT) {
29162306a36Sopenharmony_ci					printk(KERN_ERR
29262306a36Sopenharmony_ci						"%s() device memory corrupt\n",
29362306a36Sopenharmony_ci						__func__);
29462306a36Sopenharmony_ci					break;
29562306a36Sopenharmony_ci				}
29662306a36Sopenharmony_ci				if (err_flags & SAA_DEVICE_NO_IMAGE) {
29762306a36Sopenharmony_ci					printk(KERN_ERR "%s() no second image\n",
29862306a36Sopenharmony_ci						__func__);
29962306a36Sopenharmony_ci					break;
30062306a36Sopenharmony_ci				}
30162306a36Sopenharmony_ci				if (err_flags & SAA_DEVICE_IMAGE_SEARCHING) {
30262306a36Sopenharmony_ci					first_timeout -= 10;
30362306a36Sopenharmony_ci					if (first_timeout == 0) {
30462306a36Sopenharmony_ci						printk(KERN_ERR
30562306a36Sopenharmony_ci						"%s() no second image\n",
30662306a36Sopenharmony_ci							__func__);
30762306a36Sopenharmony_ci						break;
30862306a36Sopenharmony_ci					}
30962306a36Sopenharmony_ci				} else if (err_flags &
31062306a36Sopenharmony_ci					SAA_DEVICE_IMAGE_LOADING) {
31162306a36Sopenharmony_ci					second_timeout -= 10;
31262306a36Sopenharmony_ci					if (second_timeout == 0) {
31362306a36Sopenharmony_ci						printk(KERN_ERR
31462306a36Sopenharmony_ci						"%s() FW load time exceeded\n",
31562306a36Sopenharmony_ci							__func__);
31662306a36Sopenharmony_ci						break;
31762306a36Sopenharmony_ci					}
31862306a36Sopenharmony_ci				} else {
31962306a36Sopenharmony_ci					second_timeout -= 10;
32062306a36Sopenharmony_ci					if (second_timeout == 0) {
32162306a36Sopenharmony_ci						printk(KERN_ERR
32262306a36Sopenharmony_ci					"%s() Unknown bootloader flags 0x%x\n",
32362306a36Sopenharmony_ci							__func__, err_flags);
32462306a36Sopenharmony_ci						break;
32562306a36Sopenharmony_ci					}
32662306a36Sopenharmony_ci				}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci				err_flags =
32962306a36Sopenharmony_ci				saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS);
33062306a36Sopenharmony_ci			} /* err_flags != SAA_DEVICE_IMAGE_BOOTING */
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci			dprintk(DBGLVL_FW, "%s() Loader flags 1:0x%x 2:0x%x.\n",
33362306a36Sopenharmony_ci				__func__,
33462306a36Sopenharmony_ci				saa7164_readl(SAA_BOOTLOADERERROR_FLAGS),
33562306a36Sopenharmony_ci				saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS));
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci		} /* err_flags == SAA_DEVICE_IMAGE_BOOTING */
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci		/* It's possible for both firmwares to have booted,
34062306a36Sopenharmony_ci		 * but that doesn't mean they've finished booting yet.
34162306a36Sopenharmony_ci		 */
34262306a36Sopenharmony_ci		if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) ==
34362306a36Sopenharmony_ci			SAA_DEVICE_IMAGE_BOOTING) &&
34462306a36Sopenharmony_ci			(saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS) ==
34562306a36Sopenharmony_ci			SAA_DEVICE_IMAGE_BOOTING)) {
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci			dprintk(DBGLVL_FW, "%s() Loader 2 has loaded.\n",
34962306a36Sopenharmony_ci				__func__);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci			first_timeout = SAA_DEVICE_TIMEOUT;
35262306a36Sopenharmony_ci			while (first_timeout) {
35362306a36Sopenharmony_ci				msleep(10); /* Checkpatch throws a < 20ms warning */
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci				version =
35662306a36Sopenharmony_ci					saa7164_getcurrentfirmwareversion(dev);
35762306a36Sopenharmony_ci				if (version) {
35862306a36Sopenharmony_ci					dprintk(DBGLVL_FW,
35962306a36Sopenharmony_ci					"%s() All f/w loaded successfully\n",
36062306a36Sopenharmony_ci						__func__);
36162306a36Sopenharmony_ci					break;
36262306a36Sopenharmony_ci				} else {
36362306a36Sopenharmony_ci					first_timeout -= 10;
36462306a36Sopenharmony_ci					if (first_timeout == 0) {
36562306a36Sopenharmony_ci						printk(KERN_ERR
36662306a36Sopenharmony_ci						"%s() FW did not boot\n",
36762306a36Sopenharmony_ci							__func__);
36862306a36Sopenharmony_ci						break;
36962306a36Sopenharmony_ci					}
37062306a36Sopenharmony_ci				}
37162306a36Sopenharmony_ci			}
37262306a36Sopenharmony_ci		}
37362306a36Sopenharmony_ci		version = saa7164_getcurrentfirmwareversion(dev);
37462306a36Sopenharmony_ci	} /* version == 0 */
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	/* Has the firmware really booted? */
37762306a36Sopenharmony_ci	if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) ==
37862306a36Sopenharmony_ci		SAA_DEVICE_IMAGE_BOOTING) &&
37962306a36Sopenharmony_ci		(saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS) ==
38062306a36Sopenharmony_ci		SAA_DEVICE_IMAGE_BOOTING) && (version == 0)) {
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci		printk(KERN_ERR
38362306a36Sopenharmony_ci			"%s() The firmware hung, probably bad firmware\n",
38462306a36Sopenharmony_ci			__func__);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		/* Tell the second stage loader we have a deadlock */
38762306a36Sopenharmony_ci		saa7164_writel(SAA_DEVICE_DEADLOCK_DETECTED_OFFSET,
38862306a36Sopenharmony_ci			SAA_DEVICE_DEADLOCK_DETECTED);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci		saa7164_getfirmwarestatus(dev);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci		return -ENOMEM;
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	dprintk(DBGLVL_FW, "Device has Firmware Version %d.%d.%d.%d\n",
39662306a36Sopenharmony_ci		(version & 0x0000fc00) >> 10,
39762306a36Sopenharmony_ci		(version & 0x000003e0) >> 5,
39862306a36Sopenharmony_ci		(version & 0x0000001f),
39962306a36Sopenharmony_ci		(version & 0xffff0000) >> 16);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	/* Load the firmware from the disk if required */
40262306a36Sopenharmony_ci	if (version == 0) {
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		printk(KERN_INFO "%s() Waiting for firmware upload (%s)\n",
40562306a36Sopenharmony_ci			__func__, fwname);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci		ret = request_firmware(&fw, fwname, &dev->pci->dev);
40862306a36Sopenharmony_ci		if (ret) {
40962306a36Sopenharmony_ci			printk(KERN_ERR "%s() Upload failed. (file not found?)\n",
41062306a36Sopenharmony_ci			       __func__);
41162306a36Sopenharmony_ci			return -ENOMEM;
41262306a36Sopenharmony_ci		}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci		printk(KERN_INFO "%s() firmware read %zu bytes.\n",
41562306a36Sopenharmony_ci			__func__, fw->size);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci		if (fw->size != fwlength) {
41862306a36Sopenharmony_ci			printk(KERN_ERR "saa7164: firmware incorrect size %zu != %u\n",
41962306a36Sopenharmony_ci				fw->size, fwlength);
42062306a36Sopenharmony_ci			ret = -ENOMEM;
42162306a36Sopenharmony_ci			goto out;
42262306a36Sopenharmony_ci		}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci		printk(KERN_INFO "%s() firmware loaded.\n", __func__);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci		hdr = (struct fw_header *)fw->data;
42762306a36Sopenharmony_ci		printk(KERN_INFO "Firmware file header part 1:\n");
42862306a36Sopenharmony_ci		printk(KERN_INFO " .FirmwareSize = 0x%x\n", hdr->firmwaresize);
42962306a36Sopenharmony_ci		printk(KERN_INFO " .BSLSize = 0x%x\n", hdr->bslsize);
43062306a36Sopenharmony_ci		printk(KERN_INFO " .Reserved = 0x%x\n", hdr->reserved);
43162306a36Sopenharmony_ci		printk(KERN_INFO " .Version = 0x%x\n", hdr->version);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci		/* Retrieve bootloader if reqd */
43462306a36Sopenharmony_ci		if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0))
43562306a36Sopenharmony_ci			/* Second bootloader in the firmware file */
43662306a36Sopenharmony_ci			filesize = hdr->reserved * 16;
43762306a36Sopenharmony_ci		else
43862306a36Sopenharmony_ci			filesize = (hdr->firmwaresize + hdr->bslsize) *
43962306a36Sopenharmony_ci				16 + sizeof(struct fw_header);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci		printk(KERN_INFO "%s() SecBootLoader.FileSize = %d\n",
44262306a36Sopenharmony_ci			__func__, filesize);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci		/* Get bootloader (if reqd) and firmware header */
44562306a36Sopenharmony_ci		if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0)) {
44662306a36Sopenharmony_ci			/* Second boot loader is required */
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci			/* Get the loader header */
44962306a36Sopenharmony_ci			boothdr = (struct fw_header *)(fw->data +
45062306a36Sopenharmony_ci				sizeof(struct fw_header));
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci			bootloaderversion =
45362306a36Sopenharmony_ci				saa7164_readl(SAA_DEVICE_2ND_VERSION);
45462306a36Sopenharmony_ci			dprintk(DBGLVL_FW, "Onboard BootLoader:\n");
45562306a36Sopenharmony_ci			dprintk(DBGLVL_FW, "->Flag 0x%x\n",
45662306a36Sopenharmony_ci				saa7164_readl(SAA_BOOTLOADERERROR_FLAGS));
45762306a36Sopenharmony_ci			dprintk(DBGLVL_FW, "->Ack 0x%x\n",
45862306a36Sopenharmony_ci				saa7164_readl(SAA_DATAREADY_FLAG_ACK));
45962306a36Sopenharmony_ci			dprintk(DBGLVL_FW, "->FW Version 0x%x\n", version);
46062306a36Sopenharmony_ci			dprintk(DBGLVL_FW, "->Loader Version 0x%x\n",
46162306a36Sopenharmony_ci				bootloaderversion);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci			if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) ==
46462306a36Sopenharmony_ci				0x03) && (saa7164_readl(SAA_DATAREADY_FLAG_ACK)
46562306a36Sopenharmony_ci				== 0x00) && (version == 0x00)) {
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci				dprintk(DBGLVL_FW, "BootLoader version in  rom %d.%d.%d.%d\n",
46862306a36Sopenharmony_ci					(bootloaderversion & 0x0000fc00) >> 10,
46962306a36Sopenharmony_ci					(bootloaderversion & 0x000003e0) >> 5,
47062306a36Sopenharmony_ci					(bootloaderversion & 0x0000001f),
47162306a36Sopenharmony_ci					(bootloaderversion & 0xffff0000) >> 16
47262306a36Sopenharmony_ci					);
47362306a36Sopenharmony_ci				dprintk(DBGLVL_FW, "BootLoader version in file %d.%d.%d.%d\n",
47462306a36Sopenharmony_ci					(boothdr->version & 0x0000fc00) >> 10,
47562306a36Sopenharmony_ci					(boothdr->version & 0x000003e0) >> 5,
47662306a36Sopenharmony_ci					(boothdr->version & 0x0000001f),
47762306a36Sopenharmony_ci					(boothdr->version & 0xffff0000) >> 16
47862306a36Sopenharmony_ci					);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci				if (bootloaderversion == boothdr->version)
48162306a36Sopenharmony_ci					updatebootloader = 0;
48262306a36Sopenharmony_ci			}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci			/* Calculate offset to firmware header */
48562306a36Sopenharmony_ci			tmp = (boothdr->firmwaresize + boothdr->bslsize) * 16 +
48662306a36Sopenharmony_ci				(sizeof(struct fw_header) +
48762306a36Sopenharmony_ci				sizeof(struct fw_header));
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci			fwhdr = (struct fw_header *)(fw->data+tmp);
49062306a36Sopenharmony_ci		} else {
49162306a36Sopenharmony_ci			/* No second boot loader */
49262306a36Sopenharmony_ci			fwhdr = hdr;
49362306a36Sopenharmony_ci		}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci		dprintk(DBGLVL_FW, "Firmware version in file %d.%d.%d.%d\n",
49662306a36Sopenharmony_ci			(fwhdr->version & 0x0000fc00) >> 10,
49762306a36Sopenharmony_ci			(fwhdr->version & 0x000003e0) >> 5,
49862306a36Sopenharmony_ci			(fwhdr->version & 0x0000001f),
49962306a36Sopenharmony_ci			(fwhdr->version & 0xffff0000) >> 16
50062306a36Sopenharmony_ci			);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci		if (version == fwhdr->version) {
50362306a36Sopenharmony_ci			/* No download, firmware already on board */
50462306a36Sopenharmony_ci			ret = 0;
50562306a36Sopenharmony_ci			goto out;
50662306a36Sopenharmony_ci		}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci		if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0)) {
50962306a36Sopenharmony_ci			if (updatebootloader) {
51062306a36Sopenharmony_ci				/* Get ready to upload the bootloader */
51162306a36Sopenharmony_ci				bootloadersize = (boothdr->firmwaresize +
51262306a36Sopenharmony_ci					boothdr->bslsize) * 16 +
51362306a36Sopenharmony_ci					sizeof(struct fw_header);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci				bootloaderoffset = (u8 *)(fw->data +
51662306a36Sopenharmony_ci					sizeof(struct fw_header));
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci				dprintk(DBGLVL_FW, "bootloader d/l starts.\n");
51962306a36Sopenharmony_ci				printk(KERN_INFO "%s() FirmwareSize = 0x%x\n",
52062306a36Sopenharmony_ci					__func__, boothdr->firmwaresize);
52162306a36Sopenharmony_ci				printk(KERN_INFO "%s() BSLSize = 0x%x\n",
52262306a36Sopenharmony_ci					__func__, boothdr->bslsize);
52362306a36Sopenharmony_ci				printk(KERN_INFO "%s() Reserved = 0x%x\n",
52462306a36Sopenharmony_ci					__func__, boothdr->reserved);
52562306a36Sopenharmony_ci				printk(KERN_INFO "%s() Version = 0x%x\n",
52662306a36Sopenharmony_ci					__func__, boothdr->version);
52762306a36Sopenharmony_ci				ret = saa7164_downloadimage(
52862306a36Sopenharmony_ci					dev,
52962306a36Sopenharmony_ci					bootloaderoffset,
53062306a36Sopenharmony_ci					bootloadersize,
53162306a36Sopenharmony_ci					SAA_DOWNLOAD_FLAGS,
53262306a36Sopenharmony_ci					dev->bmmio + SAA_DEVICE_DOWNLOAD_OFFSET,
53362306a36Sopenharmony_ci					SAA_DEVICE_BUFFERBLOCKSIZE);
53462306a36Sopenharmony_ci				if (ret < 0) {
53562306a36Sopenharmony_ci					printk(KERN_ERR
53662306a36Sopenharmony_ci						"bootloader d/l has failed\n");
53762306a36Sopenharmony_ci					goto out;
53862306a36Sopenharmony_ci				}
53962306a36Sopenharmony_ci				dprintk(DBGLVL_FW,
54062306a36Sopenharmony_ci					"bootloader download complete.\n");
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci			}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci			printk(KERN_ERR "starting firmware download(2)\n");
54562306a36Sopenharmony_ci			bootloadersize = (boothdr->firmwaresize +
54662306a36Sopenharmony_ci				boothdr->bslsize) * 16 +
54762306a36Sopenharmony_ci				sizeof(struct fw_header);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci			bootloaderoffset =
55062306a36Sopenharmony_ci				(u8 *)(fw->data + sizeof(struct fw_header));
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci			fwloaderoffset = bootloaderoffset + bootloadersize;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci			/* TODO: fix this bounds overrun here with old f/ws */
55562306a36Sopenharmony_ci			fwloadersize = (fwhdr->firmwaresize + fwhdr->bslsize) *
55662306a36Sopenharmony_ci				16 + sizeof(struct fw_header);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci			ret = saa7164_downloadimage(
55962306a36Sopenharmony_ci				dev,
56062306a36Sopenharmony_ci				fwloaderoffset,
56162306a36Sopenharmony_ci				fwloadersize,
56262306a36Sopenharmony_ci				SAA_DEVICE_2ND_DOWNLOADFLAG_OFFSET,
56362306a36Sopenharmony_ci				dev->bmmio + SAA_DEVICE_2ND_DOWNLOAD_OFFSET,
56462306a36Sopenharmony_ci				SAA_DEVICE_2ND_BUFFERBLOCKSIZE);
56562306a36Sopenharmony_ci			if (ret < 0) {
56662306a36Sopenharmony_ci				printk(KERN_ERR "firmware download failed\n");
56762306a36Sopenharmony_ci				goto out;
56862306a36Sopenharmony_ci			}
56962306a36Sopenharmony_ci			printk(KERN_ERR "firmware download complete.\n");
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci		} else {
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci			/* No bootloader update reqd, download firmware only */
57462306a36Sopenharmony_ci			printk(KERN_ERR "starting firmware download(3)\n");
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci			ret = saa7164_downloadimage(
57762306a36Sopenharmony_ci				dev,
57862306a36Sopenharmony_ci				(u8 *)fw->data,
57962306a36Sopenharmony_ci				fw->size,
58062306a36Sopenharmony_ci				SAA_DOWNLOAD_FLAGS,
58162306a36Sopenharmony_ci				dev->bmmio + SAA_DEVICE_DOWNLOAD_OFFSET,
58262306a36Sopenharmony_ci				SAA_DEVICE_BUFFERBLOCKSIZE);
58362306a36Sopenharmony_ci			if (ret < 0) {
58462306a36Sopenharmony_ci				printk(KERN_ERR "firmware download failed\n");
58562306a36Sopenharmony_ci				goto out;
58662306a36Sopenharmony_ci			}
58762306a36Sopenharmony_ci			printk(KERN_ERR "firmware download complete.\n");
58862306a36Sopenharmony_ci		}
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	dev->firmwareloaded = 1;
59262306a36Sopenharmony_ci	ret = 0;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ciout:
59562306a36Sopenharmony_ci	release_firmware(fw);
59662306a36Sopenharmony_ci	return ret;
59762306a36Sopenharmony_ci}
598