/* <:copyright-BRCM:2015:DUAL/GPL:standard Copyright (c) 2015 Broadcom All Rights Reserved This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation (the "GPL"). This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. :> */ #include #include #include #include #include #include #include #include #include #include /* * +----------------------------------------------------- * * Defines * * +----------------------------------------------------- */ #define PCI_NUM_BAR_RESOURCES (PCI_STD_RESOURCE_END+1) #define GEPD_NUM_SUP_DEV \ (sizeof(pcie_gepd_sup_dev)/sizeof(unsigned short)) #define GEPD_DEBUG #define GEPD_DRV_NAME "pcie-gepd" /* * +----------------------------------------------------- * * Macros * * +----------------------------------------------------- */ #if defined(GEPD_DEBUG) #define GEPD_LOG(fmt, args...) \ printk(" %s: " fmt, GEPD_DRV_NAME, ##args) #else #define GEPD_LOG(fmt, args...) do {} while (0) #endif /* * +----------------------------------------------------- * * Structures * * +----------------------------------------------------- */ typedef struct pcie_gepd_bar_res { phys_addr_t phys; /* Physical Address */ unsigned long size; /* bar size */ volatile char *virt; /* Virtual address */ } pcie_gepd_bar_res_t; typedef struct pcie_gepd { pcie_gepd_bar_res_t bar[PCI_NUM_BAR_RESOURCES]; int irq; } pcie_gepd_t; /* * +----------------------------------------------------- * * Local Function prototype * * +----------------------------------------------------- */ static irqreturn_t pcie_gepd_isr(int irq, void *arg); static int pcie_gepd_probe(struct pci_dev *pdev, const struct pci_device_id *ent); static void pcie_gepd_remove(struct pci_dev *pdev); static int __init pcie_gepd_init(void); static void __exit pcie_gepd_exit(void); /* * +----------------------------------------------------- * * Global variables * * +----------------------------------------------------- */ static struct pci_device_id bcm_pcie_ep_devid[] = { { vendor: PCI_VENDOR_ID_BROADCOM, device: PCI_ANY_ID, subvendor: PCI_ANY_ID, subdevice: PCI_ANY_ID, class: PCI_CLASS_NETWORK_OTHER << 8, class_mask: 0xffff00, driver_data: 0 }, { 0 } }; static struct pci_driver pcie_gepdriver = { node: {}, name: GEPD_DRV_NAME, id_table: bcm_pcie_ep_devid, probe: pcie_gepd_probe, remove: pcie_gepd_remove, suspend: NULL, resume: NULL, }; static unsigned short pcie_gepd_sup_dev[] = { 0x6315, /* 63158 EP */ 0x6317, /* 63178 EP */ 0xf6ca, /* 63178 Internal WiFi 11ax 2x2 */ }; static struct pcie_gepd pcie_gepdriver_cb; /* * +----------------------------------------------------- * * Local Function prototype * * +----------------------------------------------------- */ /* * * Function pcie_gepd_isr (irq, arg) * * * Parameters: * irq ... interrupt number * arg ... gepd control block * * Description: * This is dummy and not implemented yet * * Return: IRQ_HANDLED */ static irqreturn_t pcie_gepd_isr(int irq, void *arg) { /* Nothing to do, Just return success */ return IRQ_HANDLED; } /* * * Function pcie_gepd_probe (pdev, ent) * * * Parameters: * pdev ... pointer to pcie device data structure * ent ... pcie device id table of driver * * Description: * Check if the device is supported by the driver. Allocate all BAR * resources corresponding to the device * * Return: 0 on success, -ve on failure */ static int pcie_gepd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int ret = 0; int res, i; int found = 0; struct pcie_gepd *epd = &pcie_gepdriver_cb; GEPD_LOG("%s\r\n", __FUNCTION__); /* Check if the pcie device is supported by the driver */ for (i = 0; i < GEPD_NUM_SUP_DEV; i++) { if (pcie_gepd_sup_dev[i] == pdev->device) { found++; GEPD_LOG("found supported device 0x%x\r\n", pdev->device); } } /* Not supported */ if (found == 0) return -ENODEV; /* Allocate HCD control block */ epd = kzalloc(sizeof(*epd), GFP_KERNEL); if (!epd) { GEPD_LOG("Unable to allocate memory for CB\r\n"); return -ENOMEM; } GEPD_LOG("Allocated [0x%p]\r\n", epd); /* Initialize the pcie data strucutre */ pci_set_drvdata(pdev, epd); /* Allocate all available BAR resources */ for (res = 0; res < PCI_NUM_BAR_RESOURCES; res++) { epd->bar[res].phys = pci_resource_start(pdev, res); epd->bar[res].size = pci_resource_len(pdev, res); if (epd->bar[res].phys && epd->bar[res].size) { GEPD_LOG("Found bar [%d] size 0x%lx\r\n", res, epd->bar[res].size); epd->bar[res].virt = (volatile char*)ioremap_nocache( (unsigned long)epd->bar[res].phys, (unsigned long)epd->bar[res].size); if (epd->bar[res].virt == NULL) { GEPD_LOG("bar [%d] mapping failed\r\n", res); ret = -ENODEV; break; } GEPD_LOG("bar %d, phys 0x%08x size 0x%lx, virt 0x%p\r\n", res, epd->bar[res].phys, epd->bar[res].size, epd->bar[res].virt); } } if (request_irq(pdev->irq, pcie_gepd_isr, IRQF_SHARED, GEPD_DRV_NAME, epd) < 0) { GEPD_LOG("request_irq(%d) failed\n", pdev->irq); } else { epd->irq = pdev->irq; } /* Enable device */ pci_restore_state(pdev); ret = pci_enable_device(pdev); if (!ret) pci_set_master(pdev); return ret; } /* * * Function pcie_gepd_remove (pdev) * * * Parameters: * pdev ... pointer to pci device data structure * * Description: * disable the pcie device and release all resources corresponding to the device * * Return: 0 on success, -ve on failure */ static void pcie_gepd_remove(struct pci_dev *pdev) { struct pcie_gepd *epd; int res; GEPD_LOG("%s\r\n", __FUNCTION__); epd = pci_get_drvdata(pdev); if (epd) { pci_disable_device(pdev); /* Free BAR resources */ for (res = 0; res < PCI_NUM_BAR_RESOURCES; res++) { if (epd->bar[res].virt) { iounmap(epd->bar[res].virt); } } /* free irq */ if (epd->irq != 0) { free_irq(epd->irq, epd); epd->irq = 0; } kfree(epd); } return; } /* * +----------------------------------------------------- * Global Functions * +----------------------------------------------------- */ /* * * Function pcie_gepd_init () * * * Parameters: * * Description: * Generic PCIe End Point driver init, register the driver to PCI stack * This inturn should call our probe() * * Return: 0 on success, -ve on failure */ static int __init pcie_gepd_init(void) { int ret = -1; GEPD_LOG("%s\r\n", __FUNCTION__); ret = pci_register_driver(&pcie_gepdriver); GEPD_LOG("register_driver returned %d\r\n", ret); return ret; } /* * * Function pcie_gepd_exit () * * * Parameters: * * Description: * Generic PCIe End Point driver exit, unregister the driver from pci core * This inturn should call our remove() * * Return: None */ static void __exit pcie_gepd_exit(void) { GEPD_LOG("%s\r\n", __FUNCTION__); pci_unregister_driver(&pcie_gepdriver); return; } module_init(pcie_gepd_init); module_exit(pcie_gepd_exit); MODULE_LICENSE("GPL");