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:
parent
80d5bda23f
commit
26c70bbb8a
48
modules/mqnic/device-tree-binding.txt
Normal file
48
modules/mqnic/device-tree-binding.txt
Normal 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>;
|
||||
};
|
||||
};
|
@ -37,7 +37,10 @@
|
||||
#define MQNIC_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#ifdef CONFIG_PCI
|
||||
#include <linux/pci.h>
|
||||
#endif
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
@ -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;
|
||||
|
@ -38,6 +38,14 @@
|
||||
|
||||
#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_IF 8
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,12 +335,24 @@ static int mqnic_common_probe(struct mqnic_dev *mqnic)
|
||||
goto fail_bar_size;
|
||||
}
|
||||
|
||||
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
|
||||
if (mqnic->phc_rb)
|
||||
@ -281,10 +430,17 @@ static void mqnic_common_remove(struct mqnic_dev *mqnic)
|
||||
mqnic_destroy_interface(&mqnic->interface[k]);
|
||||
|
||||
mqnic_unregister_phc(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");
|
||||
// Set DMA properties
|
||||
ret = mqnic_common_setdma(mqnic);
|
||||
if (ret)
|
||||
goto fail_regions;
|
||||
}
|
||||
}
|
||||
|
||||
// Set max segment size
|
||||
dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
|
||||
|
||||
// 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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user