diff --git a/arch/arm/boot/dts/xilinx/ni-zynq.dtsi b/arch/arm/boot/dts/xilinx/ni-zynq.dtsi index f8a4850c093e1..07fabec8f068a 100644 --- a/arch/arm/boot/dts/xilinx/ni-zynq.dtsi +++ b/arch/arm/boot/dts/xilinx/ni-zynq.dtsi @@ -105,6 +105,11 @@ clock-frequency = <58824000>; }; + fpgaperipheral@f8007000 { + compatible = "fpgaperipheral"; + reg = <0xf8007000 0x100>; + }; + i2c2: i2c@81000000 { compatible = "xlnx,xps-iic-2.00.a"; status = "disabled"; @@ -173,6 +178,14 @@ arm,standby-mode = <0>; }; +&devcfg { + status = "disabled"; +}; + +&fpga_full { + status = "disabled"; +}; + /* * The CoreSight debug/trace macrocells are not accessible on NI Zynq-based * targets: reading their AMBA peripheral-ID registers (done unconditionally diff --git a/arch/arm/configs/nati_zynq_defconfig b/arch/arm/configs/nati_zynq_defconfig index 8b849b1760254..ad74851ef7245 100644 --- a/arch/arm/configs/nati_zynq_defconfig +++ b/arch/arm/configs/nati_zynq_defconfig @@ -23,6 +23,7 @@ CONFIG_EXPERT=y CONFIG_PERF_EVENTS=y CONFIG_ARCH_VEXPRESS=y CONFIG_ARCH_ZYNQ=y +CONFIG_NI_ZYNQ_GIC_LEGACY_IRQDOMAIN=y CONFIG_ARM_ERRATA_754322=y CONFIG_ARM_ERRATA_764369=y CONFIG_ARM_ERRATA_775420=y diff --git a/arch/arm/mach-zynq/Kconfig b/arch/arm/mach-zynq/Kconfig index 05be5aa9b4028..6335678a1fd28 100644 --- a/arch/arm/mach-zynq/Kconfig +++ b/arch/arm/mach-zynq/Kconfig @@ -15,3 +15,13 @@ config ARCH_ZYNQ select SOC_BUS help Support for Xilinx Zynq ARM Cortex A9 Platform + +config NI_ZYNQ_GIC_LEGACY_IRQDOMAIN + bool "Use fixed GIC IRQ numbers on NI Zynq targets" + depends on ARCH_ZYNQ + help + Create fixed Linux IRQ mappings for the primary GIC so that its + Linux IRQ numbers equal the corresponding GIC hardware IRQ numbers. + This preserves compatibility with NI drivers that use fixed IRQs. + Enable this only on NI Zynq systems that require the legacy IRQ + numbering contract. diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 1269ab8eb726a..86a5001a6fbb1 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -1160,6 +1160,73 @@ static const struct irq_domain_ops gic_irq_domain_hierarchy_ops = { .free = irq_domain_free_irqs_top, }; +static const struct irq_domain_ops gic_irq_domain_legacy_ops = { + .map = gic_irq_domain_map, + .translate = gic_irq_domain_translate, + .alloc = gic_irq_domain_alloc, + .free = irq_domain_free_irqs_top, +}; + +static struct irq_domain *gic_create_irq_domain(struct gic_chip_data *gic, + struct fwnode_handle *handle, + unsigned int gic_irqs) +{ + struct irq_domain *domain; + unsigned int irq_count; + unsigned int irq; + int irq_base; + + if (!IS_ENABLED(CONFIG_NI_ZYNQ_GIC_LEGACY_IRQDOMAIN) || + gic != &gic_data[0]) + return irq_domain_create_linear(handle, gic_irqs, + &gic_irq_domain_hierarchy_ops, + gic); + + if (gic_irqs <= NR_IRQS_LEGACY) + return NULL; + + irq_count = gic_irqs - NR_IRQS_LEGACY; + irq_base = irq_alloc_descs(NR_IRQS_LEGACY, NR_IRQS_LEGACY, + irq_count, numa_node_id()); + if (irq_base != NR_IRQS_LEGACY) { + if (irq_base >= 0) + irq_free_descs(irq_base, irq_count); + pr_err("GIC: cannot reserve fixed IRQ range %u-%u (%d)\n", + NR_IRQS_LEGACY, gic_irqs - 1, irq_base); + return NULL; + } + + domain = irq_domain_create_legacy(handle, irq_count, NR_IRQS_LEGACY, + NR_IRQS_LEGACY, + &gic_irq_domain_legacy_ops, gic); + if (!domain) { + irq_free_descs(irq_base, irq_count); + return NULL; + } + + for (irq = NR_IRQS_LEGACY; irq < gic_irqs; irq++) { + if (irq_find_mapping(domain, irq) != irq) { + unsigned int mapped_irq; + + pr_err("GIC: failed to establish fixed mapping for IRQ%u\n", + irq); + for (mapped_irq = NR_IRQS_LEGACY; + mapped_irq < gic_irqs; mapped_irq++) { + if (irq_find_mapping(domain, mapped_irq) == mapped_irq) + irq_dispose_mapping(mapped_irq); + else + irq_free_desc(mapped_irq); + } + irq_domain_remove(domain); + return NULL; + } + } + + pr_info("GIC: using fixed Linux IRQ mapping %u-%u\n", + NR_IRQS_LEGACY, gic_irqs - 1); + return domain; +} + static int gic_init_bases(struct gic_chip_data *gic, struct fwnode_handle *handle) { @@ -1207,9 +1274,7 @@ static int gic_init_bases(struct gic_chip_data *gic, gic_irqs = 1020; gic->gic_irqs = gic_irqs; - gic->domain = irq_domain_create_linear(handle, gic_irqs, - &gic_irq_domain_hierarchy_ops, - gic); + gic->domain = gic_create_irq_domain(gic, handle, gic_irqs); if (WARN_ON(!gic->domain)) { ret = -ENODEV; goto error; diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index bb7f9e73ca2de..426559be1cf3a 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -4487,7 +4487,6 @@ static const struct net_device_ops macb_netdev_ops = { static int macb_fpga_notifier(struct notifier_block *nb, unsigned long val, void *data) { struct macb *bp = container_of(nb, struct macb, fpga_notifier); - struct net_device *dev = bp->dev; switch (val) { case FPGA_PERIPHERAL_DOWN: @@ -4501,9 +4500,6 @@ static int macb_fpga_notifier(struct notifier_block *nb, unsigned long val, void if (netif_running(bp->dev)) { dev_deactivate(bp->dev); macb_close(bp->dev); - if (phy_interrupt_is_valid(dev->phydev)) - phy_free_interrupt(dev->phydev); - phy_stop_machine_nolink(dev->phydev); } bp->fpga_down = 1; @@ -4524,9 +4520,6 @@ static int macb_fpga_notifier(struct notifier_block *nb, unsigned long val, void /* If the interface has been opened. */ if (netif_running(bp->dev)) { - phy_start_machine(dev->phydev); - if (phy_interrupt_is_valid(dev->phydev)) - phy_request_interrupt(dev->phydev); macb_open(bp->dev); dev_activate(bp->dev); } diff --git a/drivers/tty/serial/8250/8250_ni.c b/drivers/tty/serial/8250/8250_ni.c index e9975dfb93d3c..988e0bff6dc33 100644 --- a/drivers/tty/serial/8250/8250_ni.c +++ b/drivers/tty/serial/8250/8250_ni.c @@ -18,12 +18,17 @@ #include #include #include +#include #include #include #include #include #include +#ifdef CONFIG_FPGA_PERIPHERAL +#include +#endif + #include "8250.h" /* Extra bits in UART_ACR */ @@ -77,8 +82,58 @@ struct ni16550_device_info { struct ni16550_data { int line; struct clk *clk; +#ifdef CONFIG_FPGA_PERIPHERAL + struct notifier_block fpga_notifier; + bool fpga_notifier_registered; + bool fpga_suspended; +#endif }; +#ifdef CONFIG_FPGA_PERIPHERAL +static u32 ni16550_disabled_serial_in(struct uart_port *port, + unsigned int offset) +{ + return 0; +} + +static void ni16550_disabled_serial_out(struct uart_port *port, + unsigned int offset, u32 value) +{ +} + +static int ni16550_fpga_notify(struct notifier_block *nb, + unsigned long event, void *unused) +{ + struct ni16550_data *data = container_of(nb, struct ni16550_data, + fpga_notifier); + struct uart_8250_port *uart = serial8250_get_port(data->line); + + switch (event) { + case FPGA_PERIPHERAL_DOWN: + if (!data->fpga_suspended) { + serial8250_suspend_port(data->line); + uart->port.serial_in = ni16550_disabled_serial_in; + uart->port.serial_out = ni16550_disabled_serial_out; + data->fpga_suspended = true; + } + break; + case FPGA_PERIPHERAL_UP: + if (data->fpga_suspended) { + serial8250_set_defaults(uart); + serial8250_resume_port(data->line); + data->fpga_suspended = false; + } + break; + case FPGA_PERIPHERAL_FAILED: + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} +#endif + static int ni16550_enable_transceivers(struct uart_port *port) { u8 pcr; @@ -405,6 +460,20 @@ static int ni16550_probe(struct platform_device *pdev) return ret; data->line = ret; +#ifdef CONFIG_FPGA_PERIPHERAL + if (dev_of_node(dev)) { + data->fpga_notifier.notifier_call = ni16550_fpga_notify; + ret = blocking_notifier_chain_register(&fpgaperipheral_notifier_list, + &data->fpga_notifier); + if (ret) { + serial8250_unregister_port(data->line); + return dev_err_probe(dev, ret, + "failed to register FPGA notifier\n"); + } + data->fpga_notifier_registered = true; + } +#endif + platform_set_drvdata(pdev, data); return 0; } @@ -413,6 +482,11 @@ static void ni16550_remove(struct platform_device *pdev) { struct ni16550_data *data = platform_get_drvdata(pdev); +#ifdef CONFIG_FPGA_PERIPHERAL + if (data->fpga_notifier_registered) + blocking_notifier_chain_unregister(&fpgaperipheral_notifier_list, + &data->fpga_notifier); +#endif serial8250_unregister_port(data->line); }