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