/* * Copyright (c) 2012 HON HAI PRECISION IND.CO.,LTD. (FOXCONN) */ #include #include #include #include #include #include "ep2714_def.h" int fwloadbin = 0; module_param(fwloadbin, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(fwloadbin, "Option to specify location from which to load EP firmware:.\n" " 1 -- load firmware via the request_firmware() (hotplug).\n" " interface.\n" " 0 -- load firmware from flash.\n"); static struct ep8324_pci_func* ep8324_add_entry(char *well_known_name, uint16_t bus, uint8_t dev_num, uint8_t func_num); extern struct hw_operations fc_ops; static int dev_major; static struct class *ep8324_class; LIST_HEAD(ep8324_func_list); static struct ep8324_pci_func* ep8324_register_func(struct ep8324_hw_data *ha) { struct pci_dev *pdev = ha->pdev; struct ep8324_pci_func *func; char well_known_name[NAME_STR_SIZE + 1]; // static int fc_hic = 0; static int fc_port = 0; // static int fcoe_hic = 0; static int fcoe_port = 0; static int iscsi_hic = 0; static int iscsi_port = 0; static int eth_hic = 0; static int eth_port = 0; static int fc_onboard = 0; static int fcoe_onboard = 0; switch (ha->func_type) { case EP8324_FC: if(pdev->bus->number==0x20) { snprintf(well_known_name, NAME_STR_SIZE+1, "FC-H2P%d",/* fc_hic,*/ fc_port); if (++fc_port == MAX_PORTS_PER_HIC) { fc_port = 0; //fc_hic++; } } else if(pdev->bus->number==0x10) { snprintf(well_known_name, NAME_STR_SIZE+1, "FC-H1P%d",/* fc_hic,*/ fc_port); if (++fc_port == MAX_PORTS_PER_HIC) { fc_port = 0; //fc_hic++; } } else if(pdev->bus->number==0x42) { snprintf(well_known_name, NAME_STR_SIZE+1, "FC-H2P%d",/* fc_hic,*/ fc_port); if (++fc_port == MAX_PORTS_PER_HIC) { fc_port = 0; //fc_hic++; } } else if(pdev->bus->parent->primary==0x17) { //for falcon snprintf(well_known_name, NAME_STR_SIZE+1, "FC-H1P%d",/* fc_hic,*/ fc_port); if (++fc_port == MAX_PORTS_PER_HIC) { fc_port = 0; //fc_hic++; } } else if(pdev->bus->parent->primary==0x65) { //for falcon snprintf(well_known_name, NAME_STR_SIZE+1, "FC-H2P%d",/* fc_hic,*/ fc_port); if (++fc_port == MAX_PORTS_PER_HIC) { fc_port = 0; //fc_hic++; } } else { snprintf(well_known_name, NAME_STR_SIZE+1, "FC-BB0F%dP%d", fc_onboard, fc_port); if (++fc_port == MAX_PORTS_PER_HIC) { fc_port = 0; fc_onboard++; } } break; case EP8324_FCOE: if(pdev->bus->number==0x20) { snprintf(well_known_name, NAME_STR_SIZE+1, "CNA-H0P%d",/* fcoe_hic,*/ fcoe_port); if (++fcoe_port == MAX_PORTS_PER_HIC) { fcoe_port = 0; // fcoe_hic++; } } else if(pdev->bus->number==0x10) { snprintf(well_known_name, NAME_STR_SIZE+1, "CNA-H1P%d",/* fcoe_hic,*/ fcoe_port); if (++fcoe_port == MAX_PORTS_PER_HIC) { fcoe_port = 0; // fcoe_hic++; } } else { snprintf(well_known_name, NAME_STR_SIZE+1, "CNA-BB0F%dP%d", fcoe_onboard, fcoe_port); if (++fcoe_port == MAX_PORTS_PER_HIC) { fcoe_port = 0; fcoe_onboard++; } } break; case EP8324_ISCSI: snprintf(well_known_name, NAME_STR_SIZE+1, "CNA-ISCSI-H%dP%d", iscsi_hic, iscsi_port); if (++iscsi_port == MAX_PORTS_PER_HIC) { iscsi_port = 0; iscsi_hic++; } break; case EP8324_ETHERNET: snprintf(well_known_name, NAME_STR_SIZE+1, "CNA-ENET-H%dP%d", eth_hic, eth_port); if (++eth_port == MAX_PORTS_PER_HIC) { eth_port = 0; eth_hic++; } break; default: return NULL; } func = ep8324_add_entry(well_known_name, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); if (!func) return NULL; func->hw = ha; ha->func = func; return func; } static void ep8324_config_dma_addressing(struct ep8324_hw_data *ha) { /* Assume a 32bit DMA mask. */ ha->flags.enable_64bit_addressing = 0; if (!dma_set_mask(&ha->pdev->dev, DMA_BIT_MASK(64))) { /* Any upper-dword bits set? */ if (MSD(dma_get_required_mask(&ha->pdev->dev)) && !pci_set_consistent_dma_mask(ha->pdev, DMA_BIT_MASK(64))) { /* Ok, a 64bit DMA mask is applicable. */ ha->flags.enable_64bit_addressing = 1; return; } } dma_set_mask(&ha->pdev->dev, DMA_BIT_MASK(32)); pci_set_consistent_dma_mask(ha->pdev, DMA_BIT_MASK(32)); } static void ep8324_get_flt_info(struct ep8324_hw_data *ha, uint32_t flt_addr) { uint16_t *wptr; uint16_t cnt, chksum; uint32_t start; struct ep8324_flt_header *flt; struct ep8324_flt_region *region; ha->flt_region_flt = flt_addr; wptr = (uint16_t *)ha->dma_buf; flt = (struct ep8324_flt_header *)ha->dma_buf; region = (struct ep8324_flt_region *)&flt[1]; ep8324_read_flash_data(ha, (uint32_t*) ha->dma_buf, flt_addr, OPTROM_BURST_SIZE >> 2); if (*wptr == __constant_cpu_to_le16(0xffff)) return; if (flt->version != __constant_cpu_to_le16(1)) { printk(KERN_INFO "Unsupported FLT detected: version=0x%x length=0x%x checksum=0x%x.\n", le16_to_cpu(flt->version), le16_to_cpu(flt->length), le16_to_cpu(flt->checksum)); return; } cnt = (sizeof(struct ep8324_flt_header) + le16_to_cpu(flt->length)) >> 1; for (chksum = 0; cnt; cnt--) chksum += le16_to_cpu(*wptr++); if (chksum) { printk(KERN_INFO "Inconsistent FLT detected: version=0x%x length=0x%x checksum=0x%x.\n", le16_to_cpu(flt->version), le16_to_cpu(flt->length), le16_to_cpu(flt->checksum)); return; } cnt = le16_to_cpu(flt->length) / sizeof(struct ep8324_flt_region); for ( ; cnt; cnt--, region++) { /* Store addresses as DWORD offsets. */ start = le32_to_cpu(region->start) >> 2; dprintk("FLT[%02x]: start=0x%x end=0x%x size=0x%x.\n", le32_to_cpu(region->code), start, le32_to_cpu(region->end) >> 2, le32_to_cpu(region->size)); switch (le32_to_cpu(region->code) & 0xff) { case FLT_REG_FW_FC: if (ha->func_type == EP8324_FC) ha->flt_region_fw = start; break; case FLT_REG_FW_FCOE: if (ha->func_type == EP8324_FCOE) ha->flt_region_fw = start; break; case FLT_REG_FDT: ha->flt_region_fdt = start; break; } } dprintk(KERN_INFO "FLT: fw=0x%x, flt=0x%x\n", ha->flt_region_fw, ha->flt_region_fdt); } static void ep8324_get_fdt_info(struct ep8324_hw_data *ha) { uint16_t cnt, chksum; uint16_t *wptr; struct ep8324_fdt_layout *fdt; uint16_t mid = 0, fid = 0; wptr = (uint16_t *)ha->dma_buf; fdt = (struct ep8324_fdt_layout *)ha->dma_buf; ep8324_read_flash_data(ha, (uint32_t*) ha->dma_buf, ha->flt_region_fdt, OPTROM_BURST_SIZE >> 2); if (*wptr == __constant_cpu_to_le16(0xffff)) goto no_flash_data; if (fdt->sig[0] != 'Q' || fdt->sig[1] != 'L' || fdt->sig[2] != 'I' || fdt->sig[3] != 'D') goto no_flash_data; for (cnt = 0, chksum = 0; cnt < sizeof(struct ep8324_fdt_layout) >> 1; cnt++) { // printk("%x\n", le16_to_cpu(*wptr++)); // *wptr--; chksum += le16_to_cpu(*wptr++); } if (chksum) { printk(KERN_INFO "Inconsistent FDT detected:" " checksum=0x%x id=%c version0x%x.\n", chksum, fdt->sig[0], le16_to_cpu(fdt->version)); goto no_flash_data; } mid = le16_to_cpu(fdt->man_id); fid = le16_to_cpu(fdt->id); ha->fdt_wrt_disable = fdt->wrt_disable_bits; ha->fdt_erase_cmd = FARX_ACCESS_FLASH_CONF | 0x0300 | fdt->erase_cmd; ha->fdt_block_size = le32_to_cpu(fdt->block_size); if (fdt->unprotect_sec_cmd) { ha->fdt_unprotect_sec_cmd = FARX_ACCESS_FLASH_CONF | 0x0300 | fdt->unprotect_sec_cmd; ha->fdt_protect_sec_cmd = fdt->protect_sec_cmd ? (FARX_ACCESS_FLASH_CONF | 0x0300 | fdt->protect_sec_cmd) : (FARX_ACCESS_FLASH_CONF | 0x0336); } goto done; no_flash_data: printk(KERN_INFO "FDT unavailable, using defaults !!!\n"); ha->fdt_wrt_disable = 0xbc; ha->fdt_erase_cmd = FARX_ACCESS_FLASH_CONF | 0x0300 | 0xd8; ha->fdt_block_size = 0x10000; done: dprintk("FDT: (0x%x/0x%x) erase=0x%x " "pr=%x unpr=%x wrtd=0x%x blk=0x%x.\n", mid, fid, ha->fdt_erase_cmd, ha->fdt_protect_sec_cmd, ha->fdt_unprotect_sec_cmd, ha->fdt_wrt_disable, ha->fdt_block_size); } static int ep8324_get_flash_info(struct ep8324_hw_data *ha) { ep8324_get_flt_info(ha, FA_FLASH_LAYOUT_ADDR); ep8324_get_fdt_info(ha); return 0; } static int ep8324_initialize_adapter(struct ep8324_hw_data *ha) { int ret; dprintk("Configuring PCI space...\n"); ret = ha->hw_ops->pci_config(ha); if (ret) { printk(KERN_ERR "Unable to configure PCI space\n"); return ret; } ha->hw_ops->reset_chip(ha); ep8324_get_flash_info(ha); if (ret) printk(KERN_ERR "Unable to validate FLASH data\n"); return 0; } static int ep8324_mem_alloc(struct ep8324_hw_data *ha) { ha->dma_buf = dma_alloc_coherent(&ha->pdev->dev, EP8324_DMA_BUF_SIZE, &ha->dma, GFP_KERNEL); if (!ha->dma_buf) goto failed; ha->init_cb = dma_alloc_coherent(&ha->pdev->dev, sizeof(init_cb_t), &ha->init_cb_dma, GFP_KERNEL); if (!ha->init_cb) goto failed; ha->req_q = dma_alloc_coherent(&ha->pdev->dev, (QUEUE_ENTRY_CNT + 1) * IOCB_SIZE, &ha->req_q_dma, GFP_KERNEL); if (!ha->req_q) goto failed; ha->rsp_q = dma_alloc_coherent(&ha->pdev->dev, (QUEUE_ENTRY_CNT + 1) * IOCB_SIZE, &ha->rsp_q_dma, GFP_KERNEL); if (!ha->rsp_q) goto failed; return 0; failed: if (ha->dma && ha->dma_buf) dma_free_coherent(&ha->pdev->dev, EP8324_DMA_BUF_SIZE, ha->dma_buf, ha->dma); if (ha->init_cb && ha->init_cb_dma) dma_free_coherent(&ha->pdev->dev, sizeof(init_cb_t), ha->init_cb, ha->init_cb_dma); if (ha->req_q && ha->req_q_dma) dma_free_coherent(&ha->pdev->dev, (QUEUE_ENTRY_CNT + 1) * IOCB_SIZE, ha->req_q, ha->req_q_dma); if (ha->rsp_q && ha->req_q_dma) dma_free_coherent(&ha->pdev->dev, (QUEUE_ENTRY_CNT + 1) * IOCB_SIZE, ha->rsp_q, ha->rsp_q_dma); return -ENOMEM; } static void ep8324_mem_free(struct ep8324_hw_data *ha) { if (ha->dma && ha->dma_buf) dma_free_coherent(&ha->pdev->dev, EP8324_DMA_BUF_SIZE, ha->dma_buf, ha->dma); if (ha->init_cb && ha->init_cb_dma) dma_free_coherent(&ha->pdev->dev, sizeof(init_cb_t), ha->init_cb, ha->init_cb_dma); if (ha->req_q && ha->req_q_dma) dma_free_coherent(&ha->pdev->dev, (QUEUE_ENTRY_CNT + 1) * IOCB_SIZE, ha->req_q, ha->req_q_dma); if (ha->rsp_q && ha->req_q_dma) dma_free_coherent(&ha->pdev->dev, (QUEUE_ENTRY_CNT + 1) * IOCB_SIZE, ha->rsp_q, ha->rsp_q_dma); } static int ep8324_diag_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) { int ret = -ENODEV; int bars; struct ep8324_hw_data *ha; struct ep8324_pci_func *func; bars = pci_select_bars(pdev, IORESOURCE_MEM); if (pci_enable_device_mem(pdev)) goto probe_out; pci_enable_pcie_error_reporting(pdev); ha = kzalloc(sizeof(struct ep8324_hw_data), GFP_KERNEL); if (!ha) { ret = -ENOMEM; printk(KERN_ERR "Unable to allocate memory for ha.\n"); goto probe_out; } ha->pdev = pdev; ha->bars = bars; spin_lock_init(&ha->hardware_lock); /* Get adapter physical port no from interrupt pin register. */ pci_read_config_byte(ha->pdev, PCI_INTERRUPT_PIN, &ha->port_no); switch (pdev->device) { // case PCI_DEVICE_ID_QLOGIC_ISP2031: // case PCI_DEVICE_ID_QLOGIC_EP2831: case PCI_DEVICE_ID_QLOGIC_EP2714: ha->func_type = EP8324_FC; ha->hw_ops = &fc_ops; ha->flt_region_fw = FA_FC_FIRMWARE_ADDR; break; case PCI_DEVICE_ID_QLOGIC_ISP8031: case PCI_DEVICE_ID_QLOGIC_EP8831: ha->func_type = EP8324_FCOE; ha->hw_ops = &fc_ops; ha->flt_region_fw = FA_FCOE_FIRMWARE_ADDR; break; case PCI_DEVICE_ID_QLOGIC_ISP8032: case PCI_DEVICE_ID_QLOGIC_EP8832: ha->func_type = EP8324_ISCSI; goto register_func; /* TODO */ case PCI_DEVICE_ID_QLOGIC_ISP8030: case PCI_DEVICE_ID_QLOGIC_EP8830: ha->func_type = EP8324_ETHERNET; goto register_func; /* TODO*/ default: printk(KERN_ERR "Unsupported device(%04x)\n", pdev->device); goto probe_hw_failed; } // 2714 new add ha->flash_data_off = FARX_ACCESS_FLASH_DATA; ret = ha->hw_ops->iospace_config(ha); if (ret) goto probe_hw_failed; ep8324_config_dma_addressing(ha); dprintk("64 Bit addressing is %s.\n", ha->flags.enable_64bit_addressing ? "enable" : "disable"); /* allocate dma buffer */ ret = ep8324_mem_alloc(ha); if (ret) { printk(KERN_INFO "Failed to allocate dma buffer\n"); goto probe_hw_failed; } if (ep8324_initialize_adapter(ha)) { printk(KERN_ERR "Failed to initialize adapter\n"); ret = -ENODEV; goto probe_hw_failed; } register_func: func = ep8324_register_func(ha); if (!func) { ret = -ENODEV; goto probe_hw_failed; } pci_set_drvdata(pdev, func); return 0; probe_hw_failed: ep8324_mem_free(ha); if (ha->iobase) iounmap(ha->iobase); if (ha->mqiobase) iounmap(ha->mqiobase); pci_release_selected_regions(ha->pdev, ha->bars); kfree(ha); ha = NULL; probe_out: pci_disable_device(pdev); return ret; } static void ep8324_diag_remove_one(struct pci_dev *pdev) { struct ep8324_pci_func *func; struct ep8324_hw_data *ha; func = pci_get_drvdata(pdev); ha = func->hw; ep8324_mem_free(ha); if (ha->iobase) iounmap(ha->iobase); if (ha->mqiobase) iounmap(ha->mqiobase); pci_release_selected_regions(ha->pdev, ha->bars); kfree(ha); func->hw = NULL; pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); } static struct pci_device_id ep8324_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2031) }, /* FC */ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8031) }, /* FCoE */ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8032) }, /* iSCSI */ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8030) }, /* NIC */ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_EP2831) }, /* FC */ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_EP2714) }, /* FC */ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_EP8831) }, /* FCoE */ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_EP8832) }, /* iSCSI */ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_EP8830) }, /* NIC */ { 0 }, }; static struct pci_driver ep8324_diag_pci_driver = { .name = EP8324_DRIVER_NAME, .driver = { .owner = THIS_MODULE, }, .id_table = ep8324_pci_tbl, .probe = ep8324_diag_probe_one, .remove = ep8324_diag_remove_one, //.shutdown = , //.err_handler = , }; static struct file_operations ep8324_fops = { .owner = THIS_MODULE, .unlocked_ioctl = ep8324_ioctl, .llseek = noop_llseek, }; static struct ep8324_pci_func* ep8324_add_entry(char *well_known_name, uint16_t bus, uint8_t dev_num, uint8_t func_num) { struct ep8324_pci_func *func; func = kzalloc(sizeof(struct ep8324_pci_func), GFP_KERNEL); if (!func) { printk(KERN_INFO "Failed to allocate ep8324 function entry\n"); return NULL; } INIT_LIST_HEAD(&func->list); strcpy(func->well_known_name, well_known_name); func->bus = bus; func->dev_num = dev_num; func->func_num = func_num; list_add_tail(&func->list, &ep8324_func_list); printk(KERN_INFO "%s: %s (%04x:%02x:%02x)\n", __func__, func->well_known_name, bus, dev_num, func_num); return func; } static void ep8324_free_entries(void) { struct list_head *pos, *next; struct ep8324_pci_func *func; list_for_each_safe(pos, next, &ep8324_func_list) { func = list_entry(pos, struct ep8324_pci_func, list); list_del_init(&func->list); kfree(func); } } static int __init ep8324_diag_init(void) { int ret; dev_major = register_chrdev(0, EP8324_DEV, &ep8324_fops); if (dev_major < 0) { printk(KERN_ERR "EP8324: unable to register char device\n"); return -ENODEV; } if (dev_major >= 0) { ep8324_class = class_create(THIS_MODULE, EP8324_DEV); device_create(ep8324_class, NULL, MKDEV(dev_major, 0), NULL, "%s", EP8324_DEV); } ret = pci_register_driver(&ep8324_diag_pci_driver); ep8324_fw_init(); return ret; } static void __exit ep8324_diag_exit(void) { pci_unregister_driver(&ep8324_diag_pci_driver); ep8324_free_entries(); device_destroy(ep8324_class, MKDEV(dev_major, 0)); class_destroy(ep8324_class); unregister_chrdev(dev_major, EP8324_DEV); } module_init(ep8324_diag_init); module_exit(ep8324_diag_exit); MODULE_AUTHOR("cloud.ch.chen@foxconn.com"); MODULE_LICENSE("GPL");