1
0
mirror of https://github.com/corundum/corundum.git synced 2025-01-16 08:12:53 +08:00

modules/mqnic/: Add platform driver support

Signed-off-by: Joachim Foerster <joachim.foerster@missinglinkelectronics.com>
This commit is contained in:
Joachim Foerster 2021-10-12 19:32:25 +02:00
parent 80d5bda23f
commit 26c70bbb8a
5 changed files with 414 additions and 20 deletions

View File

@ -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>;
};
};

View File

@ -37,7 +37,10 @@
#define MQNIC_H #define MQNIC_H
#include <linux/kernel.h> #include <linux/kernel.h>
#ifdef CONFIG_PCI
#include <linux/pci.h> #include <linux/pci.h>
#endif
#include <linux/platform_device.h>
#include <linux/miscdevice.h> #include <linux/miscdevice.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
@ -93,12 +96,16 @@ struct mqnic_i2c_bus {
struct mqnic_irq { struct mqnic_irq {
int index; int index;
int irqn; int irqn;
char name[16 + 3];
struct atomic_notifier_head nh; struct atomic_notifier_head nh;
}; };
struct mqnic_dev { struct mqnic_dev {
struct device *dev; struct device *dev;
#ifdef CONFIG_PCI
struct pci_dev *pdev; struct pci_dev *pdev;
#endif
struct platform_device *pfdev;
resource_size_t hw_regs_size; resource_size_t hw_regs_size;
phys_addr_t hw_regs_phys; phys_addr_t hw_regs_phys;
@ -446,6 +453,7 @@ void free_reg_block_list(struct reg_block *list);
// mqnic_irq.c // mqnic_irq.c
int mqnic_irq_init_pcie(struct mqnic_dev *mdev); int mqnic_irq_init_pcie(struct mqnic_dev *mdev);
void mqnic_irq_deinit_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 // mqnic_dev.c
extern const struct file_operations mqnic_fops; extern const struct file_operations mqnic_fops;

View File

@ -38,6 +38,14 @@
#include <linux/types.h> #include <linux/types.h>
#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_IRQ 32
#define MQNIC_MAX_IF 8 #define MQNIC_MAX_IF 8

View File

@ -107,3 +107,45 @@ void mqnic_irq_deinit_pcie(struct mqnic_dev *mdev)
pci_free_irq_vectors(pdev); 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;
}

View File

@ -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_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)"); 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[] = { static const struct pci_device_id mqnic_pci_id_table[] = {
{PCI_DEVICE(0x1234, 0x1001)}, {PCI_DEVICE(0x1234, 0x1001)},
{PCI_DEVICE(0x5543, 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); 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 LIST_HEAD(mqnic_devices);
static DEFINE_SPINLOCK(mqnic_devices_lock); static DEFINE_SPINLOCK(mqnic_devices_lock);
@ -107,6 +117,133 @@ static void mqnic_free_id(struct mqnic_dev *mqnic)
spin_unlock(&mqnic_devices_lock); 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) static int mqnic_common_probe(struct mqnic_dev *mqnic)
{ {
int ret = 0; int ret = 0;
@ -198,11 +335,23 @@ static int mqnic_common_probe(struct mqnic_dev *mqnic)
goto fail_bar_size; goto fail_bar_size;
} }
// Board-specific init if (mqnic->pfdev) {
ret = mqnic_board_init(mqnic); #ifdef CONFIG_OF
if (ret) { ret = mqnic_platform_get_mac_address(mqnic);
dev_err(dev, "Failed to initialize board"); if (ret)
goto fail_board; 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 // register PHC
@ -281,10 +430,17 @@ static void mqnic_common_remove(struct mqnic_dev *mqnic)
mqnic_destroy_interface(&mqnic->interface[k]); mqnic_destroy_interface(&mqnic->interface[k]);
mqnic_unregister_phc(mqnic); 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); 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) static int mqnic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{ {
int ret = 0; 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; goto fail_enable_device;
} }
// Set mask // Set DMA properties
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); ret = mqnic_common_setdma(mqnic);
if (ret) { if (ret)
dev_warn(dev, "Warning: failed to set 64 bit PCI DMA mask"); goto fail_regions;
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));
// Reserve regions // Reserve regions
ret = pci_request_regions(pdev, DRIVER_NAME); ret = pci_request_regions(pdev, DRIVER_NAME);
@ -514,15 +661,156 @@ static struct pci_driver mqnic_pci_driver = {
.remove = mqnic_pci_remove, .remove = mqnic_pci_remove,
.shutdown = mqnic_pci_shutdown .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) 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) static void __exit mqnic_exit(void)
{ {
platform_driver_unregister(&mqnic_platform_driver);
#ifdef CONFIG_PCI
pci_unregister_driver(&mqnic_pci_driver); pci_unregister_driver(&mqnic_pci_driver);
#endif
} }
module_init(mqnic_init); module_init(mqnic_init);