From 26c70bbb8a14f2550a42e8b481c74d9301a05396 Mon Sep 17 00:00:00 2001 From: Joachim Foerster Date: Tue, 12 Oct 2021 19:32:25 +0200 Subject: [PATCH] modules/mqnic/: Add platform driver support Signed-off-by: Joachim Foerster --- modules/mqnic/device-tree-binding.txt | 48 ++++ modules/mqnic/mqnic.h | 8 + modules/mqnic/mqnic_hw.h | 8 + modules/mqnic/mqnic_irq.c | 42 ++++ modules/mqnic/mqnic_main.c | 328 ++++++++++++++++++++++++-- 5 files changed, 414 insertions(+), 20 deletions(-) create mode 100644 modules/mqnic/device-tree-binding.txt diff --git a/modules/mqnic/device-tree-binding.txt b/modules/mqnic/device-tree-binding.txt new file mode 100644 index 000000000..8bc38677f --- /dev/null +++ b/modules/mqnic/device-tree-binding.txt @@ -0,0 +1,48 @@ +* Corundum mqnic Ethernet controller + +Required properties: +- compatible: Should be "corundum,mqnic" +- reg: Address and length of the "control" register set. Optionally a second and + third range, the "app" and "ram" register sets may be added. +- interrupts: Should contain mqnic interrupt(s) + +Optional properties: +- nvmem-cells: Phandle of nvmem cell containing the base MAC address +- nvmem-cell-names: Should be "mac-address" +- mac-address-increment-byte: + Index of base MAC address byte to increment (default: 0x5) +- mac-address-increment: + Number to add to the chosen base MAC address byte (default: 0x0) +- mac-address-local: + Boolean, mark derived MAC addresses as "locally administrated" + (default: false) +- module-eeproms: Array of phandles to SFP module EEPROM node(s) + +Examples: + + mqnic0: ethernet@a0000000 { + compatible = "corundum,mqnic"; + reg = <0x0 0xa0000000 0x0 0x1000000>, + <0x0 0xb0000000 0x0 0x1000000>; + reg-names = "csr", "app"; + interrupt-parent = <&gic>; + interrupts = <0x0 0x59 0x1>, <0x0 0x5a 0x1>, <0x0 0x5b 0x1>, + <0x0 0x5c 0x1>; + + nvmem-cells = <&macaddress>; + nvmem-cell-names = "mac-address"; + + mac-address-increment = <0x1>; + mac-address-local; + + module-eeproms = <&module_eeprom_sfp0>, <&module_eeprom_sfp1>; + }; + + &eeprom { + #address-cells = <1>; + #size-cells = <1>; + + macaddress: macaddress@20 { + reg = <0x20 0x06>; + }; + }; diff --git a/modules/mqnic/mqnic.h b/modules/mqnic/mqnic.h index 3e21385e3..b8a81d4a4 100644 --- a/modules/mqnic/mqnic.h +++ b/modules/mqnic/mqnic.h @@ -37,7 +37,10 @@ #define MQNIC_H #include +#ifdef CONFIG_PCI #include +#endif +#include #include #include #include @@ -93,12 +96,16 @@ struct mqnic_i2c_bus { struct mqnic_irq { int index; int irqn; + char name[16 + 3]; struct atomic_notifier_head nh; }; struct mqnic_dev { struct device *dev; +#ifdef CONFIG_PCI struct pci_dev *pdev; +#endif + struct platform_device *pfdev; resource_size_t hw_regs_size; phys_addr_t hw_regs_phys; @@ -446,6 +453,7 @@ void free_reg_block_list(struct reg_block *list); // mqnic_irq.c int mqnic_irq_init_pcie(struct mqnic_dev *mdev); void mqnic_irq_deinit_pcie(struct mqnic_dev *mdev); +int mqnic_irq_init_platform(struct mqnic_dev *mdev); // mqnic_dev.c extern const struct file_operations mqnic_fops; diff --git a/modules/mqnic/mqnic_hw.h b/modules/mqnic/mqnic_hw.h index cc2832429..d4247622d 100644 --- a/modules/mqnic/mqnic_hw.h +++ b/modules/mqnic/mqnic_hw.h @@ -38,6 +38,14 @@ #include +#ifdef CONFIG_OF +/* platform driver OF-related definitions */ +#define MQNIC_PROP_MAC_ADDR_INC_BYTE "mac-address-increment-byte" +#define MQNIC_PROP_MAC_ADDR_INC "mac-address-increment" +#define MQNIC_PROP_MAC_ADDR_LOCAL "mac-address-local" +#define MQNIC_PROP_MODULE_EEPROM "module-eeproms" +#endif + #define MQNIC_MAX_IRQ 32 #define MQNIC_MAX_IF 8 diff --git a/modules/mqnic/mqnic_irq.c b/modules/mqnic/mqnic_irq.c index da1d9133c..2da6c2d0d 100644 --- a/modules/mqnic/mqnic_irq.c +++ b/modules/mqnic/mqnic_irq.c @@ -107,3 +107,45 @@ void mqnic_irq_deinit_pcie(struct mqnic_dev *mdev) pci_free_irq_vectors(pdev); } + +int mqnic_irq_init_platform(struct mqnic_dev *mdev) +{ + struct platform_device *pdev = mdev->pfdev; + struct device *dev = mdev->dev; + int k; + + // Allocate IRQs + mdev->irq_count = platform_irq_count(pdev); + + // Set up interrupts + for (k = 0; k < mdev->irq_count; k++) { + int irqn; + struct mqnic_irq *irq; + int ret; + + irqn = platform_get_irq(pdev, k); + if (irqn < 0) + return irqn; + + irq = devm_kzalloc(dev, sizeof(*irq), GFP_KERNEL); + if (!irq) + return -ENOMEM; + + ATOMIC_INIT_NOTIFIER_HEAD(&irq->nh); + + snprintf(irq->name, sizeof(irq->name), "%s-%u", mdev->name, k); + ret = devm_request_irq(dev, irqn, mqnic_irq_handler, 0, irq->name, irq); + if (ret < 0) { + dev_err(dev, "Failed to request IRQ %d (interrupt number %d)", k, irqn); + return ret; + } + + irq->index = k; + irq->irqn = irqn; + mdev->irq[k] = irq; + } + + dev_info(dev, "Configured %d IRQs", mdev->irq_count); + + return 0; +} diff --git a/modules/mqnic/mqnic_main.c b/modules/mqnic/mqnic_main.c index c2168e22d..98868a158 100644 --- a/modules/mqnic/mqnic_main.c +++ b/modules/mqnic/mqnic_main.c @@ -59,6 +59,7 @@ MODULE_PARM_DESC(num_tx_queue_entries, "number of entries to allocate per transm module_param_named(num_rx_queue_entries, mqnic_num_rx_queue_entries, uint, 0444); MODULE_PARM_DESC(num_rx_queue_entries, "number of entries to allocate per receive queue (default: 1024)"); +#ifdef CONFIG_PCI static const struct pci_device_id mqnic_pci_id_table[] = { {PCI_DEVICE(0x1234, 0x1001)}, {PCI_DEVICE(0x5543, 0x1001)}, @@ -66,6 +67,15 @@ static const struct pci_device_id mqnic_pci_id_table[] = { }; MODULE_DEVICE_TABLE(pci, mqnic_pci_id_table); +#endif + +#ifdef CONFIG_OF +static struct of_device_id mqnic_of_id_table[] = { + { .compatible = "corundum,mqnic" }, + { }, +}; +MODULE_DEVICE_TABLE(of, mqnic_of_id_table); +#endif static LIST_HEAD(mqnic_devices); static DEFINE_SPINLOCK(mqnic_devices_lock); @@ -107,6 +117,133 @@ static void mqnic_free_id(struct mqnic_dev *mqnic) spin_unlock(&mqnic_devices_lock); } +static int mqnic_common_setdma(struct mqnic_dev *mqnic) +{ + int ret; + struct device *dev = mqnic->dev; + + // Set mask + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (ret) { + dev_warn(dev, "Warning: failed to set 64 bit PCI DMA mask"); + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(dev, "Failed to set PCI DMA mask"); + return ret; + } + } + + // Set max segment size + dma_set_max_seg_size(dev, DMA_BIT_MASK(32)); + + return ret; +} + +#ifdef CONFIG_OF +static int mqnic_platform_get_mac_address(struct mqnic_dev *mqnic) +{ + int ret; + struct device *dev = mqnic->dev; + char mac_base[ETH_ALEN]; + struct device_node *np; + u32 inc_idx; + u32 inc; + int k; + + /* NOTE: Not being able to get a (base) MAC address shall not be an + * error to fail on intentionally. Thus we are warning, only. + */ + ret = eth_platform_get_mac_address(dev, mac_base); + if (ret) { + dev_warn(dev, "Unable to get MAC address\n"); + return 0; + } + + np = mqnic->dev->of_node; + if (!np) + return 0; + + if (of_property_read_u32(np, MQNIC_PROP_MAC_ADDR_INC_BYTE, &inc_idx)) + inc_idx = 5; + if ((inc_idx < 3) || (inc_idx > 5)) { + dev_err(dev, "Invalid property \"" MQNIC_PROP_MAC_ADDR_INC_BYTE "\"\n"); + return -EINVAL; + } + + ret = of_property_read_u32(np, MQNIC_PROP_MAC_ADDR_INC, &inc); + if (ret == -EINVAL) { + inc = 0; + } else if (ret) { + dev_err(dev, "Invalid property \"" MQNIC_PROP_MAC_ADDR_INC "\"\n"); + return ret; + } + + if (of_property_read_bool(np, MQNIC_PROP_MAC_ADDR_LOCAL)) + mac_base[0] |= BIT(1); + + mqnic->mac_count = mqnic->if_count; + for (k = 0; k < mqnic->mac_count; k++) { + memcpy(mqnic->mac_list[k], mac_base, ETH_ALEN); + mqnic->mac_list[k][inc_idx] += inc + k; + } + + return 0; +} + +static void mqnic_platform_module_eeprom_put(struct mqnic_dev *mqnic) +{ + int k; + + for (k = 0; k < mqnic->if_count; k++) + if (mqnic->mod_i2c_client) + put_device(&mqnic->mod_i2c_client[k]->dev); +} + +static int mqnic_platform_module_eeprom_get(struct mqnic_dev *mqnic) +{ + int ret; + struct device *dev = mqnic->dev; + int k; + + ret = 0; + + if (!dev->of_node) + return 0; + + for (k = 0; k < mqnic->if_count; k++) { + struct device_node *np; + struct i2c_client *cl; + + /* NOTE: Not being able to get a phandle for module EEPROM shall + * not be an error to fail on intentionally. Thus we are + * warning, only. + */ + np = of_parse_phandle(dev->of_node, MQNIC_PROP_MODULE_EEPROM, k); + if (!np) { + dev_warn(dev, "Missing phandle to module EEPROM for interface %d\n", k); + continue; + } + + cl = of_find_i2c_device_by_node(np); + if (!cl) { + ret = -ENOENT; + dev_err(dev, "Failed to find I2C device for module of interface %d\n", k); + of_node_put(np); + break; + } else { + mqnic->mod_i2c_client[k] = cl; + mqnic->mod_i2c_client_count++; + } + of_node_put(np); + } + + if (ret) + mqnic_platform_module_eeprom_put(mqnic); + + return ret; +} +#endif + static int mqnic_common_probe(struct mqnic_dev *mqnic) { int ret = 0; @@ -198,11 +335,23 @@ static int mqnic_common_probe(struct mqnic_dev *mqnic) goto fail_bar_size; } - // Board-specific init - ret = mqnic_board_init(mqnic); - if (ret) { - dev_err(dev, "Failed to initialize board"); - goto fail_board; + if (mqnic->pfdev) { +#ifdef CONFIG_OF + ret = mqnic_platform_get_mac_address(mqnic); + if (ret) + goto fail_board; + + ret = mqnic_platform_module_eeprom_get(mqnic); + if (ret) + goto fail_board; +#endif + } else { + // Board-specific init + ret = mqnic_board_init(mqnic); + if (ret) { + dev_err(dev, "Failed to initialize board"); + goto fail_board; + } } // register PHC @@ -281,10 +430,17 @@ static void mqnic_common_remove(struct mqnic_dev *mqnic) mqnic_destroy_interface(&mqnic->interface[k]); mqnic_unregister_phc(mqnic); - mqnic_board_deinit(mqnic); + if (mqnic->pfdev) { +#ifdef CONFIG_OF + mqnic_platform_module_eeprom_put(mqnic); +#endif + } else { + mqnic_board_deinit(mqnic); + } free_reg_block_list(mqnic->rb_list); } +#ifdef CONFIG_PCI static int mqnic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int ret = 0; @@ -376,19 +532,10 @@ static int mqnic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent goto fail_enable_device; } - // Set mask - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); - if (ret) { - dev_warn(dev, "Warning: failed to set 64 bit PCI DMA mask"); - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); - if (ret) { - dev_err(dev, "Failed to set PCI DMA mask"); - goto fail_regions; - } - } - - // Set max segment size - dma_set_max_seg_size(dev, DMA_BIT_MASK(32)); + // Set DMA properties + ret = mqnic_common_setdma(mqnic); + if (ret) + goto fail_regions; // Reserve regions ret = pci_request_regions(pdev, DRIVER_NAME); @@ -514,15 +661,156 @@ static struct pci_driver mqnic_pci_driver = { .remove = mqnic_pci_remove, .shutdown = mqnic_pci_shutdown }; +#endif /* CONFIG_PCI */ + +static int mqnic_platform_probe(struct platform_device *pdev) +{ + int ret; + struct mqnic_dev *mqnic; + struct device *dev = &pdev->dev; + struct resource *res; + + dev_info(dev, DRIVER_NAME " platform probe"); + +#ifdef CONFIG_NUMA + dev_info(dev, " NUMA node: %d", pdev->dev.numa_node); +#endif + + mqnic = devm_kzalloc(dev, sizeof(*mqnic), GFP_KERNEL); + if (!mqnic) + return -ENOMEM; + + mqnic->dev = dev; + mqnic->pfdev = pdev; + platform_set_drvdata(pdev, mqnic); + + // assign ID and add to list + mqnic_assign_id(mqnic); + + // Set DMA properties + ret = mqnic_common_setdma(mqnic); + if (ret) + goto fail; + + // Reserve and map regions + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mqnic->hw_regs_size = resource_size(res); + mqnic->hw_regs_phys = res->start; + + dev_info(dev, "Control BAR size: %llu", mqnic->hw_regs_size); + mqnic->hw_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mqnic->hw_addr)) { + ret = PTR_ERR(mqnic->hw_addr); + dev_err(dev, "Failed to map control BAR"); + goto fail; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) { + void __iomem *hw_addr; + + mqnic->app_hw_regs_size = resource_size(res); + mqnic->app_hw_regs_phys = res->start; + + dev_info(dev, "Application BAR size: %llu", mqnic->app_hw_regs_size); + hw_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hw_addr)) { + ret = PTR_ERR(hw_addr); + dev_err(dev, "Failed to map application BAR"); + goto fail; + } + mqnic->app_hw_addr = hw_addr; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (res) { + void __iomem *hw_addr; + + mqnic->ram_hw_regs_size = resource_size(res); + mqnic->ram_hw_regs_phys = res->start; + + dev_info(dev, "RAM BAR size: %llu", mqnic->ram_hw_regs_size); + hw_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hw_addr)) { + ret = PTR_ERR(hw_addr); + dev_err(dev, "Failed to map RAM BAR"); + goto fail; + } + mqnic->ram_hw_addr = hw_addr; + } + + // Set up interrupts + ret = mqnic_irq_init_platform(mqnic); + if (ret) { + dev_err(dev, "Failed to set up interrupts"); + goto fail; + } + + // Common init + ret = mqnic_common_probe(mqnic); + if (ret) + goto fail; + + // probe complete + return 0; + + // error handling +fail: + mqnic_free_id(mqnic); + return ret; +} + +static int mqnic_platform_remove(struct platform_device *pdev) +{ + struct mqnic_dev *mqnic = platform_get_drvdata(pdev); + + dev_info(&pdev->dev, DRIVER_NAME " platform remove"); + + mqnic_common_remove(mqnic); + + mqnic_free_id(mqnic); + return 0; +} + +static struct platform_driver mqnic_platform_driver = { + .probe = mqnic_platform_probe, + .remove = mqnic_platform_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(mqnic_of_id_table), + }, +}; static int __init mqnic_init(void) { - return pci_register_driver(&mqnic_pci_driver); + int rc; + +#ifdef CONFIG_PCI + rc = pci_register_driver(&mqnic_pci_driver); + if (rc) + return rc; +#endif + + rc = platform_driver_register(&mqnic_platform_driver); + if (rc) + goto err; + + return 0; + +err: +#ifdef CONFIG_PCI + pci_unregister_driver(&mqnic_pci_driver); +#endif + return rc; } static void __exit mqnic_exit(void) { + platform_driver_unregister(&mqnic_platform_driver); + +#ifdef CONFIG_PCI pci_unregister_driver(&mqnic_pci_driver); +#endif } module_init(mqnic_init);