/* * net_ss_lld.c * * GPL LICENSE SUMMARY * * Copyright(c) 2015-2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * The full GNU General Public License is included in this distribution * in the file called LICENSE.GPL. * * Contact Information: * Intel Corporation * 2200 Mission College Blvd. * Santa Clara, CA 97052 * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(CONFIG_AVM_ENHANCED) #include #include #endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ #include "netip_subsystem_defs.h" #include /* Time out if we cannot get a MUTEX within half minute */ #define NET_SUBSYSTEM_IRQ_NAME "netip_subsystem_irq" int netss_runtime_suspend(struct device *dev); int netss_runtime_resume(struct device *dev); int netss_runtime_idle(struct device *dev); static const struct dev_pm_ops netss_pm_ops = { SET_RUNTIME_PM_OPS(netss_runtime_suspend, netss_runtime_resume, netss_runtime_idle) }; int docsis_chan_cfg = 0; int cm_mode = 0; int RCE_Floor = 2; int RCE_Filter = 0; #ifdef CONFIG_NETIP_SERVICES_ON_ATOM bool sgmii0_on_atom = true; bool sgmii1_on_atom = true; bool rgmii2_on_atom = true; bool rgmii3_on_atom = true; #endif static const struct pci_device_id net_subsystem_pci_tbl[] = { { PCI_DEVICE( 0x8086, NET_SUBSYTEM_DEV_ID), .driver_data = 0 }, {0}, }; net_ip_mmios_t net_ip_mmios; module_param(docsis_chan_cfg, int , (S_IRUSR | S_IWUSR )); module_param(cm_mode, int, (S_IRUSR | S_IWUSR )); module_param(RCE_Floor, int, (S_IRUSR | S_IWUSR )); module_param(RCE_Filter, int, (S_IRUSR | S_IWUSR )); #ifdef CONFIG_NETIP_SERVICES_ON_ATOM module_param(sgmii0_on_atom, bool, (S_IRUSR | S_IWUSR )); module_param(sgmii1_on_atom, bool, (S_IRUSR | S_IWUSR )); module_param(rgmii2_on_atom, bool, (S_IRUSR | S_IWUSR )); module_param(rgmii3_on_atom, bool, (S_IRUSR | S_IWUSR )); #endif MODULE_DEVICE_TABLE(pci, net_subsystem_pci_tbl); /*This function creates boot handshake thread */ void netss_manage_netip_services(void); void netss_stop_handshake_thread(void); struct net_subsystem_drv_data *pnetss_drv_data; static unsigned int netss_get_bit_position (unsigned int irq) { return irq % NUM_ARM11_INTR_PER_REG; } static unsigned int netss_get_reg_index(unsigned int irq) { return irq / NUM_ARM11_INTR_PER_REG; } static inline void __netss_interrupt_ack(netss_interrupt_t intrpt) { __raw_writel((1<bridge_reg_base + NETIP_BRIDGE_IIR_OFFSET); } void netss_interrupt_register(netss_interrupt_t intrpt, int arm11_int_id, netss_interrupt_info_t *irq_info) { unsigned int reg_indx = 0; unsigned int irq_bit = 0; unsigned long flags; /*GBE does not register its ISR */ if(intrpt >= (NETSS_INTERUPT_MAX-1)) { printk(KERN_WARNING "Registering for Invalid NETIP interrupt %d\n", intrpt); return; } spin_lock_irqsave(&pnetss_drv_data->irq_lock, flags); if(intrpt != NETSS_INTERUPT_ARM11) { pnetss_drv_data->irqs[intrpt].func = irq_info->func; pnetss_drv_data->irqs[intrpt].args = irq_info->args; } else { pnetss_drv_data->arm11_irqs[arm11_int_id].func = irq_info->func; pnetss_drv_data->arm11_irqs[arm11_int_id].args = irq_info->args; } if(irq_info->func != NULL) { if(intrpt != NETSS_INTERUPT_ARM11) { if(pnetss_drv_data->pending_interrupts & (1<func(pnetss_drv_data->irq_num, irq_info->args); __netss_interrupt_ack(intrpt); pnetss_drv_data->pending_interrupts &= ~(1<bridge_reg_base + NETIP_BRIDGE_IIR_OFFSET), pnetss_drv_data->pending_interrupts); } } else { reg_indx = netss_get_reg_index(arm11_int_id); irq_bit = netss_get_bit_position(arm11_int_id); if(pnetss_drv_data->pending_arm11_interrupts[reg_indx] & (1 << irq_bit)) { irq_info->func(pnetss_drv_data->irq_num, irq_info->args); /*Clear interrupt in bridge only if it is present *For ARM11 interrupt bundle this might have got cleared by some other irq handle */ if( __raw_readl(pnetss_drv_data->bridge_reg_base + NETIP_BRIDGE_IIR_OFFSET) & (1<pending_arm11_interrupts[reg_indx] &= ~(1 << irq_bit); NETSS_DBG_PRINT("Cleared pending interrupt for armm11 %d IIR %08x pending %08x\n", arm11_int_id, __raw_readl(pnetss_drv_data->bridge_reg_base + NETIP_BRIDGE_IIR_OFFSET), pnetss_drv_data->pending_arm11_interrupts[reg_indx]); } } } spin_unlock_irqrestore(&pnetss_drv_data->irq_lock, flags); } EXPORT_SYMBOL(netss_interrupt_register); /**--------------------------------------------------------------------------------**\ * intc: use intc-irq-number (0-95) ! * func: NULL for deregister \**--------------------------------------------------------------------------------**/ int netss_request_npcpu_irq(int intc, const char* irq_name, netss_subdevice_irq_func func, void *args) { netss_interrupt_info_t irq_info = {.func = func, .args = args}; NETSS_DBG_PRINT("requesting irq #%d, %s", intc, irq_name); #if defined(CONFIG_AVM_ENHANCED) pr_err("%s(intc=%d '%s' %pS)\n", __func__, intc, irq_name, func); if(intc >= NETSS_INTERUPT_ARM11_MAX) { pr_err("%s(intc=%d '%s' %pS) - error onvalid intc\n", __func__, intc, irq_name, func); return -1; } if(func == NULL) { avalanche_intc_disable_irq(intc); } #endif /*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ /* request RX IRQs */ /*Check netss driver before requesting RX IRQs */ if(!netss_driver_ready()) { pr_err("unable to get IRQ from netss netss_driver_ready() failed #%d", intc); return -1; } irq_info.func = func; irq_info.args = args; /* request IRQ from IOSF bridge driver */ netss_interrupt_register(NETSS_INTERUPT_ARM11, intc, &irq_info); NETSS_DBG_PRINT("IRQ #%d for %s", intc, irq_name); //TODO: check if need to enable or not /* enabling IRQ at INTC */ #if defined(CONFIG_AVM_ENHANCED) if(func) #endif /*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ avalanche_intc_enable_irq(intc); return 0; } EXPORT_SYMBOL(netss_request_npcpu_irq); bool netss_driver_ready(void) { if(pnetss_drv_data != NULL) { return pnetss_drv_data->netss_driver_initialized; } else { return false; } } EXPORT_SYMBOL(netss_driver_ready); int netss_device_get_info(netss_dev_t subdevice, netss_dev_info_t *mmio) { int ret = -1; switch (subdevice) { case NETSS_DEV_GPIO: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_GPIO_MMIO_OFFSET; mmio->size = NETSS_DEV_GPIO_MMIO_SIZE; ret = 0; break; case NETSS_DEV_HW_MUTEX: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_HWMUTEX_MMIO_OFFSET; mmio->size = NETSS_DEV_HWMUTEX_MMIO_SIZE; ret = 0; break; case NETSS_DEV_HW_MAILBOX: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_HWMBX_MMIO_OFFSET; mmio->size = NETSS_DEV_HWMBX_MMIO_SIZE; ret = 0; break; case NETSS_DEV_PACKET_PROCESSOR1: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_PP1_MMIO_OFFSET; mmio->size = NETSS_DEV_PP1_MMIO_SIZE; ret = 0; break; case NETSS_DEV_PACKET_PROCESSOR2: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_PP2_MMIO_OFFSET; mmio->size = NETSS_DEV_PP2_MMIO_SIZE; ret = 0; break; case NETSS_DEV_ATOM_INTC: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_ATOM_INTC_MMIO_OFFSET; mmio->size = NETSS_DEV_ATOM_INTC_MMIO_SIZE; ret = 0; break; case NETSS_DEV_HW_COUNTERS: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_HW_COUNTERS_MMIO_OFFSET; mmio->size = NETSS_DEV_HW_COUNTERS_MMIO_SIZE; ret = 0; break; case NETSS_DEV_MOCA: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_MOCA_MMIO_OFFSET; mmio->size = NETSS_DEV_MOCA_MMIO_SIZE; ret = 0; break; case NETSS_DEV_GBE: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_GBEG_MMIO_OFFSET; mmio->size = NETSS_DEV_GBEG_MMIO_SIZE; ret = 0; break; case NETSS_DEV_GBE5: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_GBE5_MMIO_OFFSET; mmio->size = NETSS_DEV_GBE5_MMIO_SIZE; ret = 0; break; case NETSS_DEV_GBE4: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_GBE4_MMIO_OFFSET; mmio->size = NETSS_DEV_GBE4_MMIO_SIZE; ret = 0; break; case NETSS_DEV_GBE3: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_GBE3_MMIO_OFFSET; mmio->size = NETSS_DEV_GBE3_MMIO_SIZE; ret = 0; break; case NETSS_DEV_GBE2: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_GBE2_MMIO_OFFSET; mmio->size = NETSS_DEV_GBE2_MMIO_SIZE; ret = 0; break; case NETSS_DEV_GBE1: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_GBE1_MMIO_OFFSET; mmio->size = NETSS_DEV_GBE1_MMIO_SIZE; ret = 0; break; case NETSS_DEV_GBE0: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_GBE0_MMIO_OFFSET; mmio->size = NETSS_DEV_GBE0_MMIO_SIZE; ret = 0; break; case NETSS_DEV_CLK: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_CLK_MMIO_OFFSET; mmio->size = NETSS_DEV_CLK_MMIO_SIZE; ret = 0; break; case NETSS_DEV_CLK_CTRL: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_CLK_CTRL_MMIO_OFFSET; mmio->size = NETSS_DEV_CLK_CTRL_MMIO_SIZE; ret = 0; break; case NETSS_DEV_BBU_CTRLR: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_BBU_CTRLR_MMIO_OFFSET; mmio->size = NETSS_DEV_BBU_CTRLR_MMIO_SIZE; ret = 0; break; case NETSS_DEV_PERIPHERAL_SRAM: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_PERIPHERAL_SRAM_MMIO_OFFSET; mmio->size = NETSS_DEV_PERIPHERAL_SRAM_MMIO_SIZE; ret = 0; break; case NETSS_DEV_BOOTCFG: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_BOOTCFG_MMIO_OFFSET; mmio->size = NETSS_DEV_BOOTCFG_MMIO_SIZE; ret = 0; break; case NETSS_DEV_TDM: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_TDM_MMIO_OFFSET; mmio->size = NETSS_DEV_TDM_MMIO_SIZE; ret = 0; break; case NETSS_DEV_TIMER0: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_TIMER0_MMIO_OFFSET; mmio->size = NETSS_DEV_TIMER0_MMIO_SIZE; ret = 0; break; case NETSS_DEV_TIMER1: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_TIMER1_MMIO_OFFSET; mmio->size = NETSS_DEV_TIMER1_MMIO_SIZE; ret = 0; break; case NETSS_DEV_TDM1: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_TDM1_MMIO_OFFSET; mmio->size = NETSS_DEV_TDM1_MMIO_SIZE; ret = 0; break; case NETSS_DEV_TIMER2: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_TIMER2_MMIO_OFFSET; mmio->size = NETSS_DEV_TIMER2_MMIO_SIZE; ret = 0; break; case NETSS_DEV_TDM2: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_TDM2_MMIO_OFFSET; mmio->size = NETSS_DEV_TDM2_MMIO_SIZE; ret = 0; break; case NETSS_DEV_VCODEC: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_VCODEC_MMIO_OFFSET; mmio->size = NETSS_DEV_VCODEC_MMIO_SIZE; ret = 0; break; case NETSS_DEV_BOOT_RAM: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_BOOT_RAM_MMIO_OFFSET; mmio->size = NETSS_DEV_BOOT_RAM_MMIO_SIZE; ret = 0; break; case NETSS_DEV_PWM: mmio->base = net_ip_mmios.region1_base + NETSS_DEV_PWM_MMIO_OFFSET; mmio->size = NETSS_DEV_PWM_MMIO_SIZE; ret = 0; break; default: ret = -1; } return ret; } EXPORT_SYMBOL(netss_device_get_info); void __netss_interrupt_enable(netss_interrupt_t intrpt) { if(intrpt >= NETSS_INTERUPT_MAX) { printk(KERN_WARNING "Invalid NetIP interrupt %d\n", intrpt); return; } if(!(pnetss_drv_data->ier_reg & (1 << intrpt))) { pnetss_drv_data->ier_reg |= (1<ier_reg, pnetss_drv_data->bridge_reg_base + NETIP_BRIDGE_IER_OFFSET); NETSS_DBG_PRINT("Enabling Net IP subdevice %d IER reg %x\n", intrpt, __raw_readl(pnetss_drv_data->bridge_reg_base + NETIP_BRIDGE_IER_OFFSET)); } if((pnetss_drv_data->imr_reg & (1 << intrpt))) { pnetss_drv_data->imr_reg &= ~(1 << intrpt); __raw_writel(pnetss_drv_data->imr_reg, pnetss_drv_data->bridge_reg_base + NETIP_BRIDGE_IMR_OFFSET); NETSS_DBG_PRINT(KERN_INFO "Unmasking NetIP subdevice %d IMR reg = %x\n", __raw_readl(pnetss_drv_data->bridge_reg_base + NETIP_BRIDGE_IMR_OFFSET)); } } /* Exported temporarly for cppp driver */ EXPORT_SYMBOL(__netss_interrupt_enable); /**************************************************************************/ /*! \fn void netss_interrupt_enable(netss_interrupt_t intrpt) ************************************************************************** * \brief NetSS driver API to enable NET IP interrupts to be seen in NETIP * bridge. This API should be only called while opening/loading * or while resuming of the respective NetIP sub device driver * module. This API should not be called from the sub device driver * ISR and if it wants to switch to polling mode and wants to do * disable/enable interrupts it should do that by disabling/enabling * its source of interrupts. * \param[in] intrpt - Enum netss_interrupt_t of NETIP interrupt. * \return void. **************************************************************************/ void netss_interrupt_enable(netss_interrupt_t intrpt) { unsigned long flags; if(intrpt >= NETSS_INTERUPT_MAX) { printk(KERN_WARNING "Invalid NetIP interrupt %d\n", intrpt); return; } spin_lock_irqsave(&pnetss_drv_data->irq_lock, flags); __netss_interrupt_enable(intrpt); spin_unlock_irqrestore(&pnetss_drv_data->irq_lock, flags); } EXPORT_SYMBOL(netss_interrupt_enable); void __netss_interrupt_disable(netss_interrupt_t intrpt) { if(intrpt >= NETSS_INTERUPT_MAX) { printk(KERN_WARNING "Invalid NetIP interrupt %d\n", intrpt); return; } if(pnetss_drv_data->ier_reg & (1 << intrpt)) { pnetss_drv_data->ier_reg &= ~(1<ier_reg, pnetss_drv_data->bridge_reg_base + NETIP_BRIDGE_IER_OFFSET); NETSS_DBG_PRINT("Disabling Net IP interrupt id %d interrupts reg %x\n", intrpt, __raw_readl(pnetss_drv_data->bridge_reg_base + NETIP_BRIDGE_IER_OFFSET)); } if(!(pnetss_drv_data->imr_reg & (1 << intrpt))) { pnetss_drv_data->imr_reg |= (1 << intrpt); __raw_writel(pnetss_drv_data->imr_reg, pnetss_drv_data->bridge_reg_base + NETIP_BRIDGE_IMR_OFFSET); NETSS_DBG_PRINT(KERN_INFO "Masking NetIP subdevice %d IMR reg = %x\n", __raw_readl(pnetss_drv_data->bridge_reg_base + NETIP_BRIDGE_IMR_OFFSET)); } } /* Exported temporarly for cppp driver */ EXPORT_SYMBOL(__netss_interrupt_disable); /**************************************************************************/ /*! \fn void netss_interrupt_disable(netss_interrupt_t intrpt) ************************************************************************** * \brief NetSS driver API to enable NET IP interrupts to be seen in NETIP * bridge. This API should be only called while closing/unloading * or while suspending of the respective NetIP sub device driver * module. This API should not be called from the sub device driver * ISR and if it wants to switch to polling mode and wants to do * disable/enable interrupts it should do that by disabling/enabling * its source of interrupts. * \param[in] intrpt - Enum netss_interrupt_t of NETIP interrupt. * \return void. **************************************************************************/ void netss_interrupt_disable(netss_interrupt_t intrpt) { unsigned long flags; if(intrpt >= NETSS_INTERUPT_MAX) { printk(KERN_WARNING "Invalid NetIP interrupt %d\n", intrpt); return; } spin_lock_irqsave(&pnetss_drv_data->irq_lock, flags); __netss_interrupt_disable(intrpt); spin_unlock_irqrestore(&pnetss_drv_data->irq_lock, flags); } EXPORT_SYMBOL(netss_interrupt_disable); void netss_interrupt_ack(netss_interrupt_t intrpt) { if(intrpt >= NETSS_INTERUPT_MAX) { printk(KERN_WARNING "Invalid NetIP interrupt %d\n", intrpt); return; } __netss_interrupt_ack(intrpt); } EXPORT_SYMBOL(netss_interrupt_ack); /* * Interrupt Handler */ static irqreturn_t net_subsystem_isr(int irq, void *dev_id) { struct net_subsystem_drv_data *pnet_ss = (struct net_subsystem_drv_data *)dev_id; irqreturn_t ret = IRQ_NONE; int i; unsigned long flags; uint32_t volatile reg_val = 0; uint32_t ack_intr = 0; unsigned int reg_indx = 0; unsigned int irq_bit = 0; spin_lock_irqsave(&pnet_ss->irq_lock, flags); reg_val = __raw_readl(pnet_ss->bridge_reg_base + NETIP_BRIDGE_IIR_OFFSET); NETSS_DBG_PRINT("Net IP ISR called\n"); /**Check for interrupts 0 to 7, 8th bit indicates GBE interrupt * which is handled by GBE driver. */ for(i=0; i<(NETSS_INTERUPT_MAX-1); i++) { if((reg_val >> i) & 1) { if(i != NETSS_INTERUPT_ARM11) { if(pnet_ss->irqs[i].func != NULL) { NETSS_DBG_PRINT("Interrupt of subdevice %d\n", i); pnet_ss->irqs[i].func(i, pnet_ss->irqs[i].args); /*Prepare interrupt acknowledge mask */ ack_intr |= (1 << i); } else { pnet_ss->pending_interrupts |= (1 << i); printk("No irq registered for interrupt of subdevice %d, Marking it pending\n", i); } } else { int j; for( j = 0 ; j< NETSS_INTERUPT_ARM11_MAX; j++) { if(avalanche_intc_get_status(j)) { if(pnet_ss->arm11_irqs[j].func != NULL) { pnet_ss->arm11_irqs[j].func(j, pnet_ss->arm11_irqs[j].args); /*Arm11 bottom half will clear the NetIP bridge interrupt */ ack_intr |= (1 << i); NETSS_DBG_PRINT("Processed Arm 11 interrupt %d\n", j); } else { reg_indx = netss_get_reg_index(j); irq_bit = netss_get_bit_position(j); pnet_ss->pending_arm11_interrupts[reg_indx] |= (1 << irq_bit); avalanche_intc_disable_irq(j); ack_intr |= (1 << i); NETSS_DBG_PRINT("No irq registered for ARM11 interrupt %d, Marking it pending\n", j) ; } /* no need to clear ATOM_INTC here avalanche_intc_clear_status(j); */ /* status clear need to be done by requester */ } } } } } /* Acknowledge the interrupts that are processed */ if(ack_intr != 0) { __raw_writel(ack_intr, pnet_ss->bridge_reg_base + NETIP_BRIDGE_IIR_OFFSET); } ret = IRQ_HANDLED; spin_unlock_irqrestore(&pnet_ss->irq_lock, flags); return ret; } void netss_get_child_dev_info(void) { struct acpi_device *acpi_dev, *child; struct acpi_device_info *info; acpi_status ret; int uid = -1; pnetss_drv_data->bios_enabled_xgmiis = 0; ret = acpi_bus_get_device(pnetss_drv_data->acpi_h, &acpi_dev); if (ACPI_FAILURE(ret)) { printk("Failed to acpi dev\n"); return; } list_for_each_entry(child, &acpi_dev->children, node) { ret = acpi_get_object_info(child->handle, &info); if (ACPI_SUCCESS(ret)) { if (info->hardware_id.string != NULL) { if (0 == strncmp(info->hardware_id.string, "INT351B", 7)) { if (info->unique_id.string != NULL) { ret = kstrtoint(info->unique_id.string, 0, &uid); if (!ret && child->status.present && child->status.enabled) { pnetss_drv_data->bios_enabled_xgmiis |= (1 << uid); } } else { printk(KERN_ERR "%s unique id string is NULL\n", info->hardware_id.string); } } } kfree(info); } else { printk(KERN_ERR "Failed to acpi dev info\n"); } } #ifdef CONFIG_NETIP_SERVICES_ON_ATOM if (pnetss_drv_data->bios_enabled_xgmiis == 0) { /* Override as this will be the case if PP on ARM BIOS is used with PP on ATOM SDK */ pnetss_drv_data->bios_enabled_xgmiis = 0xF; printk(KERN_INFO "Forcing BIOS enabled xgmiis = %08X\n", pnetss_drv_data->bios_enabled_xgmiis); } else #endif { printk(KERN_INFO "BIOS enabled xgmiis = %08X\n", pnetss_drv_data->bios_enabled_xgmiis); } return; } /* * driver entry point */ static int netss_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int ret = -ENODEV; int i; ret = pci_enable_device(pdev); if (ret) { dev_err(&pdev->dev, "pci_enable_device failed.\n"); return ret; } ret = pci_enable_msi(pdev); if (ret) { if(-EINVAL == ret) { dev_err(&pdev->dev, "pci_enable_msi returned invalid param. %x", (unsigned int) ret); } dev_err(&pdev->dev, "pci_enable_msi failed. %x", (unsigned int) ret); pci_disable_device(pdev); return ret; } net_ip_mmios.region1_base = pci_resource_start(pdev,0); net_ip_mmios.region1_size = pci_resource_len(pdev,0); #ifdef PUMA7_PRE_SILICON net_ip_mmios.region2_base = pci_resource_start(pdev,1); net_ip_mmios.region2_size = pci_resource_len(pdev,1); #else net_ip_mmios.region2_base = pci_resource_start(pdev,2); net_ip_mmios.region2_size = pci_resource_len(pdev,2); #endif NETSS_DBG_PRINT(KERN_INFO "1: mem_iobase = 0x%x, mem_iosize = 0x%x\n",(unsigned int)net_ip_mmios.region1_base,(unsigned int)net_ip_mmios.region1_size); NETSS_DBG_PRINT(KERN_INFO "2: mem_iobase = 0x%x, mem_iosize = 0x%x\n",(unsigned int)net_ip_mmios.region2_base,(unsigned int)net_ip_mmios.region2_size); if (pci_request_regions(pdev, "net-subsytem")){ dev_err(&pdev->dev, "Cannot obtain PCI resources\n"); ret = -EBUSY; goto free_dev; } pnetss_drv_data = kzalloc(sizeof(struct net_subsystem_drv_data), GFP_KERNEL); if (!pnetss_drv_data){ dev_err(&pdev->dev, "Cannot allocate memory\n"); ret = -ENOMEM; goto free_resource; } pnetss_drv_data->bridge_reg_base = (void __iomem *)ioremap_nocache(net_ip_mmios.region2_base,net_ip_mmios.region2_size); if (!pnetss_drv_data->bridge_reg_base) { dev_err( &pdev->dev, "error, failed to ioremap mutex registers\n"); ret = -ENOMEM; goto free_mem; } pnetss_drv_data->ier_reg = __raw_readl(pnetss_drv_data->bridge_reg_base + NETIP_BRIDGE_IER_OFFSET); pnetss_drv_data->ier_reg |= 0xFF; __raw_writel(pnetss_drv_data->ier_reg, pnetss_drv_data->bridge_reg_base + NETIP_BRIDGE_IER_OFFSET); printk(KERN_INFO "Enabling Net IP interrupts except GBE reg %x\n", __raw_readl(pnetss_drv_data->bridge_reg_base + NETIP_BRIDGE_IER_OFFSET)); pnetss_drv_data->imr_reg = __raw_readl(pnetss_drv_data->bridge_reg_base + NETIP_BRIDGE_IMR_OFFSET); if((pnetss_drv_data->imr_reg & NETIP_BRIDGE_IRQ_MASK)) { pnetss_drv_data->imr_reg &= ~NETIP_BRIDGE_IRQ_MASK; __raw_writel(pnetss_drv_data->imr_reg, pnetss_drv_data->bridge_reg_base + NETIP_BRIDGE_IMR_OFFSET); printk(KERN_INFO "Unmasking NetIP interrupts IMR = %x\n", __raw_readl(pnetss_drv_data->bridge_reg_base + NETIP_BRIDGE_IMR_OFFSET)); } pnetss_drv_data->irq_num = pdev->irq; pnetss_drv_data->dev = pdev; spin_lock_init(&pnetss_drv_data->irq_lock); if (IS_ENABLED(CONFIG_PLATFORM_WANTS_UBIK2_ISDNSTACK_ON_CPU)) ret = request_irq_on(CONFIG_PLATFORM_WANTS_UBIK2_ISDNSTACK_ON_CPU, pnetss_drv_data->irq_num, net_subsystem_isr, IRQ_NOTHREAD, NET_SUBSYSTEM_IRQ_NAME, (void *)pnetss_drv_data); else ret = request_irq(pnetss_drv_data->irq_num, net_subsystem_isr, 0, NET_SUBSYSTEM_IRQ_NAME, (void *)pnetss_drv_data); if (ret) { pr_err("HW Mutex: Unable to allocate IRQ\n"); ret = -ENODEV; goto free_iomem; } for(i=0; iirqs[i].func = NULL; pnetss_drv_data->irqs[i].args = NULL; } for(i=0; iarm11_irqs[i].func = NULL; pnetss_drv_data->arm11_irqs[i].args = NULL; } mutex_init(&pnetss_drv_data->netss_lock); mutex_init(&pnetss_drv_data->netip_msg.lock); /**Initalize NetIP power state change handler callback function info */ for(i=0; ipower_state_change_cbinfo[i].func = NULL; pnetss_drv_data->power_state_change_cbinfo[i].args = NULL; } /**Get the ACPI handle */ pnetss_drv_data->acpi_h = ACPI_HANDLE(&pdev->dev); pci_set_drvdata(pnetss_drv_data->dev,pnetss_drv_data); pnetss_drv_data->netss_driver_initialized = true; //pm_runtime_enable(&pdev->dev); pm_runtime_allow(&pdev->dev); /*Register callbacks for messages from netip and send appropriate messages */ netss_manage_netip_services(); printk(KERN_INFO "Initializing Intel(R) NET SUBSYSTEM driver\n"); /* initilize ATOM_INTC IO memory map */ avalanche_intc_init(); printk("docsis_chan_cfg = %08x\n", docsis_chan_cfg); netss_get_child_dev_info(); return 0; free_iomem: iounmap(pnetss_drv_data->bridge_reg_base); free_mem: kfree(pnetss_drv_data); free_resource: pci_release_regions(pdev); free_dev: //pci_disable_msi(pdev); pci_disable_device(pdev); return ret; } /* * driver exit point */ static void netss_remove(struct pci_dev *pdev) { struct net_subsystem_drv_data *pnetss_drv_data = pci_get_drvdata(pdev); if (!pnetss_drv_data) return; mutex_destroy(&pnetss_drv_data->netss_lock); mutex_destroy(&pnetss_drv_data->netip_msg.lock); /*wake up and stop the thread */ netss_stop_handshake_thread(); pm_runtime_disable(&pdev->dev); iounmap(pnetss_drv_data->bridge_reg_base); kfree(pnetss_drv_data); pci_release_regions(pdev); //pci_disable_msi(pnetss_drv_data->dev); pci_disable_device(pdev); //DEBUG_PRINT; NETSS_DBG_PRINT(KERN_INFO "NET SubSytem driver: device exit \n"); return; } static struct pci_driver netss_driver = { .name = "ce-net-subsystem", .id_table = net_subsystem_pci_tbl, .probe = netss_probe, .remove = netss_remove, .driver.pm = &netss_pm_ops, }; static int __init net_subsystem_lld_init (void) { return pci_register_driver(&netss_driver); } static void __exit net_subsystem_lld_exit(void) { pci_unregister_driver(&netss_driver); } subsys_initcall(net_subsystem_lld_init); module_exit(net_subsystem_lld_exit); MODULE_DESCRIPTION("Intel(R) NET SUBSYSTEM Driver"); MODULE_AUTHOR("Intel Corporation"); MODULE_LICENSE("GPL");