Char: cyclades, add firmware loading
cyclades, add firmware loading Signed-off-by: Jiri Slaby <jirislaby@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
cf3a386c08
commit
054f5b0aaa
|
@ -646,6 +646,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
#include <asm/io.h>
|
||||
|
@ -680,6 +681,44 @@ static void cy_send_xchar(struct tty_struct *tty, char ch);
|
|||
|
||||
#define STD_COM_FLAGS (0)
|
||||
|
||||
/* firmware stuff */
|
||||
#define ZL_MAX_BLOCKS 16
|
||||
#define DRIVER_VERSION 0x02010203
|
||||
#define RAM_SIZE 0x80000
|
||||
|
||||
#define Z_FPGA_LOADED(X) ((readl(&(X)->init_ctrl) & (1<<17)) != 0)
|
||||
|
||||
enum zblock_type {
|
||||
ZBLOCK_PRG = 0,
|
||||
ZBLOCK_FPGA = 1
|
||||
};
|
||||
|
||||
struct zfile_header {
|
||||
char name[64];
|
||||
char date[32];
|
||||
char aux[32];
|
||||
u32 n_config;
|
||||
u32 config_offset;
|
||||
u32 n_blocks;
|
||||
u32 block_offset;
|
||||
u32 reserved[9];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct zfile_config {
|
||||
char name[64];
|
||||
u32 mailbox;
|
||||
u32 function;
|
||||
u32 n_blocks;
|
||||
u32 block_list[ZL_MAX_BLOCKS];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct zfile_block {
|
||||
u32 type;
|
||||
u32 file_offset;
|
||||
u32 ram_offset;
|
||||
u32 size;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
static struct tty_driver *cy_serial_driver;
|
||||
|
||||
#ifdef CONFIG_ISA
|
||||
|
@ -4735,17 +4774,295 @@ static int __init cy_detect_isa(void)
|
|||
} /* cy_detect_isa */
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
static void __devinit plx_init(void __iomem * addr, __u32 initctl)
|
||||
static inline int __devinit cyc_isfwstr(const char *str, unsigned int size)
|
||||
{
|
||||
unsigned int a;
|
||||
|
||||
for (a = 0; a < size && *str; a++, str++)
|
||||
if (*str & 0x80)
|
||||
return -EINVAL;
|
||||
|
||||
for (; a < size; a++, str++)
|
||||
if (*str)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void __devinit cyz_fpga_copy(void __iomem *fpga, u8 *data,
|
||||
unsigned int size)
|
||||
{
|
||||
for (; size > 0; size--) {
|
||||
cy_writel(fpga, *data++);
|
||||
udelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
static void __devinit plx_init(struct pci_dev *pdev, int irq,
|
||||
struct RUNTIME_9060 __iomem *addr)
|
||||
{
|
||||
/* Reset PLX */
|
||||
cy_writel(addr + initctl, readl(addr + initctl) | 0x40000000);
|
||||
cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x40000000);
|
||||
udelay(100L);
|
||||
cy_writel(addr + initctl, readl(addr + initctl) & ~0x40000000);
|
||||
cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x40000000);
|
||||
|
||||
/* Reload Config. Registers from EEPROM */
|
||||
cy_writel(addr + initctl, readl(addr + initctl) | 0x20000000);
|
||||
cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x20000000);
|
||||
udelay(100L);
|
||||
cy_writel(addr + initctl, readl(addr + initctl) & ~0x20000000);
|
||||
cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x20000000);
|
||||
|
||||
/* For some yet unknown reason, once the PLX9060 reloads the EEPROM,
|
||||
* the IRQ is lost and, thus, we have to re-write it to the PCI config.
|
||||
* registers. This will remain here until we find a permanent fix.
|
||||
*/
|
||||
pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, irq);
|
||||
}
|
||||
|
||||
static int __devinit __cyz_load_fw(const struct firmware *fw,
|
||||
const char *name, const u32 mailbox, void __iomem *base,
|
||||
void __iomem *fpga)
|
||||
{
|
||||
void *ptr = fw->data;
|
||||
struct zfile_header *h = ptr;
|
||||
struct zfile_config *c, *cs;
|
||||
struct zfile_block *b, *bs;
|
||||
unsigned int a, tmp, len = fw->size;
|
||||
#define BAD_FW KERN_ERR "Bad firmware: "
|
||||
if (len < sizeof(*h)) {
|
||||
printk(BAD_FW "too short: %u<%zu\n", len, sizeof(*h));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cs = ptr + h->config_offset;
|
||||
bs = ptr + h->block_offset;
|
||||
|
||||
if ((void *)(cs + h->n_config) > ptr + len ||
|
||||
(void *)(bs + h->n_blocks) > ptr + len) {
|
||||
printk(BAD_FW "too short");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cyc_isfwstr(h->name, sizeof(h->name)) ||
|
||||
cyc_isfwstr(h->date, sizeof(h->date))) {
|
||||
printk(BAD_FW "bad formatted header string\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (strncmp(name, h->name, sizeof(h->name))) {
|
||||
printk(BAD_FW "bad name '%s' (expected '%s')\n", h->name, name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tmp = 0;
|
||||
for (c = cs; c < cs + h->n_config; c++) {
|
||||
for (a = 0; a < c->n_blocks; a++)
|
||||
if (c->block_list[a] > h->n_blocks) {
|
||||
printk(BAD_FW "bad block ref number in cfgs\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (c->mailbox == mailbox && c->function == 0) /* 0 is normal */
|
||||
tmp++;
|
||||
}
|
||||
if (!tmp) {
|
||||
printk(BAD_FW "nothing appropriate\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (b = bs; b < bs + h->n_blocks; b++)
|
||||
if (b->file_offset + b->size > len) {
|
||||
printk(BAD_FW "bad block data offset\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* everything is OK, let's seek'n'load it */
|
||||
for (c = cs; c < cs + h->n_config; c++)
|
||||
if (c->mailbox == mailbox && c->function == 0)
|
||||
break;
|
||||
|
||||
for (a = 0; a < c->n_blocks; a++) {
|
||||
b = &bs[c->block_list[a]];
|
||||
if (b->type == ZBLOCK_FPGA) {
|
||||
if (fpga != NULL)
|
||||
cyz_fpga_copy(fpga, ptr + b->file_offset,
|
||||
b->size);
|
||||
} else {
|
||||
if (base != NULL)
|
||||
memcpy_toio(base + b->ram_offset,
|
||||
ptr + b->file_offset, b->size);
|
||||
}
|
||||
}
|
||||
#undef BAD_FW
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr,
|
||||
struct RUNTIME_9060 __iomem *ctl_addr, int irq)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
struct FIRM_ID __iomem *fid = base_addr + ID_ADDRESS;
|
||||
struct CUSTOM_REG __iomem *cust = base_addr;
|
||||
struct ZFW_CTRL __iomem *pt_zfwctrl;
|
||||
u8 *tmp;
|
||||
u32 mailbox, status;
|
||||
unsigned int i;
|
||||
int retval;
|
||||
|
||||
retval = request_firmware(&fw, "cyzfirm.bin", &pdev->dev);
|
||||
if (retval) {
|
||||
dev_err(&pdev->dev, "can't get firmware\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Check whether the firmware is already loaded and running. If
|
||||
positive, skip this board */
|
||||
if (Z_FPGA_LOADED(ctl_addr) && readl(&fid->signature) == ZFIRM_ID) {
|
||||
u32 cntval = readl(base_addr + 0x190);
|
||||
|
||||
udelay(100);
|
||||
if (cntval != readl(base_addr + 0x190)) {
|
||||
/* FW counter is working, FW is running */
|
||||
dev_dbg(&pdev->dev, "Cyclades-Z FW already loaded. "
|
||||
"Skipping board.\n");
|
||||
retval = 0;
|
||||
goto err_rel;
|
||||
}
|
||||
}
|
||||
|
||||
/* start boot */
|
||||
cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) &
|
||||
~0x00030800UL);
|
||||
|
||||
mailbox = readl(&ctl_addr->mail_box_0);
|
||||
|
||||
if (mailbox == 0 || Z_FPGA_LOADED(ctl_addr)) {
|
||||
/* stops CPU and set window to beginning of RAM */
|
||||
cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
|
||||
cy_writel(&cust->cpu_stop, 0);
|
||||
cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
plx_init(pdev, irq, ctl_addr);
|
||||
|
||||
if (mailbox != 0) {
|
||||
/* load FPGA */
|
||||
retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, NULL,
|
||||
base_addr);
|
||||
if (retval)
|
||||
goto err_rel;
|
||||
if (!Z_FPGA_LOADED(ctl_addr)) {
|
||||
dev_err(&pdev->dev, "fw upload successful, but fw is "
|
||||
"not loaded\n");
|
||||
goto err_rel;
|
||||
}
|
||||
}
|
||||
|
||||
/* stops CPU and set window to beginning of RAM */
|
||||
cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
|
||||
cy_writel(&cust->cpu_stop, 0);
|
||||
cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
|
||||
udelay(100);
|
||||
|
||||
/* clear memory */
|
||||
for (tmp = base_addr; (void *)tmp < base_addr + RAM_SIZE; tmp++)
|
||||
cy_writeb(tmp, 255);
|
||||
if (mailbox != 0) {
|
||||
/* set window to last 512K of RAM */
|
||||
cy_writel(&ctl_addr->loc_addr_base, WIN_RAM + RAM_SIZE);
|
||||
//sleep(1);
|
||||
for (tmp = base_addr; (void *)tmp < base_addr + RAM_SIZE; tmp++)
|
||||
cy_writeb(tmp, 255);
|
||||
/* set window to beginning of RAM */
|
||||
cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
|
||||
//sleep(1);
|
||||
}
|
||||
|
||||
retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, base_addr, NULL);
|
||||
release_firmware(fw);
|
||||
if (retval)
|
||||
goto err;
|
||||
|
||||
/* finish boot and start boards */
|
||||
cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
|
||||
cy_writel(&cust->cpu_start, 0);
|
||||
cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
|
||||
i = 0;
|
||||
while ((status = readl(&fid->signature)) != ZFIRM_ID && i++ < 40)
|
||||
msleep(100);
|
||||
if (status != ZFIRM_ID) {
|
||||
if (status == ZFIRM_HLT) {
|
||||
dev_err(&pdev->dev, "you need an external power supply "
|
||||
"for this number of ports. Firmware halted and "
|
||||
"board reset.\n");
|
||||
retval = -EIO;
|
||||
goto err;
|
||||
}
|
||||
dev_warn(&pdev->dev, "fid->signature = 0x%x... Waiting "
|
||||
"some more time\n", status);
|
||||
while ((status = readl(&fid->signature)) != ZFIRM_ID &&
|
||||
i++ < 200)
|
||||
msleep(100);
|
||||
if (status != ZFIRM_ID) {
|
||||
dev_err(&pdev->dev, "Board not started in 20 seconds! "
|
||||
"Giving up. (fid->signature = 0x%x)\n",
|
||||
status);
|
||||
dev_info(&pdev->dev, "*** Warning ***: if you are "
|
||||
"upgrading the FW, please power cycle the "
|
||||
"system before loading the new FW to the "
|
||||
"Cyclades-Z.\n");
|
||||
|
||||
if (Z_FPGA_LOADED(ctl_addr))
|
||||
plx_init(pdev, irq, ctl_addr);
|
||||
|
||||
retval = -EIO;
|
||||
goto err;
|
||||
}
|
||||
dev_dbg(&pdev->dev, "Firmware started after %d seconds.\n",
|
||||
i / 10);
|
||||
}
|
||||
pt_zfwctrl = base_addr + readl(&fid->zfwctrl_addr);
|
||||
|
||||
dev_dbg(&pdev->dev, "fid=> %p, zfwctrl_addr=> %x, npt_zfwctrl=> %p\n",
|
||||
base_addr + ID_ADDRESS, readl(&fid->zfwctrl_addr),
|
||||
base_addr + readl(&fid->zfwctrl_addr));
|
||||
|
||||
dev_info(&pdev->dev, "Cyclades-Z FW loaded: version = %x, ports = %u\n",
|
||||
readl(&pt_zfwctrl->board_ctrl.fw_version),
|
||||
readl(&pt_zfwctrl->board_ctrl.n_channel));
|
||||
|
||||
if (readl(&pt_zfwctrl->board_ctrl.n_channel) == 0) {
|
||||
dev_warn(&pdev->dev, "no Cyclades-Z ports were found. Please "
|
||||
"check the connection between the Z host card and the "
|
||||
"serial expanders.\n");
|
||||
|
||||
if (Z_FPGA_LOADED(ctl_addr))
|
||||
plx_init(pdev, irq, ctl_addr);
|
||||
|
||||
dev_info(&pdev->dev, "Null number of ports detected. Board "
|
||||
"reset.\n");
|
||||
retval = 0;
|
||||
goto err;
|
||||
}
|
||||
|
||||
cy_writel(&pt_zfwctrl->board_ctrl.op_system, C_OS_LINUX);
|
||||
cy_writel(&pt_zfwctrl->board_ctrl.dr_version, DRIVER_VERSION);
|
||||
|
||||
/*
|
||||
Early firmware failed to start looking for commands.
|
||||
This enables firmware interrupts for those commands.
|
||||
*/
|
||||
cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) |
|
||||
(1 << 17));
|
||||
cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) |
|
||||
0x00030800UL);
|
||||
|
||||
plx_init(pdev, irq, ctl_addr);
|
||||
|
||||
return 0;
|
||||
err_rel:
|
||||
release_firmware(fw);
|
||||
err:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int __devinit cy_pci_probe(struct pci_dev *pdev,
|
||||
|
@ -4827,16 +5144,9 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
|
|||
}
|
||||
|
||||
/* Disable interrupts on the PLX before resetting it */
|
||||
cy_writew(addr0 + 0x68,
|
||||
readw(addr0 + 0x68) & ~0x0900);
|
||||
cy_writew(addr0 + 0x68, readw(addr0 + 0x68) & ~0x0900);
|
||||
|
||||
plx_init(addr0, 0x6c);
|
||||
/* For some yet unknown reason, once the PLX9060 reloads
|
||||
the EEPROM, the IRQ is lost and, thus, we have to
|
||||
re-write it to the PCI config. registers.
|
||||
This will remain here until we find a permanent
|
||||
fix. */
|
||||
pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, irq);
|
||||
plx_init(pdev, irq, addr0);
|
||||
|
||||
mailbox = (u32)readl(&ctl_addr->mail_box_0);
|
||||
|
||||
|
@ -4877,6 +5187,9 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
|
|||
if ((mailbox == ZO_V1) || (mailbox == ZO_V2))
|
||||
cy_writel(addr2 + ID_ADDRESS, 0L);
|
||||
|
||||
retval = cyz_load_fw(pdev, addr2, addr0, irq);
|
||||
if (retval)
|
||||
goto err_unmap;
|
||||
/* This must be a Cyclades-8Zo/PCI. The extendable
|
||||
version will have a different device_id and will
|
||||
be allocated its maximum number of ports. */
|
||||
|
@ -4953,15 +5266,7 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,
|
|||
case PLX_9060:
|
||||
case PLX_9080:
|
||||
default: /* Old boards, use PLX_9060 */
|
||||
|
||||
plx_init(addr0, 0x6c);
|
||||
/* For some yet unknown reason, once the PLX9060 reloads
|
||||
the EEPROM, the IRQ is lost and, thus, we have to
|
||||
re-write it to the PCI config. registers.
|
||||
This will remain here until we find a permanent
|
||||
fix. */
|
||||
pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, irq);
|
||||
|
||||
plx_init(pdev, irq, addr0);
|
||||
cy_writew(addr0 + 0x68, readw(addr0 + 0x68) | 0x0900);
|
||||
break;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue