| /* |
| <:copyright-BRCM:2016:DUAL/GPL:standard |
| |
| Broadcom Proprietary and Confidential.(c) 2016 Broadcom |
| All Rights Reserved |
| |
| Unless you and Broadcom execute a separate written software license |
| agreement governing use of this software, this software is licensed |
| to you under the terms of the GNU General Public License version 2 |
| (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, |
| with the following added to such license: |
| |
| As a special exception, the copyright holders of this software give |
| you permission to link this software with independent modules, and |
| to copy and distribute the resulting executable under terms of your |
| choice, provided that you also meet, for each linked independent |
| module, the terms and conditions of the license of that module. |
| An independent module is a module which is not derived from this |
| software. The special exception does not apply to any modifications |
| of the software. |
| |
| Not withstanding the above, under no circumstances may you combine |
| this software in any way with any other Broadcom software provided |
| under a license other than the GPL, without Broadcom's express prior |
| written consent. |
| |
| :> |
| */ |
| |
| /* |
| might use readl or ioread32 |
| */ |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/compiler.h> |
| #include <linux/pci.h> |
| #include <linux/pci_regs.h> |
| #include <linux/init.h> |
| #include <linux/errno.h> |
| #include <linux/string.h> |
| #include <linux/proc_fs.h> |
| #include <linux/delay.h> |
| #include <linux/io.h> |
| #include <linux/uaccess.h> |
| #include <linux/gfp.h> |
| #include <linux/interrupt.h> |
| #include <linux/types.h> |
| #include <linux/spinlock.h> |
| |
| #ifdef TX_ENABLE_EVENT_TRACE |
| #include <linux/ctype.h> |
| #include <linux/file.h> |
| #include <linux/fs.h> |
| #include "bcm_fld_common.h" |
| #endif |
| #include <bcmolt_board_selector.h> |
| #include "bcmolt_llpcie.h" |
| |
| #define DRV_NAME "maple" |
| #define DRV_VERSION "1.0.3" |
| |
| #define BAR_REGS 0 |
| #define BAR_DDR 2 |
| #define BAR_SRAM 4 |
| /* |
| bar 1 = 8 Mbyte register space |
| bar 2 = 16 Mbyte DDR space |
| bar 3 = 64 Kbyte SRAM space |
| */ |
| #define MAPLE_REGS_LENGTH 0x00800000 |
| #define MAPLE_DDR_LENGTH 0x01000000 |
| #define MAPLE_SRAM_LENGTH 0x00010000 |
| |
| #ifdef TX_ENABLE_EVENT_TRACE |
| #define PCIE_PCIE_0_MISC_CPU_2_PCIE_MEM_WIN1_LO 0x064014U |
| #define PCIE_PCIE_0_MISC_CPU_2_PCIE_MEM_WIN1_HI 0x064018U |
| #define PCIE_PCIE_0_MISC_CPU_2_PCIE_MEM_WIN1_BASE_LIMIT 0x064074U |
| #endif |
| #define PCIE_REVISION_REGISTER_OFFSET 0x6406CU /* PCIE_PCIE_PCIE_0_PCIE_PCIE_0_MISC_REVISION */ |
| #define PCIE_STATUS_REGISTER_OFFSET 0x64068U /* PCIE_PCIE_PCIE_0_PCIE_PCIE_0_MISC_PCIE_STATUS */ |
| #define PCIE_HARD_DEBUG_REGISTER_OFFSET 0x64204U /* PCIE_PCIE_PCIE_0_PCIE_PCIE_0_MISC_HARD_DEBUG */ |
| #define PCIE_INT_STATUS_REGISTER_OFFSET 0x69400U /* PCIE_PCIE_PCIE_0_CPU_INTR1_INTR_STATUS */ |
| #define PCIE_INT_CLEAR_MASK_OFFSET 0x6940CU /* PCIE_PCIE_PCIE_0_CPU_INTR1_INTR_MASK_CLEAR */ |
| #define PCIE_INTR1_STATUS_REGISTER_OFFSET 0x69300U /* Interrupt Status Register */ |
| |
| #define PCIE_MAPLE_VENDOR_ID 0x14E4 /* PCIE_CFG_TYPE0_EP_SUBSYSTEM_ID_VENDOR_ID */ |
| #define PCIE_MAPLE_DEVICE_ID 0x6862 |
| #define PCIE_MAPLE_6863_SKU_DEVICE_ID 0x6863 |
| #define PCIE_MAPLE_EPON_DEVICE_ID 0x5554 |
| |
| #define MAPLE_DRIVER_NAME DRV_NAME " BCM68620 (Maple) PCI driver " DRV_VERSION |
| |
| struct proc_dir_entry *bcmolt_dir = NULL; |
| static DEFINE_PCI_DEVICE_TABLE(maple_pci_tbl) = |
| { /* vendor_id, device_id, any, any */ |
| {PCI_DEVICE(PCIE_MAPLE_VENDOR_ID, PCIE_MAPLE_DEVICE_ID)}, |
| {PCI_DEVICE(PCIE_MAPLE_VENDOR_ID, PCIE_MAPLE_6863_SKU_DEVICE_ID)}, |
| {PCI_DEVICE(PCIE_MAPLE_VENDOR_ID, PCIE_MAPLE_EPON_DEVICE_ID)}, |
| {0} |
| }; |
| MODULE_DEVICE_TABLE (pci, maple_pci_tbl); |
| |
| typedef struct |
| { |
| bcm_ll_dev_info device; |
| spinlock_t lock; |
| int err_status; |
| int phylink; |
| int prev_datalink; |
| int datalink; |
| uint32_t deviceid; |
| unsigned int *traceaddr; |
| struct pci_dev *pdev; |
| int enabled; |
| } maple_device_info; |
| |
| static int crt_device = 0; |
| static bcm_ll_pcie_status_change callback; |
| static bcmos_bool is_initialized = BCMOS_FALSE; |
| |
| static maple_device_info devices[MAPLE_MAX_DEVICES]; |
| //static irqreturn_t maple_interrupt (int irq, void *dev_instance); |
| static maple_device_info *get_maple_pci_info(int crt); |
| |
| static int maple_write(struct file *file,const char *buffer,size_t count, void *data); |
| static int maple_read(char *page, char **start, off_t off, int count, int *eof, void *data); |
| |
| /* Map bus/device to device_id */ |
| static int _pcie_bus_devfv_to_devid(int bus, int devfn) |
| { |
| const bus_devfn_devid *map; |
| for (map = bcmolt_board_pci_map_table_get(); map && map->bus > 0; map++) |
| { |
| if (map->bus == bus && map->devfn == devfn) |
| { |
| return map->device; |
| } |
| } |
| /* Not found - use incremental mapping */ |
| return crt_device; |
| } |
| |
| /* procedure used by OS to connect Maple PCI to allocated memory */ |
| |
| static int __devinit maple_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
| { |
| int rc; |
| volatile unsigned int readval; |
| maple_device_info *device_info = NULL; |
| int dev_id; |
| u32 val; |
| |
| if (!pdev) |
| return 1; |
| |
| dev_info(&pdev->dev, |
| "Bus = %d Slot = %d Vendor:device = 0x%04x:0x%04x revision = 0x%02x\n", |
| pdev->bus->number, pdev->devfn, |
| pdev->vendor, pdev->device, pdev->revision); |
| |
| if (crt_device == MAPLE_MAX_DEVICES) |
| { |
| printk(" Maximum Maple devices already defined (%d)\n", MAPLE_MAX_DEVICES); |
| return -ENODEV; |
| } |
| |
| dev_id = _pcie_bus_devfv_to_devid(pdev->bus->number, pdev->devfn); |
| ++crt_device; |
| device_info = &devices[dev_id]; |
| memset(device_info, 0, sizeof(maple_device_info)); |
| |
| spin_lock_init(&device_info->lock); |
| device_info->deviceid = dev_id; |
| device_info->pdev = pdev; |
| |
| /* Enable maple device on PCIe bus and fixup BARs. |
| * Without fixup, module unload/reload crashes with WRX host |
| * because PCI hotplug doesn't work and after unload/re-load |
| * linux believes that BARs are already set. |
| */ |
| rc = bcm_ll_pcie_dev_enable(dev_id); |
| if (rc) |
| { |
| printk("Error pci_enable_device_mem = %d\n",rc); |
| goto err_out; |
| } |
| |
| rc = pci_request_selected_regions(pdev, pci_select_bars(pdev, IORESOURCE_MEM), DRV_NAME); |
| if (rc) |
| goto err_out; |
| |
| pci_set_master(pdev); |
| pci_save_state(pdev); |
| |
| device_info->device.soc_regs_base = (unsigned long)pci_ioremap_bar(pdev, BAR_REGS); |
| device_info->device.soc_sram_base = (unsigned long)pci_ioremap_bar(pdev, BAR_SRAM); |
| device_info->device.soc_ddr_base = (unsigned long)pci_ioremap_bar(pdev, BAR_DDR); |
| device_info->device.soc_ddr_length = (uint32_t)pci_resource_len(pdev, BAR_DDR); |
| device_info->device.irq = pdev->irq; |
| pci_set_drvdata(pdev, device_info); |
| |
| //rc = request_irq(device_info->device.irq, maple_interrupt, IRQF_SHARED, "maple-pcie", device_info); |
| if (!rc && device_info->device.soc_sram_base) |
| { |
| device_info->device.irq_connected = 1; |
| readval = bcm_pci_read32((uint32_t *)(device_info->device.soc_regs_base + PCIE_REVISION_REGISTER_OFFSET)); |
| printk("Card %d version major=%02x minor=%02x\n",device_info->deviceid, |
| ((readval & 0x0000ff00) >> 8),(readval & 0x000000ff)); |
| printk("regs_start = 0x%lx\nddr_start = 0x%lx\nsram_start = 0x%lx\nddr_length = %d\n", |
| device_info->device.soc_regs_base,device_info->device.soc_ddr_base, |
| device_info->device.soc_sram_base, (int)device_info->device.soc_ddr_length); |
| pci_read_config_dword(pdev,PCI_BASE_ADDRESS_0,&val); |
| printk("BAR REGS = 0x%x\n",(unsigned int)val); |
| pci_read_config_dword(pdev,PCI_BASE_ADDRESS_2,&val); |
| printk("BAR DDR = 0x%x\n",(unsigned int)val); |
| pci_read_config_dword(pdev,PCI_BASE_ADDRESS_4,&val); |
| printk("BAR SRAM = 0x%x\n",(unsigned int)val); |
| readval = bcm_pci_read32((uint32_t *)(device_info->device.soc_regs_base + PCIE_STATUS_REGISTER_OFFSET)); |
| device_info->err_status = readval & 0x0000000f; |
| device_info->phylink = (readval >> 4) & 0x1; |
| device_info->datalink = (readval >> 5) & 0x1; |
| device_info->prev_datalink = device_info->datalink; |
| } |
| else |
| goto err_out; |
| |
| return rc; |
| |
| err_out: |
| pci_disable_device (pdev); |
| memset(device_info, 0, sizeof(*device_info)); |
| --crt_device; |
| |
| return rc; |
| } |
| |
| static void __devexit maple_pci_remove(struct pci_dev *pdev) |
| { |
| int i; |
| maple_device_info *dev = pci_get_drvdata(pdev); |
| |
| if (!dev) |
| return; |
| |
| if (dev->device.irq_connected) |
| { |
| //free_irq(dev->device.irq, dev); |
| dev->device.irq_connected = 0; |
| } |
| if (callback) |
| { |
| for (i = 0; i < crt_device; i++) |
| { |
| if (devices[i].pdev == pdev) |
| { |
| callback(i, BCM_LL_PCIE_LINK_DOWN); |
| break; |
| } |
| } |
| } |
| pci_disable_device(pdev); |
| pci_release_regions(pdev); |
| pci_set_drvdata(pdev, NULL); |
| dev->device.soc_sram_base = 0; |
| dev->device.soc_regs_base = 0; |
| dev->pdev = NULL; |
| } |
| |
| static struct pci_driver maple_pci_driver = { |
| .name = DRV_NAME, |
| .id_table = maple_pci_tbl, |
| .probe = maple_pci_probe, |
| .remove = __devexit_p(maple_pci_remove), |
| }; |
| |
| static maple_device_info *get_maple_pci_info(int crt) |
| { |
| unsigned int readval=0; |
| maple_device_info *device; |
| |
| if (crt >= BCMTR_MAX_OLTS) |
| return NULL; |
| |
| device = &devices[crt]; |
| if (device->device.soc_regs_base) |
| { |
| if (device->enabled) |
| { |
| readval = bcm_pci_read32((uint32_t *)(device->device.soc_regs_base + PCIE_STATUS_REGISTER_OFFSET)); |
| device->err_status = readval & 0x0000000f; |
| device->phylink = (readval >> 4) & 0x1; |
| device->datalink = (readval >> 5) & 0x1; |
| } |
| else |
| { |
| device->err_status = 0; |
| device->phylink = 0; |
| device->datalink = 0; |
| } |
| } |
| else |
| device = NULL; |
| |
| return device; |
| } |
| |
| #if 0 |
| static irqreturn_t maple_interrupt (int irq, void *dev_instance) |
| { |
| volatile unsigned int value; |
| maple_device_info *device_info; |
| unsigned long flags; |
| int handled = IRQ_NONE; |
| uint32_t device; |
| |
| spin_lock_irqsave(&device_info->lock, flags); |
| value = bcm_pci_read32((uint32_t *)(device_info->device.soc_regs_base + PCIE_INT_STATUS_REGISTER_OFFSET)); |
| if (value) |
| { |
| if (device_info->datalink != device_info->prev_datalink) |
| { |
| if (device_info->datalink == BCM_LL_PCIE_LINK_UP) |
| printk("Device=%d : Link is up !!!\n", device); |
| else |
| { |
| printk("Device=%d : Link is down !!!\n", device); |
| printk("phy status = 0x%x error status = 0x%x\n", device_info->phylink, device_info->err_status); |
| } |
| device_info->prev_datalink = device_info->datalink; |
| if (callback) |
| callback(device, device_info->datalink); |
| } |
| value = bcm_pci_read32((uint32_t *)(device_info->device.soc_regs_base + PCIE_INTR1_STATUS_REGISTER_OFFSET)); |
| if (!value) |
| handled = IRQ_HANDLED; |
| } |
| spin_unlock_irqrestore(&device_info->lock, flags); |
| |
| return IRQ_RETVAL(handled); |
| } |
| #endif |
| |
| /* Returns BAR register address */ |
| static int _pci_bar_reg_addr(int res) |
| { |
| return 0x10 + res*4; |
| } |
| |
| static void _pci_update_resource(struct pci_dev *dev, int resno) |
| { |
| struct pci_bus_region region; |
| u16 cmd; |
| u32 new, check, mask; |
| int reg; |
| struct resource *res = dev->resource + resno; |
| |
| /* |
| * Ignore resources for unimplemented BARs and unused resource slots |
| * for 64 bit BARs. |
| */ |
| if (!res->flags) |
| return; |
| |
| /* |
| * Ignore non-moveable resources. This might be legacy resources for |
| * which no functional BAR register exists or another important |
| * system resource we shouldn't move around. |
| */ |
| if (res->flags & IORESOURCE_PCI_FIXED) |
| return; |
| |
| pcibios_resource_to_bus(dev, ®ion, res); |
| |
| new = region.start | (res->flags & PCI_REGION_FLAG_MASK); |
| if (res->flags & IORESOURCE_IO) |
| mask = (u32)PCI_BASE_ADDRESS_IO_MASK; |
| else |
| mask = (u32)PCI_BASE_ADDRESS_MEM_MASK; |
| |
| reg = _pci_bar_reg_addr(resno); |
| |
| pci_write_config_dword(dev, reg, new); |
| pci_read_config_dword(dev, reg, &check); |
| |
| if ((new ^ check) & mask) { |
| dev_err(&dev->dev, "BAR %d: error updating (%#08x != %#08x)\n", |
| resno, new, check); |
| } |
| |
| if (res->flags & IORESOURCE_MEM_64) { |
| new = region.start >> 16 >> 16; |
| pci_write_config_dword(dev, reg + 4, new); |
| pci_read_config_dword(dev, reg + 4, &check); |
| if (check != new) { |
| dev_err(&dev->dev, "BAR %d: error updating " |
| "(high %#08x != %#08x)\n", resno, new, check); |
| } |
| } |
| |
| pci_read_config_word(dev, PCI_COMMAND, &cmd); |
| pci_write_config_word(dev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); |
| |
| res->flags &= ~IORESOURCE_UNSET; |
| dev_info(&dev->dev, "BAR %d: set to %pR (PCI address [%#llx-%#llx])\n", |
| resno, res, (unsigned long long)region.start, |
| (unsigned long long)region.end); |
| } |
| |
| /* Enable PCI access to device */ |
| bcmos_errno bcm_ll_pcie_dev_enable(int dev_id) |
| { |
| maple_device_info *device; |
| int rc; |
| |
| if (dev_id >= MAPLE_MAX_DEVICES) |
| return BCM_ERR_PARM; |
| |
| device = &devices[dev_id]; |
| if (!device->pdev) |
| return BCM_ERR_IO; |
| |
| rc = pci_enable_device_mem(device->pdev); |
| if (rc) |
| { |
| printk("Error pci_enable_device_mem for device %d = %d\n", dev_id, rc);\ |
| goto out; |
| } |
| |
| /* Refresh devices's PCIe header */ |
| _pci_update_resource(device->pdev, BAR_REGS); |
| _pci_update_resource(device->pdev, BAR_SRAM); |
| _pci_update_resource(device->pdev, BAR_DDR); |
| device->enabled = 1; |
| |
| out: |
| return rc ? BCM_ERR_IO : BCM_ERR_OK; |
| } |
| |
| /* Disable PCI access to device */ |
| bcmos_errno bcm_ll_pcie_dev_disable(int dev_id) |
| { |
| maple_device_info *device; |
| |
| printk("%s: device %d\n", __FUNCTION__, dev_id); |
| |
| if (dev_id >= MAPLE_MAX_DEVICES) |
| return BCM_ERR_PARM; |
| |
| device = &devices[dev_id]; |
| if (!device->pdev || !device->enabled) |
| return BCM_ERR_OK; |
| pci_disable_device(device->pdev); |
| device->enabled = 0; |
| |
| return BCM_ERR_OK; |
| } |
| |
| bcmos_errno bcm_ll_pcie_init(void) |
| { |
| int rc; |
| if (is_initialized) |
| return BCM_ERR_OK; |
| is_initialized = BCMOS_TRUE; |
| rc = pci_register_driver(&maple_pci_driver); |
| printk(KERN_INFO "Maple PCI driver registered with PCI subsystem. rc=%d\n", rc); |
| return rc ? BCM_ERR_INTERNAL : BCM_ERR_OK; |
| } |
| |
| void bcm_ll_pcie_cleanup(void) |
| { |
| if (!is_initialized) |
| return; |
| is_initialized = BCMOS_FALSE; |
| |
| pci_unregister_driver(&maple_pci_driver); |
| crt_device = 0; |
| memset(devices, 0, sizeof(devices)); |
| } |
| |
| /*******************************************/ |
| /* procedures executed on insmod and rmmod */ |
| /*******************************************/ |
| static int __init bcm_ll_init_module(void) |
| { |
| struct proc_dir_entry *bcmolt_file = NULL; |
| |
| if ((bcmolt_dir = create_proc_entry("bcmolt", S_IFDIR, NULL)) == NULL) |
| { |
| printk("Unable to create /proc/bcmolt entry\n"); |
| return -1; |
| } |
| if ((bcmolt_file = create_proc_entry("card", S_IFREG | S_IWUSR | S_IRUGO, bcmolt_dir)) == NULL) |
| { |
| printk("Unable to create /proc/bcmolt/card entry\n"); |
| remove_proc_entry("bcmolt", bcmolt_dir); |
| return -1; |
| } |
| bcmolt_file->write_proc = (write_proc_t *)maple_write; |
| bcmolt_file->read_proc = (read_proc_t *)maple_read; |
| |
| return 0; |
| } |
| |
| static void __exit bcm_ll_cleanup_module(void) |
| { |
| bcm_ll_pcie_cleanup(); |
| remove_proc_entry("card", bcmolt_dir); |
| remove_proc_entry("bcmolt", NULL); |
| printk("Unregister done\n"); |
| } |
| |
| /*****************************/ |
| /* APIs used by Host drivers */ |
| /*****************************/ |
| bcmos_errno bcm_ll_pcie_status_change_register(bcm_ll_pcie_status_change cb) |
| { |
| int i; |
| maple_device_info *device_info; |
| |
| callback = cb; |
| if(callback) |
| { |
| for (i = 0; i < crt_device; i++) |
| { |
| device_info = get_maple_pci_info(i); |
| if (device_info) |
| callback(i, device_info->datalink); |
| else |
| callback(i, BCM_LL_PCIE_LINK_DOWN); |
| } |
| } |
| |
| return BCM_ERR_OK; |
| } |
| |
| bcmos_errno bcm_ll_pcie_status_change_unregister(void) |
| { |
| callback = NULL; |
| |
| return BCM_ERR_OK; |
| } |
| |
| bcmos_errno bcm_ll_pcie_query(uint8_t dev_id, bcm_ll_dev_info *info) |
| { |
| maple_device_info *device_info; |
| |
| if (!info) |
| return BCM_ERR_PARM; |
| |
| if (dev_id < MAPLE_MAX_DEVICES) |
| { |
| device_info = get_maple_pci_info(dev_id); |
| if (device_info && device_info->pdev) |
| { |
| if (device_info->datalink == BCM_LL_PCIE_LINK_DOWN) |
| return BCM_ERR_IO; |
| *info = device_info->device; |
| return BCM_ERR_OK; |
| } |
| return BCM_ERR_IO; |
| } |
| return BCM_ERR_RANGE; |
| } |
| |
| bcm_ll_pcie_dev bcm_ll_pcie_dev_get(uint8_t dev_id) |
| { |
| maple_device_info *device_info; |
| |
| if (dev_id < crt_device) |
| { |
| device_info = get_maple_pci_info(dev_id); |
| return (device_info ? device_info->pdev : NULL); |
| } |
| |
| return NULL; |
| } |
| |
| bcmos_errno bcm_ll_pcie_host_reset_enable(uint8_t dev_id, bcmos_bool enabled) |
| { |
| uint32_t val; |
| |
| val = enabled ? 0 : 1; |
| bcm_pci_write32((uint32_t *)(devices[dev_id].device.soc_regs_base + PCIE_HARD_DEBUG_REGISTER_OFFSET), val); |
| |
| return BCM_ERR_OK; |
| } |
| |
| #ifdef TX_ENABLE_EVENT_TRACE |
| bcmos_errno bcm_ll_pcie_setrace(uint8_t dev_id) |
| { |
| maple_device_info *device_info; |
| uint32_t val,lim,base,size; |
| uint32_t reglo,reghi,baselim; |
| unsigned long addr; |
| |
| if (dev_id < crt_device) |
| { |
| device_info = &devices[dev_id]; |
| if (!device_info) |
| return BCM_ERR_IO; |
| size = EVENT_BUFFER_SIZE; |
| device_info->traceaddr = (unsigned int *)bcmos_alloc_not_cache(size); |
| memset(device_info->traceaddr,0,size); |
| addr = bcmos_virt_to_phys(device_info->traceaddr); /* | 0x02 = reverse byte order = swap */ |
| printk("trace window : alloc=0x%lx size=0x%x\n",(unsigned long)device_info->traceaddr, (unsigned int)size); |
| reglo = PCIE_PCIE_0_MISC_CPU_2_PCIE_MEM_WIN1_LO; |
| reghi = PCIE_PCIE_0_MISC_CPU_2_PCIE_MEM_WIN1_HI; |
| baselim = PCIE_PCIE_0_MISC_CPU_2_PCIE_MEM_WIN1_BASE_LIMIT; |
| bcm_pci_write32((uint32_t *)(device_info->device.soc_regs_base + reglo),addr & 0xffffffff); |
| bcm_pci_write32((uint32_t *)(device_info->device.soc_regs_base + reghi)),((addr >> 32) & 0xffffffff); |
| /* base */ |
| base = CPU_2_PCIE_MEM_WIN1_BASE; |
| /* limit */ |
| lim = base + size; |
| val = lim | (base >> 16); |
| bcm_pci_write32((uint32_t *)(device_info->device.soc_regs_base + baselim),val); |
| //bcm_fld_set_event_trace(dev_id); |
| bcm_pci_write32((uint32_t *)(device_info->device.soc_sram_base + BCM_FLD_EVENT_TRACE_OFFSET),0x1); |
| |
| return BCM_ERR_OK; |
| } |
| return BCM_ERR_RANGE; |
| } |
| bcmos_errno bcm_ll_pcie_cleartrace(uint8_t dev_id) |
| { |
| maple_device_info *device_info; |
| |
| if (dev_id < crt_device) |
| { |
| device_info = &devices[dev_id]; |
| if (!device_info) |
| return BCM_ERR_IO; |
| //bcm_fld_clear_event_trace(dev_id); |
| bcm_pci_write32((uint32_t *)(device_info->device.soc_sram_base + BCM_FLD_EVENT_TRACE_OFFSET),0x0); |
| return BCM_ERR_OK; |
| } |
| return BCM_ERR_RANGE; |
| } |
| bcmos_errno bcm_ll_pcie_savetrace(uint8_t dev_id, char *tracefilename) |
| { |
| maple_device_info *device_info; |
| struct file *fp; |
| unsigned long seg = 0; |
| mm_segment_t memseg; |
| |
| if (!tracefilename) |
| return BCM_ERR_PARM; |
| |
| printk("device=%d from=%d, Go to write to file %s\n",dev_id , crt_device, tracefilename); |
| |
| if (dev_id < crt_device) |
| { |
| device_info = &devices[dev_id]; |
| if (!device_info) |
| { |
| printk("Error: device_info is null\n"); |
| goto exit; |
| } |
| fp = filp_open(tracefilename, O_WRONLY | O_CREAT, 0666); |
| if ((fp == NULL) || (fp == (void *)-ENOENT)) |
| { |
| printk("Error: Can't open file %s\n",tracefilename); |
| goto exit; |
| } |
| // Get current segment descriptor |
| seg = get_fs().seg; |
| |
| // Set segment descriptor associated to kernel space |
| set_fs(get_ds()); |
| fp->f_op->write(fp, (char *)device_info->traceaddr,EVENT_BUFFER_SIZE, &fp->f_pos); |
| |
| memseg.seg = seg; |
| set_fs(memseg); |
| filp_close(fp, NULL); |
| printk("Wrote file %s\n",tracefilename); |
| exit: |
| bcmos_free_not_cache(device_info->traceaddr); |
| |
| return BCM_ERR_OK; |
| } |
| return BCM_ERR_RANGE; |
| } |
| #endif |
| |
| /*************************************************************/ |
| /* User interface functions : implement echo/cat to proc file*/ |
| /*************************************************************/ |
| |
| typedef struct |
| { |
| char *name; |
| void (*callback)(int cmdid, char *cmd); |
| char *description; |
| } maple_card_command; |
| |
| static char *find_command(char *cmd); |
| static void command_reg(int cmdid, char *line); |
| static void command_mem(int cmdid, char *line); |
| static void command_cfg(int cmdid, char *line); |
| #ifdef TX_ENABLE_EVENT_TRACE |
| static void command_trace(int cmdid, char *line); |
| #endif |
| |
| #define READCMD 0 |
| #define WRITECMD 1 |
| |
| static maple_card_command commands[] = |
| { |
| {"readreg", command_reg, "read register <index> <register offset - hex>"}, |
| {"writereg", command_reg, "write register <index> <register offset - hex> <value - hex>"}, |
| {"read", command_mem, "read <address - hex> <number of words - dec>"}, |
| {"write", command_mem, "write <address - hex> <value - hex>"}, |
| {"rcfg", command_cfg, "read dev <address - hex> <number of words - dec>"}, |
| {"wcfg", command_cfg, "write dev <address - hex> <value - hex>"}, |
| #ifdef TX_ENABLE_EVENT_TRACE |
| {"trace", command_trace, "trace <index> on/off/save tx trace"}, |
| #endif |
| {NULL, NULL, NULL} |
| }; |
| |
| static char *find_command(char *cmd) |
| { |
| char *rest; |
| int i = 0; |
| maple_card_command *cmdptr = &commands[0]; |
| |
| cmd = skip_spaces(cmd); |
| if (!strncmp(cmd,"help",strlen("help"))) |
| { |
| while (cmdptr->name) |
| { |
| printk("%s\t: %s\n",cmdptr->name, cmdptr->description); |
| cmdptr = &commands[++i]; |
| } |
| return NULL; |
| } |
| while (cmdptr->name) |
| { |
| if (!strncmp(cmdptr->name, cmd, strlen(cmdptr->name))) |
| { |
| rest = cmd + strlen(cmdptr->name); |
| rest = skip_spaces(rest); |
| cmdptr->callback(i%2, rest); |
| return NULL; |
| } |
| cmdptr = &commands[++i]; |
| } |
| |
| return "Wrong command name"; |
| } |
| /********************************************************************** |
| answers to cat /proc/bcmolt command |
| **********************************************************************/ |
| static int maple_read(char *page, char **start, off_t off, int count, int *eof, void *data) |
| { |
| int i; |
| int len; |
| maple_device_info *dev = NULL; |
| |
| len = sprintf(page, "Maple Device Info\n\n"); |
| for (i = 0; i < MAPLE_MAX_DEVICES; i++) |
| { |
| dev = get_maple_pci_info(i); |
| if (dev) |
| { |
| len += sprintf(page + len, "id=%d regs_start=0x%lx ddr_start=0x%lx sram_start=0x%lx ddr_length=%d\n", |
| dev->deviceid, |
| dev->device.soc_regs_base,dev->device.soc_ddr_base, |
| dev->device.soc_sram_base, (int)dev->device.soc_ddr_length); |
| len += sprintf(page + len, "Phy Link is %s Data Link is %s Error status = %d\n\n", |
| dev->phylink == 1 ? "UP" : "DOWN", dev->datalink == 1 ? "UP" : "DOWN", (int)dev->err_status); |
| } |
| } |
| |
| return len; |
| } |
| #ifdef TX_ENABLE_EVENT_TRACE |
| static void command_trace(int cmdid, char *line) |
| { |
| char token[5]; |
| int index; |
| |
| if (sscanf(line, "%d %s", &index, token) != 2) |
| { |
| printk("Wrong parameter\n"); |
| return; |
| } |
| printk("TRACE commands : %s\n",token); |
| if (!strncmp(token,"on",2)) |
| { |
| //on |
| bcm_ll_pcie_setrace(index); |
| return; |
| } |
| if (!strncmp(token,"off",3)) |
| { |
| // off |
| bcm_ll_pcie_cleartrace(index); |
| return; |
| } |
| if (!strncmp(token,"save",4)) |
| { |
| //get |
| char filename[20]; |
| sprintf(filename,"./tt_trace_%d.trx",index); |
| bcm_ll_pcie_savetrace(index,filename); |
| return; |
| } |
| printk("Wrong command\n"); |
| } |
| #endif |
| |
| |
| static void command_reg(int cmdindex, char *line) |
| { |
| char *rest; |
| char *token; |
| int index; |
| unsigned long address; |
| unsigned int reg; |
| |
| if (sscanf(line, "%d %lx", &index, &address) != 2) |
| { |
| printk("Wrong parameter\n"); |
| return; |
| } |
| address += (unsigned long)devices[index].device.soc_regs_base; |
| switch (cmdindex) |
| { |
| case READCMD: |
| reg = bcm_pci_read32((uint32_t *)address); |
| printk("0x%lx : %08x\n", address, reg); |
| break; |
| case WRITECMD: |
| rest = line; |
| token = strsep(&rest, " "); /* skip index */ |
| token = strsep(&rest, " "); /* skip address */ |
| token = strsep(&rest, " "); |
| if (!token) |
| { |
| printk("Wrong parameter\n"); |
| return; |
| } |
| sscanf(token, "%x", ®); |
| printk("write 0x%x to 0x%lx\n", reg, address); |
| bcm_pci_write32((uint32_t *)address,reg); |
| break; |
| } |
| } |
| |
| static void command_mem(int cmdindex, char *line) |
| { |
| char *token; |
| char *rest; |
| unsigned long address; |
| unsigned int reg; |
| int i, number_of_words; |
| int in_line=0, max_in_line; |
| |
| if (sscanf(line, "%lx", &address) != 1) |
| { |
| printk("Wrong parameter\n"); |
| return; |
| } |
| rest = line; |
| token = strsep(&rest, " "); /* skip address */ |
| switch (cmdindex) |
| { |
| case READCMD: |
| token = strsep(&rest, " "); |
| if (!token) |
| number_of_words = 1; |
| else |
| sscanf(token, "%d", &number_of_words); |
| |
| max_in_line = 4; |
| for (i = 0; i < number_of_words; i++, address += 4) |
| { |
| if (!in_line) |
| printk("%08lx: ", address); |
| reg = bcm_pci_read32((uint32_t *)address); |
| printk("%08x", reg); |
| if (++in_line < max_in_line) |
| printk(" "); |
| else |
| { |
| printk("\n"); |
| in_line = 0; |
| } |
| } |
| if (in_line) |
| printk("\n"); |
| break; |
| case WRITECMD: |
| token = strsep(&rest, " "); |
| if (!token) |
| { |
| printk("Wrong parameter\n"); |
| return; |
| } |
| sscanf(token, "%x", ®); |
| bcm_pci_write32((uint32_t *)address,reg); |
| break; |
| } |
| } |
| |
| static void command_cfg(int cmdindex, char *line) |
| { |
| unsigned long address; |
| unsigned int reg; |
| unsigned int val_or_num; |
| int crt; |
| int i; |
| int in_line=0, max_in_line; |
| maple_device_info *device; |
| |
| if (sscanf(line, "%d %lx %x", &crt, &address, &val_or_num) != 3) |
| { |
| printk("Wrong parameter\n"); |
| return; |
| } |
| |
| if (crt >= crt_device || !devices[crt].pdev) |
| { |
| printk("Invalid device %d\n", crt); |
| return; |
| } |
| |
| device = &devices[crt]; |
| |
| switch (cmdindex) |
| { |
| case READCMD: |
| max_in_line = 4; |
| for (i = 0; i < val_or_num; i++, address += 4) |
| { |
| if (!in_line) |
| printk("%08lx: ", address); |
| pci_read_config_dword(device->pdev, address, ®); |
| printk("%08x", reg); |
| if (++in_line < max_in_line) |
| printk(" "); |
| else |
| { |
| printk("\n"); |
| in_line = 0; |
| } |
| } |
| if (in_line) |
| printk("\n"); |
| break; |
| |
| case WRITECMD: |
| pci_write_config_dword(device->pdev, address, val_or_num); |
| break; |
| } |
| } |
| |
| /********************************************************************** |
| answers to echo [...] > /proc/bcmolt command |
| **********************************************************************/ |
| static int maple_write(struct file *file,const char *buffer,size_t count, void *data) |
| { |
| char *internal; |
| |
| if (buffer == NULL) |
| return -EFAULT; |
| |
| internal = kmalloc(count, GFP_KERNEL); |
| if (!internal) |
| { |
| printk("Not enough memmory\n"); |
| return count; |
| } |
| |
| memset(internal, 0, count); |
| if (copy_from_user(internal, buffer, count)) |
| { |
| printk("Not enough memmory\n"); |
| goto error; |
| } |
| find_command(internal); |
| |
| error: |
| kfree(internal); |
| |
| return count; |
| } |
| |
| EXPORT_SYMBOL(bcmolt_dir); |
| EXPORT_SYMBOL(bcm_ll_pcie_init); |
| EXPORT_SYMBOL(bcm_ll_pcie_cleanup); |
| EXPORT_SYMBOL(bcm_ll_pcie_status_change_register); |
| EXPORT_SYMBOL(bcm_ll_pcie_status_change_unregister); |
| EXPORT_SYMBOL(bcm_ll_pcie_query); |
| EXPORT_SYMBOL(bcm_ll_pcie_dev_get); |
| EXPORT_SYMBOL(bcm_ll_pcie_dev_enable); |
| EXPORT_SYMBOL(bcm_ll_pcie_dev_disable); |
| EXPORT_SYMBOL(bcm_ll_pcie_host_reset_enable); |
| |
| #ifdef TX_ENABLE_EVENT_TRACE |
| EXPORT_SYMBOL(bcm_ll_pcie_setrace); |
| EXPORT_SYMBOL(bcm_ll_pcie_cleartrace); |
| EXPORT_SYMBOL(bcm_ll_pcie_savetrace); |
| #endif |
| module_init(bcm_ll_init_module); |
| module_exit(bcm_ll_cleanup_module); |
| |
| MODULE_DESCRIPTION("maple line card"); |
| MODULE_LICENSE("Dual BSD/GPL"); |