From: Isaac Hermida Date: Tue, 1 Apr 2025 11:13:34 +0200 Subject: [PATCH] add RT support based on latest linux_6.6.36 Add a unique patch with the RT functionality between NXP lf-6.6.36-2.1.0 and realtime/linux_6.6.36 in https://github.com/nxp-real-time-edge-sw/real-time-edge-linux.git https://onedigi.atlassian.net/browse/DEL-9324 Signed-off-by: Isaac Hermida --- .../mailbox/generic-software-mbox.yaml | 65 + .../devicetree/bindings/net/fsl,fec.yaml | 24 + .../devicetree/bindings/tty/rpmsg_tty.yaml | 67 + Documentation/printk-ringbuffer.txt | 377 ++ arch/arm/Kconfig | 6 +- arch/arm/boot/dts/nxp/imx/Makefile | 9 + .../dts/nxp/imx/imx6ul-14x14-evk-ecat.dts | 11 + .../boot/dts/nxp/imx/imx6ul-14x14-evk-igh.dts | 11 + .../dts/nxp/imx/imx6ull-14x14-evk-avb-mcr.dts | 59 + .../dts/nxp/imx/imx6ull-14x14-evk-avb.dts | 29 + .../dts/nxp/imx/imx6ull-14x14-evk-ecat.dts | 11 + .../dts/nxp/imx/imx6ull-14x14-evk-igh.dts | 11 + .../dts/nxp/imx/imx6ull-14x14-evk-lpuart.dts | 25 + .../dts/nxp/imx/imx6ull-14x14-evk-reve.dtso | 11 + arch/arm/boot/dts/nxp/ls/Makefile | 3 +- arch/arm/boot/dts/nxp/ls/ls1021a-iot-bm.dts | 259 ++ arch/arm/boot/dts/nxp/ls/ls1021a.dtsi | 4 +- arch/arm/configs/imx6ullevk-plc.config | 3333 +++++++++++++++++ arch/arm/configs/imx_avb.config | 8 + arch/arm/configs/imx_up.config | 2 + arch/arm/configs/imx_v6_v7_defconfig | 3 +- arch/arm/configs/imx_v7_defconfig | 9 +- arch/arm/configs/lsdk.config | 20 + arch/arm/configs/multi_v7_defconfig | 2 + arch/arm/kernel/smp.c | 22 + arch/arm/mach-imx/Kconfig | 10 + arch/arm/mm/fault.c | 6 + arch/arm/vfp/vfpmodule.c | 74 +- arch/arm64/Kconfig | 1 + arch/arm64/Kconfig.platforms | 30 + arch/arm64/boot/dts/freescale/Makefile | 104 +- .../boot/dts/freescale/fii-ls1028a-tsn.dts | 325 ++ .../fsl-ls1028a-rdb-dsa-swp5-eno3.dts | 37 + ...sl-ls1028a-rdb-jailhouse-without-enetc.dts | 98 + .../freescale/fsl-ls1028a-rdb-jailhouse.dts | 59 + .../dts/freescale/fsl-ls1028a-rdb-sdk-bm.dts | 15 + .../boot/dts/freescale/fsl-ls1028a-rdb.dts | 25 + .../arm64/boot/dts/freescale/fsl-ls1028a.dtsi | 2 +- .../dts/freescale/fsl-ls1043a-rdb-sdk-bm.dts | 44 + ...sl-ls1043a-rdb-sdk-jailhouse-with-dpaa.dts | 263 ++ .../fsl-ls1043a-rdb-sdk-jailhouse.dts | 267 ++ .../arm64/boot/dts/freescale/fsl-ls1043a.dtsi | 2 +- .../dts/freescale/fsl-ls1046a-rdb-sdk-bm.dts | 47 + ...sl-ls1046a-rdb-sdk-jailhouse-with-dpaa.dts | 274 ++ .../fsl-ls1046a-rdb-sdk-jailhouse.dts | 278 ++ .../arm64/boot/dts/freescale/fsl-ls1046a.dtsi | 2 +- .../boot/dts/freescale/imx8-ss-audio.dtsi | 20 + .../dts/freescale/imx8dxl-evk-enet0-avb.dts | 9 + .../dts/freescale/imx8dxl-evk-enet0-avb.dtsi | 54 + .../freescale/imx8dxl-evk-enet0-sja1105.dts | 168 + .../imx8dxl-evk-enet0-tja1100-avb.dts | 9 + arch/arm64/boot/dts/freescale/imx8dxl-evk.dts | 58 + .../boot/dts/freescale/imx8dxl-ss-adma.dtsi | 1 + .../dts/freescale/imx8m-generic-mbox-1.dtsi | 27 + .../dts/freescale/imx8m-generic-mbox.dtsi | 27 + .../dts/freescale/imx8m-rpmsg-ca53-1.dtsi | 37 + .../boot/dts/freescale/imx8m-rpmsg-ca53.dtsi | 37 + .../boot/dts/freescale/imx8m-rproc-ca53.dtsi | 34 + .../boot/dts/freescale/imx8mm-evk-avb.dts | 37 + .../dts/freescale/imx8mm-evk-baremetal.dts | 39 + .../freescale/imx8mm-evk-ecat-userspace.dts | 15 + .../boot/dts/freescale/imx8mm-evk-ecat.dts | 10 + .../dts/freescale/imx8mm-evk-harpoon-avb.dts | 11 + .../imx8mm-evk-harpoon-industrial.dts | 11 + .../imx8mm-evk-harpoon-virtio-net.dts | 11 + .../boot/dts/freescale/imx8mm-evk-harpoon.dts | 31 + .../boot/dts/freescale/imx8mm-evk-igh.dts | 10 + .../freescale/imx8mm-evk-multicore-rpmsg.dts | 80 + .../freescale/imx8mm-evk-multicore-rtos.dts | 78 + .../imx8mm-evk-qca-wifi-harpoon-avb.dts | 7 + ...imx8mm-evk-qca-wifi-harpoon-industrial.dts | 7 + ...imx8mm-evk-qca-wifi-harpoon-virtio-net.dts | 7 + .../freescale/imx8mm-evk-qca-wifi-harpoon.dts | 7 + .../dts/freescale/imx8mm-evk-qca-wifi.dts | 123 +- .../dts/freescale/imx8mm-evk-qca-wifi.dtsi | 124 + .../dts/freescale/imx8mm-evk-rpmsg-8m-buf.dts | 228 ++ .../dts/freescale/imx8mm-evk-rpmsg-ca53.dts | 40 + .../boot/dts/freescale/imx8mm-evk-rpmsg.dts | 96 +- .../freescale/imx8mm-evk-virtio-net-ca53.dts | 83 + .../freescale/imx8mm-evk-virtio-net-cm4.dts | 74 + .../freescale/imx8mm-evk-virtio-perf-ca53.dts | 79 + .../freescale/imx8mm-evk-virtio-perf-cm4.dts | 63 + .../dts/freescale/imx8mm-virtio-ca53.dtsi | 46 + arch/arm64/boot/dts/freescale/imx8mm.dtsi | 11 + .../dts/freescale/imx8mn-evk-harpoon-avb.dts | 11 + .../imx8mn-evk-harpoon-industrial.dts | 11 + .../boot/dts/freescale/imx8mn-evk-harpoon.dts | 31 + .../boot/dts/freescale/imx8mp-evk-avb.dts | 42 + .../dts/freescale/imx8mp-evk-baremetal.dts | 37 + .../dts/freescale/imx8mp-evk-dsa-enetc.dts | 17 + .../dts/freescale/imx8mp-evk-dsa-fec-swp0.dts | 92 + .../dts/freescale/imx8mp-evk-dsa-fec-swp3.dts | 92 + .../boot/dts/freescale/imx8mp-evk-dsa.dts | 92 + .../freescale/imx8mp-evk-ecat-userspace.dts | 33 + .../boot/dts/freescale/imx8mp-evk-ecat.dts | 10 + .../dts/freescale/imx8mp-evk-harpoon-avb.dts | 11 + .../imx8mp-evk-harpoon-industrial.dts | 15 + .../imx8mp-evk-harpoon-virtio-net.dts | 40 + .../boot/dts/freescale/imx8mp-evk-harpoon.dts | 35 + .../boot/dts/freescale/imx8mp-evk-igh.dts | 10 + .../freescale/imx8mp-evk-multicore-lwip.dts | 21 + .../freescale/imx8mp-evk-multicore-rpmsg.dts | 179 + .../freescale/imx8mp-evk-multicore-rtos.dts | 80 + .../dts/freescale/imx8mp-evk-rpmsg-ca53.dts | 45 + .../freescale/imx8mp-evk-virtio-net-ca53.dts | 82 + .../freescale/imx8mp-evk-virtio-net-cm7.dts | 85 + .../freescale/imx8mp-evk-virtio-perf-ca53.dts | 69 + .../freescale/imx8mp-evk-virtio-perf-cm7.dts | 69 + .../dts/freescale/imx8mp-generic-mbox-1.dtsi | 27 + .../dts/freescale/imx8mp-generic-mbox.dtsi | 27 + .../dts/freescale/imx8mp-rpmsg-ca53-1.dtsi | 37 + .../boot/dts/freescale/imx8mp-rpmsg-ca53.dtsi | 37 + .../dts/freescale/imx93-11x11-evk-avb.dts | 9 + .../freescale/imx93-11x11-evk-baremetal.dts | 31 + .../dts/freescale/imx93-11x11-evk-dpdk.dts | 16 + .../freescale/imx93-11x11-evk-dsa-enetc.dts | 17 + .../imx93-11x11-evk-dsa-fec-swp0.dts | 108 + .../imx93-11x11-evk-dsa-fec-swp3.dts | 108 + .../dts/freescale/imx93-11x11-evk-dsa.dts | 108 + .../imx93-11x11-evk-ecat-userspace.dts | 16 + .../dts/freescale/imx93-11x11-evk-ecat.dts | 11 + .../freescale/imx93-11x11-evk-harpoon-avb.dts | 10 + .../imx93-11x11-evk-harpoon-industrial.dts | 19 + .../imx93-11x11-evk-harpoon-virtio-net.dts | 46 + .../dts/freescale/imx93-11x11-evk-harpoon.dts | 35 + .../dts/freescale/imx93-11x11-evk-igh.dts | 11 + .../imx93-11x11-evk-multicore-rpmsg.dts | 47 + .../imx93-11x11-evk-multicore-rtos.dts | 39 + .../imx93-11x11-evk-uart-sharing-cm33.dts | 96 + .../imx93-11x11-evk-virtio-net-ca55.dts | 56 + .../imx93-11x11-evk-virtio-net-cm33.dts | 58 + .../boot/dts/freescale/imx93-11x11-evk.dts | 60 +- .../freescale/imx93-14x14-evk-aud-hat-avb.dts | 9 + .../dts/freescale/imx93-14x14-evk-aud-hat.dts | 2 +- .../imx93-14x14-evk-imxai2eth-ath.dts | 126 + .../dts/freescale/imx93-14x14-evk-mqs.dts | 70 +- .../dts/freescale/imx93-14x14-evk-mqs.dtsi | 71 + .../imx93-14x14-evk-multicore-rpmsg.dts | 47 + .../imx93-14x14-evk-multicore-rtos.dts | 39 + .../freescale/imx93-14x14-evk-sja1105-avb.dts | 8 + .../dts/freescale/imx93-14x14-evk-sja1105.dts | 3 +- .../dts/freescale/imx93-14x14-evk-tja1103.dts | 63 +- .../imx93-14x14-evk-uart-sharing-cm33.dts | 96 + .../imx93-14x14-evk-virtio-net-ca55.dts | 56 + .../imx93-14x14-evk-virtio-net-cm33.dts | 58 + .../dts/freescale/imx93-9x9-qsb-baremetal.dts | 31 + .../dts/freescale/imx93-9x9-qsb-inmate.dts | 151 + .../boot/dts/freescale/imx93-9x9-qsb-root.dts | 54 + .../imx93-9x9-qsb-uart-sharing-cm33.dts | 96 + .../boot/dts/freescale/imx93-9x9-qsb.dts | 58 + .../boot/dts/freescale/imx93-evk-avb.dtsi | 37 + .../dts/freescale/imx93-generic-mbox.dtsi | 27 + .../boot/dts/freescale/imx93-rpmsg-ca55.dtsi | 37 + .../boot/dts/freescale/imx93-rproc-ca55.dtsi | 14 + .../imx95-15x15-evk-harpoon-industrial.dts | 25 + .../dts/freescale/imx95-15x15-evk-harpoon.dts | 19 + .../imx95-15x15-evk-multicore-rtos.dts | 29 + .../imx95-19x19-evk-harpoon-industrial.dts | 29 + .../dts/freescale/imx95-19x19-evk-harpoon.dts | 19 + .../imx95-19x19-evk-multicore-rtos.dts | 29 + .../dts/freescale/imx95-generic-mbox.dtsi | 27 + .../boot/dts/freescale/imx95-rpmsg-ca55.dtsi | 37 + .../boot/dts/freescale/imx95-rproc-ca55.dtsi | 48 + arch/arm64/configs/imx_avb.config | 8 + arch/arm64/configs/imx_v8_defconfig | 30 +- arch/arm64/configs/linux-dpaa-ethercat.config | 1 + arch/arm64/configs/linux-rpmsg-8m-buf.config | 1 + arch/arm64/configs/lsdk.config | 25 +- arch/arm64/kernel/smp.c | 25 + arch/powerpc/Kconfig | 2 + arch/powerpc/include/asm/stackprotector.h | 7 +- arch/powerpc/kernel/traps.c | 7 +- arch/powerpc/kvm/Kconfig | 1 + arch/powerpc/platforms/pseries/Kconfig | 1 + arch/powerpc/platforms/pseries/iommu.c | 31 +- arch/riscv/Kconfig | 2 + arch/riscv/include/asm/cpufeature.h | 2 - arch/riscv/include/asm/thread_info.h | 2 + arch/riscv/kernel/cpufeature.c | 90 +- arch/riscv/kernel/smpboot.c | 1 - arch/x86/Kconfig | 2 + arch/x86/include/asm/thread_info.h | 6 +- drivers/acpi/processor_idle.c | 2 +- drivers/base/Kconfig | 2 +- drivers/block/zram/zram_drv.c | 37 + drivers/block/zram/zram_drv.h | 3 + drivers/clk/imx/Makefile | 1 + drivers/clk/imx/clk-frac-pll.c | 130 + drivers/clk/imx/clk-fracn-gppll.c | 209 +- drivers/clk/imx/clk-imx8-acm.c | 46 + drivers/clk/imx/clk-imx8mq.c | 2 +- drivers/clk/imx/clk-pll.c | 83 + drivers/clk/imx/clk-pll.h | 33 + drivers/clk/imx/clk-pll14xx.c | 120 + drivers/clk/imx/clk-pllv3.c | 109 + drivers/clk/imx/clk-scu.c | 86 + .../gpu/drm/amd/display/amdgpu_dm/dc_fpu.c | 53 +- .../drm/amd/display/dc/dcn20/dcn20_resource.c | 10 +- .../drm/amd/display/dc/dcn21/dcn21_resource.c | 10 +- .../drm/amd/display/dc/dml/dcn20/dcn20_fpu.c | 23 +- .../drm/amd/display/dc/dml/dcn20/dcn20_fpu.h | 10 +- drivers/gpu/drm/i915/Kconfig | 1 - drivers/gpu/drm/i915/display/intel_crtc.c | 15 +- drivers/gpu/drm/i915/display/intel_vblank.c | 6 +- drivers/gpu/drm/i915/gt/intel_breadcrumbs.c | 7 +- .../drm/i915/gt/intel_execlists_submission.c | 17 +- drivers/gpu/drm/i915/gt/intel_reset.c | 12 +- drivers/gpu/drm/i915/gt/uc/intel_guc.h | 2 +- drivers/gpu/drm/i915/i915_request.c | 2 - drivers/gpu/drm/i915/i915_trace.h | 6 +- drivers/gpu/drm/i915/i915_utils.h | 2 +- drivers/irqchip/Kconfig | 4 + drivers/irqchip/Makefile | 1 + drivers/irqchip/ipi-baremetal.c | 454 +++ drivers/irqchip/irq-gic-v3.c | 10 +- drivers/irqchip/irq-gic.c | 4 +- drivers/mailbox/Kconfig | 9 + drivers/mailbox/Makefile | 2 + drivers/mailbox/generic-software-mailbox.c | 322 ++ drivers/mailbox/mailbox.c | 2 +- drivers/mxc/ipu3/ipu_common.c | 10 +- drivers/mxc/ipu3/ipu_device.c | 2 +- drivers/net/dsa/Kconfig | 2 + drivers/net/dsa/Makefile | 1 + drivers/net/dsa/netc/Kconfig | 18 + drivers/net/dsa/netc/Makefile | 14 + drivers/net/dsa/netc/netc.h | 131 + drivers/net/dsa/netc/netc_config.c | 696 ++++ drivers/net/dsa/netc/netc_config.h | 557 +++ drivers/net/dsa/netc/netc_devlink.c | 107 + drivers/net/dsa/netc/netc_ethtool.c | 344 ++ drivers/net/dsa/netc/netc_main.c | 1439 +++++++ drivers/net/dsa/netc/netc_ptp.c | 375 ++ drivers/net/dsa/netc/netc_ptp.h | 77 + drivers/net/dsa/netc/netc_spi.c | 147 + drivers/net/dsa/ocelot/felix.c | 183 +- drivers/net/dsa/ocelot/felix.h | 5 + drivers/net/dsa/ocelot/felix_tsn.c | 6 +- drivers/net/dsa/ocelot/felix_vsc9959.c | 99 +- drivers/net/dsa/sja1105/sja1105.h | 5 + drivers/net/dsa/sja1105/sja1105_clocking.c | 21 +- drivers/net/dsa/sja1105/sja1105_main.c | 68 +- drivers/net/dsa/sja1105/sja1105_tas.c | 4 + drivers/net/ethernet/freescale/Kconfig | 20 + drivers/net/ethernet/freescale/Makefile | 2 + drivers/net/ethernet/freescale/enetc/enetc.c | 6 +- drivers/net/ethernet/freescale/enetc/enetc.h | 8 + .../ethernet/freescale/enetc/enetc_ethtool.c | 153 + .../net/ethernet/freescale/enetc/enetc_pf.c | 1 + .../net/ethernet/freescale/enetc/enetc_ptp.c | 15 +- .../net/ethernet/freescale/enetc/enetc_qos.c | 5 +- .../net/ethernet/freescale/enetc/enetc_tsn.c | 179 +- drivers/net/ethernet/freescale/fec.h | 100 +- drivers/net/ethernet/freescale/fec_ecat.c | 3063 +++++++++++++++ drivers/net/ethernet/freescale/fec_ecat.h | 718 ++++ drivers/net/ethernet/freescale/fec_main.c | 1452 ++++++- drivers/net/ethernet/freescale/fec_ptp.c | 445 ++- drivers/net/ethernet/freescale/fec_uio.c | 4 +- .../net/ethernet/freescale/gianfar_ethtool.c | 29 + .../net/ethernet/freescale/sdk_dpaa/Kconfig | 11 +- .../net/ethernet/freescale/sdk_dpaa/Makefile | 3 + .../ethernet/freescale/sdk_dpaa/dpaa_eth.h | 13 +- .../freescale/sdk_dpaa/dpaa_eth_common.c | 56 + .../ethernet/freescale/sdk_dpaa/dpaa_eth_sg.c | 28 +- .../freescale/sdk_dpaa/dpaa_ethercat.c | 1216 ++++++ drivers/net/ethernet/mscc/ocelot.c | 45 +- drivers/net/ethernet/mscc/ocelot_flower.c | 30 +- drivers/net/ethernet/mscc/ocelot_ptp.c | 125 +- drivers/net/ethernet/mscc/ocelot_vcap.c | 173 +- .../net/ethernet/stmicro/stmmac/dwmac4_core.c | 2 + drivers/net/ethernet/stmicro/stmmac/dwmac5.c | 19 +- drivers/net/ethernet/stmicro/stmmac/dwmac5.h | 6 +- .../ethernet/stmicro/stmmac/dwxgmac2_core.c | 3 +- drivers/net/ethernet/stmicro/stmmac/hwif.h | 11 +- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 1 + .../ethernet/stmicro/stmmac/stmmac_ethtool.c | 106 + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 36 +- .../net/ethernet/stmicro/stmmac/stmmac_tc.c | 38 +- drivers/net/phy/phy.c | 5 +- drivers/nfc/Kconfig | 1 + drivers/nfc/Makefile | 1 + drivers/nfc/pn5xx/Kconfig | 12 + drivers/nfc/pn5xx/Makefile | 6 + drivers/nfc/pn5xx/README.md | 2 + drivers/nfc/pn5xx/pn5xx_i2c.c | 669 ++++ drivers/nfc/pn5xx/pn5xx_i2c.h | 38 + drivers/nfc/pn5xx/sample_devicetree.txt | 17 + drivers/pmdomain/imx/scu-pd.c | 1 + drivers/ptp/ptp_chardev.c | 23 + drivers/ptp/ptp_clock.c | 19 + drivers/ptp/ptp_private.h | 18 + drivers/ptp/ptp_sysfs.c | 16 +- drivers/ptp/ptp_vclock.c | 204 +- drivers/remoteproc/imx_rproc.c | 196 +- drivers/remoteproc/imx_rproc.h | 2 + drivers/remoteproc/remoteproc_core.c | 8 +- drivers/rpmsg/Kconfig | 16 +- drivers/rpmsg/Makefile | 1 + drivers/rpmsg/imx_rpmsg.c | 57 +- drivers/rpmsg/imx_rpmsg_tty.c | 18 +- drivers/rpmsg/rpmsg_perf.c | 545 +++ drivers/rpmsg/virtio_rpmsg_bus.c | 5 + drivers/rtc/Kconfig | 1 + drivers/staging/fsl_qbman/qman_driver.c | 49 + drivers/staging/fsl_qbman/qman_high.c | 39 + drivers/staging/fsl_qbman/qman_private.h | 7 + drivers/tty/rpmsg_tty.c | 191 +- drivers/tty/serial/21285.c | 8 +- drivers/tty/serial/8250/8250_aspeed_vuart.c | 6 +- drivers/tty/serial/8250/8250_bcm7271.c | 28 +- drivers/tty/serial/8250/8250_core.c | 54 +- drivers/tty/serial/8250/8250_dma.c | 8 +- drivers/tty/serial/8250/8250_dw.c | 8 +- drivers/tty/serial/8250/8250_exar.c | 4 +- drivers/tty/serial/8250/8250_fsl.c | 6 +- drivers/tty/serial/8250/8250_mtk.c | 8 +- drivers/tty/serial/8250/8250_omap.c | 52 +- drivers/tty/serial/8250/8250_pci1xxxx.c | 8 +- drivers/tty/serial/8250/8250_port.c | 259 +- drivers/tty/serial/altera_jtaguart.c | 28 +- drivers/tty/serial/altera_uart.c | 20 +- drivers/tty/serial/amba-pl010.c | 20 +- drivers/tty/serial/amba-pl011.c | 78 +- drivers/tty/serial/apbuart.c | 8 +- drivers/tty/serial/ar933x_uart.c | 26 +- drivers/tty/serial/arc_uart.c | 16 +- drivers/tty/serial/atmel_serial.c | 24 +- drivers/tty/serial/bcm63xx_uart.c | 22 +- drivers/tty/serial/cpm_uart.c | 8 +- drivers/tty/serial/digicolor-usart.c | 18 +- drivers/tty/serial/dz.c | 32 +- drivers/tty/serial/fsl_linflexuart.c | 26 +- drivers/tty/serial/fsl_lpuart.c | 88 +- drivers/tty/serial/icom.c | 26 +- drivers/tty/serial/imx.c | 84 +- drivers/tty/serial/ip22zilog.c | 36 +- drivers/tty/serial/jsm/jsm_neo.c | 4 +- drivers/tty/serial/jsm/jsm_tty.c | 16 +- drivers/tty/serial/liteuart.c | 20 +- drivers/tty/serial/lpc32xx_hs.c | 26 +- drivers/tty/serial/ma35d1_serial.c | 22 +- drivers/tty/serial/mcf.c | 20 +- drivers/tty/serial/men_z135_uart.c | 8 +- drivers/tty/serial/meson_uart.c | 30 +- drivers/tty/serial/milbeaut_usio.c | 16 +- drivers/tty/serial/mpc52xx_uart.c | 12 +- drivers/tty/serial/mps2-uart.c | 16 +- drivers/tty/serial/msm_serial.c | 38 +- drivers/tty/serial/mvebu-uart.c | 18 +- drivers/tty/serial/omap-serial.c | 44 +- drivers/tty/serial/owl-uart.c | 26 +- drivers/tty/serial/pch_uart.c | 10 +- drivers/tty/serial/pic32_uart.c | 20 +- drivers/tty/serial/pmac_zilog.c | 52 +- drivers/tty/serial/pxa.c | 30 +- drivers/tty/serial/qcom_geni_serial.c | 8 +- drivers/tty/serial/rda-uart.c | 34 +- drivers/tty/serial/rp2.c | 20 +- drivers/tty/serial/sa1100.c | 20 +- drivers/tty/serial/samsung_tty.c | 50 +- drivers/tty/serial/sb1250-duart.c | 12 +- drivers/tty/serial/sc16is7xx.c | 5 + drivers/tty/serial/serial-tegra.c | 32 +- drivers/tty/serial/serial_core.c | 92 +- drivers/tty/serial/serial_mctrl_gpio.c | 4 +- drivers/tty/serial/serial_port.c | 4 +- drivers/tty/serial/serial_txx9.c | 26 +- drivers/tty/serial/sh-sci.c | 68 +- drivers/tty/serial/sifive.c | 16 +- drivers/tty/serial/sprd_serial.c | 30 +- drivers/tty/serial/st-asc.c | 18 +- drivers/tty/serial/stm32-usart.c | 38 +- drivers/tty/serial/sunhv.c | 28 +- drivers/tty/serial/sunplus-uart.c | 26 +- drivers/tty/serial/sunsab.c | 34 +- drivers/tty/serial/sunsu.c | 46 +- drivers/tty/serial/sunzilog.c | 42 +- drivers/tty/serial/timbuart.c | 8 +- drivers/tty/serial/uartlite.c | 18 +- drivers/tty/serial/ucc_uart.c | 4 +- drivers/tty/serial/vt8500_serial.c | 8 +- drivers/tty/serial/xilinx_uartps.c | 56 +- drivers/tty/tty_io.c | 11 +- drivers/virtio/Kconfig | 8 + drivers/virtio/Makefile | 1 + drivers/virtio/virtio_mmio.c | 306 +- drivers/virtio/virtio_ring.c | 2 - drivers/virtio/virtio_trans.c | 793 ++++ fs/proc/consoles.c | 14 +- include/dt-bindings/clock/imx8-clock.h | 10 + include/dt-bindings/rpmsg/imx_srtm.h | 20 + include/linux/bottom_half.h | 2 + include/linux/clk/imx-pll.h | 26 + include/linux/console.h | 150 + include/linux/dsa/netc.h | 69 + include/linux/dsa/sja1105.h | 2 + include/linux/entry-common.h | 2 +- include/linux/entry-kvm.h | 2 +- include/linux/ethtool.h | 7 + include/linux/fec.h | 120 + include/linux/fsl_qman.h | 8 + include/linux/interrupt.h | 29 + include/linux/ipi_baremetal.h | 27 + include/linux/net.h | 2 + include/linux/netdevice.h | 48 + include/linux/printk.h | 30 +- include/linux/rpmsg/imx_srtm.h | 65 + include/linux/sched.h | 16 +- include/linux/sched/idle.h | 8 +- include/linux/sched/rt.h | 4 + include/linux/serial_8250.h | 6 + include/linux/serial_core.h | 43 +- include/linux/thread_info.h | 24 + include/linux/trace_events.h | 8 +- include/net/dsa.h | 18 + include/net/flow_offload.h | 9 + include/net/switchdev.h | 1 + include/net/tc_act/tc_frer.h | 52 + include/soc/mscc/ocelot.h | 12 +- include/soc/mscc/ocelot_ptp.h | 3 - include/soc/mscc/ocelot_vcap.h | 1 + include/uapi/linux/ethtool.h | 30 + include/uapi/linux/ethtool_netlink.h | 21 + include/uapi/linux/if_ether.h | 1 + include/uapi/linux/pkt_cls.h | 1 + include/uapi/linux/ptp_clock.h | 12 + include/uapi/linux/tc_act/tc_frer.h | 50 + include/uapi/linux/virtio_ids.h | 1 + include/uapi/linux/virtio_mmio.h | 7 +- include/uapi/linux/virtio_trans.h | 60 + kernel/Kconfig.preempt | 17 +- kernel/dma/coherent.c | 15 +- kernel/entry/common.c | 4 +- kernel/entry/kvm.c | 2 +- kernel/futex/pi.c | 87 +- kernel/futex/requeue.c | 6 +- kernel/ksysfs.c | 12 + kernel/locking/lockdep.c | 5 + kernel/locking/rtmutex.c | 37 +- kernel/locking/rwbase_rt.c | 19 + kernel/locking/rwsem.c | 8 +- kernel/locking/spinlock_rt.c | 6 + kernel/locking/ww_rt_mutex.c | 2 +- kernel/panic.c | 9 + kernel/printk/Makefile | 2 +- kernel/printk/internal.h | 121 + kernel/printk/nbcon.c | 1665 ++++++++ kernel/printk/printk.c | 750 +++- kernel/printk/printk_ringbuffer.c | 360 +- kernel/printk/printk_ringbuffer.h | 54 +- kernel/printk/printk_safe.c | 12 + kernel/rcu/rcutorture.c | 6 + kernel/rcu/tree_stall.h | 5 + kernel/sched/core.c | 127 +- kernel/sched/debug.c | 19 + kernel/sched/fair.c | 46 +- kernel/sched/features.h | 2 + kernel/sched/idle.c | 3 +- kernel/sched/rt.c | 5 +- kernel/sched/sched.h | 1 + kernel/signal.c | 30 +- kernel/softirq.c | 95 +- kernel/time/hrtimer.c | 4 +- kernel/time/tick-sched.c | 2 +- kernel/time/timer.c | 11 +- kernel/trace/trace.c | 2 + kernel/trace/trace_output.c | 16 +- localversion-rt | 1 + net/bridge/br_switchdev.c | 24 + net/core/dev.c | 62 +- net/core/skbuff.c | 7 +- net/dsa/Kconfig | 6 + net/dsa/Makefile | 1 + net/dsa/slave.c | 49 +- net/dsa/tag_netc.c | 489 +++ net/dsa/tag_sja1105.c | 3 +- net/ethtool/Makefile | 2 +- net/ethtool/ioctl.c | 55 + net/ethtool/netlink.c | 17 + net/ethtool/netlink.h | 3 + net/ethtool/preempt.c | 191 + net/packet/af_packet.c | 11 +- net/sched/Kconfig | 13 + net/sched/Makefile | 1 + net/sched/act_frer.c | 731 ++++ net/sched/sch_cbs.c | 5 + net/socket.c | 27 + net/tsn/genl_tsn.c | 12 +- tools/virtio/vt_test.sh | 79 + 489 files changed, 35912 insertions(+), 2267 deletions(-) create mode 100644 Documentation/devicetree/bindings/mailbox/generic-software-mbox.yaml create mode 100644 Documentation/devicetree/bindings/tty/rpmsg_tty.yaml create mode 100644 Documentation/printk-ringbuffer.txt create mode 100644 arch/arm/boot/dts/nxp/imx/imx6ul-14x14-evk-ecat.dts create mode 100644 arch/arm/boot/dts/nxp/imx/imx6ul-14x14-evk-igh.dts create mode 100644 arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-avb-mcr.dts create mode 100644 arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-avb.dts create mode 100644 arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-ecat.dts create mode 100644 arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-igh.dts create mode 100644 arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-lpuart.dts create mode 100644 arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-reve.dtso create mode 100644 arch/arm/boot/dts/nxp/ls/ls1021a-iot-bm.dts create mode 100644 arch/arm/configs/imx6ullevk-plc.config create mode 100644 arch/arm/configs/imx_avb.config create mode 100644 arch/arm/configs/imx_up.config create mode 100644 arch/arm64/boot/dts/freescale/fii-ls1028a-tsn.dts create mode 100644 arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-dsa-swp5-eno3.dts create mode 100644 arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-jailhouse-without-enetc.dts create mode 100644 arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-jailhouse.dts create mode 100644 arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-sdk-bm.dts create mode 100644 arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-bm.dts create mode 100755 arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-jailhouse-with-dpaa.dts create mode 100644 arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-jailhouse.dts create mode 100644 arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-bm.dts create mode 100755 arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-jailhouse-with-dpaa.dts create mode 100755 arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-jailhouse.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-avb.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-avb.dtsi create mode 100644 arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-sja1105.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-tja1100-avb.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8m-generic-mbox-1.dtsi create mode 100644 arch/arm64/boot/dts/freescale/imx8m-generic-mbox.dtsi create mode 100644 arch/arm64/boot/dts/freescale/imx8m-rpmsg-ca53-1.dtsi create mode 100644 arch/arm64/boot/dts/freescale/imx8m-rpmsg-ca53.dtsi create mode 100644 arch/arm64/boot/dts/freescale/imx8m-rproc-ca53.dtsi create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-avb.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-baremetal.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-ecat-userspace.dts create mode 100755 arch/arm64/boot/dts/freescale/imx8mm-evk-ecat.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-avb.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-industrial.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-virtio-net.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon.dts create mode 100755 arch/arm64/boot/dts/freescale/imx8mm-evk-igh.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-multicore-rpmsg.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-multicore-rtos.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-avb.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-industrial.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-virtio-net.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi.dtsi create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg-8m-buf.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg-ca53.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-net-ca53.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-net-cm4.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-perf-ca53.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-perf-cm4.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mm-virtio-ca53.dtsi create mode 100644 arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon-avb.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon-industrial.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-avb.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-baremetal.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-dsa-enetc.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-dsa-fec-swp0.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-dsa-fec-swp3.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-dsa.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-ecat-userspace.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-ecat.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-avb.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-industrial.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-virtio-net.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-igh.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-lwip.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-rpmsg.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-rtos.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-rpmsg-ca53.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-net-ca53.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-net-cm7.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-perf-ca53.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-perf-cm7.dts create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-generic-mbox-1.dtsi create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-generic-mbox.dtsi create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-rpmsg-ca53-1.dtsi create mode 100644 arch/arm64/boot/dts/freescale/imx8mp-rpmsg-ca53.dtsi create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-avb.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-baremetal.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-dpdk.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-enetc.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-fec-swp0.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-fec-swp3.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-ecat-userspace.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-ecat.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-avb.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-industrial.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-virtio-net.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-igh.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rpmsg.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rtos.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-uart-sharing-cm33.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-virtio-net-ca55.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-11x11-evk-virtio-net-cm33.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-14x14-evk-aud-hat-avb.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-14x14-evk-imxai2eth-ath.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-14x14-evk-mqs.dtsi create mode 100644 arch/arm64/boot/dts/freescale/imx93-14x14-evk-multicore-rpmsg.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-14x14-evk-multicore-rtos.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-14x14-evk-sja1105-avb.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-14x14-evk-uart-sharing-cm33.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-14x14-evk-virtio-net-ca55.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-14x14-evk-virtio-net-cm33.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-9x9-qsb-baremetal.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-9x9-qsb-inmate.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-9x9-qsb-root.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-9x9-qsb-uart-sharing-cm33.dts create mode 100644 arch/arm64/boot/dts/freescale/imx93-evk-avb.dtsi create mode 100644 arch/arm64/boot/dts/freescale/imx93-generic-mbox.dtsi create mode 100644 arch/arm64/boot/dts/freescale/imx93-rpmsg-ca55.dtsi create mode 100644 arch/arm64/boot/dts/freescale/imx93-rproc-ca55.dtsi create mode 100644 arch/arm64/boot/dts/freescale/imx95-15x15-evk-harpoon-industrial.dts create mode 100644 arch/arm64/boot/dts/freescale/imx95-15x15-evk-harpoon.dts create mode 100644 arch/arm64/boot/dts/freescale/imx95-15x15-evk-multicore-rtos.dts create mode 100644 arch/arm64/boot/dts/freescale/imx95-19x19-evk-harpoon-industrial.dts create mode 100644 arch/arm64/boot/dts/freescale/imx95-19x19-evk-harpoon.dts create mode 100644 arch/arm64/boot/dts/freescale/imx95-19x19-evk-multicore-rtos.dts create mode 100644 arch/arm64/boot/dts/freescale/imx95-generic-mbox.dtsi create mode 100644 arch/arm64/boot/dts/freescale/imx95-rpmsg-ca55.dtsi create mode 100644 arch/arm64/boot/dts/freescale/imx95-rproc-ca55.dtsi create mode 100644 arch/arm64/configs/imx_avb.config create mode 100644 arch/arm64/configs/linux-dpaa-ethercat.config create mode 100644 arch/arm64/configs/linux-rpmsg-8m-buf.config create mode 100644 drivers/clk/imx/clk-pll.c create mode 100644 drivers/clk/imx/clk-pll.h create mode 100644 drivers/irqchip/ipi-baremetal.c create mode 100644 drivers/mailbox/generic-software-mailbox.c create mode 100644 drivers/net/dsa/netc/Kconfig create mode 100644 drivers/net/dsa/netc/Makefile create mode 100644 drivers/net/dsa/netc/netc.h create mode 100644 drivers/net/dsa/netc/netc_config.c create mode 100644 drivers/net/dsa/netc/netc_config.h create mode 100644 drivers/net/dsa/netc/netc_devlink.c create mode 100644 drivers/net/dsa/netc/netc_ethtool.c create mode 100644 drivers/net/dsa/netc/netc_main.c create mode 100644 drivers/net/dsa/netc/netc_ptp.c create mode 100644 drivers/net/dsa/netc/netc_ptp.h create mode 100644 drivers/net/dsa/netc/netc_spi.c create mode 100644 drivers/net/ethernet/freescale/fec_ecat.c create mode 100644 drivers/net/ethernet/freescale/fec_ecat.h create mode 100644 drivers/net/ethernet/freescale/sdk_dpaa/dpaa_ethercat.c create mode 100644 drivers/nfc/pn5xx/Kconfig create mode 100644 drivers/nfc/pn5xx/Makefile create mode 100644 drivers/nfc/pn5xx/README.md create mode 100644 drivers/nfc/pn5xx/pn5xx_i2c.c create mode 100644 drivers/nfc/pn5xx/pn5xx_i2c.h create mode 100644 drivers/nfc/pn5xx/sample_devicetree.txt create mode 100644 drivers/rpmsg/rpmsg_perf.c create mode 100644 drivers/virtio/virtio_trans.c create mode 100644 include/dt-bindings/rpmsg/imx_srtm.h create mode 100644 include/linux/clk/imx-pll.h create mode 100644 include/linux/dsa/netc.h create mode 100644 include/linux/ipi_baremetal.h create mode 100644 include/linux/rpmsg/imx_srtm.h create mode 100644 include/net/tc_act/tc_frer.h create mode 100644 include/uapi/linux/tc_act/tc_frer.h create mode 100644 include/uapi/linux/virtio_trans.h create mode 100644 kernel/printk/nbcon.c create mode 100644 localversion-rt create mode 100644 net/dsa/tag_netc.c create mode 100644 net/ethtool/preempt.c create mode 100644 net/sched/act_frer.c create mode 100755 tools/virtio/vt_test.sh diff --git a/Documentation/devicetree/bindings/mailbox/generic-software-mbox.yaml b/Documentation/devicetree/bindings/mailbox/generic-software-mbox.yaml new file mode 100644 index 000000000000..57c91ab2c80a --- /dev/null +++ b/Documentation/devicetree/bindings/mailbox/generic-software-mbox.yaml @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mailbox/generic-software-mbox.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Generic Software Mailbox + +maintainers: + - Hou Zhiqiang + +description: | + The Generic Software Mailbox is a virtual device, which uses unused + interrupt line to notify remote side and shared memory to emulate + device MMIO registers. + +properties: + compatible: + oneOf: + - const: fsl,generic-software-mbox + reg: + maxItems: 1 + + "#mbox-cells": + description: | + <&phandle type channel ack> + phandle : Label name of controller + type : Channel type + channel : Channel index + ack : 0: Receiver doesn't trigger an ACK interrupt to sender after receive message + 1: Receiver triggers an ACK interrupt to sender after receive message + + This mailbox support 3 type of unidirectional channels, each type + has 32 channels. Following types are supported: + 0 - TX channel with 32bit transmit register + 1 - RX channel with 32bit receive register and IRQ support + 2 - RX doorbell channel. + const: 2 + +required: + - compatible + - reg + - "#mbox-cells" + - interrupts + - interrupt-names + +additionalProperties: false + +examples: + - | + reserved-memory { + gen-sw-mbox@b8500000 { + no-map; + reg = <0 0xb8500000 0 0x1000>; + }; + }; + + generic-software-mailbox@b8500000 { + compatible = "fsl,generic-software-mbox"; + reg = <0 0xb8500000 0 0x1000>; + #mbox-cells = <3>; + interrupts = , + ; + interrupt-names = "irq", "remote_irq"; + }; diff --git a/Documentation/devicetree/bindings/net/fsl,fec.yaml b/Documentation/devicetree/bindings/net/fsl,fec.yaml index ab1575b781dd..eb4924742dc2 100644 --- a/Documentation/devicetree/bindings/net/fsl,fec.yaml +++ b/Documentation/devicetree/bindings/net/fsl,fec.yaml @@ -186,6 +186,30 @@ properties: description: Register bits of stop mode control, the format is <&gpr req_gpr req_bit>. + fsl,rx-phy-delay-100-ns: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + If present, should specify the delay (MAC-PHY) compensation + in ns to be applied on packets timestamps on receive for 100 Mbps link speed + + fsl,tx-phy-delay-100-ns: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + If present, should specify the delay (MAC-PHY) compensation + in ns to be applied on packets timestamps on transmit for 100 Mbps link speed + + fsl,rx-phy-delay-1000-ns: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + If present, should specify the delay (MAC-PHY) compensation + in ns to be applied on packets timestamps on receive for 1 Gbps link speed + + fsl,tx-phy-delay-1000-ns: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + If present, should specify the delay (MAC-PHY) compensation + in ns to be applied on packets timestamps on transmit for 1 Gbps link speed + mii-exclusive: $ref: /schemas/types.yaml#/definitions/flag description: diff --git a/Documentation/devicetree/bindings/tty/rpmsg_tty.yaml b/Documentation/devicetree/bindings/tty/rpmsg_tty.yaml new file mode 100644 index 000000000000..9e11fadd16eb --- /dev/null +++ b/Documentation/devicetree/bindings/tty/rpmsg_tty.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/tty/rpmsg_tty.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: rpmsg tty driver + +maintainers: + - Biwen Li + +properties: + compatible: + const: fsl,uart-rpbus + + bus_id: + items: + - description: Used for imx srtm uart application protocol. Specify which real uart(LPUARTx/UARTx, x is instance number) will be used by a virtual tty(/dev/ttyRPMSGx), bus_id = 0xff when bus_id is not specified in dts and specified the flag(IMX_SRTM_UART_SUPPORT_MULTI_UART_MSG_FLAG). + maxItems: 1 + + flags: + items: + - description: used for imx srtm uart application protocol(IMX_SRTM_UART_SUPPORT_MULTI_UART_MSG_FLAG: support multi uart message protocol; IMX_SRTM_UART_SPECIFY_PORT_NUM_MASK: whether specify destination uart id; IMX_SRTM_UART_PORT_NUM_MASK: which destination uart id will be used) + maxItems: 1 + + status: + maxItems: 1 + +examples: + - | + #include + + uart-rpbus-0 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + flags=; + status = "okay"; + }; /* /dev/ttyRPMSG0 on linux(running on acore) <--SRTM PROTOCOL--> srtm uart channel 0(endpoint on mcore) <---MULTI UART MSG PROTOCOL---> UART3(mcore is the owner) */ + + uart-rpbus-1 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + flags=; + status = "okay"; + }; /* /dev/ttyRPMSG1 on linux(running on acore) <--SRTM PROTOCOL--> srtm uart channel 1(endpoint on mcore) <---MULTI UART MSG PROTOCOL---> UART3(mcore is the owner) */ + + uart-rpbus-2 { + compatible = "fsl,uart-rpbus"; + bus_id = <2>; /* use uart2 */ + flags=; + status = "okay"; + }; /* /dev/ttyRPMSG2 on linux(running on acore) <--SRTM PROTOCOL--> srtm uart channel 2(endpoint on mcore) <---MULTI UART MSG PROTOCOL---> UART2(mcore is the owner) */ + + uart-rpbus-3 { + compatible = "fsl,uart-rpbus"; + bus_id = <4>; /* use uart4 */ + flags=; + status = "okay"; + }; /* [/dev/ttyRPMSG3 on linux(running on acore) <--SRTM PROTOCOL--> srtm uart channel 3(endpoint on mcore) <---MULTI UART MSG PROTOCOL---> UART4(mcore is the owner), first board] <---MULTI UART MSG PROTOCOL--> [UARTx(mcore is the owner) <---MULTI UART MSG PROTOCOL---> srtm uart channel 8 <---SRTM PROTOCOL--> /dev/ttyRPMSG8 on linux(running on acore), second board ] */ + + uart-rpbus-4 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use uart5 */ + status = "okay"; + }; /* /dev/ttyRPMSG4 on linux(running on acore) <--SRTM PROTOCOL--> srtm uart channel 4(endpoint on mcore) <-----> UART5(mcore is the owner) <----> GPS device(The device have exclusive use of the UART5, other virtual tty cannot use UART5) */ + + /* Note: when nothing is specified(include compatible, bus_id, flags, status), it's a normal rpmsg tty. /dev/ttyRPMSG7(running on acore) <--RPMSG--> rpmsg endpoint(running on mcore) */ diff --git a/Documentation/printk-ringbuffer.txt b/Documentation/printk-ringbuffer.txt new file mode 100644 index 000000000000..6bde5dbd8545 --- /dev/null +++ b/Documentation/printk-ringbuffer.txt @@ -0,0 +1,377 @@ +struct printk_ringbuffer +------------------------ +John Ogness + +Overview +~~~~~~~~ +As the name suggests, this ring buffer was implemented specifically to serve +the needs of the printk() infrastructure. The ring buffer itself is not +specific to printk and could be used for other purposes. _However_, the +requirements and semantics of printk are rather unique. If you intend to use +this ring buffer for anything other than printk, you need to be very clear on +its features, behavior, and pitfalls. + +Features +^^^^^^^^ +The printk ring buffer has the following features: + +- single global buffer +- resides in initialized data section (available at early boot) +- lockless readers +- supports multiple writers +- supports multiple non-consuming readers +- safe from any context (including NMI) +- groups bytes into variable length blocks (referenced by entries) +- entries tagged with sequence numbers + +Behavior +^^^^^^^^ +Since the printk ring buffer readers are lockless, there exists no +synchronization between readers and writers. Basically writers are the tasks +in control and may overwrite any and all committed data at any time and from +any context. For this reason readers can miss entries if they are overwritten +before the reader was able to access the data. The reader API implementation +is such that reader access to entries is atomic, so there is no risk of +readers having to deal with partial or corrupt data. Also, entries are +tagged with sequence numbers so readers can recognize if entries were missed. + +Writing to the ring buffer consists of 2 steps. First a writer must reserve +an entry of desired size. After this step the writer has exclusive access +to the memory region. Once the data has been written to memory, it needs to +be committed to the ring buffer. After this step the entry has been inserted +into the ring buffer and assigned an appropriate sequence number. + +Once committed, a writer must no longer access the data directly. This is +because the data may have been overwritten and no longer exists. If a +writer must access the data, it should either keep a private copy before +committing the entry or use the reader API to gain access to the data. + +Because of how the data backend is implemented, entries that have been +reserved but not yet committed act as barriers, preventing future writers +from filling the ring buffer beyond the location of the reserved but not +yet committed entry region. For this reason it is *important* that writers +perform both reserve and commit as quickly as possible. Also, be aware that +preemption and local interrupts are disabled and writing to the ring buffer +is processor-reentrant locked during the reserve/commit window. Writers in +NMI contexts can still preempt any other writers, but as long as these +writers do not write a large amount of data with respect to the ring buffer +size, this should not become an issue. + +API +~~~ + +Declaration +^^^^^^^^^^^ +The printk ring buffer can be instantiated as a static structure: + + /* declare a static struct printk_ringbuffer */ + #define DECLARE_STATIC_PRINTKRB(name, szbits, cpulockptr) + +The value of szbits specifies the size of the ring buffer in bits. The +cpulockptr field is a pointer to a prb_cpulock struct that is used to +perform processor-reentrant spin locking for the writers. It is specified +externally because it may be used for multiple ring buffers (or other +code) to synchronize writers without risk of deadlock. + +Here is an example of a declaration of a printk ring buffer specifying a +32KB (2^15) ring buffer: + +.... +DECLARE_STATIC_PRINTKRB_CPULOCK(rb_cpulock); +DECLARE_STATIC_PRINTKRB(rb, 15, &rb_cpulock); +.... + +If writers will be using multiple ring buffers and the ordering of that usage +is not clear, the same prb_cpulock should be used for both ring buffers. + +Writer API +^^^^^^^^^^ +The writer API consists of 2 functions. The first is to reserve an entry in +the ring buffer, the second is to commit that data to the ring buffer. The +reserved entry information is stored within a provided `struct prb_handle`. + + /* reserve an entry */ + char *prb_reserve(struct prb_handle *h, struct printk_ringbuffer *rb, + unsigned int size); + + /* commit a reserved entry to the ring buffer */ + void prb_commit(struct prb_handle *h); + +Here is an example of a function to write data to a ring buffer: + +.... +int write_data(struct printk_ringbuffer *rb, char *data, int size) +{ + struct prb_handle h; + char *buf; + + buf = prb_reserve(&h, rb, size); + if (!buf) + return -1; + memcpy(buf, data, size); + prb_commit(&h); + + return 0; +} +.... + +Pitfalls +++++++++ +Be aware that prb_reserve() can fail. A retry might be successful, but it +depends entirely on whether or not the next part of the ring buffer to +overwrite belongs to reserved but not yet committed entries of other writers. +Writers can use the prb_inc_lost() function to allow readers to notice that a +message was lost. + +Reader API +^^^^^^^^^^ +The reader API utilizes a `struct prb_iterator` to track the reader's +position in the ring buffer. + + /* declare a pre-initialized static iterator for a ring buffer */ + #define DECLARE_STATIC_PRINTKRB_ITER(name, rbaddr) + + /* initialize iterator for a ring buffer (if static macro NOT used) */ + void prb_iter_init(struct prb_iterator *iter, + struct printk_ringbuffer *rb, u64 *seq); + + /* make a deep copy of an iterator */ + void prb_iter_copy(struct prb_iterator *dest, + struct prb_iterator *src); + + /* non-blocking, advance to next entry (and read the data) */ + int prb_iter_next(struct prb_iterator *iter, char *buf, + int size, u64 *seq); + + /* blocking, advance to next entry (and read the data) */ + int prb_iter_wait_next(struct prb_iterator *iter, char *buf, + int size, u64 *seq); + + /* position iterator at the entry seq */ + int prb_iter_seek(struct prb_iterator *iter, u64 seq); + + /* read data at current position */ + int prb_iter_data(struct prb_iterator *iter, char *buf, + int size, u64 *seq); + +Typically prb_iter_data() is not needed because the data can be retrieved +directly with prb_iter_next(). + +Here is an example of a non-blocking function that will read all the data in +a ring buffer: + +.... +void read_all_data(struct printk_ringbuffer *rb, char *buf, int size) +{ + struct prb_iterator iter; + u64 prev_seq = 0; + u64 seq; + int ret; + + prb_iter_init(&iter, rb, NULL); + + for (;;) { + ret = prb_iter_next(&iter, buf, size, &seq); + if (ret > 0) { + if (seq != ++prev_seq) { + /* "seq - prev_seq" entries missed */ + prev_seq = seq; + } + /* process buf here */ + } else if (ret == 0) { + /* hit the end, done */ + break; + } else if (ret < 0) { + /* + * iterator is invalid, a writer overtook us, reset the + * iterator and keep going, entries were missed + */ + prb_iter_init(&iter, rb, NULL); + } + } +} +.... + +Pitfalls +++++++++ +The reader's iterator can become invalid at any time because the reader was +overtaken by a writer. Typically the reader should reset the iterator back +to the current oldest entry (which will be newer than the entry the reader +was at) and continue, noting the number of entries that were missed. + +Utility API +^^^^^^^^^^^ +Several functions are available as convenience for external code. + + /* query the size of the data buffer */ + int prb_buffer_size(struct printk_ringbuffer *rb); + + /* skip a seq number to signify a lost record */ + void prb_inc_lost(struct printk_ringbuffer *rb); + + /* processor-reentrant spin lock */ + void prb_lock(struct prb_cpulock *cpu_lock, unsigned int *cpu_store); + + /* processor-reentrant spin unlock */ + void prb_lock(struct prb_cpulock *cpu_lock, unsigned int *cpu_store); + +Pitfalls +++++++++ +Although the value returned by prb_buffer_size() does represent an absolute +upper bound, the amount of data that can be stored within the ring buffer +is actually less because of the additional storage space of a header for each +entry. + +The prb_lock() and prb_unlock() functions can be used to synchronize between +ring buffer writers and other external activities. The function of a +processor-reentrant spin lock is to disable preemption and local interrupts +and synchronize against other processors. It does *not* protect against +multiple contexts of a single processor, i.e NMI. + +Implementation +~~~~~~~~~~~~~~ +This section describes several of the implementation concepts and details to +help developers better understand the code. + +Entries +^^^^^^^ +All ring buffer data is stored within a single static byte array. The reason +for this is to ensure that any pointers to the data (past and present) will +always point to valid memory. This is important because the lockless readers +may be preempted for long periods of time and when they resume may be working +with expired pointers. + +Entries are identified by start index and size. (The start index plus size +is the start index of the next entry.) The start index is not simply an +offset into the byte array, but rather a logical position (lpos) that maps +directly to byte array offsets. + +For example, for a byte array of 1000, an entry may have have a start index +of 100. Another entry may have a start index of 1100. And yet another 2100. +All of these entry are pointing to the same memory region, but only the most +recent entry is valid. The other entries are pointing to valid memory, but +represent entries that have been overwritten. + +Note that due to overflowing, the most recent entry is not necessarily the one +with the highest lpos value. Indeed, the printk ring buffer initializes its +data such that an overflow happens relatively quickly in order to validate the +handling of this situation. The implementation assumes that an lpos (unsigned +long) will never completely wrap while a reader is preempted. If this were to +become an issue, the seq number (which never wraps) could be used to increase +the robustness of handling this situation. + +Buffer Wrapping +^^^^^^^^^^^^^^^ +If an entry starts near the end of the byte array but would extend beyond it, +a special terminating entry (size = -1) is inserted into the byte array and +the real entry is placed at the beginning of the byte array. This can waste +space at the end of the byte array, but simplifies the implementation by +allowing writers to always work with contiguous buffers. + +Note that the size field is the first 4 bytes of the entry header. Also note +that calc_next() always ensures that there are at least 4 bytes left at the +end of the byte array to allow room for a terminating entry. + +Ring Buffer Pointers +^^^^^^^^^^^^^^^^^^^^ +Three pointers (lpos values) are used to manage the ring buffer: + + - _tail_: points to the oldest entry + - _head_: points to where the next new committed entry will be + - _reserve_: points to where the next new reserved entry will be + +These pointers always maintain a logical ordering: + + tail <= head <= reserve + +The reserve pointer moves forward when a writer reserves a new entry. The +head pointer moves forward when a writer commits a new entry. + +The reserve pointer cannot overwrite the tail pointer in a wrap situation. In +such a situation, the tail pointer must be "pushed forward", thus +invalidating that oldest entry. Readers identify if they are accessing a +valid entry by ensuring their entry pointer is `>= tail && < head`. + +If the tail pointer is equal to the head pointer, it cannot be pushed and any +reserve operation will fail. The only resolution is for writers to commit +their reserved entries. + +Processor-Reentrant Locking +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The purpose of the processor-reentrant locking is to limit the interruption +scenarios of writers to 2 contexts. This allows for a simplified +implementation where: + +- The reserve/commit window only exists on 1 processor at a time. A reserve + can never fail due to uncommitted entries of other processors. + +- When committing entries, it is trivial to handle the situation when + subsequent entries have already been committed, i.e. managing the head + pointer. + +Performance +~~~~~~~~~~~ +Some basic tests were performed on a quad Intel(R) Xeon(R) CPU E5-2697 v4 at +2.30GHz (36 cores / 72 threads). All tests involved writing a total of +32,000,000 records at an average of 33 bytes each. Each writer was pinned to +its own CPU and would write as fast as it could until a total of 32,000,000 +records were written. All tests involved 2 readers that were both pinned +together to another CPU. Each reader would read as fast as it could and track +how many of the 32,000,000 records it could read. All tests used a ring buffer +of 16KB in size, which holds around 350 records (header + data for each +entry). + +The only difference between the tests is the number of writers (and thus also +the number of records per writer). As more writers are added, the time to +write a record increases. This is because data pointers, modified via cmpxchg, +and global data access in general become more contended. + +1 writer +^^^^^^^^ + runtime: 0m 18s + reader1: 16219900/32000000 (50%) records + reader2: 16141582/32000000 (50%) records + +2 writers +^^^^^^^^^ + runtime: 0m 32s + reader1: 16327957/32000000 (51%) records + reader2: 16313988/32000000 (50%) records + +4 writers +^^^^^^^^^ + runtime: 0m 42s + reader1: 16421642/32000000 (51%) records + reader2: 16417224/32000000 (51%) records + +8 writers +^^^^^^^^^ + runtime: 0m 43s + reader1: 16418300/32000000 (51%) records + reader2: 16432222/32000000 (51%) records + +16 writers +^^^^^^^^^^ + runtime: 0m 54s + reader1: 16539189/32000000 (51%) records + reader2: 16542711/32000000 (51%) records + +32 writers +^^^^^^^^^^ + runtime: 1m 13s + reader1: 16731808/32000000 (52%) records + reader2: 16735119/32000000 (52%) records + +Comments +^^^^^^^^ +It is particularly interesting to compare/contrast the 1-writer and 32-writer +tests. Despite the writing of the 32,000,000 records taking over 4 times +longer, the readers (which perform no cmpxchg) were still unable to keep up. +This shows that the memory contention between the increasing number of CPUs +also has a dramatic effect on readers. + +It should also be noted that in all cases each reader was able to read >=50% +of the records. This means that a single reader would have been able to keep +up with the writer(s) in all cases, becoming slightly easier as more writers +are added. This was the purpose of pinning 2 readers to 1 CPU: to observe how +maximum reader performance changes. diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 0694e03e20f8..98fc1c4aaade 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -34,6 +34,7 @@ config ARM select ARCH_OPTIONAL_KERNEL_RWX_DEFAULT if CPU_V7 select ARCH_SUPPORTS_ATOMIC_RMW select ARCH_SUPPORTS_HUGETLBFS if ARM_LPAE + select ARCH_SUPPORTS_RT if HAVE_POSIX_CPU_TIMERS_TASK_WORK select ARCH_USE_BUILTIN_BSWAP select ARCH_USE_CMPXCHG_LOCKREF select ARCH_USE_MEMTEST @@ -73,7 +74,7 @@ config ARM select HAS_IOPORT select HAVE_ARCH_AUDITSYSCALL if AEABI && !OABI_COMPAT select HAVE_ARCH_BITREVERSE if (CPU_32v7M || CPU_32v7) && !CPU_32v6 - select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL && !CPU_ENDIAN_BE32 && MMU + select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL && !CPU_ENDIAN_BE32 && MMU && !PREEMPT_RT select HAVE_ARCH_KFENCE if MMU && !XIP_KERNEL select HAVE_ARCH_KGDB if !CPU_ENDIAN_BE32 && MMU select HAVE_ARCH_KASAN if MMU && !XIP_KERNEL @@ -96,7 +97,7 @@ config ARM select HAVE_DYNAMIC_FTRACE_WITH_REGS if HAVE_DYNAMIC_FTRACE select HAVE_EFFICIENT_UNALIGNED_ACCESS if (CPU_V6 || CPU_V6K || CPU_V7) && MMU select HAVE_EXIT_THREAD - select HAVE_FAST_GUP if ARM_LPAE + select HAVE_FAST_GUP if ARM_LPAE && !(PREEMPT_RT && HIGHPTE) select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL select HAVE_FUNCTION_ERROR_INJECTION select HAVE_FUNCTION_GRAPH_TRACER @@ -118,6 +119,7 @@ config ARM select HAVE_PERF_EVENTS select HAVE_PERF_REGS select HAVE_PERF_USER_STACK_DUMP + select HAVE_POSIX_CPU_TIMERS_TASK_WORK if !KVM select MMU_GATHER_RCU_TABLE_FREE if SMP && ARM_LPAE select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_RSEQ diff --git a/arch/arm/boot/dts/nxp/imx/Makefile b/arch/arm/boot/dts/nxp/imx/Makefile index 12e091dc4c4c..65faf0074601 100644 --- a/arch/arm/boot/dts/nxp/imx/Makefile +++ b/arch/arm/boot/dts/nxp/imx/Makefile @@ -336,6 +336,8 @@ dtb-$(CONFIG_SOC_IMX6SX) += \ imx6sx-udoo-neo-full.dtb dtb-$(CONFIG_SOC_IMX6UL) += \ imx6ul-14x14-evk.dtb \ + imx6ul-14x14-evk-ecat.dtb \ + imx6ul-14x14-evk-igh.dtb \ imx6ul-14x14-evk-csi.dtb \ imx6ul-14x14-evk-emmc.dtb \ imx6ul-14x14-evk-btwifi.dtb \ @@ -367,6 +369,11 @@ dtb-$(CONFIG_SOC_IMX6UL) += \ imx6ul-tx6ul-0011.dtb \ imx6ul-tx6ul-mainboard.dtb \ imx6ull-14x14-evk.dtb \ + imx6ull-14x14-evk-ecat.dtb \ + imx6ull-14x14-evk-igh.dtb \ + imx6ull-14x14-evk-lpuart.dtb \ + imx6ull-14x14-evk-avb.dtb \ + imx6ull-14x14-evk-avb-mcr.dtb \ imx6ull-14x14-evk-emmc.dtb \ imx6ull-14x14-evk-btwifi.dtb \ imx6ull-14x14-evk-gpmi-weim.dtb \ @@ -418,6 +425,7 @@ imx6ul-14x14-evk-reve-ecspi-slave-dtbs := imx6ul-14x14-evk-ecspi-slave.dtb imx6u imx6ul-14x14-evk-reve-ecspi-dtbs := imx6ul-14x14-evk-ecspi.dtb imx6ul-14x14-evk-reve.dtbo imx6ul-14x14-evk-reve-gpmi-weim-dtbs := imx6ul-14x14-evk-gpmi-weim.dtb imx6ul-14x14-evk-reve.dtbo imx6ull-14x14-evk-reve-dtbs := imx6ull-14x14-evk.dtb imx6ul-14x14-evk-reve.dtbo +imx6ull-14x14-evk-reve-avb-dtbs := imx6ull-14x14-evk-avb.dtb imx6ul-14x14-evk-reve.dtbo imx6ull-14x14-evk-reve.dtbo imx6ull-14x14-evk-reve-emmc-dtbs := imx6ull-14x14-evk-emmc.dtb imx6ul-14x14-evk-reve.dtbo imx6ull-14x14-evk-reve-btwifi-dtbs := imx6ull-14x14-evk-btwifi.dtb imx6ul-14x14-evk-reve.dtbo imx6ull-14x14-evk-reve-gpmi-weim-dtbs := imx6ull-14x14-evk-gpmi-weim.dtb imx6ul-14x14-evk-reve.dtbo @@ -434,6 +442,7 @@ dtb-$(CONFIG_SOC_IMX6UL) += \ imx6ul-14x14-evk-reve-ecspi.dtb \ imx6ul-14x14-evk-reve-gpmi-weim.dtb \ imx6ull-14x14-evk-reve.dtb \ + imx6ull-14x14-evk-reve-avb.dtb \ imx6ull-14x14-evk-reve-emmc.dtb \ imx6ull-14x14-evk-reve-btwifi.dtb \ imx6ull-14x14-evk-reve-gpmi-weim.dtb \ diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-14x14-evk-ecat.dts b/arch/arm/boot/dts/nxp/imx/imx6ul-14x14-evk-ecat.dts new file mode 100644 index 000000000000..2a99ab3960f1 --- /dev/null +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-14x14-evk-ecat.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2023 NXP + */ + +#include "imx6ul-14x14-evk.dts" + + +&fec1 { + compatible = "fsl,imx6ul-fec-ecat", "fsl,imx6q-fec-ecat"; +}; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul-14x14-evk-igh.dts b/arch/arm/boot/dts/nxp/imx/imx6ul-14x14-evk-igh.dts new file mode 100644 index 000000000000..01b4bc6b4fa7 --- /dev/null +++ b/arch/arm/boot/dts/nxp/imx/imx6ul-14x14-evk-igh.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2024 NXP + */ + +#include "imx6ul-14x14-evk.dts" + + +&fec1 { + compatible = "fsl,imx6ul-fec-igh-native", "fsl,imx6q-fec-igh-native"; +}; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-avb-mcr.dts b/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-avb-mcr.dts new file mode 100644 index 000000000000..e06167bd96bc --- /dev/null +++ b/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-avb-mcr.dts @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright 2022 NXP + +#include "imx6ull-14x14-evk-avb.dts" + +/****************************************************************************************** +* This dts adds the media clock recovery capability for AVB to the imx6ull-14x14-evk board. +* A hardware rework is necessary for this feature: +* -connect SD1_DATA2 and GPIO1_IO05 pads (can be done by connecting R1728 and TP2120) +* -connect JTAG_MOD and JTAG_TMS (SAI2_MCLK) pads (can be done by connecting R1023 and +* JTAG pin number 7) +* +* Warning: this will prevent using SD1 Slot. +*******************************************************************************************/ + +&iomuxc { + pinctrl_avb: avbgrp { + fsl,pins = < + MX6UL_PAD_SD1_DATA2__GPT2_CAPTURE1 0x110b0 + MX6UL_PAD_GPIO1_IO05__ENET2_1588_EVENT0_OUT 0x88 + MX6UL_PAD_JTAG_MOD__GPT2_CLK 0x1f0b0 + >; + }; +}; + +/* AVB MCR timer */ +&gpt2 { + compatible = "fsl,avb-gpt"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_avb>; + prescale = <1>; + + rec-channel = <1 0 0>; /* capture channel, eth port, ENET TC id */ + domain = <0>; + + clocks = <&clks IMX6UL_CLK_GPT2_BUS>, <&clks IMX6UL_CLK_GPT2_SERIAL>, + <&clks IMX6UL_CLK_SAI2>, <&clks IMX6UL_CLK_PLL4>; + clock-names = "ipg", "per", "clk_in", "audio_pll"; + + status = "okay"; +}; + +/* Pin conflict on GPIO1_IO05 */ +&usdhc1 { + status = "disabled"; +}; + +/* Set PERCLK parent to IPG to make sure that GPT_CLK set above is lower + * than 1/4 of frequency of the peripheral clock + * (Keep previsouly set, from included dts files, clocks in the node) + */ +&clks { + assigned-clocks = <&clks IMX6UL_CLK_PERCLK_SEL>, + <&clks IMX6UL_CLK_PLL3_PFD2>, + <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>; + assigned-clock-parents = <&clks IMX6UL_CLK_IPG>; + assigned-clock-rates = <0>, <320000000>, <786432000>; +}; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-avb.dts b/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-avb.dts new file mode 100644 index 000000000000..09af5531b3e0 --- /dev/null +++ b/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-avb.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright 2018 NXP + +#include "imx6ull-14x14-evk.dts" + +/* AVB HW timer*/ +&epit1 { + compatible = "fsl,avb-epit"; + clocks = <&clks IMX6UL_CLK_EPIT1>; + clock-names = "epit1"; + + status = "okay"; +}; + +&fec1 { + fsl,rx-phy-delay-100-ns = <670>; + fsl,tx-phy-delay-100-ns = <670>; + fsl,rx-phy-delay-1000-ns = <0>; + fsl,tx-phy-delay-1000-ns = <0>; +}; + +&fec2 { + fsl,rx-phy-delay-100-ns = <670>; + fsl,tx-phy-delay-100-ns = <670>; + fsl,rx-phy-delay-1000-ns = <0>; + fsl,tx-phy-delay-1000-ns = <0>; +}; + diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-ecat.dts b/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-ecat.dts new file mode 100644 index 000000000000..67df18babf56 --- /dev/null +++ b/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-ecat.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2023 NXP + */ + +#include "imx6ull-14x14-evk.dts" + + +&fec1 { + compatible = "fsl,imx6ul-fec-ecat", "fsl,imx6q-fec-ecat"; +}; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-igh.dts b/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-igh.dts new file mode 100644 index 000000000000..5eabf5eab1ab --- /dev/null +++ b/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-igh.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2024 NXP + */ + +#include "imx6ull-14x14-evk.dts" + + +&fec1 { + compatible = "fsl,imx6ul-fec-igh-native", "fsl,imx6q-fec-igh-native"; +}; diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-lpuart.dts b/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-lpuart.dts new file mode 100644 index 000000000000..d7a378c5349c --- /dev/null +++ b/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-lpuart.dts @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +#include "imx6ull-14x14-evk.dts" + +/* Unbind lpuart2 from Bluetooth, use it for lpuart test. */ +&uart2 { + /delete-node/ bluetooth; +}; + +/* + * uart test port1. Note: don't use Bluetooth at the sametime. + * J1703: Pin1--RX Pin2--TX + */ +&pinctrl_uart2 { + fsl,pins = < + MX6UL_PAD_UART2_TX_DATA__UART2_DCE_TX 0x1b0b1 + MX6UL_PAD_UART2_RX_DATA__UART2_DCE_RX 0x1b0b1 + MX6UL_PAD_UART3_RX_DATA__UART2_DCE_RTS 0x1b0b1 + MX6UL_PAD_UART3_TX_DATA__UART2_DCE_CTS 0x1b0b1 + >; +}; + diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-reve.dtso b/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-reve.dtso new file mode 100644 index 000000000000..e135f3f6e17b --- /dev/null +++ b/arch/arm/boot/dts/nxp/imx/imx6ull-14x14-evk-reve.dtso @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2024 NXP + */ + +/dts-v1/; +/plugin/; + +&{/} { + model = "Freescale i.MX6 ULL 14x14 RevE EVK Board"; +}; diff --git a/arch/arm/boot/dts/nxp/ls/Makefile b/arch/arm/boot/dts/nxp/ls/Makefile index 14759331dba2..766e8dbce535 100644 --- a/arch/arm/boot/dts/nxp/ls/Makefile +++ b/arch/arm/boot/dts/nxp/ls/Makefile @@ -5,4 +5,5 @@ dtb-$(CONFIG_SOC_LS1021A) += \ ls1021a-qds.dtb \ ls1021a-tqmls1021a-mbls1021a.dtb \ ls1021a-tsn.dtb \ - ls1021a-twr.dtb + ls1021a-twr.dtb \ + ls1021a-iot.dtb diff --git a/arch/arm/boot/dts/nxp/ls/ls1021a-iot-bm.dts b/arch/arm/boot/dts/nxp/ls/ls1021a-iot-bm.dts new file mode 100644 index 000000000000..3a23b97f0081 --- /dev/null +++ b/arch/arm/boot/dts/nxp/ls/ls1021a-iot-bm.dts @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * DTS file for NXP Layerscape baremetal + * + * Copyright 2018-2023 NXP + * + * Author: Changming Huang + * + */ + +/dts-v1/; +#include "ls1021a.dtsi" + +/ { + model = "LS1021A IOT Board"; + + sys_mclk: clock-mclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24576000>; + }; + + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + reg_3p3v: regulator@0 { + compatible = "regulator-fixed"; + reg = <0>; + regulator-name = "3P3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + reg_2p5v: regulator@1 { + compatible = "regulator-fixed"; + reg = <1>; + regulator-name = "2P5V"; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2500000>; + regulator-always-on; + }; + }; + + sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,widgets = + "Microphone", "Microphone Jack", + "Headphone", "Headphone Jack", + "Speaker", "Speaker Ext", + "Line", "Line In Jack"; + simple-audio-card,routing = + "MIC_IN", "Microphone Jack", + "Microphone Jack", "Mic Bias", + "LINE_IN", "Line In Jack", + "Headphone Jack", "HP_OUT", + "Speaker Ext", "LINE_OUT"; + + simple-audio-card,cpu { + sound-dai = <&sai2>; + frame-master; + bitclock-master; + }; + + simple-audio-card,codec { + sound-dai = <&codec>; + frame-master; + bitclock-master; + }; + }; + + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + }; +}; + +&enet0 { + tbi-handle = <&tbi1>; + phy-handle = <&phy1>; + phy-connection-type = "sgmii"; + status = "okay"; +}; + +&enet1 { + tbi-handle = <&tbi1>; + phy-handle = <&phy3>; + phy-connection-type = "sgmii"; + status = "okay"; +}; + +&enet2 { + fixed-link = <0 1 1000 0 0>; + phy-connection-type = "rgmii-id"; + status = "okay"; +}; + +&can0{ + status = "disabled"; +}; + +&can1{ + status = "disabled"; +}; + +&can2{ + status = "disabled"; +}; + +&can3{ + status = "okay"; +}; + +&esdhc{ + status = "okay"; +}; + +&i2c0 { + status = "okay"; + + max1239@35 { + compatible = "maxim,max1239"; + reg = <0x35>; + #io-channel-cells = <1>; + }; + + codec: sgtl5000@2a { + #sound-dai-cells=<0x0>; + compatible = "fsl,sgtl5000"; + reg = <0x2a>; + VDDA-supply = <®_3p3v>; + VDDIO-supply = <®_2p5v>; + clocks = <&sys_mclk 1>; + }; + + pca9555: pca9555@23 { + compatible = "nxp,pca9555"; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x23>; + }; + + ina220@44 { + compatible = "ti,ina220"; + reg = <0x44>; + shunt-resistor = <1000>; + }; + + ina220@45 { + compatible = "ti,ina220"; + reg = <0x45>; + shunt-resistor = <1000>; + }; + + lm75b@48 { + compatible = "nxp,lm75a"; + reg = <0x48>; + }; + + adt7461a@4c { + compatible = "adt7461a"; + reg = <0x4c>; + }; + + hdmi: sii9022a@39 { + compatible = "fsl,sii902x"; + reg = <0x39>; + interrupts = ; + }; +}; + +&i2c1 { + status = "disabled"; +}; + +&ifc { + status = "disabled"; +}; + +&lpuart0 { + status = "okay"; +}; + +&mdio0 { + phy0: ethernet-phy@0 { + reg = <0x0>; + }; + phy1: ethernet-phy@1 { + reg = <0x1>; + }; + phy2: ethernet-phy@2 { + reg = <0x2>; + }; + phy3: ethernet-phy@3 { + reg = <0x3>; + }; + tbi1: tbi-phy@1f { + reg = <0x1f>; + device_type = "tbi-phy"; + }; +}; + +&sai2 { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&uart1 { + status = "okay"; +}; + +&dcu { + display = <&display>; + status = "okay"; + + display: display@0 { + bits-per-pixel = <24>; + + display-timings { + native-mode = <&timing0>; + + timing0: mode0 { + clock-frequency = <25000000>; + hactive = <640>; + vactive = <480>; + hback-porch = <80>; + hfront-porch = <80>; + vback-porch = <16>; + vfront-porch = <16>; + hsync-len = <12>; + vsync-len = <2>; + hsync-active = <1>; + vsync-active = <1>; + }; + }; + }; +}; + +&usb3 { + status = "disabled"; +}; + +&pcie1 { + status = "okay"; +}; + +&pcie2 { + status = "disabled"; +}; diff --git a/arch/arm/boot/dts/nxp/ls/ls1021a.dtsi b/arch/arm/boot/dts/nxp/ls/ls1021a.dtsi index cd0d699985c6..f1657b7c6e8b 100644 --- a/arch/arm/boot/dts/nxp/ls/ls1021a.dtsi +++ b/arch/arm/boot/dts/nxp/ls/ls1021a.dtsi @@ -815,7 +815,7 @@ usb3: usb@3100000 { snps,host-vbus-glitches; }; - pcie@3400000 { + pcie1: pcie@3400000 { compatible = "fsl,ls1021a-pcie"; reg = <0x00 0x03400000 0x0 0x00010000>, /* controller registers */ <0x40 0x00000000 0x0 0x00002000>; /* configuration space */ @@ -841,7 +841,7 @@ pcie@3400000 { status = "disabled"; }; - pcie@3500000 { + pcie2: pcie@3500000 { compatible = "fsl,ls1021a-pcie"; reg = <0x00 0x03500000 0x0 0x00010000>, /* controller registers */ <0x48 0x00000000 0x0 0x00002000>; /* configuration space */ diff --git a/arch/arm/configs/imx6ullevk-plc.config b/arch/arm/configs/imx6ullevk-plc.config new file mode 100644 index 000000000000..7a309a93b547 --- /dev/null +++ b/arch/arm/configs/imx6ullevk-plc.config @@ -0,0 +1,3333 @@ +# +# General setup +# +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +# CONFIG_COMPILE_TEST is not set +# CONFIG_WERROR is not set +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_BUILD_SALT="" +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_HAVE_KERNEL_LZ4=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_XZ is not set +CONFIG_KERNEL_LZO=y +# CONFIG_KERNEL_LZ4 is not set +CONFIG_DEFAULT_INIT="" +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y +# CONFIG_WATCH_QUEUE is not set +CONFIG_CROSS_MEMORY_ATTACH=y +# CONFIG_USELIB is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_ARCH_AUDITSYSCALL=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_SHOW_LEVEL=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_HANDLE_DOMAIN_IRQ=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_SPARSE_IRQ=y +# end of IRQ subsystem + +CONFIG_GENERIC_IRQ_MULTI_HANDLER=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_HAVE_POSIX_CPU_TIMERS_TASK_WORK=y +CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y + +# +# Timers subsystem +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ_COMMON=y +# CONFIG_HZ_PERIODIC is not set +CONFIG_NO_HZ_IDLE=y +# CONFIG_NO_HZ is not set +CONFIG_HIGH_RES_TIMERS=y +# end of Timers subsystem + +CONFIG_BPF=y +CONFIG_HAVE_EBPF_JIT=y + +# +# BPF subsystem +# +# CONFIG_BPF_SYSCALL is not set +# CONFIG_BPF_JIT is not set +# end of BPF subsystem + +CONFIG_HAVE_PREEMPT_LAZY=y +CONFIG_PREEMPT_LAZY=y +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_PREEMPT_RT=y +CONFIG_PREEMPT_COUNT=y +CONFIG_PREEMPTION=y + +# +# CPU/Task time and stats accounting +# +CONFIG_TICK_CPU_ACCOUNTING=y +# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set +# CONFIG_IRQ_TIME_ACCOUNTING is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_PSI is not set +# end of CPU/Task time and stats accounting + +# +# RCU Subsystem +# +CONFIG_TREE_RCU=y +CONFIG_PREEMPT_RCU=y +# CONFIG_RCU_EXPERT is not set +CONFIG_SRCU=y +CONFIG_TREE_SRCU=y +CONFIG_TASKS_RCU_GENERIC=y +CONFIG_TASKS_RCU=y +CONFIG_RCU_STALL_COMMON=y +CONFIG_RCU_NEED_SEGCBLIST=y +CONFIG_RCU_BOOST=y +CONFIG_RCU_BOOST_DELAY=500 +# end of RCU Subsystem + +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +# CONFIG_IKHEADERS is not set +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=13 +CONFIG_GENERIC_SCHED_CLOCK=y + +# +# Scheduler features +# +# end of Scheduler features + +# CONFIG_CGROUPS is not set +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_IPC_NS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +# CONFIG_NET_NS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +CONFIG_RELAY=y +# CONFIG_BLK_DEV_INITRD is not set +# CONFIG_BOOT_CONFIG is not set +CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_LD_ORPHAN_WARN=y +CONFIG_SYSCTL=y +CONFIG_HAVE_UID16=y +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_MULTIUSER=y +# CONFIG_SGETMASK_SYSCALL is not set +CONFIG_SYSFS_SYSCALL=y +CONFIG_FHANDLE=y +CONFIG_POSIX_TIMERS=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_FUTEX_PI=y +CONFIG_HAVE_FUTEX_CMPXCHG=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y +CONFIG_IO_URING=y +CONFIG_ADVISE_SYSCALLS=y +CONFIG_MEMBARRIER=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_KALLSYMS_BASE_RELATIVE=y +# CONFIG_USERFAULTFD is not set +CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE=y +CONFIG_KCMP=y +CONFIG_RSEQ=y +# CONFIG_DEBUG_RSEQ is not set +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y +# CONFIG_PC104 is not set + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# end of Kernel Performance Events And Counters + +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_SLUB_DEBUG is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_SLUB=y +# CONFIG_SLAB_MERGE_DEFAULT is not set +# CONFIG_SLAB_FREELIST_RANDOM is not set +# CONFIG_SLAB_FREELIST_HARDENED is not set +# CONFIG_SHUFFLE_PAGE_ALLOCATOR is not set +# CONFIG_PROFILING is not set +# end of General setup + +CONFIG_ARM=y +CONFIG_ARM_HAS_SG_CHAIN=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_ARCH_SUPPORTS_UPROBES=y +CONFIG_ARM_PATCH_PHYS_VIRT=y +CONFIG_GENERIC_BUG=y +CONFIG_PGTABLE_LEVELS=2 + +# +# System Type +# +CONFIG_MMU=y +CONFIG_ARCH_MMAP_RND_BITS_MIN=8 +CONFIG_ARCH_MMAP_RND_BITS_MAX=15 +CONFIG_ARCH_MULTIPLATFORM=y +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C24XX is not set +# CONFIG_ARCH_OMAP1 is not set + +# +# Multiple platform selection +# + +# +# CPU Core family selection +# +# CONFIG_ARCH_MULTI_V6 is not set +CONFIG_ARCH_MULTI_V7=y +CONFIG_ARCH_MULTI_V6_V7=y +# end of Multiple platform selection + +# CONFIG_ARCH_VIRT is not set +# CONFIG_ARCH_ACTIONS is not set +# CONFIG_ARCH_ALPINE is not set +# CONFIG_ARCH_ARTPEC is not set +# CONFIG_ARCH_ASPEED is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCM is not set +# CONFIG_ARCH_BERLIN is not set +# CONFIG_ARCH_DIGICOLOR is not set +# CONFIG_ARCH_EXYNOS is not set +# CONFIG_ARCH_HIGHBANK is not set +# CONFIG_ARCH_HISI is not set +CONFIG_ARCH_MXC=y +CONFIG_HAVE_IMX_ANATOP=y +CONFIG_HAVE_IMX_GPC=y +CONFIG_HAVE_IMX_GPCV2=y +CONFIG_HAVE_IMX_MMDC=y +CONFIG_HAVE_IMX_AMP=y +CONFIG_HAVE_IMX_DDRC=y +CONFIG_HAVE_IMX_BUSFREQ=y +CONFIG_HAVE_IMX_MU=y +CONFIG_HAVE_IMX_RPMSG=y +CONFIG_HAVE_IMX_SRC=y + +# +# Cortex-A platforms +# +# CONFIG_SOC_IMX50 is not set +# CONFIG_SOC_IMX51 is not set +# CONFIG_SOC_IMX53 is not set +CONFIG_SOC_IMX6=y +CONFIG_SOC_IMX6Q=y +CONFIG_SOC_IMX6SL=y +CONFIG_SOC_IMX6SLL=y +CONFIG_SOC_IMX6SX=y +CONFIG_SOC_IMX6UL=y +# CONFIG_SOC_LS1021A is not set +# CONFIG_SOC_IMX6Q_BAREMETAL is not set + +# +# Cortex-A/Cortex-M asymmetric multiprocessing platforms +# +CONFIG_SOC_IMX7D_CA7=y +CONFIG_SOC_IMX7D=y +# CONFIG_SOC_IMX7ULP is not set +# CONFIG_SOC_VF610 is not set +# CONFIG_ARCH_KEYSTONE is not set +# CONFIG_ARCH_MEDIATEK is not set +# CONFIG_ARCH_MESON is not set +# CONFIG_ARCH_MILBEAUT is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_MSTARV7 is not set +# CONFIG_ARCH_MVEBU is not set +# CONFIG_ARCH_NPCM is not set + +# +# TI OMAP/AM/DM/DRA Family +# +# CONFIG_ARCH_OMAP3 is not set +# CONFIG_ARCH_OMAP4 is not set +# CONFIG_SOC_OMAP5 is not set +# CONFIG_SOC_AM33XX is not set +# CONFIG_SOC_AM43XX is not set +# CONFIG_SOC_DRA7XX is not set +# end of TI OMAP/AM/DM/DRA Family + +# CONFIG_ARCH_QCOM is not set +# CONFIG_ARCH_RDA is not set +# CONFIG_ARCH_REALTEK is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_ROCKCHIP is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_RENESAS is not set +# CONFIG_ARCH_INTEL_SOCFPGA is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_ARCH_STI is not set +# CONFIG_ARCH_STM32 is not set +# CONFIG_ARCH_SUNXI is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_UNIPHIER is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_WM8850 is not set +# CONFIG_ARCH_ZYNQ is not set + +# +# Processor Type +# +CONFIG_CPU_V7=y +CONFIG_CPU_THUMB_CAPABLE=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +# CONFIG_ARM_LPAE is not set +CONFIG_ARM_THUMB=y +# CONFIG_ARM_THUMBEE is not set +CONFIG_ARM_VIRT_EXT=y +# CONFIG_SWP_EMULATE is not set +# CONFIG_CPU_BIG_ENDIAN is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_CPU_SPECTRE=y +CONFIG_HARDEN_BRANCH_PREDICTOR=y +CONFIG_HARDEN_BRANCH_HISTORY=y +CONFIG_KUSER_HELPERS=y +CONFIG_VDSO=y +CONFIG_OUTER_CACHE=y +CONFIG_OUTER_CACHE_SYNC=y +CONFIG_MIGHT_HAVE_CACHE_L2X0=y +CONFIG_CACHE_L2X0=y +# CONFIG_PL310_ERRATA_588369 is not set +# CONFIG_PL310_ERRATA_727915 is not set +# CONFIG_PL310_ERRATA_753970 is not set +CONFIG_PL310_ERRATA_769419=y +CONFIG_ARM_L1_CACHE_SHIFT_6=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_ARM_HEAVY_MB=y +CONFIG_ARCH_SUPPORTS_BIG_ENDIAN=y +# CONFIG_DEBUG_ALIGN_RODATA is not set +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_720789 is not set +CONFIG_ARM_ERRATA_754322=y +CONFIG_ARM_ERRATA_775420=y +# CONFIG_ARM_ERRATA_773022 is not set +# CONFIG_ARM_ERRATA_818325_852422 is not set +# CONFIG_ARM_ERRATA_821420 is not set +# CONFIG_ARM_ERRATA_825619 is not set +# CONFIG_ARM_ERRATA_857271 is not set +# CONFIG_ARM_ERRATA_852421 is not set +# CONFIG_ARM_ERRATA_852423 is not set +# CONFIG_ARM_ERRATA_857272 is not set +# end of System Type + +# +# Bus support +# +CONFIG_ARM_ERRATA_814220=y +# end of Bus support + +# +# Kernel Features +# +CONFIG_HAVE_SMP=y +# CONFIG_SMP is not set +CONFIG_HAVE_ARM_ARCH_TIMER=y +CONFIG_HAVE_ARM_TWD=y +# CONFIG_VMSPLIT_3G is not set +# CONFIG_VMSPLIT_3G_OPT is not set +CONFIG_VMSPLIT_2G=y +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0x80000000 +CONFIG_ARM_PSCI=y +CONFIG_ARCH_NR_GPIO=0 +CONFIG_HZ_FIXED=0 +CONFIG_HZ_100=y +# CONFIG_HZ_200 is not set +# CONFIG_HZ_250 is not set +# CONFIG_HZ_300 is not set +# CONFIG_HZ_500 is not set +# CONFIG_HZ_1000 is not set +CONFIG_HZ=100 +CONFIG_SCHED_HRTICK=y +# CONFIG_THUMB2_KERNEL is not set +CONFIG_ARM_PATCH_IDIV=y +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +CONFIG_ARCH_SELECT_MEMORY_MODEL=y +CONFIG_ARCH_FLATMEM_ENABLE=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +# CONFIG_HIGHMEM is not set +# CONFIG_CPU_SW_DOMAIN_PAN is not set +CONFIG_ARCH_WANT_GENERAL_HUGETLB=y +CONFIG_ARM_MODULE_PLTS=y +CONFIG_FORCE_MAX_ZONEORDER=14 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_PARAVIRT is not set +# CONFIG_PARAVIRT_TIME_ACCOUNTING is not set +# CONFIG_XEN is not set +# end of Kernel Features + +# +# Boot options +# +CONFIG_USE_OF=y +CONFIG_ATAGS=y +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +# CONFIG_ARM_APPENDED_DTB is not set +CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +CONFIG_AUTO_ZRELADDR=y +# CONFIG_EFI is not set +# end of Boot options + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +# CONFIG_CPU_FREQ_STAT is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set + +# +# CPU frequency scaling drivers +# +# CONFIG_CPUFREQ_DT is not set +CONFIG_ARM_IMX6Q_CPUFREQ=y +# end of CPU Frequency scaling + +# +# CPU Idle +# +# CONFIG_CPU_IDLE is not set +# end of CPU Idle +# end of CPU Power Management + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y +CONFIG_KERNEL_MODE_NEON=y +# end of Floating point emulation + +# +# Power management options +# +# CONFIG_SUSPEND is not set +# CONFIG_PM is not set +# CONFIG_APM_EMULATION is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +# end of Power management options + +CONFIG_ARM_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM=y +# CONFIG_CRYPTO_SHA1_ARM_NEON is not set +# CONFIG_CRYPTO_SHA1_ARM_CE is not set +# CONFIG_CRYPTO_SHA2_ARM_CE is not set +CONFIG_CRYPTO_SHA256_ARM=y +CONFIG_CRYPTO_SHA512_ARM=y +CONFIG_CRYPTO_BLAKE2S_ARM=y +# CONFIG_CRYPTO_BLAKE2B_NEON is not set +CONFIG_CRYPTO_AES_ARM=y +# CONFIG_CRYPTO_AES_ARM_BS is not set +# CONFIG_CRYPTO_AES_ARM_CE is not set +# CONFIG_CRYPTO_GHASH_ARM_CE is not set +# CONFIG_CRYPTO_CRCT10DIF_ARM_CE is not set +# CONFIG_CRYPTO_CRC32_ARM_CE is not set +CONFIG_CRYPTO_CHACHA20_NEON=y +CONFIG_CRYPTO_POLY1305_ARM=y +# CONFIG_CRYPTO_NHPOLY1305_NEON is not set +# CONFIG_CRYPTO_CURVE25519_NEON is not set +CONFIG_AS_VFP_VMRS_FPINST=y + +# +# General architecture-dependent options +# +CONFIG_SET_FS=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y +CONFIG_ARCH_USE_BUILTIN_BSWAP=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_OPTPROBES=y +CONFIG_HAVE_NMI=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_ARCH_HAS_FORTIFY_SOURCE=y +CONFIG_ARCH_HAS_KEEPINITRD=y +CONFIG_ARCH_HAS_SET_MEMORY=y +CONFIG_HAVE_ARCH_THREAD_STRUCT_WHITELIST=y +CONFIG_ARCH_32BIT_OFF_T=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_RSEQ=y +CONFIG_HAVE_PERF_REGS=y +CONFIG_HAVE_PERF_USER_STACK_DUMP=y +CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_HAVE_ARCH_SECCOMP=y +CONFIG_HAVE_ARCH_SECCOMP_FILTER=y +CONFIG_SECCOMP=y +CONFIG_SECCOMP_FILTER=y +# CONFIG_SECCOMP_CACHE_DEBUG is not set +CONFIG_HAVE_STACKPROTECTOR=y +CONFIG_STACKPROTECTOR=y +# CONFIG_STACKPROTECTOR_STRONG is not set +CONFIG_LTO_NONE=y +CONFIG_HAVE_CONTEXT_TRACKING=y +CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_ARCH_HAS_ELF_RANDOMIZE=y +CONFIG_HAVE_ARCH_MMAP_RND_BITS=y +CONFIG_HAVE_EXIT_THREAD=y +CONFIG_ARCH_MMAP_RND_BITS=8 +CONFIG_ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_OLD_SIGACTION=y +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_ARCH_SUPPORTS_RT=y +CONFIG_ARCH_OPTIONAL_KERNEL_RWX=y +CONFIG_ARCH_OPTIONAL_KERNEL_RWX_DEFAULT=y +CONFIG_ARCH_HAS_STRICT_KERNEL_RWX=y +CONFIG_STRICT_KERNEL_RWX=y +CONFIG_ARCH_HAS_STRICT_MODULE_RWX=y +# CONFIG_STRICT_MODULE_RWX is not set +CONFIG_ARCH_HAS_PHYS_TO_DMA=y +CONFIG_ARCH_WANT_LD_ORPHAN_WARN=y +CONFIG_HAVE_ARCH_PFN_VALID=y + +# +# GCOV-based kernel profiling +# +CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y +# end of GCOV-based kernel profiling + +CONFIG_HAVE_GCC_PLUGINS=y +# CONFIG_GCC_PLUGINS is not set +# end of General architecture-dependent options + +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +# CONFIG_MODULE_UNLOAD is not set +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +# CONFIG_MODULE_SIG is not set +CONFIG_MODULE_COMPRESS_NONE=y +# CONFIG_MODULE_COMPRESS_GZIP is not set +# CONFIG_MODULE_COMPRESS_XZ is not set +# CONFIG_MODULE_COMPRESS_ZSTD is not set +# CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS is not set +CONFIG_MODPROBE_PATH="/sbin/modprobe" +# CONFIG_TRIM_UNUSED_KSYMS is not set +CONFIG_BLOCK=y +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set +# CONFIG_BLK_DEV_ZONED is not set +# CONFIG_BLK_WBT is not set +# CONFIG_BLK_SED_OPAL is not set +# CONFIG_BLK_INLINE_ENCRYPTION is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_EFI_PARTITION=y +# end of Partition Types + +CONFIG_BLK_MQ_VIRTIO=y + +# +# IO Schedulers +# +CONFIG_MQ_IOSCHED_DEADLINE=y +# CONFIG_MQ_IOSCHED_KYBER is not set +# CONFIG_IOSCHED_BFQ is not set +# end of IO Schedulers + +CONFIG_ASN1=y +CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y +CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE=y + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_ELF_FDPIC is not set +CONFIG_ELFCORE=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_BINFMT_SCRIPT=y +CONFIG_ARCH_HAS_BINFMT_FLAT=y +# CONFIG_BINFMT_FLAT is not set +CONFIG_BINFMT_FLAT_ARGVP_ENVP_ON_STACK=y +# CONFIG_BINFMT_MISC is not set +CONFIG_COREDUMP=y +# end of Executable file formats + +# +# Memory Management options +# +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_MEMORY_ISOLATION=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_COMPACTION is not set +# CONFIG_PAGE_REPORTING is not set +CONFIG_MIGRATION=y +CONFIG_CONTIG_ALLOC=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_CMA=y +# CONFIG_CMA_DEBUG is not set +# CONFIG_CMA_SYSFS is not set +CONFIG_CMA_AREAS=7 +# CONFIG_ZPOOL is not set +# CONFIG_ZSMALLOC is not set +CONFIG_GENERIC_EARLY_IOREMAP=y +# CONFIG_IDLE_PAGE_TRACKING is not set +# CONFIG_PERCPU_STATS is not set + +# +# GUP_TEST needs to have DEBUG_FS enabled +# + +# +# Data Access Monitoring +# +# CONFIG_DAMON is not set +# end of Data Access Monitoring +# end of Memory Management options + +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_DIAG is not set +CONFIG_UNIX=y +CONFIG_UNIX_SCM=y +CONFIG_AF_UNIX_OOB=y +# CONFIG_UNIX_DIAG is not set +CONFIG_TLS=y +CONFIG_TLS_DEVICE=y +# CONFIG_TLS_TOE is not set +# CONFIG_XFRM_USER is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_NET_IPVTI is not set +# CONFIG_NET_FOU is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_INET_UDP_DIAG is not set +# CONFIG_INET_RAW_DIAG is not set +# CONFIG_INET_DIAG_DESTROY is not set +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IPV6 is not set +# CONFIG_MPTCP is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +# CONFIG_NETFILTER is not set +# CONFIG_BPFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +# CONFIG_NET_DSA is not set +CONFIG_VLAN_8021Q=y +# CONFIG_VLAN_8021Q_GVRP is not set +# CONFIG_VLAN_8021Q_MVRP is not set +# CONFIG_DECNET is not set +CONFIG_LLC=y +CONFIG_LLC2=y +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +# CONFIG_VSOCKETS is not set +# CONFIG_NETLINK_DIAG is not set +# CONFIG_MPLS is not set +# CONFIG_NET_NSH is not set +# CONFIG_HSR is not set +# CONFIG_NET_SWITCHDEV is not set +# CONFIG_NET_L3_MASTER_DEV is not set +# CONFIG_QRTR is not set +# CONFIG_NET_NCSI is not set +CONFIG_SOCK_RX_QUEUE_MAPPING=y +CONFIG_BQL=y + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# end of Network testing +# end of Networking options + +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set +# CONFIG_AF_KCM is not set +CONFIG_STREAM_PARSER=y +# CONFIG_MCTP is not set +# CONFIG_WIRELESS is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set +# CONFIG_NFC is not set +# CONFIG_PSAMPLE is not set +# CONFIG_NET_IFE is not set +# CONFIG_LWTUNNEL is not set +CONFIG_SOCK_VALIDATE_XMIT=y +CONFIG_NET_SELFTESTS=y +CONFIG_NET_SOCK_MSG=y +# CONFIG_FAILOVER is not set +CONFIG_ETHTOOL_NETLINK=y + +# +# Device Drivers +# +CONFIG_HAVE_PCI=y +# CONFIG_PCI is not set +# CONFIG_PCCARD is not set + +# +# Generic Driver Options +# +# CONFIG_UEVENT_HELPER is not set +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +# CONFIG_STANDALONE is not set +CONFIG_PREVENT_FIRMWARE_BUILD=y + +# +# Firmware loader +# +CONFIG_FW_LOADER=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_EXTRA_FIRMWARE="" +CONFIG_FW_LOADER_USER_HELPER=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +# CONFIG_FW_LOADER_COMPRESS is not set +# end of Firmware loader + +CONFIG_ALLOW_DEV_COREDUMP=y +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_DEBUG_TEST_DRIVER_REMOVE is not set +# CONFIG_TEST_ASYNC_DRIVER_PROBE is not set +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_CPU_VULNERABILITIES=y +CONFIG_SOC_BUS=y +CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGMAP_IRQ=y +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_DMA_FENCE_TRACE is not set +# end of Generic Driver Options + +# +# Bus devices +# +# CONFIG_BRCMSTB_GISB_ARB is not set +CONFIG_IMX_WEIM=y +# CONFIG_VEXPRESS_CONFIG is not set +# CONFIG_FSL_MC_BUS is not set +# CONFIG_MHI_BUS is not set +# end of Bus devices + +# CONFIG_CONNECTOR is not set + +# +# Firmware Drivers +# + +# +# ARM System Control and Management Interface Protocol +# +# CONFIG_ARM_SCMI_PROTOCOL is not set +# end of ARM System Control and Management Interface Protocol + +# CONFIG_ARM_SCPI_PROTOCOL is not set +# CONFIG_FIRMWARE_MEMMAP is not set +# CONFIG_FW_CFG_SYSFS is not set +# CONFIG_TRUSTED_FOUNDATIONS is not set +# CONFIG_GOOGLE_FIRMWARE is not set +# CONFIG_IMX_DSP is not set +# CONFIG_IMX_SCU is not set +# CONFIG_IMX_SECO_MU is not set +# CONFIG_IMX_EL_ENCLAVE is not set +CONFIG_ARM_PSCI_FW=y +CONFIG_HAVE_ARM_SMCCC=y +CONFIG_HAVE_ARM_SMCCC_DISCOVERY=y +CONFIG_ARM_SMCCC_SOC_ID=y + +# +# Tegra firmware driver +# +# end of Tegra firmware driver +# end of Firmware Drivers + +# CONFIG_GNSS is not set +# CONFIG_MTD is not set +CONFIG_DTC=y +CONFIG_OF=y +# CONFIG_OF_UNITTEST is not set +CONFIG_OF_FLATTREE=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_KOBJ=y +CONFIG_OF_DYNAMIC=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_IRQ=y +CONFIG_OF_RESERVED_MEM=y +CONFIG_OF_RESOLVE=y +CONFIG_OF_OVERLAY=y +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_NULL_BLK is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_DRBD is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_VIRTIO_BLK is not set +# CONFIG_BLK_DEV_RBD is not set + +# +# NVME Support +# +# CONFIG_NVME_FC is not set +# CONFIG_NVME_TCP is not set +# CONFIG_NVME_TARGET is not set +# end of NVME Support + +# +# Misc devices +# +# CONFIG_AD525X_DPOT is not set +# CONFIG_DUMMY_IRQ is not set +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_DS1682 is not set +CONFIG_SRAM=y +CONFIG_SRAM_EXEC=y +# CONFIG_XILINX_SDFEC is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +CONFIG_EEPROM_AT24=y +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_IDT_89HPESX is not set +# CONFIG_EEPROM_EE1004 is not set +# end of EEPROM support + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# end of Texas Instruments shared transport line discipline + +# CONFIG_SENSORS_LIS3_I2C is not set +# CONFIG_ALTERA_STAPL is not set +# CONFIG_ECHO is not set +# CONFIG_PVPANIC is not set +# end of Misc devices + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# end of SCSI device support + +# CONFIG_ATA is not set +# CONFIG_MD is not set +# CONFIG_TARGET_CORE is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_WIREGUARD is not set +# CONFIG_EQUALIZER is not set +# CONFIG_NET_TEAM is not set +# CONFIG_MACVLAN is not set +# CONFIG_IPVLAN is not set +# CONFIG_VXLAN is not set +# CONFIG_GENEVE is not set +# CONFIG_BAREUDP is not set +# CONFIG_GTP is not set +# CONFIG_MACSEC is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_TUN is not set +# CONFIG_TUN_VNET_CROSS_LE is not set +# CONFIG_VETH is not set +# CONFIG_VIRTIO_NET is not set +# CONFIG_NLMON is not set +CONFIG_ETHERNET=y +# CONFIG_NET_VENDOR_ALACRITECH is not set +# CONFIG_ALTERA_TSE is not set +# CONFIG_NET_VENDOR_AMAZON is not set +# CONFIG_NET_VENDOR_AQUANTIA is not set +# CONFIG_NET_VENDOR_ARC is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_CADENCE is not set +# CONFIG_NET_VENDOR_CAVIUM is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_NET_VENDOR_CORTINA is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_EZCHIP is not set +# CONFIG_NET_VENDOR_FARADAY is not set +CONFIG_NET_VENDOR_FREESCALE=y +CONFIG_FEC=y +CONFIG_FEC_ECAT=y +# CONFIG_FEC_UIO is not set +# CONFIG_AVB_SUPPORT is not set +# CONFIG_FSL_PQ_MDIO is not set +# CONFIG_FSL_XGMAC_MDIO is not set +# CONFIG_GIANFAR is not set + +# +# Frame Manager support +# +# end of Frame Manager support + +# CONFIG_FSL_ENETC_IERB is not set +# CONFIG_NET_VENDOR_GOOGLE is not set +# CONFIG_NET_VENDOR_HISILICON is not set +# CONFIG_NET_VENDOR_HUAWEI is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_LITEX is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MELLANOX is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_MICROSEMI is not set +# CONFIG_NET_VENDOR_MICROSOFT is not set +# CONFIG_NET_VENDOR_NI is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NETRONOME is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_PENSANDO is not set +# CONFIG_NET_VENDOR_QUALCOMM is not set +# CONFIG_NET_VENDOR_RENESAS is not set +# CONFIG_NET_VENDOR_ROCKER is not set +# CONFIG_NET_VENDOR_SAMSUNG is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SOLARFLARE is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_SOCIONEXT is not set +# CONFIG_NET_VENDOR_STMICRO is not set +# CONFIG_NET_VENDOR_SYNOPSYS is not set +# CONFIG_NET_VENDOR_VIA is not set +# CONFIG_NET_VENDOR_WIZNET is not set +# CONFIG_NET_VENDOR_XILINX is not set +CONFIG_PHYLIB=y +CONFIG_SWPHY=y +CONFIG_FIXED_PHY=y + +# +# MII PHY device drivers +# +# CONFIG_AMD_PHY is not set +# CONFIG_ADIN_PHY is not set +# CONFIG_AQUANTIA_PHY is not set +CONFIG_AX88796B_PHY=y +# CONFIG_BROADCOM_PHY is not set +# CONFIG_BCM54140_PHY is not set +# CONFIG_BCM7XXX_PHY is not set +# CONFIG_BCM84881_PHY is not set +# CONFIG_BCM87XX_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_CORTINA_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_INPHI_PHY is not set +# CONFIG_INTEL_XWAY_PHY is not set +# CONFIG_LSI_ET1011C_PHY is not set +# CONFIG_MARVELL_PHY is not set +# CONFIG_MARVELL_10G_PHY is not set +# CONFIG_MARVELL_88X2222_PHY is not set +# CONFIG_MAXLINEAR_GPHY is not set +# CONFIG_MEDIATEK_GE_PHY is not set +CONFIG_MICREL_PHY=y +CONFIG_MICROCHIP_PHY=y +# CONFIG_MICROCHIP_T1_PHY is not set +# CONFIG_MICROSEMI_PHY is not set +# CONFIG_MOTORCOMM_PHY is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_NXP_C45_TJA11XX_PHY is not set +# CONFIG_NXP_TJA11XX_PHY is not set +CONFIG_AT803X_PHY=y +# CONFIG_QSEMI_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_RENESAS_PHY is not set +# CONFIG_ROCKCHIP_PHY is not set +CONFIG_SMSC_PHY=y +# CONFIG_STE10XP is not set +# CONFIG_TERANETICS_PHY is not set +# CONFIG_DP83822_PHY is not set +# CONFIG_DP83TC811_PHY is not set +# CONFIG_DP83848_PHY is not set +# CONFIG_DP83867_PHY is not set +# CONFIG_DP83869_PHY is not set +# CONFIG_VITESSE_PHY is not set +# CONFIG_XILINX_GMII2RGMII is not set +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_BUS=y +CONFIG_FWNODE_MDIO=y +CONFIG_OF_MDIO=y +CONFIG_MDIO_DEVRES=y +# CONFIG_MDIO_BITBANG is not set +# CONFIG_MDIO_BCM_UNIMAC is not set +# CONFIG_MDIO_HISI_FEMAC is not set +# CONFIG_MDIO_MSCC_MIIM is not set +# CONFIG_MDIO_IPQ4019 is not set +# CONFIG_MDIO_IPQ8064 is not set + +# +# MDIO Multiplexers +# +# CONFIG_MDIO_BUS_MUX_GPIO is not set +# CONFIG_MDIO_BUS_MUX_MULTIPLEXER is not set +# CONFIG_MDIO_BUS_MUX_MMIOREG is not set + +# +# PCS device drivers +# +# CONFIG_PCS_XPCS is not set +# end of PCS device drivers + +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Host-side USB support is needed for USB Network Adapter support +# +# CONFIG_WLAN is not set +# CONFIG_WAN is not set + +# +# Wireless WAN +# +# CONFIG_WWAN is not set +# end of Wireless WAN + +# CONFIG_NET_FAILOVER is not set +# CONFIG_IMX_SHMEM_NET is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_SPARSEKMAP is not set +# CONFIG_INPUT_MATRIXKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +CONFIG_KEYBOARD_SNVS_PWRKEY=y +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_TOUCHSCREEN_FTS is not set +# CONFIG_INPUT_MISC is not set +# CONFIG_RMI4_CORE is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set +# end of Hardware I/O ports +# end of Input device support + +# +# Character devices +# +CONFIG_TTY=y +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set +CONFIG_LDISC_AUTOLOAD=y + +# +# Serial drivers +# +CONFIG_SERIAL_EARLYCON=y +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set +CONFIG_SERIAL_IMX=y +CONFIG_SERIAL_IMX_CONSOLE=y +CONFIG_SERIAL_IMX_EARLYCON=y +# CONFIG_SERIAL_UARTLITE is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_SIFIVE is not set +# CONFIG_SERIAL_SCCNXP is not set +# CONFIG_SERIAL_SC16IS7XX is not set +# CONFIG_SERIAL_BCM63XX is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +# CONFIG_SERIAL_ARC is not set +CONFIG_SERIAL_FSL_LPUART=y +CONFIG_SERIAL_FSL_LPUART_CONSOLE=y +# CONFIG_SERIAL_FSL_LINFLEXUART is not set +# CONFIG_SERIAL_CONEXANT_DIGICOLOR is not set +# CONFIG_SERIAL_ST_ASC is not set +# CONFIG_SERIAL_SPRD is not set +# end of Serial drivers + +CONFIG_SERIAL_MCTRL_GPIO=y +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set +# CONFIG_NULL_TTY is not set +# CONFIG_HVC_DCC is not set +# CONFIG_RPMSG_TTY is not set +CONFIG_SERIAL_DEV_BUS=y +CONFIG_SERIAL_DEV_CTRL_TTYPORT=y +# CONFIG_TTY_PRINTK is not set +# CONFIG_VIRTIO_CONSOLE is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_IPMB_DEVICE_INTERFACE is not set +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +# CONFIG_HW_RANDOM_BA431 is not set +# CONFIG_HW_RANDOM_VIRTIO is not set +CONFIG_HW_RANDOM_IMX_RNGC=y +CONFIG_HW_RANDOM_OPTEE=y +# CONFIG_HW_RANDOM_CCTRNG is not set +# CONFIG_HW_RANDOM_XIPHERA is not set +CONFIG_HW_RANDOM_ARM_SMCCC_TRNG=y +# CONFIG_DEVMEM is not set +# CONFIG_TCG_TPM is not set +# CONFIG_XILLYBUS is not set +CONFIG_IMX_SEMA4=y +CONFIG_RANDOM_TRUST_BOOTLOADER=y +# end of Character devices + +# +# I2C support +# +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +# CONFIG_I2C_COMPAT is not set +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX=y + +# +# Multiplexer I2C Chip support +# +# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set +CONFIG_I2C_MUX_GPIO=y +# CONFIG_I2C_MUX_GPMUX is not set +# CONFIG_I2C_MUX_LTC4306 is not set +# CONFIG_I2C_MUX_PCA9541 is not set +# CONFIG_I2C_MUX_PCA954x is not set +# CONFIG_I2C_MUX_PINCTRL is not set +# CONFIG_I2C_MUX_REG is not set +# CONFIG_I2C_DEMUX_PINCTRL is not set +# CONFIG_I2C_MUX_MLXCPLD is not set +# end of Multiplexer I2C Chip support + +# CONFIG_I2C_HELPER_AUTO is not set +# CONFIG_I2C_SMBUS is not set + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +CONFIG_I2C_ALGOPCF=y +CONFIG_I2C_ALGOPCA=y +# end of I2C Algorithms + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_CBUS_GPIO is not set +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_EMEV2 is not set +CONFIG_I2C_GPIO=y +# CONFIG_I2C_GPIO_FAULT_INJECTOR is not set +CONFIG_I2C_IMX=y +CONFIG_I2C_IMX_LPI2C=y +CONFIG_I2C_IMX_FLEXIO=y +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_RK3X is not set +# CONFIG_I2C_RPBUS is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_TAOS_EVM is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_VIRTIO is not set +# end of I2C Hardware Bus support + +# CONFIG_I2C_STUB is not set +CONFIG_I2C_SLAVE=y +# CONFIG_I2C_SLAVE_EEPROM is not set +# CONFIG_I2C_SLAVE_TESTUNIT is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# end of I2C support + +# CONFIG_I3C is not set +# CONFIG_SPI is not set +# CONFIG_SPMI is not set +# CONFIG_HSI is not set +CONFIG_PPS=y +# CONFIG_PPS_DEBUG is not set + +# +# PPS clients support +# +# CONFIG_PPS_CLIENT_KTIMER is not set +# CONFIG_PPS_CLIENT_LDISC is not set +# CONFIG_PPS_CLIENT_GPIO is not set + +# +# PPS generators support +# + +# +# PTP clock support +# +# CONFIG_PTP_1588_CLOCK is not set +CONFIG_PTP_1588_CLOCK_OPTIONAL=y + +# +# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. +# +# end of PTP clock support + +CONFIG_PINCTRL=y +CONFIG_GENERIC_PINCTRL_GROUPS=y +CONFIG_PINMUX=y +CONFIG_GENERIC_PINMUX_FUNCTIONS=y +CONFIG_PINCONF=y +CONFIG_GENERIC_PINCONF=y +# CONFIG_DEBUG_PINCTRL is not set +# CONFIG_PINCTRL_DA9062 is not set +# CONFIG_PINCTRL_MCP23S08 is not set +# CONFIG_PINCTRL_SINGLE is not set +# CONFIG_PINCTRL_SX150X is not set +# CONFIG_PINCTRL_STMFX is not set +# CONFIG_PINCTRL_OCELOT is not set +# CONFIG_PINCTRL_MICROCHIP_SGPIO is not set +CONFIG_PINCTRL_IMX=y +CONFIG_PINCTRL_IMX6Q=y +CONFIG_PINCTRL_IMX6SL=y +CONFIG_PINCTRL_IMX6SLL=y +CONFIG_PINCTRL_IMX6SX=y +CONFIG_PINCTRL_IMX6UL=y +CONFIG_PINCTRL_IMX7D=y +# CONFIG_PINCTRL_IMX8MM is not set +# CONFIG_PINCTRL_IMX8MN is not set +# CONFIG_PINCTRL_IMX8MP is not set +# CONFIG_PINCTRL_IMX8MQ is not set +# CONFIG_PINCTRL_IMX8ULP is not set +# CONFIG_PINCTRL_IMX93 is not set +# CONFIG_PINCTRL_S32V_CORE is not set + +# +# Renesas pinctrl drivers +# +# end of Renesas pinctrl drivers + +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y +CONFIG_GPIOLIB=y +CONFIG_GPIOLIB_FASTPATH_LIMIT=512 +CONFIG_OF_GPIO=y +CONFIG_GPIOLIB_IRQCHIP=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set +CONFIG_GPIO_CDEV=y +CONFIG_GPIO_CDEV_V1=y +CONFIG_GPIO_GENERIC=y + +# +# Memory mapped GPIO drivers +# +# CONFIG_GPIO_74XX_MMIO is not set +# CONFIG_GPIO_ALTERA is not set +# CONFIG_GPIO_CADENCE is not set +# CONFIG_GPIO_DWAPB is not set +# CONFIG_GPIO_FTGPIO010 is not set +# CONFIG_GPIO_GENERIC_PLATFORM is not set +# CONFIG_GPIO_GRGPIO is not set +# CONFIG_GPIO_HLWD is not set +# CONFIG_GPIO_LOGICVC is not set +# CONFIG_GPIO_MB86S7X is not set +# CONFIG_GPIO_MPC8XXX is not set +CONFIG_GPIO_MXC=y +# CONFIG_GPIO_SAMA5D2_PIOBU is not set +# CONFIG_GPIO_SIFIVE is not set +# CONFIG_GPIO_SYSCON is not set +CONFIG_GPIO_VF610=y +CONFIG_GPIO_IMX_RPMSG=y +# CONFIG_GPIO_XILINX is not set +# CONFIG_GPIO_ZEVIO is not set +# CONFIG_GPIO_AMD_FCH is not set +# end of Memory mapped GPIO drivers + +# +# I2C GPIO expanders +# +# CONFIG_GPIO_ADP5588 is not set +# CONFIG_GPIO_ADNP is not set +# CONFIG_GPIO_GW_PLD is not set +# CONFIG_GPIO_MAX7300 is not set +CONFIG_GPIO_MAX732X=y +# CONFIG_GPIO_MAX732X_IRQ is not set +CONFIG_GPIO_PCA953X=y +# CONFIG_GPIO_PCA953X_IRQ is not set +# CONFIG_GPIO_PCA9570 is not set +CONFIG_GPIO_PCF857X=y +# CONFIG_GPIO_TPIC2810 is not set +# CONFIG_GPIO_TS4900 is not set +# end of I2C GPIO expanders + +# +# MFD GPIO expanders +# +# CONFIG_GPIO_DA9052 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_GPIO_WM8994 is not set +# end of MFD GPIO expanders + +# +# Virtual GPIO drivers +# +# CONFIG_GPIO_AGGREGATOR is not set +# CONFIG_GPIO_MOCKUP is not set +# CONFIG_GPIO_VIRTIO is not set +# end of Virtual GPIO drivers + +# CONFIG_W1 is not set +CONFIG_POWER_RESET=y +# CONFIG_POWER_RESET_BRCMKONA is not set +# CONFIG_POWER_RESET_BRCMSTB is not set +# CONFIG_POWER_RESET_GPIO is not set +# CONFIG_POWER_RESET_GPIO_RESTART is not set +# CONFIG_POWER_RESET_LTC2952 is not set +# CONFIG_POWER_RESET_REGULATOR is not set +# CONFIG_POWER_RESET_RESTART is not set +# CONFIG_POWER_RESET_VERSATILE is not set +CONFIG_POWER_RESET_SYSCON=y +CONFIG_POWER_RESET_SYSCON_POWEROFF=y +# CONFIG_SYSCON_REBOOT_MODE is not set +# CONFIG_NVMEM_REBOOT_MODE is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_POWER_SUPPLY_HWMON is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_CHARGER_ADP5061 is not set +# CONFIG_BATTERY_CW2015 is not set +# CONFIG_BATTERY_DS2780 is not set +# CONFIG_BATTERY_DS2781 is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_SBS is not set +# CONFIG_CHARGER_SBS is not set +# CONFIG_MANAGER_SBS is not set +# CONFIG_BATTERY_BQ27XXX is not set +# CONFIG_BATTERY_DA9052 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_LP8727 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_CHARGER_MANAGER is not set +# CONFIG_CHARGER_LT3651 is not set +# CONFIG_CHARGER_LTC4162L is not set +# CONFIG_CHARGER_DETECTOR_MAX14656 is not set +# CONFIG_CHARGER_BQ2415X is not set +# CONFIG_CHARGER_BQ24190 is not set +# CONFIG_CHARGER_BQ24257 is not set +# CONFIG_CHARGER_BQ24735 is not set +# CONFIG_CHARGER_BQ2515X is not set +# CONFIG_CHARGER_BQ25890 is not set +# CONFIG_CHARGER_BQ25980 is not set +# CONFIG_CHARGER_BQ256XX is not set +# CONFIG_CHARGER_SMB347 is not set +# CONFIG_BATTERY_GAUGE_LTC2941 is not set +# CONFIG_BATTERY_GOLDFISH is not set +# CONFIG_BATTERY_RT5033 is not set +# CONFIG_CHARGER_RT9455 is not set +# CONFIG_CHARGER_UCS1002 is not set +# CONFIG_CHARGER_BD99954 is not set +CONFIG_HWMON=y +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Native drivers +# +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM1177 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7410 is not set +# CONFIG_SENSORS_ADT7411 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_AHT10 is not set +# CONFIG_SENSORS_AS370 is not set +# CONFIG_SENSORS_ASC7621 is not set +# CONFIG_SENSORS_AXI_FAN_CONTROL is not set +# CONFIG_SENSORS_ASPEED is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_CORSAIR_CPRO is not set +# CONFIG_SENSORS_CORSAIR_PSU is not set +# CONFIG_SENSORS_DS620 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_DA9052_ADC is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +CONFIG_SENSORS_MC13783_ADC=y +# CONFIG_SENSORS_FTSTEUTATES is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_G762 is not set +CONFIG_SENSORS_GPIO_FAN=y +# CONFIG_SENSORS_HIH6130 is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_JC42 is not set +# CONFIG_SENSORS_POWR1220 is not set +# CONFIG_SENSORS_LINEAGE is not set +# CONFIG_SENSORS_LTC2945 is not set +# CONFIG_SENSORS_LTC2947_I2C is not set +# CONFIG_SENSORS_LTC2990 is not set +# CONFIG_SENSORS_LTC2992 is not set +# CONFIG_SENSORS_LTC4151 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4222 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LTC4260 is not set +# CONFIG_SENSORS_LTC4261 is not set +# CONFIG_SENSORS_MAX127 is not set +# CONFIG_SENSORS_MAX16065 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX1668 is not set +CONFIG_SENSORS_MAX17135=y +# CONFIG_SENSORS_MAX197 is not set +# CONFIG_SENSORS_MAX31730 is not set +# CONFIG_SENSORS_MAX6621 is not set +# CONFIG_SENSORS_MAX6639 is not set +# CONFIG_SENSORS_MAX6642 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_MAX6697 is not set +# CONFIG_SENSORS_MAX31790 is not set +# CONFIG_SENSORS_MCP3021 is not set +# CONFIG_SENSORS_TC654 is not set +# CONFIG_SENSORS_TPS23861 is not set +# CONFIG_SENSORS_MR75203 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM73 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LM95234 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_LM95245 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_NTC_THERMISTOR is not set +# CONFIG_SENSORS_NCT6683 is not set +# CONFIG_SENSORS_NCT6775 is not set +# CONFIG_SENSORS_NCT7802 is not set +# CONFIG_SENSORS_NCT7904 is not set +# CONFIG_SENSORS_NPCM7XX is not set +# CONFIG_SENSORS_OCC_P8_I2C is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_PMBUS is not set +# CONFIG_SENSORS_SBTSI is not set +# CONFIG_SENSORS_SBRMI is not set +# CONFIG_SENSORS_SHT15 is not set +# CONFIG_SENSORS_SHT21 is not set +# CONFIG_SENSORS_SHT3x is not set +# CONFIG_SENSORS_SHT4x is not set +# CONFIG_SENSORS_SHTC1 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_EMC1403 is not set +# CONFIG_SENSORS_EMC2103 is not set +# CONFIG_SENSORS_EMC6W201 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_SCH5627 is not set +# CONFIG_SENSORS_SCH5636 is not set +# CONFIG_SENSORS_STTS751 is not set +# CONFIG_SENSORS_SMM665 is not set +# CONFIG_SENSORS_ADC128D818 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_AMC6821 is not set +# CONFIG_SENSORS_INA209 is not set +# CONFIG_SENSORS_INA2XX is not set +# CONFIG_SENSORS_INA3221 is not set +# CONFIG_SENSORS_TC74 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP102 is not set +# CONFIG_SENSORS_TMP103 is not set +# CONFIG_SENSORS_TMP108 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set +# CONFIG_SENSORS_TMP513 is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_W83773G is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83795 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set +CONFIG_THERMAL=y +# CONFIG_THERMAL_NETLINK is not set +CONFIG_THERMAL_STATISTICS=y +CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 +CONFIG_THERMAL_HWMON=y +CONFIG_THERMAL_OF=y +CONFIG_THERMAL_WRITABLE_TRIPS=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set +# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set +# CONFIG_THERMAL_GOV_FAIR_SHARE is not set +CONFIG_THERMAL_GOV_STEP_WISE=y +# CONFIG_THERMAL_GOV_BANG_BANG is not set +# CONFIG_THERMAL_GOV_USER_SPACE is not set +CONFIG_CPU_THERMAL=y +CONFIG_CPU_FREQ_THERMAL=y +# CONFIG_THERMAL_EMULATION is not set +# CONFIG_THERMAL_MMIO is not set +CONFIG_IMX_THERMAL=y +# CONFIG_IMX8MM_THERMAL is not set +CONFIG_DEVICE_THERMAL=y +# CONFIG_DA9062_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +# CONFIG_WATCHDOG_NOWAYOUT is not set +CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED=y +CONFIG_WATCHDOG_OPEN_TIMEOUT=0 +# CONFIG_WATCHDOG_SYSFS is not set +# CONFIG_WATCHDOG_HRTIMER_PRETIMEOUT is not set + +# +# Watchdog Pretimeout Governors +# +# CONFIG_WATCHDOG_PRETIMEOUT_GOV is not set + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_DA9052_WATCHDOG is not set +CONFIG_DA9063_WATCHDOG=y +CONFIG_DA9062_WATCHDOG=y +# CONFIG_GPIO_WATCHDOG is not set +# CONFIG_XILINX_WATCHDOG is not set +# CONFIG_ZIIRAVE_WATCHDOG is not set +# CONFIG_CADENCE_WATCHDOG is not set +# CONFIG_FTWDT010_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +CONFIG_RN5T618_WATCHDOG=y +# CONFIG_MAX63XX_WATCHDOG is not set +CONFIG_IMX2_WDT=y +CONFIG_IMX7ULP_WDT=y +# CONFIG_ARM_SMC_WATCHDOG is not set +# CONFIG_MEN_A21_WDT is not set +CONFIG_SSB_POSSIBLE=y +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +CONFIG_MFD_CORE=y +# CONFIG_MFD_ADP5585 is not set +# CONFIG_MFD_ACT8945A is not set +# CONFIG_MFD_AS3711 is not set +# CONFIG_MFD_AS3722 is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_ATMEL_FLEXCOM is not set +# CONFIG_MFD_ATMEL_HLCDC is not set +# CONFIG_MFD_BCM590XX is not set +# CONFIG_MFD_BD9571MWV is not set +# CONFIG_MFD_AXP20X_I2C is not set +# CONFIG_MFD_MADERA is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_PMIC_DA903X is not set +CONFIG_PMIC_DA9052=y +CONFIG_MFD_DA9052_I2C=y +# CONFIG_MFD_DA9055 is not set +CONFIG_MFD_DA9062=y +CONFIG_MFD_DA9063=y +# CONFIG_MFD_DA9150 is not set +# CONFIG_MFD_GATEWORKS_GSC is not set +CONFIG_MFD_MC13XXX=y +CONFIG_MFD_MC13XXX_I2C=y +# CONFIG_MFD_MP2629 is not set +# CONFIG_MFD_IMX_MIX is not set +CONFIG_MFD_MXC_HDMI=y +# CONFIG_MFD_HI6421_PMIC is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_MFD_IQS62X is not set +# CONFIG_MFD_KEMPLD is not set +# CONFIG_MFD_88PM800 is not set +# CONFIG_MFD_88PM805 is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_MAX14577 is not set +CONFIG_MFD_MAX17135=y +# CONFIG_MFD_FP9931 is not set +# CONFIG_MFD_MAX77620 is not set +# CONFIG_MFD_MAX77650 is not set +# CONFIG_MFD_MAX77686 is not set +# CONFIG_MFD_MAX77693 is not set +# CONFIG_MFD_MAX77843 is not set +# CONFIG_MFD_MAX8907 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_MT6360 is not set +# CONFIG_MFD_MT6397 is not set +# CONFIG_MFD_MENF21BMC is not set +# CONFIG_MFD_NTXEC is not set +# CONFIG_MFD_RETU is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_PM8XXX is not set +# CONFIG_MFD_RT4831 is not set +# CONFIG_MFD_RT5033 is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_MFD_RK808 is not set +CONFIG_MFD_RN5T618=y +# CONFIG_MFD_SEC_CORE is not set +CONFIG_MFD_SI476X_CORE=y +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_SKY81452 is not set +# CONFIG_MFD_STMPE is not set +CONFIG_MFD_SYSCON=y +# CONFIG_MFD_TI_AM335X_TSCADC is not set +# CONFIG_MFD_LP3943 is not set +# CONFIG_MFD_LP8788 is not set +# CONFIG_MFD_TI_LMU is not set +# CONFIG_MFD_PALMAS is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65086 is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TI_LP873X is not set +# CONFIG_MFD_TI_LP87565 is not set +# CONFIG_MFD_TPS65218 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS80031 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_LM3533 is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_MFD_TQMX86 is not set +# CONFIG_MFD_LOCHNAGAR is not set +# CONFIG_MFD_ARIZONA_I2C is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM8350_I2C is not set +CONFIG_MFD_WM8994=y +# CONFIG_MFD_ROHM_BD718XX is not set +# CONFIG_MFD_ROHM_BD70528 is not set +# CONFIG_MFD_ROHM_BD71828 is not set +# CONFIG_MFD_ROHM_BD957XMUF is not set +# CONFIG_MFD_STPMIC1 is not set +# CONFIG_MFD_STMFX is not set +# CONFIG_MFD_ATC260X_I2C is not set +# CONFIG_MFD_QCOM_PM8008 is not set +# CONFIG_RAVE_SP_CORE is not set +# CONFIG_MFD_RSMU_I2C is not set +# end of Multifunction device drivers + +CONFIG_REGULATOR=y +# CONFIG_REGULATOR_DEBUG is not set +CONFIG_REGULATOR_FIXED_VOLTAGE=y +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_88PG86X is not set +# CONFIG_REGULATOR_ACT8865 is not set +# CONFIG_REGULATOR_AD5398 is not set +CONFIG_REGULATOR_ANATOP=y +CONFIG_REGULATOR_DA9052=y +CONFIG_REGULATOR_DA9062=y +CONFIG_REGULATOR_DA9063=y +# CONFIG_REGULATOR_DA9121 is not set +# CONFIG_REGULATOR_DA9210 is not set +# CONFIG_REGULATOR_DA9211 is not set +# CONFIG_REGULATOR_FAN53555 is not set +# CONFIG_REGULATOR_FAN53880 is not set +CONFIG_REGULATOR_GPIO=y +# CONFIG_REGULATOR_ISL9305 is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +# CONFIG_REGULATOR_LP872X is not set +# CONFIG_REGULATOR_LP8755 is not set +# CONFIG_REGULATOR_LTC3589 is not set +CONFIG_REGULATOR_LTC3676=y +# CONFIG_REGULATOR_MAX1586 is not set +CONFIG_REGULATOR_MAX17135=y +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8893 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_MAX8973 is not set +# CONFIG_REGULATOR_MAX77826 is not set +CONFIG_REGULATOR_MC13XXX_CORE=y +CONFIG_REGULATOR_MC13783=y +CONFIG_REGULATOR_MC13892=y +# CONFIG_REGULATOR_MCP16502 is not set +# CONFIG_REGULATOR_MP5416 is not set +# CONFIG_REGULATOR_MP8859 is not set +# CONFIG_REGULATOR_MP886X is not set +# CONFIG_REGULATOR_MPQ7920 is not set +# CONFIG_REGULATOR_MT6311 is not set +# CONFIG_REGULATOR_PCA9450 is not set +# CONFIG_REGULATOR_PF8X00 is not set +# CONFIG_REGULATOR_PF1550_RPMSG is not set +CONFIG_REGULATOR_PFUZE100=y +# CONFIG_REGULATOR_PV88060 is not set +# CONFIG_REGULATOR_PV88080 is not set +# CONFIG_REGULATOR_PV88090 is not set +# CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY is not set +CONFIG_REGULATOR_RN5T618=y +# CONFIG_REGULATOR_RT4801 is not set +# CONFIG_REGULATOR_RT6160 is not set +# CONFIG_REGULATOR_RT6245 is not set +# CONFIG_REGULATOR_RTQ2134 is not set +# CONFIG_REGULATOR_RTMV20 is not set +# CONFIG_REGULATOR_RTQ6752 is not set +# CONFIG_REGULATOR_SLG51000 is not set +# CONFIG_REGULATOR_SY8106A is not set +# CONFIG_REGULATOR_SY8824X is not set +# CONFIG_REGULATOR_SY8827N is not set +# CONFIG_REGULATOR_TPS51632 is not set +# CONFIG_REGULATOR_TPS62360 is not set +# CONFIG_REGULATOR_TPS65023 is not set +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_TPS65132 is not set +# CONFIG_REGULATOR_VCTRL is not set +# CONFIG_REGULATOR_WM8994 is not set +# CONFIG_RC_CORE is not set +# CONFIG_MEDIA_CEC_SUPPORT is not set +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_IMX_IPUV3_CORE is not set +# CONFIG_IMX_DPU_CORE is not set +# CONFIG_IMX_LCDIF_CORE is not set +# CONFIG_IMX_LCDIFV3_CORE is not set +CONFIG_DRM=y +# CONFIG_DRM_DP_AUX_CHARDEV is not set +# CONFIG_DRM_DEBUG_MM is not set +# CONFIG_DRM_DEBUG_SELFTEST is not set +CONFIG_DRM_KMS_HELPER=y +# CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS is not set +# CONFIG_DRM_LOAD_EDID_FIRMWARE is not set +# CONFIG_DRM_DP_CEC is not set + +# +# I2C encoder or helper chips +# +# CONFIG_DRM_I2C_CH7006 is not set +# CONFIG_DRM_I2C_SIL164 is not set +# CONFIG_DRM_I2C_NXP_TDA998X is not set +# CONFIG_DRM_I2C_NXP_TDA9950 is not set +# end of I2C encoder or helper chips + +# +# ARM devices +# +# CONFIG_DRM_HDLCD is not set +# CONFIG_DRM_MALI_DISPLAY is not set +# CONFIG_DRM_KOMEDA is not set +# end of ARM devices + +# CONFIG_DRM_VGEM is not set +# CONFIG_DRM_VKMS is not set +# CONFIG_DRM_EXYNOS is not set +# CONFIG_DRM_ARMADA is not set +# CONFIG_DRM_RCAR_DW_HDMI is not set +# CONFIG_DRM_RCAR_LVDS is not set +# CONFIG_DRM_OMAP is not set +# CONFIG_DRM_TILCDC is not set +# CONFIG_DRM_FSL_DCU is not set +# CONFIG_DRM_STM is not set +CONFIG_DRM_PANEL=y + +# +# Display Panels +# +# CONFIG_DRM_PANEL_ARM_VERSATILE is not set +CONFIG_DRM_PANEL_LVDS=y +# CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6E63M0 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0 is not set +CONFIG_DRM_PANEL_SEIKO_43WVF1G=y +# CONFIG_DRM_PANEL_SHARP_LS037V7DW01 is not set +# CONFIG_DRM_PANEL_WKS_101WX001 is not set +# end of Display Panels + +CONFIG_DRM_BRIDGE=y +CONFIG_DRM_PANEL_BRIDGE=y + +# +# Display Interface Bridges +# +# CONFIG_DRM_CDNS_DSI is not set +# CONFIG_DRM_CHIPONE_ICN6211 is not set +# CONFIG_DRM_CHRONTEL_CH7033 is not set +# CONFIG_DRM_DISPLAY_CONNECTOR is not set +# CONFIG_DRM_LONTIUM_LT8912B is not set +# CONFIG_DRM_LONTIUM_LT9611 is not set +# CONFIG_DRM_LONTIUM_LT9611UXC is not set +# CONFIG_DRM_ITE_IT66121 is not set +# CONFIG_DRM_FSL_IMX_LVDS_BRIDGE is not set +# CONFIG_DRM_LVDS_CODEC is not set +# CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW is not set +# CONFIG_DRM_NWL_MIPI_DSI is not set +# CONFIG_DRM_SEC_MIPI_DSIM is not set +# CONFIG_DRM_NXP_SEIKO_43WVFIG is not set +# CONFIG_DRM_NXP_PTN3460 is not set +# CONFIG_DRM_PARADE_PS8622 is not set +# CONFIG_DRM_PARADE_PS8640 is not set +# CONFIG_DRM_SIL_SII8620 is not set +# CONFIG_DRM_SII902X is not set +# CONFIG_DRM_SII9234 is not set +# CONFIG_DRM_SIMPLE_BRIDGE is not set +# CONFIG_DRM_THINE_THC63LVD1024 is not set +# CONFIG_DRM_TOSHIBA_TC358762 is not set +# CONFIG_DRM_TOSHIBA_TC358764 is not set +# CONFIG_DRM_TOSHIBA_TC358767 is not set +# CONFIG_DRM_TOSHIBA_TC358768 is not set +# CONFIG_DRM_TOSHIBA_TC358775 is not set +CONFIG_DRM_TI_TFP410=y +# CONFIG_DRM_TI_SN65DSI83 is not set +# CONFIG_DRM_TI_SN65DSI86 is not set +# CONFIG_DRM_TI_TPD12S015 is not set +# CONFIG_DRM_ANALOGIX_ANX6345 is not set +# CONFIG_DRM_ANALOGIX_ANX78XX is not set +# CONFIG_DRM_ANALOGIX_ANX7625 is not set +# CONFIG_DRM_I2C_ADV7511 is not set +# CONFIG_DRM_CDNS_MHDP8546 is not set +# CONFIG_DRM_CDNS_MHDP is not set +# CONFIG_DRM_CDNS_HDMI_CEC is not set +# CONFIG_DRM_ITE_IT6263 is not set +# CONFIG_DRM_ITE_IT6161 is not set +# end of Display Interface Bridges + +# CONFIG_DRM_STI is not set +# CONFIG_IMX8MP_HDMI_PAVI is not set +# CONFIG_DRM_IMX_DCNANO is not set +# CONFIG_DRM_ETNAVIV is not set +# CONFIG_DRM_MXSFB is not set +# CONFIG_DRM_ARCPGU is not set +# CONFIG_DRM_SIMPLEDRM is not set +# CONFIG_DRM_PL111 is not set +# CONFIG_DRM_TVE200 is not set +# CONFIG_DRM_LIMA is not set +# CONFIG_DRM_PANFROST is not set +# CONFIG_DRM_MCDE is not set +# CONFIG_DRM_TIDSS is not set +# CONFIG_DRM_LEGACY is not set +CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=y + +# +# Frame buffer Devices +# +CONFIG_FB_CMDLINE=y +# CONFIG_FB is not set +# CONFIG_FB_MXC_DISP_FRAMEWORK is not set +# end of Frame buffer Devices + +# +# Backlight & LCD device support +# +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_LCD_PLATFORM=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +# CONFIG_BACKLIGHT_KTD253 is not set +# CONFIG_BACKLIGHT_DA9052 is not set +# CONFIG_BACKLIGHT_QCOM_WLED is not set +# CONFIG_BACKLIGHT_ADP8860 is not set +# CONFIG_BACKLIGHT_ADP8870 is not set +# CONFIG_BACKLIGHT_LM3639 is not set +CONFIG_BACKLIGHT_GPIO=y +# CONFIG_BACKLIGHT_LV5207LP is not set +# CONFIG_BACKLIGHT_BD6107 is not set +# CONFIG_BACKLIGHT_ARCXCNN is not set +# end of Backlight & LCD device support + +CONFIG_VIDEOMODE_HELPERS=y +CONFIG_HDMI=y + +# +# Console display driver support +# +CONFIG_DUMMY_CONSOLE=y +# end of Console display driver support +# end of Graphics support + +# CONFIG_SOUND is not set + +# +# HID support +# +CONFIG_HID=y +# CONFIG_HID_BATTERY_STRENGTH is not set +# CONFIG_HIDRAW is not set +# CONFIG_UHID is not set +CONFIG_HID_GENERIC=y + +# +# Special HID drivers +# +# CONFIG_HID_A4TECH is not set +# CONFIG_HID_ACRUX is not set +# CONFIG_HID_APPLE is not set +# CONFIG_HID_AUREAL is not set +# CONFIG_HID_BELKIN is not set +# CONFIG_HID_CHERRY is not set +# CONFIG_HID_COUGAR is not set +# CONFIG_HID_MACALLY is not set +# CONFIG_HID_CMEDIA is not set +# CONFIG_HID_CYPRESS is not set +# CONFIG_HID_DRAGONRISE is not set +# CONFIG_HID_EMS_FF is not set +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_EZKEY is not set +# CONFIG_HID_GEMBIRD is not set +# CONFIG_HID_GFRM is not set +# CONFIG_HID_GLORIOUS is not set +# CONFIG_HID_VIVALDI is not set +# CONFIG_HID_KEYTOUCH is not set +# CONFIG_HID_KYE is not set +# CONFIG_HID_WALTOP is not set +# CONFIG_HID_VIEWSONIC is not set +# CONFIG_HID_GYRATION is not set +# CONFIG_HID_ICADE is not set +# CONFIG_HID_ITE is not set +# CONFIG_HID_JABRA is not set +# CONFIG_HID_TWINHAN is not set +# CONFIG_HID_KENSINGTON is not set +# CONFIG_HID_LCPOWER is not set +# CONFIG_HID_LENOVO is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_MALTRON is not set +# CONFIG_HID_MAYFLASH is not set +# CONFIG_HID_REDRAGON is not set +# CONFIG_HID_MICROSOFT is not set +# CONFIG_HID_MONTEREY is not set +CONFIG_HID_MULTITOUCH=y +# CONFIG_HID_NTI is not set +# CONFIG_HID_ORTEK is not set +# CONFIG_HID_PANTHERLORD is not set +# CONFIG_HID_PETALYNX is not set +# CONFIG_HID_PICOLCD is not set +# CONFIG_HID_PLANTRONICS is not set +# CONFIG_HID_PLAYSTATION is not set +# CONFIG_HID_PRIMAX is not set +# CONFIG_HID_SAITEK is not set +# CONFIG_HID_SEMITEK is not set +# CONFIG_HID_SPEEDLINK is not set +# CONFIG_HID_STEAM is not set +# CONFIG_HID_STEELSERIES is not set +# CONFIG_HID_SUNPLUS is not set +# CONFIG_HID_RMI is not set +# CONFIG_HID_GREENASIA is not set +# CONFIG_HID_SMARTJOYPLUS is not set +# CONFIG_HID_TIVO is not set +# CONFIG_HID_TOPSEED is not set +# CONFIG_HID_UDRAW_PS3 is not set +# CONFIG_HID_XINMO is not set +# CONFIG_HID_ZEROPLUS is not set +# CONFIG_HID_ZYDACRON is not set +# CONFIG_HID_SENSOR_HUB is not set +# CONFIG_HID_ALPS is not set +# end of Special HID drivers + +# +# I2C HID support +# +# CONFIG_I2C_HID_OF is not set +# CONFIG_I2C_HID_OF_GOODIX is not set +# end of I2C HID support +# end of HID support + +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +# CONFIG_USB_SUPPORT is not set +CONFIG_MMC=y +CONFIG_PWRSEQ_EMMC=y +CONFIG_PWRSEQ_SIMPLE=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_IO_ACCESSORS=y +CONFIG_MMC_SDHCI_PLTFM=y +# CONFIG_MMC_SDHCI_OF_ARASAN is not set +# CONFIG_MMC_SDHCI_OF_ASPEED is not set +# CONFIG_MMC_SDHCI_OF_AT91 is not set +# CONFIG_MMC_SDHCI_OF_ESDHC is not set +# CONFIG_MMC_SDHCI_OF_DWCMSHC is not set +# CONFIG_MMC_SDHCI_CADENCE is not set +CONFIG_MMC_SDHCI_ESDHC_IMX=y +# CONFIG_MMC_SDHCI_F_SDH30 is not set +# CONFIG_MMC_SDHCI_MILBEAUT is not set +# CONFIG_MMC_MXC is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_USDHI6ROL0 is not set +CONFIG_MMC_CQHCI=y +# CONFIG_MMC_HSQ is not set +# CONFIG_MMC_MTK is not set +# CONFIG_MMC_SDHCI_XENON is not set +# CONFIG_MMC_SDHCI_OMAP is not set +# CONFIG_MMC_SDHCI_AM654 is not set +# CONFIG_MEMSTICK is not set +# CONFIG_NEW_LEDS is not set +# CONFIG_ACCESSIBILITY is not set +# CONFIG_INFINIBAND is not set +CONFIG_EDAC_ATOMIC_SCRUB=y +CONFIG_EDAC_SUPPORT=y +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +CONFIG_RTC_SYSTOHC=y +CONFIG_RTC_SYSTOHC_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set +CONFIG_RTC_NVMEM=y + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +CONFIG_RTC_INTF_DEV_UIE_EMUL=y +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_ABB5ZES3 is not set +# CONFIG_RTC_DRV_ABEOZ9 is not set +# CONFIG_RTC_DRV_ABX80X is not set +CONFIG_RTC_DRV_DS1307=y +# CONFIG_RTC_DRV_DS1307_CENTURY is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_HYM8563 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +CONFIG_RTC_DRV_ISL1208=y +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_ISL12026 is not set +# CONFIG_RTC_DRV_X1205 is not set +CONFIG_RTC_DRV_PCF8523=y +# CONFIG_RTC_DRV_PCF85063 is not set +# CONFIG_RTC_DRV_PCF85363 is not set +CONFIG_RTC_DRV_PCF8563=y +# CONFIG_RTC_DRV_PCF8583 is not set +CONFIG_RTC_DRV_M41T80=y +# CONFIG_RTC_DRV_M41T80_WDT is not set +# CONFIG_RTC_DRV_BQ32K is not set +CONFIG_RTC_DRV_RC5T619=y +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8010 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3028 is not set +# CONFIG_RTC_DRV_RV3032 is not set +# CONFIG_RTC_DRV_RV8803 is not set +# CONFIG_RTC_DRV_SD3078 is not set + +# +# SPI RTC drivers +# +CONFIG_RTC_I2C_AND_SPI=y + +# +# SPI and I2C RTC drivers +# +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_PCF2127 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set +# CONFIG_RTC_DRV_RX6110 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1685_FAMILY is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_DS2404 is not set +# CONFIG_RTC_DRV_DA9052 is not set +CONFIG_RTC_DRV_DA9063=y +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set +# CONFIG_RTC_DRV_ZYNQMP is not set + +# +# on-CPU RTC drivers +# +# CONFIG_RTC_DRV_IMXDI is not set +# CONFIG_RTC_DRV_CADENCE is not set +# CONFIG_RTC_DRV_FTRTC010 is not set +CONFIG_RTC_DRV_MC13XXX=y +CONFIG_RTC_DRV_MXC=y +CONFIG_RTC_DRV_MXC_V2=y +CONFIG_RTC_DRV_SNVS=y +# CONFIG_RTC_DRV_BBNSM is not set +CONFIG_RTC_DRV_IMX_RPMSG=y +# CONFIG_RTC_DRV_R7301 is not set + +# +# HID Sensor RTC drivers +# +# CONFIG_RTC_DRV_GOLDFISH is not set +CONFIG_DMADEVICES=y +# CONFIG_DMADEVICES_DEBUG is not set + +# +# DMA Devices +# +CONFIG_DMA_ENGINE=y +CONFIG_DMA_VIRTUAL_CHANNELS=y +CONFIG_DMA_OF=y +# CONFIG_ALTERA_MSGDMA is not set +# CONFIG_CRYPTO_DEV_FSL_CAAM_DMA is not set +# CONFIG_DW_AXI_DMAC is not set +CONFIG_FSL_EDMA=y +# CONFIG_FSL_QDMA is not set +# CONFIG_FSL_EDMA_V3 is not set +# CONFIG_IMX_DMA is not set +CONFIG_IMX_SDMA=y +# CONFIG_INTEL_IDMA64 is not set +CONFIG_MXS_DMA=y +# CONFIG_MXC_PXP_V2 is not set +# CONFIG_MXC_PXP_V3 is not set +# CONFIG_MX3_IPU is not set +# CONFIG_NBPFAXI_DMA is not set +# CONFIG_XILINX_ZYNQMP_DPDMA is not set +# CONFIG_QCOM_HIDMA_MGMT is not set +# CONFIG_QCOM_HIDMA is not set +# CONFIG_DW_DMAC is not set +# CONFIG_SF_PDMA is not set + +# +# DMA Clients +# +# CONFIG_ASYNC_TX_DMA is not set +# CONFIG_DMATEST is not set + +# +# DMABUF options +# +CONFIG_SYNC_FILE=y +# CONFIG_UDMABUF is not set +# CONFIG_DMABUF_MOVE_NOTIFY is not set +# CONFIG_DMABUF_DEBUG is not set +# CONFIG_DMABUF_SELFTESTS is not set +# CONFIG_DMABUF_HEAPS is not set +# CONFIG_DMABUF_SYSFS_STATS is not set +# end of DMABUF options + +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +# CONFIG_VFIO is not set +# CONFIG_VIRT_DRIVERS is not set +CONFIG_VIRTIO=y +# CONFIG_VIRTIO_MENU is not set +# CONFIG_VDPA is not set +# CONFIG_VHOST_MENU is not set + +# +# Microsoft Hyper-V guest support +# +# end of Microsoft Hyper-V guest support + +# CONFIG_GREYBUS is not set +# CONFIG_COMEDI is not set +# CONFIG_STAGING is not set +# CONFIG_GOLDFISH is not set +# CONFIG_CHROME_PLATFORMS is not set +# CONFIG_MELLANOX_PLATFORM is not set +CONFIG_HAVE_CLK=y +CONFIG_HAVE_CLK_PREPARE=y +CONFIG_COMMON_CLK=y + +# +# Clock driver for ARM Reference designs +# +# CONFIG_ICST is not set +# CONFIG_CLK_SP810 is not set +# end of Clock driver for ARM Reference designs + +# CONFIG_COMMON_CLK_MAX9485 is not set +# CONFIG_COMMON_CLK_SI5341 is not set +# CONFIG_COMMON_CLK_SI5351 is not set +# CONFIG_COMMON_CLK_SI514 is not set +# CONFIG_COMMON_CLK_SI544 is not set +# CONFIG_COMMON_CLK_SI570 is not set +# CONFIG_COMMON_CLK_CDCE706 is not set +# CONFIG_COMMON_CLK_CDCE925 is not set +# CONFIG_COMMON_CLK_CS2000_CP is not set +# CONFIG_COMMON_CLK_AXI_CLKGEN is not set +# CONFIG_COMMON_CLK_VC5 is not set +# CONFIG_COMMON_CLK_FIXED_MMIO is not set +CONFIG_MXC_CLK=y +CONFIG_CLK_IMX6Q=y +CONFIG_CLK_IMX6SL=y +CONFIG_CLK_IMX6SLL=y +CONFIG_CLK_IMX6SX=y +CONFIG_CLK_IMX6UL=y +CONFIG_CLK_IMX7D=y +# CONFIG_CLK_IMX8MM is not set +# CONFIG_CLK_IMX8MN is not set +# CONFIG_CLK_IMX8MP is not set +# CONFIG_CLK_IMX8MQ is not set +# CONFIG_CLK_IMX8ULP is not set +# CONFIG_CLK_IMX93 is not set +# CONFIG_ARCH_S32_CLK is not set +# CONFIG_XILINX_VCU is not set +# CONFIG_HWSPINLOCK is not set + +# +# Clock Source drivers +# +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_CLKSRC_MMIO=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +CONFIG_CLKSRC_IMX_GPT=y +# CONFIG_MICROCHIP_PIT64B is not set +# end of Clock Source drivers + +CONFIG_MAILBOX=y +CONFIG_IMX_MBOX=y +# CONFIG_PLATFORM_MHU is not set +# CONFIG_ALTERA_MBOX is not set +# CONFIG_MAILBOX_TEST is not set +CONFIG_IOMMU_SUPPORT=y + +# +# Generic IOMMU Pagetable Support +# +# CONFIG_IOMMU_IO_PGTABLE_LPAE is not set +# CONFIG_IOMMU_IO_PGTABLE_ARMV7S is not set +# end of Generic IOMMU Pagetable Support + +# CONFIG_ARM_SMMU is not set + +# +# Remoteproc drivers +# +# CONFIG_REMOTEPROC is not set +# end of Remoteproc drivers + +# +# Rpmsg drivers +# +CONFIG_RPMSG=y +# CONFIG_RPMSG_CHAR is not set +# CONFIG_RPMSG_CTRL is not set +CONFIG_RPMSG_NS=y +# CONFIG_RPMSG_QCOM_GLINK_RPM is not set +CONFIG_RPMSG_VIRTIO=y +# CONFIG_IMX_RPMSG_PINGPONG is not set +# CONFIG_IMX_RPMSG_TTY is not set +# end of Rpmsg drivers + +# CONFIG_SOUNDWIRE is not set + +# +# SOC (System On Chip) specific Drivers +# + +# +# Amlogic SoC drivers +# +# end of Amlogic SoC drivers + +# +# Broadcom SoC drivers +# +# CONFIG_SOC_BRCMSTB is not set +# end of Broadcom SoC drivers + +# +# NXP/Freescale QorIQ SoC drivers +# +# CONFIG_QUICC_ENGINE is not set +# CONFIG_FSL_QIXIS is not set +# end of NXP/Freescale QorIQ SoC drivers + +# +# i.MX SoC drivers +# +# CONFIG_SOC_IMX_MU is not set +# CONFIG_SOC_IMX9 is not set +# CONFIG_SOC_IMX8M is not set +# CONFIG_RPMSG_LIFE_CYCLE is not set +# CONFIG_IMX8ULP_LPM_CTRL is not set +# end of i.MX SoC drivers + +# +# Enable LiteX SoC Builder specific drivers +# +# CONFIG_LITEX_SOC_CONTROLLER is not set +# end of Enable LiteX SoC Builder specific drivers + +# +# Qualcomm SoC drivers +# +# end of Qualcomm SoC drivers + +# CONFIG_SOC_TI is not set + +# +# Xilinx SoC drivers +# +# end of Xilinx SoC drivers +# end of SOC (System On Chip) specific Drivers + +# CONFIG_PM_DEVFREQ is not set +CONFIG_EXTCON=y + +# +# Extcon Device Drivers +# +# CONFIG_EXTCON_FSA9480 is not set +# CONFIG_EXTCON_GPIO is not set +# CONFIG_EXTCON_MAX3355 is not set +# CONFIG_EXTCON_PTN5150 is not set +# CONFIG_EXTCON_RT8973A is not set +# CONFIG_EXTCON_SM5502 is not set +CONFIG_EXTCON_USB_GPIO=y +# CONFIG_EXTCON_USBC_TUSB320 is not set +# CONFIG_MEMORY is not set +# CONFIG_IIO is not set +# CONFIG_PWM is not set + +# +# IRQ chip support +# +CONFIG_IRQCHIP=y +CONFIG_ARM_GIC=y +CONFIG_ARM_GIC_MAX_NR=1 +# CONFIG_AL_FIC is not set +CONFIG_IMX_GPCV2=y +# CONFIG_IMX_IRQSTEER is not set +CONFIG_IMX_INTMUX=y +# end of IRQ chip support + +# CONFIG_IPACK_BUS is not set +CONFIG_ARCH_HAS_RESET_CONTROLLER=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RESET_DISPMIX=y +CONFIG_RESET_IMX7=y +# CONFIG_RESET_IMX8ULP_SIM is not set +CONFIG_RESET_GPIO=y +# CONFIG_RESET_TI_SYSCON is not set + +# +# PHY Subsystem +# +CONFIG_GENERIC_PHY=y +# CONFIG_PHY_CAN_TRANSCEIVER is not set +# CONFIG_PHY_MIXEL_LVDS is not set +# CONFIG_PHY_MIXEL_LVDS_COMBO is not set +# CONFIG_BCM_KONA_USB2_PHY is not set +# CONFIG_PHY_CADENCE_TORRENT is not set +# CONFIG_PHY_CADENCE_DPHY is not set +# CONFIG_PHY_CADENCE_SIERRA is not set +# CONFIG_PHY_CADENCE_SALVO is not set +# CONFIG_PHY_FSL_IMX8MP_LVDS is not set +# CONFIG_PHY_FSL_IMX8MQ_USB is not set +# CONFIG_PHY_FSL_IMX93_MIPI_DPHY is not set +# CONFIG_PHY_MIXEL_MIPI_DPHY is not set +# CONFIG_PHY_SAMSUNG_HDMI_PHY is not set +CONFIG_PHY_FSL_IMX_PCIE=y +# CONFIG_PHY_FSL_LYNX_28G is not set +# CONFIG_PHY_PXA_28NM_HSIC is not set +# CONFIG_PHY_PXA_28NM_USB2 is not set +# CONFIG_PHY_OCELOT_SERDES is not set +# end of PHY Subsystem + +# CONFIG_POWERCAP is not set +# CONFIG_MCB is not set +# CONFIG_RAS is not set + +# +# Android +# +# CONFIG_ANDROID is not set +# end of Android + +# CONFIG_DAX is not set +CONFIG_NVMEM=y +CONFIG_NVMEM_SYSFS=y +# CONFIG_NVMEM_IMX_IIM is not set +CONFIG_NVMEM_IMX_OCOTP=y +CONFIG_NVMEM_SNVS_LPGPR=y +# CONFIG_NVMEM_RMEM is not set + +# +# HW tracing support +# +# CONFIG_STM is not set +# CONFIG_INTEL_TH is not set +# end of HW tracing support + +# CONFIG_FPGA is not set +# CONFIG_FSI is not set +CONFIG_TEE=y + +# +# TEE drivers +# +CONFIG_OPTEE=y +CONFIG_OPTEE_SHM_NUM_PRIV_PAGES=1 +# end of TEE drivers + +CONFIG_PM_OPP=y +# CONFIG_SIOX is not set +# CONFIG_SLIMBUS is not set +# CONFIG_INTERCONNECT is not set +# CONFIG_COUNTER is not set +# CONFIG_MOST is not set + +# +# MXC support drivers +# +# CONFIG_MXC_SIM is not set +# CONFIG_MXC_IPU is not set + +# +# MXC Vivante GPU support +# +# CONFIG_MXC_GPU_VIV is not set +# end of MXC Vivante GPU support + +# +# MXC SIM Support +# +# end of MXC SIM Support + +# +# MXC Media Local Bus Driver +# +CONFIG_MXC_MLB=y +CONFIG_MXC_MLB150=y +# end of MXC Media Local Bus Driver + +# +# MXC HDMI CEC (Consumer Electronics Control) support +# +# end of MXC HDMI CEC (Consumer Electronics Control) support + +# +# MXC MIPI Support +# +CONFIG_MXC_MIPI_CSI2=y +# end of MXC MIPI Support + +# +# MXC VPU(Video Processing Unit) support +# +CONFIG_MXC_VPU=y +# CONFIG_MXC_VPU_DEBUG is not set +# CONFIG_MX6_VPU_352M is not set +# end of MXC VPU(Video Processing Unit) support +# end of MXC support drivers +# end of Device Drivers + +# +# File systems +# +CONFIG_DCACHE_WORD_ACCESS=y +# CONFIG_VALIDATE_FS_PARSER is not set +CONFIG_FS_IOMAP=y +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +# CONFIG_F2FS_FS is not set +CONFIG_FS_POSIX_ACL=y +CONFIG_EXPORTFS=y +# CONFIG_EXPORTFS_BLOCK_OPS is not set +CONFIG_FILE_LOCKING=y +# CONFIG_FS_ENCRYPTION is not set +# CONFIG_FS_VERITY is not set +# CONFIG_DNOTIFY is not set +# CONFIG_INOTIFY_USER is not set +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_FUSE_FS is not set +# CONFIG_OVERLAY_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set +# end of Caches + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set +# end of CD-ROM/DVD Filesystems + +# +# DOS/FAT/EXFAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_FAT_DEFAULT_UTF8 is not set +# CONFIG_EXFAT_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS3_FS is not set +# end of DOS/FAT/EXFAT/NT Filesystems + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_PROC_CHILDREN=y +CONFIG_KERNFS=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_TMPFS_XATTR=y +CONFIG_MEMFD_CREATE=y +CONFIG_CONFIGFS_FS=y +# end of Pseudo filesystems + +# CONFIG_MISC_FILESYSTEMS is not set +# CONFIG_NETWORK_FILESYSTEMS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="cp437" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +CONFIG_NLS_ISO8859_15=y +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_MAC_ROMAN is not set +# CONFIG_NLS_MAC_CELTIC is not set +# CONFIG_NLS_MAC_CENTEURO is not set +# CONFIG_NLS_MAC_CROATIAN is not set +# CONFIG_NLS_MAC_CYRILLIC is not set +# CONFIG_NLS_MAC_GAELIC is not set +# CONFIG_NLS_MAC_GREEK is not set +# CONFIG_NLS_MAC_ICELAND is not set +# CONFIG_NLS_MAC_INUIT is not set +# CONFIG_NLS_MAC_ROMANIAN is not set +# CONFIG_NLS_MAC_TURKISH is not set +CONFIG_NLS_UTF8=y +# CONFIG_DLM is not set +# CONFIG_UNICODE is not set +CONFIG_IO_WQ=y +# end of File systems + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR=y +# CONFIG_HARDENED_USERCOPY is not set +# CONFIG_FORTIFY_SOURCE is not set +# CONFIG_STATIC_USERMODEHELPER is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_LSM="landlock,lockdown,yama,loadpin,safesetid,integrity,bpf" + +# +# Kernel hardening options +# + +# +# Memory initialization +# +CONFIG_INIT_STACK_NONE=y +# CONFIG_INIT_ON_ALLOC_DEFAULT_ON is not set +# CONFIG_INIT_ON_FREE_DEFAULT_ON is not set +CONFIG_CC_HAS_ZERO_CALL_USED_REGS=y +# CONFIG_ZERO_CALL_USED_REGS is not set +# end of Memory initialization +# end of Kernel hardening options +# end of Security options + +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_SKCIPHER=y +CONFIG_CRYPTO_SKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_RNG_DEFAULT=y +CONFIG_CRYPTO_AKCIPHER2=y +CONFIG_CRYPTO_AKCIPHER=y +CONFIG_CRYPTO_KPP2=y +CONFIG_CRYPTO_KPP=y +CONFIG_CRYPTO_ACOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_USER=y +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +CONFIG_CRYPTO_GF128MUL=y +CONFIG_CRYPTO_NULL=y +CONFIG_CRYPTO_NULL2=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set +CONFIG_CRYPTO_ENGINE=y + +# +# Public-key cryptography +# +CONFIG_CRYPTO_RSA=y +# CONFIG_CRYPTO_DH is not set +CONFIG_CRYPTO_ECC=y +CONFIG_CRYPTO_ECDH=y +# CONFIG_CRYPTO_ECDSA is not set +# CONFIG_CRYPTO_ECRDSA is not set +# CONFIG_CRYPTO_SM2 is not set +# CONFIG_CRYPTO_CURVE25519 is not set + +# +# Authenticated Encryption with Associated Data +# +CONFIG_CRYPTO_CCM=y +CONFIG_CRYPTO_GCM=y +# CONFIG_CRYPTO_CHACHA20POLY1305 is not set +# CONFIG_CRYPTO_AEGIS128 is not set +# CONFIG_CRYPTO_SEQIV is not set +CONFIG_CRYPTO_ECHAINIV=y +CONFIG_CRYPTO_TLS=y + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_CFB=y +CONFIG_CRYPTO_CTR=y +CONFIG_CRYPTO_CTS=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_LRW=y +CONFIG_CRYPTO_OFB=y +CONFIG_CRYPTO_PCBC=y +CONFIG_CRYPTO_XTS=y +# CONFIG_CRYPTO_KEYWRAP is not set +# CONFIG_CRYPTO_ADIANTUM is not set +CONFIG_CRYPTO_ESSIV=y + +# +# Hash modes +# +CONFIG_CRYPTO_CMAC=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_XCBC=y +CONFIG_CRYPTO_VMAC=y + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_CRC32 is not set +CONFIG_CRYPTO_XXHASH=y +CONFIG_CRYPTO_BLAKE2B=y +CONFIG_CRYPTO_BLAKE2S=y +CONFIG_CRYPTO_CRCT10DIF=y +CONFIG_CRYPTO_GHASH=y +# CONFIG_CRYPTO_POLY1305 is not set +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_MICHAEL_MIC=y +CONFIG_CRYPTO_RMD160=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_SHA3=y +CONFIG_CRYPTO_SM3=y +CONFIG_CRYPTO_STREEBOG=y +CONFIG_CRYPTO_WP512=y + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_AES_TI is not set +CONFIG_CRYPTO_ANUBIS=y +CONFIG_CRYPTO_ARC4=y +CONFIG_CRYPTO_BLOWFISH=y +CONFIG_CRYPTO_BLOWFISH_COMMON=y +CONFIG_CRYPTO_CAMELLIA=y +CONFIG_CRYPTO_CAST_COMMON=y +CONFIG_CRYPTO_CAST5=y +CONFIG_CRYPTO_CAST6=y +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_FCRYPT=y +CONFIG_CRYPTO_KHAZAD=y +# CONFIG_CRYPTO_CHACHA20 is not set +CONFIG_CRYPTO_SEED=y +CONFIG_CRYPTO_SERPENT=y +CONFIG_CRYPTO_SM4=y +CONFIG_CRYPTO_TEA=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_LZO=y +# CONFIG_CRYPTO_842 is not set +# CONFIG_CRYPTO_LZ4 is not set +# CONFIG_CRYPTO_LZ4HC is not set +CONFIG_CRYPTO_ZSTD=y + +# +# Random Number Generation +# +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_DRBG_MENU=y +CONFIG_CRYPTO_DRBG_HMAC=y +# CONFIG_CRYPTO_DRBG_HASH is not set +# CONFIG_CRYPTO_DRBG_CTR is not set +CONFIG_CRYPTO_DRBG=y +CONFIG_CRYPTO_JITTERENTROPY=y +CONFIG_CRYPTO_USER_API=y +CONFIG_CRYPTO_USER_API_HASH=y +CONFIG_CRYPTO_USER_API_SKCIPHER=y +CONFIG_CRYPTO_USER_API_RNG=y +# CONFIG_CRYPTO_USER_API_RNG_CAVP is not set +CONFIG_CRYPTO_USER_API_AEAD=y +CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE=y +# CONFIG_CRYPTO_STATS is not set +CONFIG_CRYPTO_HW=y +CONFIG_CRYPTO_DEV_FSL_CAAM_COMMON=y +CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_DESC=y +CONFIG_CRYPTO_DEV_FSL_CAAM_AHASH_API_DESC=y +CONFIG_CRYPTO_DEV_FSL_CAAM_KEYBLOB_API_DESC=y +CONFIG_CRYPTO_DEV_FSL_CAAM_SECVIO=y +CONFIG_CRYPTO_DEV_FSL_CAAM=y +# CONFIG_CRYPTO_DEV_FSL_CAAM_DEBUG is not set +CONFIG_CRYPTO_DEV_FSL_CAAM_JR=y +CONFIG_CRYPTO_DEV_FSL_CAAM_RINGSIZE=9 +# CONFIG_CRYPTO_DEV_FSL_CAAM_INTC is not set +CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API=y +CONFIG_CRYPTO_DEV_FSL_CAAM_AHASH_API=y +CONFIG_CRYPTO_DEV_FSL_CAAM_PKC_API=y +CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_API=y +CONFIG_CRYPTO_DEV_FSL_CAAM_PRNG_API=y +CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API=y +# CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API_TEST is not set +# CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_TEST is not set +CONFIG_CRYPTO_DEV_FSL_CAAM_SM=y +CONFIG_CRYPTO_DEV_FSL_CAAM_SM_SLOTSIZE=7 +# CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST is not set +CONFIG_CRYPTO_DEV_SAHARA=y +# CONFIG_CRYPTO_DEV_ATMEL_ECC is not set +# CONFIG_CRYPTO_DEV_ATMEL_SHA204A is not set +CONFIG_CRYPTO_DEV_MXS_DCP=y +# CONFIG_CRYPTO_DEV_VIRTIO is not set +# CONFIG_CRYPTO_DEV_SAFEXCEL is not set +# CONFIG_CRYPTO_DEV_CCREE is not set +# CONFIG_CRYPTO_DEV_AMLOGIC_GXL is not set + +# +# Certificates for signature checking +# +# end of Certificates for signature checking + +# +# Library routines +# +CONFIG_LINEAR_RANGES=y +# CONFIG_PACKING is not set +CONFIG_BITREVERSE=y +CONFIG_HAVE_ARCH_BITREVERSE=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_NET_UTILS=y +# CONFIG_CORDIC is not set +# CONFIG_PRIME_NUMBERS is not set +CONFIG_RATIONAL=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_STMP_DEVICE=y +CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y + +# +# Crypto library routines +# +CONFIG_CRYPTO_LIB_AES=y +CONFIG_CRYPTO_LIB_ARC4=y +CONFIG_CRYPTO_ARCH_HAVE_LIB_BLAKE2S=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_ARCH_HAVE_LIB_CHACHA=y +# CONFIG_CRYPTO_LIB_CHACHA is not set +# CONFIG_CRYPTO_LIB_CURVE25519 is not set +CONFIG_CRYPTO_LIB_DES=y +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=9 +CONFIG_CRYPTO_ARCH_HAVE_LIB_POLY1305=y +# CONFIG_CRYPTO_LIB_POLY1305 is not set +# CONFIG_CRYPTO_LIB_CHACHA20POLY1305 is not set +CONFIG_CRYPTO_LIB_SHA256=y +CONFIG_CRYPTO_LIB_SM4=y +# end of Crypto library routines + +CONFIG_LIB_MEMNEQ=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +CONFIG_CRC_T10DIF=y +CONFIG_CRC_ITU_T=y +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC64 is not set +# CONFIG_CRC4 is not set +CONFIG_CRC7=y +CONFIG_LIBCRC32C=y +# CONFIG_CRC8 is not set +CONFIG_XXHASH=y +# CONFIG_RANDOM32_SELFTEST is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_ZSTD_COMPRESS=y +CONFIG_ZSTD_DECOMPRESS=y +CONFIG_XZ_DEC=y +CONFIG_XZ_DEC_X86=y +CONFIG_XZ_DEC_POWERPC=y +CONFIG_XZ_DEC_IA64=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +CONFIG_XZ_DEC_SPARC=y +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HAS_DMA=y +CONFIG_DMA_OPS=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_DMA_DECLARE_COHERENT=y +CONFIG_ARCH_HAS_SETUP_DMA_OPS=y +CONFIG_ARCH_HAS_TEARDOWN_DMA_OPS=y +CONFIG_DMA_NONCOHERENT_MMAP=y +CONFIG_DMA_REMAP=y +CONFIG_DMA_CMA=y +# CONFIG_DMA_PERNUMA_CMA is not set + +# +# Default contiguous memory area size: +# +CONFIG_CMA_SIZE_MBYTES=16 +CONFIG_CMA_SIZE_SEL_MBYTES=y +# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set +# CONFIG_CMA_SIZE_SEL_MIN is not set +# CONFIG_CMA_SIZE_SEL_MAX is not set +CONFIG_CMA_ALIGNMENT=8 +# CONFIG_DMA_API_DEBUG is not set +CONFIG_SGL_ALLOC=y +CONFIG_DQL=y +CONFIG_GLOB=y +# CONFIG_GLOB_SELFTEST is not set +CONFIG_NLATTR=y +CONFIG_CLZ_TAB=y +# CONFIG_IRQ_POLL is not set +CONFIG_MPILIB=y +CONFIG_LIBFDT=y +CONFIG_HAVE_GENERIC_VDSO=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_VDSO_32=y +CONFIG_SBITMAP=y +# end of Library routines + +CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y + +# +# Kernel hacking +# + +# +# printk and dmesg options +# +# CONFIG_PRINTK_TIME is not set +# CONFIG_PRINTK_CALLER is not set +# CONFIG_STACKTRACE_BUILD_ID is not set +CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7 +CONFIG_CONSOLE_LOGLEVEL_QUIET=4 +CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DYNAMIC_DEBUG_CORE is not set +# CONFIG_SYMBOLIC_ERRNAME is not set +CONFIG_DEBUG_BUGVERBOSE=y +# end of printk and dmesg options + +# +# Compile-time checks and compiler options +# +# CONFIG_DEBUG_INFO is not set +CONFIG_FRAME_WARN=1024 +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_READABLE_ASM is not set +# CONFIG_HEADERS_INSTALL is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set +# CONFIG_VMLINUX_MAP is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# end of Compile-time checks and compiler options + +# +# Generic Kernel Debugging Instruments +# +CONFIG_MAGIC_SYSRQ=y +CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1 +CONFIG_MAGIC_SYSRQ_SERIAL=y +CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE="" +# CONFIG_DEBUG_FS is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_UBSAN is not set +CONFIG_HAVE_KCSAN_COMPILER=y +# end of Generic Kernel Debugging Instruments + +CONFIG_DEBUG_KERNEL=y +CONFIG_DEBUG_MISC=y + +# +# Memory Debugging +# +# CONFIG_PAGE_EXTENSION is not set +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_PAGE_OWNER is not set +# CONFIG_PAGE_POISONING is not set +# CONFIG_DEBUG_RODATA_TEST is not set +# CONFIG_DEBUG_WX is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +CONFIG_HAVE_DEBUG_KMEMLEAK=y +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_SCHED_STACK_END_CHECK is not set +# CONFIG_DEBUG_VM is not set +CONFIG_ARCH_HAS_DEBUG_VIRTUAL=y +# CONFIG_DEBUG_VIRTUAL is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +CONFIG_HAVE_ARCH_KASAN=y +CONFIG_CC_HAS_KASAN_GENERIC=y +CONFIG_CC_HAS_WORKING_NOSANITIZE_ADDRESS=y +# CONFIG_KASAN is not set +# end of Memory Debugging + +# CONFIG_DEBUG_SHIRQ is not set + +# +# Debug Oops, Lockups and Hangs +# +# CONFIG_PANIC_ON_OOPS is not set +CONFIG_PANIC_ON_OOPS_VALUE=0 +CONFIG_PANIC_TIMEOUT=0 +# CONFIG_SOFTLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_WQ_WATCHDOG is not set +# CONFIG_TEST_LOCKUP is not set +# end of Debug Oops, Lockups and Hangs + +# +# Scheduler Debugging +# +CONFIG_SCHED_DEBUG=y +# CONFIG_SCHEDSTATS is not set +# end of Scheduler Debugging + +# CONFIG_DEBUG_TIMEKEEPING is not set +CONFIG_DEBUG_PREEMPT=y + +# +# Lock Debugging (spinlocks, mutexes, etc...) +# +CONFIG_LOCK_DEBUGGING_SUPPORT=y +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set +# CONFIG_DEBUG_RWSEMS is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_LOCK_TORTURE_TEST is not set +# CONFIG_WW_MUTEX_SELFTEST is not set +# CONFIG_SCF_TORTURE_TEST is not set +# end of Lock Debugging (spinlocks, mutexes, etc...) + +# CONFIG_DEBUG_IRQFLAGS is not set +# CONFIG_STACKTRACE is not set +# CONFIG_WARN_ALL_UNSEEDED_RANDOM is not set +# CONFIG_DEBUG_KOBJECT is not set + +# +# Debug kernel data structures +# +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_PLIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_BUG_ON_DATA_CORRUPTION is not set +# end of Debug kernel data structures + +# CONFIG_DEBUG_CREDENTIALS is not set + +# +# RCU Debugging +# +# CONFIG_RCU_SCALE_TEST is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_REF_SCALE_TEST is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=21 +CONFIG_RCU_TRACE=y +# CONFIG_RCU_EQS_DEBUG is not set +# end of RCU Debugging + +# CONFIG_DEBUG_WQ_FORCE_RR_CPU is not set +# CONFIG_LATENCYTOP is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACE_CLOCK=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_SAMPLES is not set + +# +# arm Debugging +# +# CONFIG_ARM_PTDUMP_DEBUGFS is not set +# CONFIG_UNWINDER_FRAME_POINTER is not set +CONFIG_UNWINDER_ARM=y +CONFIG_ARM_UNWIND=y +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_LL is not set +CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" +CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" +# CONFIG_PID_IN_CONTEXTIDR is not set +# CONFIG_CORESIGHT is not set +# end of arm Debugging + +# +# Kernel Testing and Coverage +# +# CONFIG_KUNIT is not set +# CONFIG_NOTIFIER_ERROR_INJECTION is not set +# CONFIG_FAULT_INJECTION is not set +CONFIG_ARCH_HAS_KCOV=y +CONFIG_CC_HAS_SANCOV_TRACE_PC=y +# CONFIG_KCOV is not set +# CONFIG_RUNTIME_TESTING_MENU is not set +CONFIG_ARCH_USE_MEMTEST=y +# CONFIG_MEMTEST is not set +# end of Kernel Testing and Coverage +# end of Kernel hacking diff --git a/arch/arm/configs/imx_avb.config b/arch/arm/configs/imx_avb.config new file mode 100644 index 000000000000..49b8d258c306 --- /dev/null +++ b/arch/arm/configs/imx_avb.config @@ -0,0 +1,8 @@ +CONFIG_EXPERT=y +CONFIG_PREEMPT_RT=y +CONFIG_AVB_SUPPORT=y +CONFIG_NET_SWITCHDEV=y +CONFIG_DEBUG_INFO=y +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y diff --git a/arch/arm/configs/imx_up.config b/arch/arm/configs/imx_up.config new file mode 100644 index 000000000000..18b877a41fa7 --- /dev/null +++ b/arch/arm/configs/imx_up.config @@ -0,0 +1,2 @@ +CONFIG_SMP=n +CONFIG_HIGHMEM=n diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig index b5a304ce400a..f223f5d9970a 100644 --- a/arch/arm/configs/imx_v6_v7_defconfig +++ b/arch/arm/configs/imx_v6_v7_defconfig @@ -49,7 +49,8 @@ CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" CONFIG_KEXEC=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_STAT=y -CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y diff --git a/arch/arm/configs/imx_v7_defconfig b/arch/arm/configs/imx_v7_defconfig index 472dff26b4b3..8c1ba26181b8 100644 --- a/arch/arm/configs/imx_v7_defconfig +++ b/arch/arm/configs/imx_v7_defconfig @@ -25,7 +25,7 @@ CONFIG_SOC_IMX6SLL=y CONFIG_SOC_IMX6SX=y CONFIG_SOC_IMX6UL=y CONFIG_SOC_IMX7D=y -CONFIG_SOC_IMX7ULP=y +# CONFIG_SOC_IMX7ULP is not set CONFIG_SMP=y CONFIG_VMSPLIT_2G=y CONFIG_ARM_PSCI=y @@ -35,7 +35,8 @@ CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" CONFIG_KEXEC=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_STAT=y -CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y @@ -287,7 +288,7 @@ CONFIG_REGULATOR_LTC3676=y CONFIG_REGULATOR_MAX17135=y CONFIG_REGULATOR_MC13783=y CONFIG_REGULATOR_MC13892=y -CONFIG_REGULATOR_PF1550_RPMSG=y +# CONFIG_REGULATOR_PF1550_RPMSG is not set CONFIG_REGULATOR_PFUZE100=y CONFIG_REGULATOR_RN5T618=y CONFIG_RC_CORE=y @@ -595,3 +596,5 @@ CONFIG_CRYPTO_SM4_GENERIC=m CONFIG_CRYPTO_ARIA=m CONFIG_CRYPTO_HCTR2=m CONFIG_CRYPTO_SM3_GENERIC=m + +CONFIG_PREEMPT_RT=y diff --git a/arch/arm/configs/lsdk.config b/arch/arm/configs/lsdk.config index d10724fbff81..1fcf47b5c19d 100644 --- a/arch/arm/configs/lsdk.config +++ b/arch/arm/configs/lsdk.config @@ -64,6 +64,26 @@ CONFIG_IP6_NF_NAT=y CONFIG_IP6_NF_FILTER=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y +# can +CONFIG_CAN_FLEXCAN=y +# dspi +CONFIG_SPI_BITBANG=y +CONFIG_SPI_FSL_DSPI=y +# watchdog +CONFIG_IMX2_WDT=y +# sound +CONFIG_SND_MIXER_OSS=y +CONFIG_SND_PCM_OSS=y +CONFIG_SND_SOC_FSL_SAI=y +CONFIG_SND_SOC_SGTL5000=y +CONFIG_SND_SIMPLE_CARD=y +# Video +CONFIG_FB=y +CONFIG_FB_FSL_DCU=y +CONFIG_FB_FSL_SII902X=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +CONFIG_LOGO=y +CONFIG_SOC_VF610=n # filesystems CONFIG_EXT4_FS_POSIX_ACL=y CONFIG_EXT4_FS_SECURITY=y diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index c42c2372b339..b3ae89407dd3 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -1316,3 +1316,5 @@ CONFIG_CMA_SIZE_MBYTES=64 CONFIG_PRINTK_TIME=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_FS=y +# PREEMPT RT +CONFIG_PREEMPT_RT=y \ No newline at end of file diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 3431c0553f45..3d44ece9ff74 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -50,6 +50,10 @@ #include +#ifdef CONFIG_BAREMETAL +#include +#endif + /* * as from 2.5, kernels no longer have an init_tasks structure * so we need some other way of telling a new secondary core @@ -65,6 +69,9 @@ enum ipi_msg_type { IPI_CPU_STOP, IPI_IRQ_WORK, IPI_COMPLETION, +#ifdef CONFIG_BAREMETAL + IPI_BAREMETAL_COMM = 8, +#endif NR_IPI, /* * CPU_BACKTRACE is special and not included in NR_IPI @@ -539,6 +546,9 @@ static const char *ipi_types[NR_IPI] __tracepoint_string = { [IPI_CPU_STOP] = "CPU stop interrupts", [IPI_IRQ_WORK] = "IRQ work interrupts", [IPI_COMPLETION] = "completion interrupts", +#ifdef CONFIG_BAREMETAL + [IPI_BAREMETAL_COMM] = "Baremetal inter-core interrupts", +#endif }; static void smp_cross_call(const struct cpumask *target, unsigned int ipinr); @@ -669,6 +679,18 @@ static void do_handle_IPI(int ipinr) ipi_complete(cpu); break; +#ifdef CONFIG_BAREMETAL +#define GICC_IAR_MASK 0x1fff + case IPI_BAREMETAL_COMM: { + /* FIXME: use the fixed source coreID from core1 */ + int irqsrc = 1; + /*linux core is 0 core, so iterate from 1 core.*/ + for(irqsrc = 1; irqsrc < CONFIG_MAX_CPUS; irqsrc++) + ipi_baremetal_handle(ipinr, irqsrc); + } + break; +#endif + case IPI_CPU_BACKTRACE: printk_deferred_enter(); nmi_cpu_backtrace(get_irq_regs()); diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 0e672fc9a80f..1ee568a27ff5 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -217,6 +217,16 @@ config SOC_LS1021A help This enables support for Freescale LS1021A processor. +config LS1021A_BAREMETAL + bool "NXP LS1021A baremetal support" + depends on SOC_LS1021A + select BAREMETAL + +config SOC_IMX6Q_BAREMETAL + bool "NXP IMX6Q baremetal support" + depends on SOC_IMX6Q + select BAREMETAL + endif if ARCH_MULTI_V7 || ARM_SINGLE_ARMV7M diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index fef62e4a9edd..622a30243f4a 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -404,6 +404,9 @@ do_translation_fault(unsigned long addr, unsigned int fsr, if (addr < TASK_SIZE) return do_page_fault(addr, fsr, regs); + if (interrupts_enabled(regs)) + local_irq_enable(); + if (user_mode(regs)) goto bad_area; @@ -474,6 +477,9 @@ do_translation_fault(unsigned long addr, unsigned int fsr, static int do_sect_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { + if (interrupts_enabled(regs)) + local_irq_enable(); + do_bad_area(addr, fsr, regs); return 0; } diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index 7e8773a2d99d..9fde36fcb80c 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c @@ -55,6 +55,34 @@ extern unsigned int VFP_arch_feroceon __alias(VFP_arch); */ union vfp_state *vfp_current_hw_state[NR_CPUS]; +/* + * Claim ownership of the VFP unit. + * + * The caller may change VFP registers until vfp_unlock() is called. + * + * local_bh_disable() is used to disable preemption and to disable VFP + * processing in softirq context. On PREEMPT_RT kernels local_bh_disable() is + * not sufficient because it only serializes soft interrupt related sections + * via a local lock, but stays preemptible. Disabling preemption is the right + * choice here as bottom half processing is always in thread context on RT + * kernels so it implicitly prevents bottom half processing as well. + */ +static void vfp_lock(void) +{ + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) + local_bh_disable(); + else + preempt_disable(); +} + +static void vfp_unlock(void) +{ + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) + local_bh_enable(); + else + preempt_enable(); +} + /* * Is 'thread's most up to date state stored in this CPUs hardware? * Must be called from non-preemptible context. @@ -240,7 +268,7 @@ static void vfp_panic(char *reason, u32 inst) /* * Process bitmask of exception conditions. */ -static void vfp_raise_exceptions(u32 exceptions, u32 inst, u32 fpscr, struct pt_regs *regs) +static int vfp_raise_exceptions(u32 exceptions, u32 inst, u32 fpscr) { int si_code = 0; @@ -248,8 +276,7 @@ static void vfp_raise_exceptions(u32 exceptions, u32 inst, u32 fpscr, struct pt_ if (exceptions == VFP_EXCEPTION_ERROR) { vfp_panic("unhandled bounce", inst); - vfp_raise_sigfpe(FPE_FLTINV, regs); - return; + return FPE_FLTINV; } /* @@ -277,8 +304,7 @@ static void vfp_raise_exceptions(u32 exceptions, u32 inst, u32 fpscr, struct pt_ RAISE(FPSCR_OFC, FPSCR_OFE, FPE_FLTOVF); RAISE(FPSCR_IOC, FPSCR_IOE, FPE_FLTINV); - if (si_code) - vfp_raise_sigfpe(si_code, regs); + return si_code; } /* @@ -324,6 +350,8 @@ static u32 vfp_emulate_instruction(u32 inst, u32 fpscr, struct pt_regs *regs) static void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) { u32 fpscr, orig_fpscr, fpsid, exceptions; + int si_code2 = 0; + int si_code = 0; pr_debug("VFP: bounce: trigger %08x fpexc %08x\n", trigger, fpexc); @@ -369,8 +397,8 @@ static void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) * unallocated VFP instruction but with FPSCR.IXE set and not * on VFP subarch 1. */ - vfp_raise_exceptions(VFP_EXCEPTION_ERROR, trigger, fpscr, regs); - return; + si_code = vfp_raise_exceptions(VFP_EXCEPTION_ERROR, trigger, fpscr); + goto exit; } /* @@ -394,14 +422,14 @@ static void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) */ exceptions = vfp_emulate_instruction(trigger, fpscr, regs); if (exceptions) - vfp_raise_exceptions(exceptions, trigger, orig_fpscr, regs); + si_code2 = vfp_raise_exceptions(exceptions, trigger, orig_fpscr); /* * If there isn't a second FP instruction, exit now. Note that * the FPEXC.FP2V bit is valid only if FPEXC.EX is 1. */ if ((fpexc & (FPEXC_EX | FPEXC_FP2V)) != (FPEXC_EX | FPEXC_FP2V)) - return; + goto exit; /* * The barrier() here prevents fpinst2 being read @@ -413,7 +441,13 @@ static void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) emulate: exceptions = vfp_emulate_instruction(trigger, orig_fpscr, regs); if (exceptions) - vfp_raise_exceptions(exceptions, trigger, orig_fpscr, regs); + si_code = vfp_raise_exceptions(exceptions, trigger, orig_fpscr); +exit: + vfp_unlock(); + if (si_code2) + vfp_raise_sigfpe(si_code2, regs); + if (si_code) + vfp_raise_sigfpe(si_code, regs); } static void vfp_enable(void *unused) @@ -512,11 +546,9 @@ static inline void vfp_pm_init(void) { } */ void vfp_sync_hwstate(struct thread_info *thread) { - unsigned int cpu = get_cpu(); + vfp_lock(); - local_bh_disable(); - - if (vfp_state_in_hw(cpu, thread)) { + if (vfp_state_in_hw(raw_smp_processor_id(), thread)) { u32 fpexc = fmrx(FPEXC); /* @@ -527,8 +559,7 @@ void vfp_sync_hwstate(struct thread_info *thread) fmxr(FPEXC, fpexc); } - local_bh_enable(); - put_cpu(); + vfp_unlock(); } /* Ensure that the thread reloads the hardware VFP state on the next use. */ @@ -683,7 +714,7 @@ static int vfp_support_entry(struct pt_regs *regs, u32 trigger) if (!user_mode(regs)) return vfp_kmode_exception(regs, trigger); - local_bh_disable(); + vfp_lock(); fpexc = fmrx(FPEXC); /* @@ -748,6 +779,7 @@ static int vfp_support_entry(struct pt_regs *regs, u32 trigger) * replay the instruction that trapped. */ fmxr(FPEXC, fpexc); + vfp_unlock(); } else { /* Check for synchronous or asynchronous exceptions */ if (!(fpexc & (FPEXC_EX | FPEXC_DEX))) { @@ -762,17 +794,17 @@ static int vfp_support_entry(struct pt_regs *regs, u32 trigger) if (!(fpscr & FPSCR_IXE)) { if (!(fpscr & FPSCR_LENGTH_MASK)) { pr_debug("not VFP\n"); - local_bh_enable(); + vfp_unlock(); return -ENOEXEC; } fpexc |= FPEXC_DEX; } } bounce: regs->ARM_pc += 4; + /* VFP_bounce() will invoke vfp_unlock() */ VFP_bounce(trigger, fpexc, regs); } - local_bh_enable(); return 0; } @@ -819,7 +851,7 @@ void kernel_neon_begin(void) unsigned int cpu; u32 fpexc; - local_bh_disable(); + vfp_lock(); /* * Kernel mode NEON is only allowed outside of hardirq context with @@ -850,7 +882,7 @@ void kernel_neon_end(void) { /* Disable the NEON/VFP unit. */ fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); - local_bh_enable(); + vfp_unlock(); } EXPORT_SYMBOL(kernel_neon_end); diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 1731a4c573e7..ff3ee8dc3c79 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -97,6 +97,7 @@ config ARM64 select ARCH_SUPPORTS_NUMA_BALANCING select ARCH_SUPPORTS_PAGE_TABLE_CHECK select ARCH_SUPPORTS_PER_VMA_LOCK + select ARCH_SUPPORTS_RT select ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH select ARCH_WANT_COMPAT_IPC_PARSE_VERSION if COMPAT select ARCH_WANT_DEFAULT_BPF_JIT diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms index e3b0450229e9..314b08100c82 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -204,6 +204,24 @@ config ARCH_LAYERSCAPE help This enables support for the Freescale Layerscape SoC family. +config LS104XA_BAREMETAL + bool "NXP LS1043A/LS1046A baremetal support" + select BAREMETAL + help + This enables support for Freescale LS1043A and LS1046A baremetal. + +config LS1028A_BAREMETAL + bool "NXP LS1028A baremetal support" + select BAREMETAL + help + This enables support for NXP LS1028A baremetal. + +config LX2160A_BAREMETAL + bool "NXP LX2160A baremetal support" + select BAREMETAL + help + This enables support for NXP LX2160A baremetal. + config ARCH_MXC bool "NXP i.MX SoC family" select ARM64_ERRATUM_843419 @@ -257,6 +275,18 @@ config ARCH_NPCM General support for NPCM8xx BMC (Arbel). Nuvoton NPCM8xx BMC based on the Cortex A35. +config IMX8M_BAREMETAL + bool "NXP IMX8MM/IMX8MP baremetal support" + select BAREMETAL + help + This enables support for NXP i.MX8MM and i.MX8MP baremetal. + +config IMX93_BAREMETAL + bool "NXP IMX93 baremetal support" + select BAREMETAL + help + This enables support for NXP i.MX93 baremetal. + config ARCH_QCOM bool "Qualcomm Platforms" select GPIOLIB diff --git a/arch/arm64/boot/dts/freescale/Makefile b/arch/arm64/boot/dts/freescale/Makefile index efe32533733e..c4ad4060ad41 100644 --- a/arch/arm64/boot/dts/freescale/Makefile +++ b/arch/arm64/boot/dts/freescale/Makefile @@ -14,11 +14,19 @@ dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1028a-kontron-sl28-var3-ads2.dtb dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1028a-kontron-sl28-var4.dtb dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1028a-qds.dtb dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1028a-rdb.dtb +dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1028a-rdb-jailhouse.dtb dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1028a-rdb-dpdk.dtb +dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1028a-rdb-jailhouse-without-enetc.dtb +dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1028a-rdb-sdk-bm.dtb +dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1028a-rdb-dsa-swp5-eno3.dtb +dtb-$(CONFIG_ARCH_LAYERSCAPE) += fii-ls1028a-tsn.dtb dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1043a-qds.dtb dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1043a-qds-sdk.dtb dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1043a-rdb.dtb dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1043a-rdb-sdk.dtb +dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1043a-rdb-sdk-jailhouse.dtb +dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1043a-rdb-sdk-jailhouse-with-dpaa.dtb +dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1043a-rdb-sdk-bm.dtb dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1043a-rdb-usdpaa.dtb dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-frwy.dtb dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-frwy-sdk.dtb @@ -26,7 +34,10 @@ dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-frwy-usdpaa.dtb dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-qds.dtb dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-qds-sdk.dtb dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-rdb.dtb +dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-rdb-sdk-bm.dtb dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-rdb-sdk.dtb +dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-rdb-sdk-jailhouse.dtb +dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-rdb-sdk-jailhouse-with-dpaa.dtb dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-rdb-usdpaa.dtb dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-rdb-shared-mac9-only.dtb dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls1046a-rdb-usdpaa-shared-mac10.dtb @@ -75,13 +86,38 @@ dtb-$(CONFIG_ARCH_MXC) += imx8mm-ab2.dtb imx8mm-ab2-m4.dtb imx8mm-ddr4-ab2.dtb i dtb-$(CONFIG_ARCH_MXC) += imx8mm-evk.dtb imx8mm-evk-rpmsg.dtb imx8mm-evk-rm67191.dtb \ imx8mm-evk-root.dtb imx8mm-evk-inmate.dtb imx8mm-evk-revb-qca-wifi.dtb \ imx8mm-evk-ecspi-slave.dtb \ + imx8mm-evk-baremetal.dtb \ + imx8mm-evk-avb.dtb \ imx8mm-evk-pcie-ep.dtb \ imx8mm-evk-usd-wifi.dtb \ imx8mm-evk-qca-wifi.dtb \ imx8mm-evk-dpdk.dtb \ + imx8mm-evk-harpoon.dtb imx8mm-evk-harpoon-industrial.dtb imx8mm-evk-harpoon-avb.dtb \ + imx8mm-evk-harpoon-virtio-net.dtb \ + imx8mm-evk-qca-wifi-harpoon.dtb imx8mm-evk-qca-wifi-harpoon-industrial.dtb \ + imx8mm-evk-qca-wifi-harpoon-avb.dtb imx8mm-evk-qca-wifi-harpoon-virtio-net.dtb \ imx8mm-evk-rm67199.dtb imx8mm-evk-rm67191-cmd-ram.dtb imx8mm-evk-rm67199-cmd-ram.dtb \ - imx8mm-evk-lk.dtb imx8mm-evk-rpmsg-wm8524-lpv.dtb + imx8mm-evk-lk.dtb imx8mm-evk-rpmsg-wm8524-lpv.dtb \ + imx8mm-evk-rpmsg-8m-buf.dtb \ + imx8mm-evk-ecat.dtb \ + imx8mm-evk-igh.dtb \ + imx8mm-evk-ecat-userspace.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mm-evk-rpmsg-wm8524.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mm-evk-rpmsg-ca53.dtb \ + imx8mm-evk-virtio-perf-ca53.dtb \ + imx8mm-evk-virtio-perf-cm4.dtb \ + imx8mm-evk-virtio-net-ca53.dtb \ + imx8mm-evk-virtio-net-cm4.dtb \ + imx8mm-evk-multicore-rtos.dtb \ + imx8mm-evk-multicore-rpmsg.dtb \ + imx8mp-evk-rpmsg-ca53.dtb \ + imx8mp-evk-virtio-net-ca53.dtb \ + imx8mp-evk-virtio-net-cm7.dtb \ + imx8mp-evk-virtio-perf-ca53.dtb \ + imx8mp-evk-virtio-perf-cm7.dtb \ + imx8mp-evk-multicore-rtos.dtb \ + imx8mp-evk-multicore-lwip.dtb \ + imx8mp-evk-multicore-rpmsg.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mm-evk-ak4497.dtb imx8mm-evk-ak5558.dtb imx8mm-evk-audio-tdm.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mm-evk-8mic-revE.dtb imx8mm-evk-8mic-swpdm.dtb \ imx8mm-evk-iqaudio-dacplus.dtb imx8mm-evk-iqaudio-dacpro.dtb imx8mm-evk-hifiberry-dacplus.dtb \ @@ -115,7 +151,8 @@ dtb-$(CONFIG_ARCH_MXC) += imx8mn-ddr4-evk.dtb imx8mn-ddr4-evk-rm67191.dtb imx8mn imx8mn-ddr4-evk-usd-wifi.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mn-ddr4-evk-rpmsg.dtb imx8mn-ddr3l-evk-rpmsg.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mn-ddr4-evk-root.dtb imx8mn-ddr4-evk-inmate.dtb imx8mn-evk-root.dtb imx8mn-evk-inmate.dtb \ - imx8mn-evk-lk.dtb imx8mn-ddr4-evk-lk.dtb + imx8mn-evk-lk.dtb imx8mn-ddr4-evk-lk.dtb imx8mn-evk-harpoon.dtb imx8mn-evk-harpoon-industrial.dtb \ + imx8mn-evk-harpoon-avb.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mn-ddr3l-evk-ak5558.dtb imx8mn-ddr4-evk-ak5558.dtb imx8mn-evk-ak5558.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mn-ab2.dtb imx8mn-ddr3l-ab2.dtb imx8mn-ddr4-ab2.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mn-evk-iqaudio-dacplus.dtb imx8mn-evk-iqaudio-dacpro.dtb imx8mn-evk-hifiberry-dacplus.dtb \ @@ -123,7 +160,9 @@ dtb-$(CONFIG_ARCH_MXC) += imx8mn-evk-iqaudio-dacplus.dtb imx8mn-evk-iqaudio-dacp dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk.dtb imx8mp-evk-rm67191.dtb imx8mp-evk-it6263-lvds-dual-channel.dtb \ imx8mp-evk-pcie-ep.dtb imx8mp-evk-rpmsg.dtb imx8mp-evk-ecspi-slave.dtb \ imx8mp-evk-jdi-wuxga-lvds-panel.dtb imx8mp-evk-flexcan2.dtb \ - imx8mp-evk-root.dtb imx8mp-evk-inmate.dtb imx8mp-evk-ov2775.dtb \ + imx8mp-evk-root.dtb imx8mp-evk-harpoon.dtb imx8mp-evk-harpoon-industrial.dtb imx8mp-evk-inmate.dtb \ + imx8mp-evk-harpoon-avb.dtb imx8mp-evk-harpoon-virtio-net.dtb \ + imx8mp-evk-ov2775.dtb \ imx8mp-evk-ov2775-ov5640.dtb imx8mp-evk-basler-ov5640.dtb imx8mp-evk-basler.dtb \ imx8mp-evk-basler-ov2775.dtb imx8mp-evk-dual-basler.dtb \ imx8mp-evk-dual-ov2775.dtb imx8mp-evk-spdif-lb.dtb imx8mp-evk-dsp.dtb \ @@ -133,13 +172,19 @@ dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk.dtb imx8mp-evk-rm67191.dtb imx8mp-evk-it626 imx8mp-evk-iqaudio-dacplus.dtb imx8mp-evk-iqaudio-dacpro.dtb imx8mp-evk-hifiberry-dacplus.dtb \ imx8mp-evk-hifiberry-dac2.dtb imx8mp-evk-hifiberry-dacplusadc.dtb \ imx8mp-evk-usdhc1-m2.dtb imx8mp-evk-rm67199.dtb \ - imx8mp-evk-dpdk.dtb imx8mp-evk-8mic-swpdm.dtb imx8mp-evk-rpmsg-lpv.dtb imx8mp-evk-revA3-8mic-revE.dtb + imx8mp-evk-dpdk.dtb imx8mp-evk-8mic-swpdm.dtb imx8mp-evk-rpmsg-lpv.dtb imx8mp-evk-revA3-8mic-revE.dtb \ + imx8mp-evk-baremetal.dtb imx8mp-evk-avb.dtb \ + imx8mp-evk-ecat.dtb imx8mp-evk-igh.dtb imx8mp-evk-ecat-userspace.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-msc-sm2s-ep1.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-phyboard-pollux-rdk.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-ab2.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-navq.dtb imx8mp-navq-ov5640-ov5645.dtb imx8mp-navq-ov5647-ov5640.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-ddr4-evk.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-ndm.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-dsa.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-dsa-enetc.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-dsa-fec-swp0.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-dsa-fec-swp3.dtb imx8mp-evk-revb4-dtbs := imx8mp-evk.dtb imx8mp-evk-revb4.dtbo imx8mp-evk-revb4-rm67191-dtbs := imx8mp-evk-rm67191.dtb imx8mp-evk-revb4.dtbo @@ -172,6 +217,10 @@ imx8mp-evk-revb4-8mic-swpdm-dtbs := imx8mp-evk-8mic-swpdm.dtb imx8mp-evk-revb4.d imx8mp-evk-revb4-8mic-revE-dtbs := imx8mp-evk-revA3-8mic-revE.dtb imx8mp-evk-revb4.dtbo imx8mp-ddr4-evk-revb4-dtbs := imx8mp-ddr4-evk.dtb imx8mp-evk-revb4.dtbo imx8mp-evk-revb4-ndm-dtbs := imx8mp-evk-ndm.dtb imx8mp-evk-revb4.dtbo +imx8mp-evk-revb4-dsa-dtbs := imx8mp-evk-dsa.dtb imx8mp-evk-revb4.dtbo +imx8mp-evk-revb4-dsa-enetc-dtbs := imx8mp-evk-dsa-enetc.dtb imx8mp-evk-revb4.dtbo +imx8mp-evk-revb4-dsa-fec-swp0-dtbs := imx8mp-evk-dsa-fec-swp0.dtb imx8mp-evk-revb4.dtbo +imx8mp-evk-revb4-dsa-fec-swp3-dtbs := imx8mp-evk-dsa-fec-swp3.dtb imx8mp-evk-revb4.dtbo dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-revb4.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-revb4-rm67191.dtb @@ -207,6 +256,10 @@ dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-revb4-ndm.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-revb4-sof-wm8962.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-revb4-rpmsg.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-revb4-rpmsg-lpv.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-revb4-dsa.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-revb4-dsa-enetc.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-revb4-dsa-fec-swp0.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mp-evk-revb4-dsa-fec-swp3.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mq-evk.dtb imx8mq-evk-rpmsg.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mq-evk.dtb imx8mq-evk-rpmsg.dtb imx8mq-evk-pcie1-m2.dtb imx8mq-evk-usd-wifi.dtb \ @@ -314,7 +367,8 @@ dtb-$(CONFIG_ARCH_MXC) += imx8ulp-evk.dtb imx8ulp-evk-lpspi-slave.dtb \ imx8ulp-9x9-evk-i3c.dtb imx8ulp-9x9-evk-sof-btsco.dtb \ imx8ulp-evk-lpa.dtb imx8ulp-9x9-evk-lpa.dtb dtb-$(CONFIG_ARCH_MXC) += imx8dxl-evk.dtb \ - imx8dxl-evk-enet0.dtb imx8dxl-evk-enet0-tja1100.dtb \ + imx8dxl-evk-enet0.dtb imx8dxl-evk-enet0-tja1100.dtb imx8dxl-evk-enet0-tja1100-avb.dtb \ + imx8dxl-evk-enet0-avb.dtb imx8dxl-evk-enet0-sja1105.dtb \ imx8dxl-evk-pcie-ep.dtb \ imx8dxl-evk-lcdif.dtb \ imx8dxl-evk-lpspi-slave.dtb \ @@ -370,13 +424,21 @@ dtb-$(CONFIG_ARCH_MXC) += imx8qxp-mek.dtb imx8qxp-mek-ov5640.dtb \ imx8qxp-17x17-val.dtb imx8dx-lpddr4-val.dtb imx8dx-17x17-val.dtb dtb-$(CONFIG_ARCH_MXC) += imx8qxp-mek-dom0.dtb imx8qxp-mek-root.dtb \ imx8qxp-mek-inmate.dtb -dtb-$(CONFIG_ARCH_MXC) += imx93-14x14-evk.dtb \ +dtb-$(CONFIG_ARCH_MXC) += imx93-14x14-evk.dtb imx93-14x14-evk-aud-hat-avb.dtb \ imx93-14x14-evk-tja1103.dtb imx93-14x14-evk-rm67199.dtb \ imx93-14x14-evk-mqs.dtb imx93-14x14-evk-aud-hat.dtb \ - imx93-14x14-evk-lvds-it6263.dtb imx93-14x14-evk-sja1105.dtb \ + imx93-14x14-evk-lvds-it6263.dtb imx93-14x14-evk-sja1105.dtb imx93-14x14-evk-sja1105-avb.dtb \ imx93-14x14-evk-flexspi-m2.dtb imx93-14x14-evk-dsi-serdes.dtb \ - imx93-14x14-evk-i3c.dtb + imx93-14x14-evk-i3c.dtb imx93-14x14-evk-imxai2eth-ath.dtb \ + imx93-14x14-evk-virtio-net-ca55.dtb \ + imx93-14x14-evk-virtio-net-cm33.dtb \ + imx93-14x14-evk-uart-sharing-cm33.dtb \ + imx93-14x14-evk-multicore-rpmsg.dtb \ + imx93-14x14-evk-multicore-rtos.dtb dtb-$(CONFIG_ARCH_MXC) += imx93-11x11-evk.dtb \ + imx93-11x11-evk-avb.dtb \ + imx93-11x11-evk-harpoon.dtb imx93-11x11-evk-harpoon-industrial.dtb imx93-11x11-evk-harpoon-avb.dtb \ + imx93-11x11-evk-harpoon-virtio-net.dtb \ imx93-11x11-evk-inmate.dtb imx93-11x11-evk-root.dtb imx93-11x11-evk-flexio-i2c.dtb \ imx93-11x11-evk-i2c-spi-slave.dtb \ imx93-11x11-evk-i3c.dtb imx93-11x11-evk-lpuart.dtb \ @@ -386,7 +448,21 @@ dtb-$(CONFIG_ARCH_MXC) += imx93-11x11-evk.dtb \ imx93-11x11-evk-mt9m114.dtb \ imx93-11x11-evk-ld.dtb \ imx93-11x11-evk-iw612-otbr.dtb \ - imx93-11x11-evk-rpmsg.dtb imx93-11x11-evk-rpmsg-lpv.dtb + imx93-11x11-evk-rpmsg.dtb imx93-11x11-evk-rpmsg-lpv.dtb \ + imx93-11x11-evk-virtio-net-ca55.dtb \ + imx93-11x11-evk-virtio-net-cm33.dtb \ + imx93-11x11-evk-uart-sharing-cm33.dtb \ + imx93-11x11-evk-baremetal.dtb \ + imx93-11x11-evk-ecat.dtb \ + imx93-11x11-evk-igh.dtb \ + imx93-11x11-evk-ecat-userspace.dtb \ + imx93-11x11-evk-multicore-rpmsg.dtb \ + imx93-11x11-evk-multicore-rtos.dtb \ + imx93-11x11-evk-dsa.dtb \ + imx93-11x11-evk-dsa-enetc.dtb \ + imx93-11x11-evk-dsa-fec-swp0.dtb \ + imx93-11x11-evk-dsa-fec-swp3.dtb \ + imx93-11x11-evk-dpdk.dtb dtb-$(CONFIG_ARCH_MXC) += imx91-11x11-evk.dtb \ imx91-11x11-evk-flexspi-m2.dtb imx91-11x11-evk-flexspi-nand-m2.dtb \ @@ -438,9 +514,12 @@ dtb-$(CONFIG_ARCH_MXC) += imx93-9x9-qsb.dtb \ imx93-9x9-qsb-mt9m114.dtb \ imx93-9x9-qsb-i3c.dtb \ imx93-9x9-qsb-lpspi.dtb imx93-9x9-qsb-lpspi-slave.dtb \ + imx93-9x9-qsb-baremetal.dtb \ imx93-9x9-qsb-aud-hat.dtb \ imx93-9x9-qsb-ld.dtb \ - imx93-9x9-qsb-rpmsg.dtb imx93-9x9-qsb-rpmsg-lpv.dtb + imx93-9x9-qsb-rpmsg.dtb imx93-9x9-qsb-rpmsg-lpv.dtb \ + imx93-9x9-qsb-uart-sharing-cm33.dtb \ + imx93-9x9-qsb-inmate.dts imx93-9x9-qsb-root.dts dtb-$(CONFIG_ARCH_MXC) += imx93-tqma9352-mba93xxla.dtb dtb-$(CONFIG_ARCH_MXC) += imx91-9x9-qsb.dtb \ imx91-9x9-qsb-can1.dtb \ @@ -456,8 +535,9 @@ dtb-$(CONFIG_ARCH_MXC) += imx91-9x9-qsb-i3c.dtb dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk.dtb dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk-flexspi-m2.dtb dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk-mqs.dtb +dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk-multicore-rtos.dtb dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk-aud-hat.dtb -dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk-root.dtb imx95-15x15-evk-inmate.dtb +dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk-root.dtb imx95-15x15-evk-inmate.dtb imx95-15x15-evk-harpoon.dtb imx95-15x15-evk-harpoon-industrial.dtb dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-ab2.dtb dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk-i2c-spi-slave.dtb DTC_FLAGS_imx95-15x15-evk-boe-wxga-lvds0-panel := -@ @@ -516,6 +596,8 @@ DTC_FLAGS_imx95-15x15-evk-rpmsg-lpv := -@ dtb-$(CONFIG_ARCH_MXC) += imx95-15x15-evk-rpmsg.dtb imx95-15x15-evk-rpmsg-lpv.dtb dtb-$(CONFIG_ARCH_MXC) += imx95-19x19-evk.dtb imx95-19x19-evk-root.dtb imx95-19x19-evk-inmate.dtb \ + imx95-19x19-evk-harpoon.dtb imx95-19x19-evk-harpoon-industrial.dtb \ + imx95-19x19-evk-multicore-rtos.dtb \ imx95-19x19-evk-lpspi-slave.dtb imx95-15x15-evk-ox03c10-dtbs := imx95-15x15-evk.dtb imx95-15x15-evk-ox03c10.dtbo diff --git a/arch/arm64/boot/dts/freescale/fii-ls1028a-tsn.dts b/arch/arm64/boot/dts/freescale/fii-ls1028a-tsn.dts new file mode 100644 index 000000000000..a28b47206334 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/fii-ls1028a-tsn.dts @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Device Tree file for Foxconn LS1028A TSN Board. + * + * Copyright 2020-2022 NXP + * + * Wes Li + * Vladimir Oltean + */ + +/dts-v1/; +#include "fsl-ls1028a.dtsi" + +/ { + model = "Foxconn LS1028A TSN Board"; + compatible = "fsl,ls1028a-rdb", "fsl,ls1028a"; + + aliases { + crypto = &crypto; + serial0 = &duart0; + serial1 = &duart1; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x1 0x0000000>; + }; + + sys_mclk: clock-mclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + }; + + reg_1p8v: regulator-1p8v { + compatible = "regulator-fixed"; + regulator-name = "1P8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + sb_3v3: regulator-sb3v3 { + compatible = "regulator-fixed"; + regulator-name = "3v3_vbus"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; +}; + +&dspi2 { + bus-num = <2>; + status = "okay"; + + sja1105_switch0: ethernet-switch@0 { + reg = <0x0>; + #address-cells = <1>; + #size-cells = <0>; + compatible = "nxp,sja1105s"; + dsa,member = <1 1>; + /* 20 MHz */ + spi-max-frequency = <20000000>; + /* Sample data on trailing clock edge */ + spi-cpha; + /* SPI controller settings for SJA1105 timing requirements */ + fsl,spi-cs-sck-delay = <1000>; + fsl,spi-sck-cs-delay = <1000>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + label = "sw0p0"; + phy-handle = <&sw0p0_rgmii_phy>; + phy-mode = "rgmii-id"; + reg = <0>; + }; + + port@1 { + label = "sw0p1"; + phy-handle = <&sw0p1_rgmii_phy>; + phy-mode = "rgmii-id"; + reg = <1>; + }; + + port@2 { + label = "sw0p2"; + phy-handle = <&sw0p2_rgmii_phy>; + phy-mode = "rgmii-id"; + reg = <2>; + }; + + port@3 { + reg = <3>; + status = "disabled"; + }; + + port@4 { + ethernet = <&mscc_felix_port0>; + phy-mode = "sgmii"; + reg = <4>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; + + /* Daughter card 1 */ + sja1105_switch1: ethernet-switch@1 { + reg = <0x1>; + #address-cells = <1>; + #size-cells = <0>; + compatible = "nxp,sja1105s"; + dsa,member = <2 2>; + /* 20 MHz */ + spi-max-frequency = <20000000>; + spi-cpha; + fsl,spi-cs-sck-delay = <1000>; + fsl,spi-sck-cs-delay = <1000>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + label = "sw1p0"; + phy-handle = <&sw1p0_rgmii_phy>; + phy-mode = "rgmii-id"; + reg = <0>; + }; + + port@1 { + label = "sw1p1"; + phy-handle = <&sw1p1_rgmii_phy>; + phy-mode = "rgmii-id"; + reg = <1>; + }; + + port@2 { + label = "sw1p2"; + phy-handle = <&sw1p2_rgmii_phy>; + phy-mode = "rgmii-id"; + reg = <2>; + }; + + port@3 { + label = "sw1p3"; + phy-handle = <&sw1p3_rgmii_phy>; + phy-mode = "rgmii-id"; + reg = <3>; + }; + + port@4 { + ethernet = <&mscc_felix_port2>; + phy-mode = "sgmii"; + reg = <4>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; +}; + +&duart0 { + status = "okay"; +}; + +&duart1 { + status = "okay"; +}; + +&enetc_mdio_pf3 { + status = "okay"; + + /* + * PHYs on main board + */ + + /* RTL8211FSI */ + sw0p0_rgmii_phy: ethernet-phy@7 { + reg = <0x7>; + }; + + /* VSC8502 SGMII dual PHY */ + sw0p1_rgmii_phy: ethernet-phy@0 { + reg = <0x0>; + }; + + sw0p2_rgmii_phy: ethernet-phy@1 { + reg = <0x1>; + }; + + /* + * PHYs on daughter board 1 + */ + + /* RTL8211FSI */ + sw1p0_rgmii_phy: ethernet-phy@9 { + reg = <0x9>; + }; + + sw1p1_rgmii_phy: ethernet-phy@8 { + reg = <0x8>; + }; + + /* VSC8502 RGMII dual PHY */ + sw1p2_rgmii_phy: ethernet-phy@5 { + reg = <0x5>; + }; + + sw1p3_rgmii_phy: ethernet-phy@4 { + reg = <0x4>; + }; +}; + +&enetc_port0 { + status = "disabled"; +}; + +&enetc_port1 { + status = "disabled"; +}; + +&enetc_port2 { + status = "okay"; +}; + +&esdhc { + cap-sd-highspeed; + status = "okay"; +}; + +&esdhc1 { + mmc-hs200-1_8v; + status = "okay"; +}; + +&i2c0 { + status = "okay"; + + i2c-mux@77 { + compatible = "nxp,pca9847"; + reg = <0x77>; + #address-cells = <1>; + #size-cells = <0>; + + i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0x02>; + + current-monitor@40 { + compatible = "ti,ina220"; + reg = <0x40>; + shunt-resistor = <500>; + }; + }; + + i2c@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0x3>; + + temperature-sensor@4c { + compatible = "nxp,sa56004"; + reg = <0x4c>; + vcc-supply = <&sb_3v3>; + }; + + rtc@51 { + compatible = "nxp,pcf2129"; + reg = <0x51>; + }; + }; + }; +}; + +&mscc_felix { + status = "okay"; +}; + +/* Master for SJA1105 on main board */ +&mscc_felix_port0 { + phy-mode = "sgmii"; + status = "okay"; + + fixed-link { + speed = <1000>; + full-duplex; + }; +}; + +/* Master for SJA1105 on daughter card 1 */ +&mscc_felix_port2 { + phy-mode = "sgmii"; + status = "okay"; + + fixed-link { + speed = <1000>; + full-duplex; + }; +}; + +&mscc_felix_port4 { + ethernet = <&enetc_port2>; + status = "okay"; +}; + +&hdptx0 { + lane-mapping = <0x4e>; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-dsa-swp5-eno3.dts b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-dsa-swp5-eno3.dts new file mode 100644 index 000000000000..4a2eebbc9b43 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-dsa-swp5-eno3.dts @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Device Tree file for NXP LS1028A RDB with dsa master swp5-eno3. + * + * Copyright 2018-2023 NXP + * + * Hongbo Wang + * + */ + +/dts-v1/; +#include "fsl-ls1028a-rdb.dts" + +&enetc_port2 { + fixed-link { + pause; + }; +}; + +&enetc_port3 { + status = "okay"; +}; + +&mscc_felix_port4 { + label = "swp4"; + /delete-property/ ethernet; + status = "okay"; + + fixed-link { + pause; + }; +}; + +&mscc_felix_port5 { + ethernet = <&enetc_port3>; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-jailhouse-without-enetc.dts b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-jailhouse-without-enetc.dts new file mode 100644 index 000000000000..00ade901c736 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-jailhouse-without-enetc.dts @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Device Tree file for NXP LS1028A RDB Board Jailhouse case. + * + * Copyright 2021-2023 NXP + * + * Hou Zhiqiang + */ + +#include "fsl-ls1028a-rdb.dts" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* Reserve 832MB for Jailhouse from 0xc000,0000 */ + /* 4MB */ + jh_reserved: jh@0xc0000000 { + no-map; + reg = <0x0 0xc0000000 0x0 0x400000>; + }; + /* 1MB */ + loader_reserved: loader@0xc0400000 { + no-map; + reg = <0x0 0xc0400000 0x0 0x00100000>; + }; + /* 2MB */ + ivshmem_reserved: ivshmem@0xc0500000 { + no-map; + reg = <0x0 0xc0500000 0x0 0x00200000>; + }; + /* 2MB */ + pci_reserved: pci@0xc0700000 { + no-map; + reg = <0x0 0xc0700000 0x0 0x00200000>; + }; + /* 823MB */ + inmate_reserved: inmate@0xc0900000 { + no-map; + reg = <0x0 0xc0900000 0x0 0x33700000>; + }; + }; +}; + +&duart0 { + status = "okay"; + /delete-property/ interrupts; +}; + +&its { + status = "disabled"; +}; + +&duart1 { + status = "disabled"; +}; + +&smmu { + status = "disabled"; +}; + +&enetc_pcie { + status = "disabled"; +}; + +&enetc_port0 { + status = "disabled"; +}; + +&enetc_port2 { + status = "disabled"; +}; + +&mscc_felix { + status = "disabled"; +}; + +&mscc_felix_port0 { + status = "disabled"; +}; + +&mscc_felix_port1 { + status = "disabled"; +}; + +&mscc_felix_port2 { + status = "disabled"; +}; + +&mscc_felix_port3 { + status = "disabled"; +}; + +&mscc_felix_port4 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-jailhouse.dts b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-jailhouse.dts new file mode 100644 index 000000000000..00a241511258 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-jailhouse.dts @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Device Tree file for NXP LS1028A RDB Board Jailhouse case. + * + * Copyright 2018-2023 NXP + * + * Hongbo Wang + * + */ + +#include "fsl-ls1028a-rdb.dts" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* Reserve 832MB for Jailhouse from 0xc000,0000 */ + /* 4MB */ + jh_reserved: jh@0xc0000000 { + no-map; + reg = <0x0 0xc0000000 0x0 0x400000>; + }; + /* 1MB */ + loader_reserved: loader@0xc0400000 { + no-map; + reg = <0x0 0xc0400000 0x0 0x00100000>; + }; + /* 2MB */ + ivshmem_reserved: ivshmem@0xc0500000 { + no-map; + reg = <0x0 0xc0500000 0x0 0x00200000>; + }; + /* 2MB */ + pci_reserved: pci@0xc0700000 { + no-map; + reg = <0x0 0xc0700000 0x0 0x00200000>; + }; + /* 823MB */ + inmate_reserved: inmate@0xc0900000 { + no-map; + reg = <0x0 0xc0900000 0x0 0x33700000>; + }; + }; +}; + +&duart0 { + status = "okay"; + /delete-property/ interrupts; +}; + +&duart1 { + status = "disabled"; +}; + +&gpio3 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-sdk-bm.dts b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-sdk-bm.dts new file mode 100644 index 000000000000..dd9a9d7b425a --- /dev/null +++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb-sdk-bm.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * DTS file for NXP Layerscape baremetal + * + * Copyright 2019-2023 NXP + * + * Author: Hongbo Wang + * + */ + +#include "fsl-ls1028a-rdb.dts" + +&enetc_pcie { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb.dts b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb.dts index 51218c553bd8..c65326e89be4 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb.dts +++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb.dts @@ -96,6 +96,19 @@ simple-audio-card,codec { }; }; +&dspi2 { + bus-num = <2>; + status = "okay"; + + mikrobus@0 { + compatible = "semtech,sx1301"; + reg = <0>; + spi-max-frequency = <2000000>; + fsl,spi-cs-sck-delay = <1000000>; + fsl,spi-sck-cs-delay = <50>; + }; +}; + &can0 { status = "okay"; @@ -252,6 +265,18 @@ rtc@51 { reg = <0x51>; }; }; + + i2c@6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0x6>; + + pn7120: pn7120@28 { + compatible = "nxp,pn7120", "nxp,pn544"; + reg = <0x28>; + clock-frequency = <1000000>; + }; + }; }; }; diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi index 71890c8d3d56..e5f2553a7b7a 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi @@ -1113,7 +1113,7 @@ tmu: tmu@1f80000 { #thermal-sensor-cells = <1>; }; - pcie@1f0000000 { /* Integrated Endpoint Root Complex */ + enetc_pcie: pcie@1f0000000 { /* Integrated Endpoint Root Complex */ compatible = "pci-host-ecam-generic"; reg = <0x01 0xf0000000 0x0 0x100000>; #address-cells = <3>; diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-bm.dts b/arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-bm.dts new file mode 100644 index 000000000000..a7036efb1729 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-bm.dts @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * DTS file for NXP Layerscape baremetal + * + * Copyright 2018-2023 NXP + * + * Author: Changming Huang + * + */ + +#include "fsl-ls1043a-rdb-sdk.dts" + + +&usb0 { + status = "disabled"; +}; + +&usb1 { + status = "disabled"; +}; + +&usb2 { + status = "disabled"; +}; + +&pcie1 { + status = "okay"; +}; + +&pcie2 { + status = "disabled"; +}; + +&pcie3 { + status = "disabled"; +}; + +&optee { + status = "disabled"; +}; + +&ifc { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-jailhouse-with-dpaa.dts b/arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-jailhouse-with-dpaa.dts new file mode 100755 index 000000000000..77bd7c1e6ed1 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-jailhouse-with-dpaa.dts @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * DTS file for NXP Layerscape-1043ARDB jailhouse case + * + * Copyright 2018-2023 NXP + * + * Author: Hongbo Wang + * + */ + +#include "fsl-ls1043a-rdb.dts" +#include "qoriq-qman-portals-sdk.dtsi" +#include "qoriq-bman-portals-sdk.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* Reserve 832MB for Jailhouse from 0xc000,0000 */ + /* 4MB */ + jh_reserved: jh@0xc0000000 { + no-map; + reg = <0x0 0xc0000000 0x0 0x400000>; + }; + /* 1MB */ + loader_reserved: loader@0xc0400000 { + no-map; + reg = <0x0 0xc0400000 0x0 0x00100000>; + }; + /* 2MB */ + ivshmem_reserved: ivshmem@0xc0500000 { + no-map; + reg = <0x0 0xc0500000 0x0 0x00200000>; + }; + /* 2MB */ + pci_reserved: pci@0xc0700000 { + no-map; + reg = <0x0 0xc0700000 0x0 0x00200000>; + }; + /* 823MB */ + inmate_reserved: inmate@0xc0900000 { + no-map; + reg = <0x0 0xc0900000 0x0 0x33700000>; + }; + }; +}; + +&bman_fbpr { + compatible = "fsl,bman-fbpr"; + alloc-ranges = <0 0 0x10000 0>; +}; +&qman_fqd { + compatible = "fsl,qman-fqd"; + alloc-ranges = <0 0 0x10000 0>; +}; +&qman_pfdr { + compatible = "fsl,qman-pfdr"; + alloc-ranges = <0 0 0x10000 0>; +}; + +&soc { +/delete-property/ dma-coherent; + +#include "qoriq-dpaa-eth.dtsi" +#include "qoriq-fman3-0-6oh.dtsi" + +pcie@3400000 { + /delete-property/ iommu-map; + dma-coherent; +}; + +pcie@3500000 { + /delete-property/ iommu-map; + dma-coherent; +}; + +pcie@3600000 { + /delete-property/ iommu-map; + dma-coherent; +}; + +/delete-node/ iommu@9000000; +}; + +&fman0 { + compatible = "fsl,fman", "simple-bus"; +}; + +&clockgen { + dma-coherent; +}; + +&scfg { + dma-coherent; +}; + +&crypto { + dma-coherent; +}; + +&dcfg { + dma-coherent; +}; + +&ifc { + dma-coherent; +}; + +&qspi { + dma-coherent; +}; + +&esdhc { + dma-coherent; +}; + +&ddr { + dma-coherent; +}; + +&tmu { + dma-coherent; +}; + +&qman { + dma-coherent; +}; + +&bman { + dma-coherent; +}; + +&bportals { + dma-coherent; +}; + +&qportals { + dma-coherent; +}; + +&dspi0 { + dma-coherent; +}; + +&dspi1 { + dma-coherent; +}; + +&i2c0 { + dma-coherent; +}; + +&i2c1 { + dma-coherent; +}; + +&i2c2 { + dma-coherent; +}; + +&i2c3 { + dma-coherent; +}; + +&duart0 { + dma-coherent; + /delete-property/ interrupts; +}; + +&duart1 { + dma-coherent; + status = "disabled"; +}; + +&duart2 { + dma-coherent; +}; + +&duart3 { + dma-coherent; +}; + +&gpio1 { + dma-coherent; +}; + +&gpio2 { + dma-coherent; +}; + +&gpio3 { + dma-coherent; +}; + +&gpio4 { + dma-coherent; +}; + +&lpuart0 { + dma-coherent; +}; + +&lpuart1 { + dma-coherent; +}; + +&lpuart2 { + dma-coherent; +}; + +&lpuart3 { + dma-coherent; +}; + +&lpuart4 { + dma-coherent; +}; + +&lpuart5 { + dma-coherent; +}; + +&ftm_alarm0 { + dma-coherent; +}; + +&wdog0 { + dma-coherent; +}; + +&edma0 { + dma-coherent; +}; + +&qdma { + dma-coherent; +}; + +&msi1 { + dma-coherent; +}; + +&msi2 { + dma-coherent; +}; + +&msi3 { + dma-coherent; +}; + +&fman0 { + dma-coherent; +}; + +&ptp_timer0 { + dma-coherent; +}; + +&fsldpaa { + dma-coherent; +}; diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-jailhouse.dts b/arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-jailhouse.dts new file mode 100644 index 000000000000..a7335581c469 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/fsl-ls1043a-rdb-sdk-jailhouse.dts @@ -0,0 +1,267 @@ + // SPDX-License-Identifier: GPL-2.0+ +/* + * DTS file for NXP Layerscape-1043ARDB jailhouse case + * + * Copyright 2018-2023 NXP + * + * Author: Hongbo Wang + * + */ + +#include "fsl-ls1043a-rdb.dts" +#include "qoriq-qman-portals-sdk.dtsi" +#include "qoriq-bman-portals-sdk.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* Reserve 832MB for Jailhouse from 0xc000,0000 */ + /* 4MB */ + jh_reserved: jh@0xc0000000 { + no-map; + reg = <0x0 0xc0000000 0x0 0x400000>; + }; + /* 1MB */ + loader_reserved: loader@0xc0400000 { + no-map; + reg = <0x0 0xc0400000 0x0 0x00100000>; + }; + /* 2MB */ + ivshmem_reserved: ivshmem@0xc0500000 { + no-map; + reg = <0x0 0xc0500000 0x0 0x00200000>; + }; + /* 2MB */ + pci_reserved: pci@0xc0700000 { + no-map; + reg = <0x0 0xc0700000 0x0 0x00200000>; + }; + /* 823MB */ + inmate_reserved: inmate@0xc0900000 { + no-map; + reg = <0x0 0xc0900000 0x0 0x33700000>; + }; + }; +}; + +&bman_fbpr { + compatible = "fsl,bman-fbpr"; + alloc-ranges = <0 0 0x10000 0>; +}; +&qman_fqd { + compatible = "fsl,qman-fqd"; + alloc-ranges = <0 0 0x10000 0>; +}; +&qman_pfdr { + compatible = "fsl,qman-pfdr"; + alloc-ranges = <0 0 0x10000 0>; +}; + +&soc { +/delete-property/ dma-coherent; + +#include "qoriq-dpaa-eth.dtsi" +#include "qoriq-fman3-0-6oh.dtsi" + +pcie@3400000 { + /delete-property/ iommu-map; + dma-coherent; +}; + +pcie@3500000 { + /delete-property/ iommu-map; + dma-coherent; +}; + +pcie@3600000 { + /delete-property/ iommu-map; + dma-coherent; +}; + +/delete-node/ iommu@9000000; +/delete-node/ qman@1880000; +/delete-node/ bman@1890000; +/delete-node/ bman-portals@508000000; +/delete-node/ qman-portals@500000000; +}; + +&fman0 { + compatible = "fsl,fman", "simple-bus"; +}; + +&clockgen { + dma-coherent; +}; + +&scfg { + dma-coherent; +}; + +&crypto { + dma-coherent; +}; + +&dcfg { + dma-coherent; +}; + +&ifc { + dma-coherent; +}; + +&qspi { + dma-coherent; +}; + +&esdhc { + dma-coherent; +}; + +&ddr { + dma-coherent; +}; + +&tmu { + dma-coherent; +}; + +&dspi0 { + dma-coherent; +}; + +&dspi1 { + dma-coherent; +}; + +&i2c0 { + dma-coherent; +}; + +&i2c1 { + dma-coherent; +}; + +&i2c2 { + dma-coherent; +}; + +&i2c3 { + dma-coherent; +}; + +&duart0 { + dma-coherent; + /delete-property/ interrupts; +}; + +&duart1 { + dma-coherent; + status = "disabled"; +}; + +&duart2 { + dma-coherent; +}; + +&duart3 { + dma-coherent; +}; + +&gpio1 { + dma-coherent; +}; + +&gpio2 { + dma-coherent; + status = "disabled"; +}; + +&gpio3 { + dma-coherent; +}; + +&gpio4 { + dma-coherent; +}; + +&lpuart0 { + dma-coherent; +}; + +&lpuart1 { + dma-coherent; +}; + +&lpuart2 { + dma-coherent; +}; + +&lpuart3 { + dma-coherent; +}; + +&lpuart4 { + dma-coherent; +}; + +&lpuart5 { + dma-coherent; +}; + +&ftm_alarm0 { + dma-coherent; +}; + +&wdog0 { + dma-coherent; +}; + +&edma0 { + dma-coherent; +}; + +&qdma { + dma-coherent; +}; + +&msi1 { + dma-coherent; +}; + +&msi2 { + dma-coherent; +}; + +&msi3 { + dma-coherent; +}; + +&fman0 { + dma-coherent; + status = "disabled"; +}; + +&ptp_timer0 { + dma-coherent; + status = "disabled"; +}; + +&fsldpaa { + dma-coherent; + status = "disabled"; +}; + +&bman_fbpr { + status = "disabled"; +}; + +&qman_fqd { + status = "disabled"; +}; + +&qman_pfdr { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi index 2d1e9ed7dac6..01e1194fff66 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi @@ -1063,7 +1063,7 @@ ftm_alarm0: timer@29d0000 { }; firmware { - optee { + optee: optee { compatible = "linaro,optee-tz"; method = "smc"; }; diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-bm.dts b/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-bm.dts new file mode 100644 index 000000000000..097a95802919 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-bm.dts @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * DTS file for NXP Layerscape baremetal + * + * Copyright 2018-2023 NXP + * + * Author: Changming Huang + * + */ + +#include "fsl-ls1046a-rdb-sdk.dts" + +&usb0 { + status = "disabled"; +}; + +&usb1 { + status = "disabled"; +}; + +&usb2 { + status = "disabled"; +}; + +&pcie1 { + status = "okay"; +}; + +&pcie2 { + status = "disabled"; +}; + +&pcie3 { + status = "disabled"; +}; + +&optee { + status = "disabled"; +}; + +&ifc { + status = "disabled"; +}; + +&gpio1 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-jailhouse-with-dpaa.dts b/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-jailhouse-with-dpaa.dts new file mode 100755 index 000000000000..35ec99ac4b3e --- /dev/null +++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-jailhouse-with-dpaa.dts @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * DTS file for NXP Layerscape-1046ARDB jailhouse case + * + * Copyright 2018-2023 NXP + * + * Author: Hongbo Wang + * + */ + +#include "fsl-ls1046a-rdb.dts" +#include "qoriq-qman-portals-sdk.dtsi" +#include "qoriq-bman-portals-sdk.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* Reserve 832MB for Jailhouse from 0xc000,0000 */ + /* 4MB */ + jh_reserved: jh@0xc0000000 { + no-map; + reg = <0x0 0xc0000000 0x0 0x400000>; + }; + /* 1MB */ + loader_reserved: loader@0xc0400000 { + no-map; + reg = <0x0 0xc0400000 0x0 0x00100000>; + }; + /* 2MB */ + ivshmem_reserved: ivshmem@0xc0500000 { + no-map; + reg = <0x0 0xc0500000 0x0 0x00200000>; + }; + /* 2MB */ + pci_reserved: pci@0xc0700000 { + no-map; + reg = <0x0 0xc0700000 0x0 0x00200000>; + }; + /* 823MB */ + inmate_reserved: inmate@0xc0900000 { + no-map; + reg = <0x0 0xc0900000 0x0 0x33700000>; + }; + }; +}; + +&bman_fbpr { + compatible = "fsl,bman-fbpr"; + alloc-ranges = <0 0 0x10000 0>; +}; +&qman_fqd { + compatible = "fsl,qman-fqd"; + alloc-ranges = <0 0 0x10000 0>; +}; +&qman_pfdr { + compatible = "fsl,qman-pfdr"; + alloc-ranges = <0 0 0x10000 0>; +}; + +&soc { +/delete-property/ dma-coherent; + +#include "qoriq-dpaa-eth.dtsi" +#include "qoriq-fman3-0-6oh.dtsi" + +pcie@3400000 { + /delete-property/ iommu-map; +}; + +pcie@3500000 { + /delete-property/ iommu-map; +}; + +pcie@3600000 { + /delete-property/ iommu-map; +}; + +/delete-node/ iommu@9000000; +}; + +&fsldpaa { + ethernet@0 { + status = "disabled"; + }; + ethernet@1 { + status = "disabled"; + }; + ethernet@9 { + compatible = "fsl,dpa-ethernet"; + fsl,fman-mac = <&enet7>; + dma-coherent; + }; +}; + +&fman0 { + compatible = "fsl,fman", "simple-bus"; +}; + +&clockgen { + dma-coherent; +}; + +&scfg { + dma-coherent; +}; + +&crypto { + dma-coherent; +}; + +&dcfg { + dma-coherent; +}; + +&ifc { + dma-coherent; +}; + +&qspi { + dma-coherent; +}; + +&esdhc { + dma-coherent; +}; + +&ddr { + dma-coherent; +}; + +&tmu { + dma-coherent; +}; + +&qman { + dma-coherent; +}; + +&bman { + dma-coherent; +}; + +&bportals { + dma-coherent; +}; + +&qportals { + dma-coherent; +}; + +&dspi { + dma-coherent; +}; + +&i2c0 { + dma-coherent; +}; + +&i2c1 { + dma-coherent; +}; + +&i2c2 { + dma-coherent; +}; + +&i2c3 { + dma-coherent; +}; + +&duart0 { + dma-coherent; + /delete-property/ interrupts; +}; + +&duart1 { + dma-coherent; + status = "disabled"; +}; + +&duart2 { + dma-coherent; +}; + +&duart3 { + dma-coherent; +}; + +&gpio0 { + dma-coherent; +}; + +&gpio1 { + dma-coherent; +}; + +&gpio2 { + dma-coherent; +}; + +&gpio3 { + dma-coherent; +}; + +&lpuart0 { + dma-coherent; +}; + +&lpuart1 { + dma-coherent; +}; + +&lpuart2 { + dma-coherent; +}; + +&lpuart3 { + dma-coherent; +}; + +&lpuart4 { + dma-coherent; +}; + +&lpuart5 { + dma-coherent; +}; + +&ftm_alarm0 { + dma-coherent; +}; + +&wdog0 { + dma-coherent; +}; + +&edma0 { + dma-coherent; +}; + +&sata { + dma-coherent; +}; + +&qdma { + dma-coherent; +}; + +&msi1 { + dma-coherent; +}; + +&msi2 { + dma-coherent; +}; + +&msi3 { + dma-coherent; +}; + +&fman0 { + dma-coherent; +}; + +&ptp_timer0 { + dma-coherent; +}; + +&fsldpaa { + dma-coherent; +}; diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-jailhouse.dts b/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-jailhouse.dts new file mode 100755 index 000000000000..8bf8d3eb03ee --- /dev/null +++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a-rdb-sdk-jailhouse.dts @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * DTS file for NXP Layerscape-1046ARDB jailhouse case + * + * Copyright 2018-2023 NXP + * + * Author: Hongbo Wang + * + */ + +#include "fsl-ls1046a-rdb.dts" +#include "qoriq-qman-portals-sdk.dtsi" +#include "qoriq-bman-portals-sdk.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* Reserve 832MB for Jailhouse from 0xc000,0000 */ + /* 4MB */ + jh_reserved: jh@0xc0000000 { + no-map; + reg = <0x0 0xc0000000 0x0 0x400000>; + }; + /* 1MB */ + loader_reserved: loader@0xc0400000 { + no-map; + reg = <0x0 0xc0400000 0x0 0x00100000>; + }; + /* 2MB */ + ivshmem_reserved: ivshmem@0xc0500000 { + no-map; + reg = <0x0 0xc0500000 0x0 0x00200000>; + }; + /* 2MB */ + pci_reserved: pci@0xc0700000 { + no-map; + reg = <0x0 0xc0700000 0x0 0x00200000>; + }; + /* 823MB */ + inmate_reserved: inmate@0xc0900000 { + no-map; + reg = <0x0 0xc0900000 0x0 0x33700000>; + }; + }; +}; + +&bman_fbpr { + compatible = "fsl,bman-fbpr"; + alloc-ranges = <0 0 0x10000 0>; +}; +&qman_fqd { + compatible = "fsl,qman-fqd"; + alloc-ranges = <0 0 0x10000 0>; +}; +&qman_pfdr { + compatible = "fsl,qman-pfdr"; + alloc-ranges = <0 0 0x10000 0>; +}; + +&soc { +/delete-property/ dma-coherent; + +#include "qoriq-dpaa-eth.dtsi" +#include "qoriq-fman3-0-6oh.dtsi" + +pcie@3400000 { + /delete-property/ iommu-map; +}; + +pcie@3500000 { + /delete-property/ iommu-map; +}; + +pcie@3600000 { + /delete-property/ iommu-map; +}; + +/delete-node/ iommu@9000000; +/delete-node/ qman@1880000; +/delete-node/ bman@1890000; +/delete-node/ bman-portals@508000000; +/delete-node/ qman-portals@500000000; +}; + +&fsldpaa { + ethernet@0 { + status = "disabled"; + }; + ethernet@1 { + status = "disabled"; + }; + ethernet@9 { + compatible = "fsl,dpa-ethernet"; + fsl,fman-mac = <&enet7>; + dma-coherent; + }; +}; + +&fman0 { + compatible = "fsl,fman", "simple-bus"; +}; + +&clockgen { + dma-coherent; +}; + +&scfg { + dma-coherent; +}; + +&crypto { + dma-coherent; +}; + +&dcfg { + dma-coherent; +}; + +&ifc { + dma-coherent; +}; + +&qspi { + dma-coherent; +}; + +&esdhc { + dma-coherent; +}; + +&ddr { + dma-coherent; +}; + +&tmu { + dma-coherent; +}; + +&dspi { + dma-coherent; +}; + +&i2c0 { + dma-coherent; +}; + +&i2c1 { + dma-coherent; +}; + +&i2c2 { + dma-coherent; +}; + +&i2c3 { + dma-coherent; +}; + +&duart0 { + dma-coherent; + /delete-property/ interrupts; +}; + +&duart1 { + dma-coherent; + status = "disabled"; +}; + +&duart2 { + dma-coherent; +}; + +&duart3 { + dma-coherent; +}; + +&gpio0 { + dma-coherent; +}; + +&gpio1 { + dma-coherent; +}; + +&gpio2 { + dma-coherent; + status = "disabled"; +}; + +&gpio3 { + dma-coherent; +}; + +&lpuart0 { + dma-coherent; +}; + +&lpuart1 { + dma-coherent; +}; + +&lpuart2 { + dma-coherent; +}; + +&lpuart3 { + dma-coherent; +}; + +&lpuart4 { + dma-coherent; +}; + +&lpuart5 { + dma-coherent; +}; + +&ftm_alarm0 { + dma-coherent; +}; + +&wdog0 { + dma-coherent; +}; + +&edma0 { + dma-coherent; +}; + +&sata { + dma-coherent; +}; + +&qdma { + dma-coherent; +}; + +&msi1 { + dma-coherent; +}; + +&msi2 { + dma-coherent; +}; + +&msi3 { + dma-coherent; +}; + +&fman0 { + dma-coherent; + status = "disabled"; +}; + +&ptp_timer0 { + dma-coherent; + status = "disabled"; +}; + +&fsldpaa { + dma-coherent; + status = "disabled"; +}; + +&bman_fbpr { + status = "disabled"; +}; + +&qman_fqd { + status = "disabled"; +}; + +&qman_pfdr { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi index 10922fea2fc5..981fd470a1b9 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi @@ -1049,7 +1049,7 @@ qman_pfdr: qman-pfdr { }; firmware { - optee { + optee: optee { compatible = "linaro,optee-tz"; method = "smc"; }; diff --git a/arch/arm64/boot/dts/freescale/imx8-ss-audio.dtsi b/arch/arm64/boot/dts/freescale/imx8-ss-audio.dtsi index 1145f3298053..ff6cfe8971a0 100644 --- a/arch/arm64/boot/dts/freescale/imx8-ss-audio.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8-ss-audio.dtsi @@ -247,6 +247,15 @@ sai3: sai@59070000 { status = "disabled"; }; + gpt5: gpt@590b0000 { + reg = <0x590b0000 0x10000>; + clocks = <&gpt5_lpcg 1>, + <&gpt5_lpcg 1>; + clock-names = "ipg", "per"; + power-domains = <&pd IMX_SC_R_GPT_5>; + status = "disabled"; + }; + asrc1: asrc@59800000 { compatible = "fsl,imx8qm-asrc"; reg = <0x59800000 0x10000>; @@ -567,6 +576,17 @@ sai3_lpcg: clock-controller@59470000 { power-domains = <&pd IMX_SC_R_SAI_3>; }; + gpt5_lpcg: clock-controller@594b0000 { + compatible = "fsl,imx8qxp-lpcg"; + reg = <0x594b0000 0x10000>; + #clock-cells = <1>; + clocks = <&acm IMX_ADMA_ACM_GPT0_MUX_CLK_SEL>, + <&audio_ipg_clk>; + bit-offset = <0 16>; + clock-output-names = "gpt5_lpcg_clk_in", "gpt5_lpcg_ipg_clk"; + power-domains = <&pd IMX_SC_R_GPT_5>; + }; + asrc1_lpcg: clock-controller@59c00000 { compatible = "fsl,imx8qxp-lpcg"; reg = <0x59c00000 0x10000>; diff --git a/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-avb.dts b/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-avb.dts new file mode 100644 index 000000000000..36cc1a616a9a --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-avb.dts @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2022-2023 NXP + */ + +/dts-v1/; + +#include "imx8dxl-evk-enet0.dts" +#include "imx8dxl-evk-enet0-avb.dtsi" diff --git a/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-avb.dtsi b/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-avb.dtsi new file mode 100644 index 000000000000..d7c268f056cc --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-avb.dtsi @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +/* Set GPT Capture input to Ethernet 0 event */ +&acm { + gpt-capture-select = ; +}; + +/* AVB HW timer*/ +&gpt5 { + compatible = "fsl,avb-gpt"; + + clocks = <&gpt5_lpcg 1>, + <&gpt5_lpcg 1>, + <&gpt5_lpcg 0>, + <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_PLL>; + clock-names = "ipg", "per", "clk_in", "audio_pll"; + + /* - Set ACM_AUD_CLK0 source to ACM_AUD_REC_CLK0 + * - Set clk_in to Audio PLL0 Divider + * - Enable Audio PLL and its Master Bus clock (AUD_REC_CLK0): keep this in sync with sai nodes + */ + + assigned-clocks = <&acm IMX_ADMA_ACM_AUD_CLK0_SEL>, + <&acm IMX_ADMA_ACM_GPT0_MUX_CLK_SEL>, + <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_PLL>, + <&clk IMX_SC_R_AUDIO_PLL_0 IMX_SC_PM_CLK_MST_BUS>; + + assigned-clock-parents = <&aud_rec0_lpcg 0>, + <&acm IMX_ADMA_ACM_AUD_CLK0_SEL>; + + assigned-clock-rates = <0>, <0>, <786432000>, <12288000>; + + /* Audio PLL is controlled through SCU */ + fsl,pll-scu-controlled; + + timer-channel = <1>; /* Use output compare channel 1*/ + prescale = <1>; + domain = <0>; + rec-channel = <1 0 1>; // capture channel, eth port, ENET TC id + + interrupts = ; + + status = "okay"; +}; + +&fec1 { + fsl,rx-phy-delay-100-ns = <670>; + fsl,tx-phy-delay-100-ns = <670>; + fsl,rx-phy-delay-1000-ns = <0>; + fsl,tx-phy-delay-1000-ns = <0>; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-sja1105.dts b/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-sja1105.dts new file mode 100644 index 000000000000..ddfdadfbe3f8 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-sja1105.dts @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2023 NXP + */ + +/dts-v1/; + +#include "imx8dxl-evk.dts" + +&iomuxc { + pinctrl_lpspi2: lpspi2grp { + fsl,pins = < + IMX8DXL_USDHC1_VSELECT_ADMA_SPI2_SDO 0x600004c + IMX8DXL_USDHC1_WP_ADMA_SPI2_SDI 0x600004c + IMX8DXL_USDHC1_RESET_B_ADMA_SPI2_SCK 0x600004c + >; + }; + + pinctrl_lpspi2_cs: lpspi2cs { + fsl,pins = < + IMX8DXL_USDHC1_CD_B_LSIO_GPIO4_IO22 0x21 + >; + }; +}; + +&lpspi2 { + fsl,spi-num-chipselects = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lpspi2 &pinctrl_lpspi2_cs>; + cs-gpios = <&lsio_gpio4 22 GPIO_ACTIVE_LOW>; + status = "okay"; + + sja1105p@0 { + compatible = "nxp,sja1105q"; + spi-max-frequency = <12500000>; + /* SJA1105PQRS operates in SPI MODE 1 : CPOL=0 CPHA=1 */ + spi-cpha; + /* use queue 5 for managment traffic */ + hostprio = <5>; + + reg = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "cpu"; + ethernet = <&fec1>; + phy-mode = "rgmii"; + rx-internal-delay-ps = <2000>; + tx-internal-delay-ps = <2000>; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + port@1 { + reg = <1>; + label = "swp0"; + phy-handle = <&phy0>; + phy-mode = "mii"; + }; + + port@2 { + reg = <2>; + label = "swp1"; + phy-handle = <&phy1>; + phy-mode = "mii"; + }; + + port@3 { + reg = <3>; + label = "swp2"; + phy-handle = <&phy2>; + phy-mode = "rmii"; + }; + + port@4 { + reg = <4>; + label = "swp3"; + phy-handle = <&phy3>; + phy-mode = "rmii"; + }; + }; + }; +}; + +ðphy1 { + status = "disabled"; +}; + +&fec1 { + assigned-clocks = <&clk IMX_SC_R_ENET_0 IMX_SC_C_CLKDIV>; + assigned-clock-rates = <12000000>; + phy-supply = <&mii_select>; + status = "okay"; + + fsl,magic-packet; + phy-mode = "rgmii"; + /delete-property/ rx-internal-delay-ps; + /delete-property/ phy-handle; + + fixed-link { + speed = <1000>; + full-duplex; + }; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + /* SJA1105Q EVB switch ports : NXP TJA1102 + the phys SMI addresses on EVB are hard coded giving + first TJA device at offset 0x8 and the 2nd once at offet 0xe */ + phy0: ethernet-phy@8 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0x8>; + max-speed = <100>; + phy1: ethernet-phy@9 { + reg = <0x9>; + max-speed = <100>; + }; + }; + + phy2: ethernet-phy@e { + #address-cells = <1>; + #size-cells = <0>; + reg = <0xe>; + max-speed = <100>; + phy3: ethernet-phy@f { + reg = <0xf>; + max-speed = <100>; + }; + }; + }; +}; + +®_fec1_sel { + status = "okay"; +}; + +/* This would set max7233 (0) to high (intented for PHY power on) and would + * enable LPC SPI/SMI on the switch EVB which interferes with + * SPI communcation between the iMX and the SJA1105 +*/ +®_fec1_io { + status = "disabled"; +}; + +&max7322 { + status = "disabled"; +}; + +&mii_select { + /delete-property/ enable-active-high; +}; + +&eqos { + status = "disabled"; +}; + +&usdhc2 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-tja1100-avb.dts b/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-tja1100-avb.dts new file mode 100644 index 000000000000..121b506cbc8c --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8dxl-evk-enet0-tja1100-avb.dts @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +/dts-v1/; + +#include "imx8dxl-evk-enet0-tja1100.dts" +#include "imx8dxl-evk-enet0-avb.dtsi" diff --git a/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts b/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts index 02a40cf803de..f71727fed226 100644 --- a/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts @@ -320,6 +320,9 @@ &eqos { phy-handle = <ðphy0>; nvmem-cells = <&fec_mac1>; nvmem-cell-names = "mac-address"; + snps,force_thresh_dma_mode; + snps,mtl-tx-config = <&mtl_tx_setup>; + snps,mtl-rx-config = <&mtl_rx_setup>; status = "okay"; mdio { @@ -343,6 +346,61 @@ vddio0: vddio-regulator { }; }; }; + + mtl_tx_setup: tx-queues-config { + snps,tx-queues-to-use = <5>; + snps,tx-sched-sp; + queue0 { + snps,dcb-algorithm; + snps,priority = <0x1>; + }; + queue1 { + snps,dcb-algorithm; + snps,priority = <0x2>; + }; + queue2 { + snps,dcb-algorithm; + snps,priority = <0x4>; + }; + queue3 { + snps,dcb-algorithm; + snps,priority = <0x8>; + }; + queue4 { + snps,dcb-algorithm; + snps,priority = <0xf0>; + }; + }; + + mtl_rx_setup: rx-queues-config { + snps,rx-queues-to-use = <5>; + snps,rx-sched-sp; + queue0 { + snps,dcb-algorithm; + snps,priority = <0x1>; + snps,map-to-dma-channel = <0>; + }; + queue1 { + snps,dcb-algorithm; + snps,priority = <0x2>; + snps,map-to-dma-channel = <1>; + }; + queue2 { + snps,dcb-algorithm; + snps,priority = <0x4>; + snps,map-to-dma-channel = <2>; + }; + queue3 { + snps,dcb-algorithm; + snps,priority = <0x8>; + snps,map-to-dma-channel = <3>; + }; + queue4 { + snps,dcb-algorithm; + snps,priority = <0xf0>; + snps,map-to-dma-channel = <4>; + }; + }; }; /* diff --git a/arch/arm64/boot/dts/freescale/imx8dxl-ss-adma.dtsi b/arch/arm64/boot/dts/freescale/imx8dxl-ss-adma.dtsi index c8ec4328ee6e..7df83c1f9e59 100644 --- a/arch/arm64/boot/dts/freescale/imx8dxl-ss-adma.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8dxl-ss-adma.dtsi @@ -32,6 +32,7 @@ &acm { <&pd IMX_SC_R_AUDIO_PLL_0>, <&pd IMX_SC_R_AUDIO_PLL_1>, <&pd IMX_SC_R_ASRC_0>, + <&pd IMX_SC_R_GPT_5>, <&pd IMX_SC_R_SAI_0>, <&pd IMX_SC_R_SAI_1>, <&pd IMX_SC_R_SAI_2>, diff --git a/arch/arm64/boot/dts/freescale/imx8m-generic-mbox-1.dtsi b/arch/arm64/boot/dts/freescale/imx8m-generic-mbox-1.dtsi new file mode 100644 index 000000000000..428e9b8cb4ae --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8m-generic-mbox-1.dtsi @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + gen_sw_mbox_1_reserved: gen-sw-mbox-1@b8501000 { + reg = <0 0xb8501000 0 0x1000>; + no-map; + }; + + }; + + gen_sw_mbox_1: generic-software-mailbox-1@b8501000 { + compatible = "fsl,generic-software-mbox"; + reg = <0 0xb8501000 0 0x1000>; + #mbox-cells = <3>; + /* Use 2 unused SPI interrupts */ + interrupts = , ; + interrupt-names = "irq", "remote_irq"; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8m-generic-mbox.dtsi b/arch/arm64/boot/dts/freescale/imx8m-generic-mbox.dtsi new file mode 100644 index 000000000000..e97e82c78aaa --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8m-generic-mbox.dtsi @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + gen_sw_mbox_reserved: gen-sw-mbox@b8500000 { + reg = <0 0xb8500000 0 0x1000>; + no-map; + }; + + }; + + gen_sw_mbox: generic-software-mailbox@b8500000 { + compatible = "fsl,generic-software-mbox"; + reg = <0 0xb8500000 0 0x1000>; + #mbox-cells = <3>; + /* Use 2 unused SPI interrupts */ + interrupts = , ; + interrupt-names = "irq", "remote_irq"; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8m-rpmsg-ca53-1.dtsi b/arch/arm64/boot/dts/freescale/imx8m-rpmsg-ca53-1.dtsi new file mode 100644 index 000000000000..70e33e6d834b --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8m-rpmsg-ca53-1.dtsi @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +#include "imx8m-generic-mbox-1.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + rpmsg_ca53_1_reserved: rpmsg-ca53-1@b8610000 { + reg = <0 0xb8610000 0 0x10000>; + no-map; + }; + + vdevbuffer_ca53_1: vdevbuffer-ca53-1@b8800000 { + compatible = "shared-dma-pool"; + reg = <0 0xb8800000 0 0x100000>; + no-map; + }; + }; + + rpmsg-ca53-1 { + compatible = "fsl,imx8mm-rpmsg"; + reg = <0x0 0xb8610000 0x0 0x10000>; /* 64K for one rpmsg instance */ + dma-coherent; + memory-region = <&vdevbuffer_ca53_1>; + mbox-names = "tx", "rx", "rxdb"; + mboxes = <&gen_sw_mbox_1 0 0 1 /* Tx channel with ACK */ + &gen_sw_mbox_1 1 0 0 /* Rx channel without ACK */ + &gen_sw_mbox_1 2 0 1>; /* RXDB channel with ACK */ + vdev-nums = <1>; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8m-rpmsg-ca53.dtsi b/arch/arm64/boot/dts/freescale/imx8m-rpmsg-ca53.dtsi new file mode 100644 index 000000000000..a3b0dbf563e8 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8m-rpmsg-ca53.dtsi @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2022-2023 NXP + */ + +#include "imx8m-generic-mbox.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + rpmsg_ca53_reserved: rpmsg-ca53@b8600000 { + reg = <0 0xb8600000 0 0x10000>; + no-map; + }; + + vdevbuffer_ca53: vdevbuffer-ca53@b8700000 { + compatible = "shared-dma-pool"; + reg = <0 0xb8700000 0 0x100000>; + no-map; + }; + }; + + rpmsg-ca53 { + compatible = "fsl,imx8mm-rpmsg"; + reg = <0x0 0xb8600000 0x0 0x10000>; /* 64K for one rpmsg instance */ + dma-coherent; + memory-region = <&vdevbuffer_ca53>; + mbox-names = "tx", "rx", "rxdb"; + mboxes = <&gen_sw_mbox 0 0 1 /* Tx channel with ACK */ + &gen_sw_mbox 1 0 0 /* Rx channel without ACK */ + &gen_sw_mbox 2 0 1>; /* RXDB channel with ACK */ + vdev-nums = <1>; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8m-rproc-ca53.dtsi b/arch/arm64/boot/dts/freescale/imx8m-rproc-ca53.dtsi new file mode 100644 index 000000000000..c44e6a3d8cf0 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8m-rproc-ca53.dtsi @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2024 NXP + */ + +/ { + ca53_1: remoteproc-ca53-1 { + compatible = "fsl,imx-rproc-psci"; + /* bitmask:0b0010, assign A53 Core 1 */ + fsl,cpus-bits = <0x2>; + memory-region = <&rtos_ca53_reserved>; + }; + + ca53_2: remoteproc-ca53-2 { + compatible = "fsl,imx-rproc-psci"; + /* bitmask:0b0100, assign A53 Core 2 */ + fsl,cpus-bits = <0x4>; + memory-region = <&rtos_ca53_reserved>; + }; + + ca53_3: remoteproc-ca53-3 { + compatible = "fsl,imx-rproc-psci"; + /* bitmask:0b1000, assign A53 Core 3 */ + fsl,cpus-bits = <0x8>; + memory-region = <&rtos_ca53_reserved>; + }; + + ca53_2_3: remoteproc-ca53-2-3 { + compatible = "fsl,imx-rproc-psci"; + /* bitmask:0b1100, assign A53 Core 2 and Core 3 */ + fsl,cpus-bits = <0xc>; + memory-region = <&rtos_ca53_reserved>; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-avb.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-avb.dts new file mode 100644 index 000000000000..f5b15dddea51 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-avb.dts @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2020-2021, 2024 NXP + */ + +/dts-v1/; + +#include "imx8mm-evk.dts" + +/* AVB HW timer*/ +&gpt1 { + compatible = "fsl,avb-gpt"; + timer-channel = <1>; /* Use output compare channel 1*/ + prescale = <1>; + domain = <0>; + + clocks = <&clk IMX8MM_CLK_GPT1_ROOT>, + <&clk IMX8MM_CLK_GPT1_ROOT>, <&clk IMX8MM_AUDIO_PLL1>; + clock-names = "ipg", "per", "audio_pll"; + + /* Make the GPT clk root derive from the audio PLL */ + assigned-clocks = <&clk IMX8MM_CLK_GPT1>; + assigned-clock-parents = <&clk IMX8MM_AUDIO_PLL1_OUT>; + assigned-clock-rates = <98304000>; + + /* Enble SW sampling for media clock recovery on port 0 */ + sw-recovery = <0>; + + status = "okay"; +}; + +&fec1 { + fsl,rx-phy-delay-100-ns = <670>; + fsl,tx-phy-delay-100-ns = <670>; + fsl,rx-phy-delay-1000-ns = <0>; + fsl,tx-phy-delay-1000-ns = <0>; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-baremetal.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-baremetal.dts new file mode 100644 index 000000000000..4a36d0e0c7a1 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-baremetal.dts @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2020-2023 NXP + */ + +/dts-v1/; + +#include "imx8mm-evk.dts" + +/ { + model = "FSL i.MX8MM LPDDR4 EVK RevB board - Baremetal"; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + rpmsg_reserved: rpmsg@b8000000 { + no-map; + reg = <0 0xb8000000 0 0x400000>; + }; + bm_reserved: baremetal@60000000 { + no-map; + reg = <0 0x60000000 0 0x10000000>; + }; + }; +}; + +&fec1 { + status = "disabled"; +}; + +&gpio5 { + status = "disabled"; +}; + +&uart3 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-ecat-userspace.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-ecat-userspace.dts new file mode 100644 index 000000000000..a2ff71b9c26b --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-ecat-userspace.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include "imx8mm-evk.dts" + +ðphy0 { + /delete-property/ reset-assert-us; + /delete-property/ reset-deassert-us; +}; + +&fec1 { + compatible = "fsl,imx8mm-fec-uio"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-ecat.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-ecat.dts new file mode 100755 index 000000000000..cde59ddaa3cf --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-ecat.dts @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2023 NXP + */ + +#include "imx8mm-evk.dts" + +&fec1 { + compatible = "fsl,imx8mm-fec-ecat", "fsl,imx8mq-fec-ecat", "fsl,imx6sx-fec-ecat"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-avb.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-avb.dts new file mode 100644 index 000000000000..866a9a8c239b --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-avb.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2022-2023 NXP + */ + +#include "imx8mm-evk-harpoon.dts" +#include "imx8m-rpmsg-ca53.dtsi" + +&fec1 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-industrial.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-industrial.dts new file mode 100644 index 000000000000..bedfd8da1e70 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-industrial.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2022-2023 NXP + */ + +#include "imx8mm-evk-root.dts" +#include "imx8m-rpmsg-ca53.dtsi" + +&fec1 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-virtio-net.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-virtio-net.dts new file mode 100644 index 000000000000..e7a560c16aa3 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon-virtio-net.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +#include "imx8mm-evk-root.dts" +#include "imx8mm-virtio-ca53.dtsi" + +&fec1 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon.dts new file mode 100644 index 000000000000..70b2fa9b6b31 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-harpoon.dts @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2021-2023 NXP + */ + +#include "imx8mm-evk-root.dts" +#include "imx8m-rpmsg-ca53.dtsi" + +&i2c3 { + status = "disabled"; +}; + +&sai1 { + status = "disabled"; +}; + +&sai2 { + status = "disabled"; +}; + +&sai3 { + status = "disabled"; +}; + +&sai5 { + status = "disabled"; +}; + +&sai6 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-igh.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-igh.dts new file mode 100755 index 000000000000..14bf12d0dd82 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-igh.dts @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2024 NXP + */ + +#include "imx8mm-evk.dts" + +&fec1 { + compatible = "fsl,imx8mm-fec-igh-native", "fsl,imx8mq-fec-igh-native", "fsl,imx6sx-fec-igh-native"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-multicore-rpmsg.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-multicore-rpmsg.dts new file mode 100644 index 000000000000..c403173bd960 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-multicore-rpmsg.dts @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2023, 2025 NXP + */ + +/dts-v1/; + +#include "imx8mm-evk-multicore-rtos.dts" +#include "imx8m-generic-mbox.dtsi" +#include "imx8m-generic-mbox-1.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + vdev0vring0_ca53_2: vdev0vring0@b8600000 { + reg = <0 0xb8600000 0 0x8000>; + no-map; + }; + + vdev0vring1_ca53_2: vdev0vring1@b8608000 { + reg = <0 0xb8608000 0 0x8000>; + no-map; + }; + + rsc_table_ca53_2: rsc-table@b86f0000 { + reg = <0 0xb86f0000 0 0x1000>; + no-map; + }; + + vdev0buffer_ca53_2: vdev0buffer@b8700000 { + compatible = "shared-dma-pool"; + reg = <0 0xb8700000 0 0x100000>; + no-map; + }; + + vdev0vring0_ca53_3: vdev0vring0@b8610000 { + reg = <0 0xb8610000 0 0x8000>; + no-map; + }; + + vdev0vring1_ca53_3: vdev0vring1@b8618000 { + reg = <0 0xb8618000 0 0x8000>; + no-map; + }; + + rsc_table_ca53_3: rsc-table@b86f1000 { + reg = <0 0xb86f1000 0 0x1000>; + no-map; + }; + + vdev0buffer_ca53_3: vdev0buffer@b8800000 { + compatible = "shared-dma-pool"; + reg = <0 0xb8800000 0 0x100000>; + no-map; + }; + }; +}; + +&ca53_2 { + memory-region = <&vdev0buffer_ca53_2>, <&vdev0vring0_ca53_2>, <&vdev0vring1_ca53_2>, <&rsc_table_ca53_2>, <&rtos_ca53_reserved>; + dma-coherent; + mbox-names = "tx", "rx", "rxdb"; + mboxes = <&gen_sw_mbox 0 0 1 /* Tx channel with ACK */ + &gen_sw_mbox 1 0 0 /* Rx channel without ACK */ + &gen_sw_mbox 2 0 1>; /* RXDB channel with ACK */ + fsl,startup-delay-ms = <50>; +}; + +&ca53_3 { + memory-region = <&vdev0buffer_ca53_3>, <&vdev0vring0_ca53_3>, <&vdev0vring1_ca53_3>, <&rsc_table_ca53_3>, <&rtos_ca53_reserved>; + dma-coherent; + mbox-names = "tx", "rx", "rxdb"; + mboxes = <&gen_sw_mbox_1 0 0 1 /* Tx channel with ACK */ + &gen_sw_mbox_1 1 0 0 /* Rx channel without ACK */ + &gen_sw_mbox_1 2 0 1>; /* RXDB channel with ACK */ + fsl,startup-delay-ms = <50>; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-multicore-rtos.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-multicore-rtos.dts new file mode 100644 index 000000000000..c00cf94ce50e --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-multicore-rtos.dts @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2023-2024 NXP + */ + +/dts-v1/; + +#include "imx8mm-evk.dts" +#include "imx8m-rproc-ca53.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* + * Reserve up to 48MB (16MB x 3) for three FreeRTOS instances running on + * three Cortex-A Cores when booting Linux on at least on Cortex-A Core. + */ + rtos_ca53_reserved: rtos-ca53@93c00000 { + no-map; + reg = <0 0x93c00000 0x0 0x3000000>; + }; + + /* Reserve 16MB for FreeRTOS running on CM4 */ + m4_reserved: m4@80000000 { + no-map; + reg = <0 0x80000000 0 0x1000000>; + }; + + vdev0vring0: vdev0vring0@b8000000 { + reg = <0 0xb8000000 0 0x8000>; + no-map; + }; + + vdev0vring1: vdev0vring1@b8008000 { + reg = <0 0xb8008000 0 0x8000>; + no-map; + }; + + rsc_table: rsc-table@b80ff000 { + reg = <0 0xb80ff000 0 0x1000>; + no-map; + }; + + vdevbuffer: vdevbuffer@b8400000 { + compatible = "shared-dma-pool"; + reg = <0 0xb8400000 0 0x100000>; + no-map; + }; + }; + + imx8mm-cm4 { + compatible = "fsl,imx8mm-cm4"; + rsc-da = <0xb8000000>; + clocks = <&clk IMX8MM_CLK_M4_DIV>; + mbox-names = "tx", "rx", "rxdb"; + mboxes = <&mu 0 1 + &mu 1 1 + &mu 3 1>; + memory-region = <&vdevbuffer>, <&vdev0vring0>, <&vdev0vring1>, <&rsc_table>; + syscon = <&src>; + fsl,startup-delay-ms = <500>; + }; +}; + +&{/busfreq} { + status = "disabled"; +}; + +&uart4 { + status = "disabled"; +}; + +&clk { + init-on-array = ; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-avb.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-avb.dts new file mode 100644 index 000000000000..20f26f3b642f --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-avb.dts @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2024 NXP + */ + +#include "imx8mm-evk-harpoon-avb.dts" +#include "imx8mm-evk-qca-wifi.dtsi" diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-industrial.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-industrial.dts new file mode 100644 index 000000000000..77aae065a883 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-industrial.dts @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2024 NXP + */ + +#include "imx8mm-evk-harpoon-industrial.dts" +#include "imx8mm-evk-qca-wifi.dtsi" diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-virtio-net.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-virtio-net.dts new file mode 100644 index 000000000000..caf41f260a50 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon-virtio-net.dts @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2024 NXP + */ + +#include "imx8mm-evk-harpoon-virtio-net.dts" +#include "imx8mm-evk-qca-wifi.dtsi" diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon.dts new file mode 100644 index 000000000000..01d7ef234886 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi-harpoon.dts @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2024 NXP + */ + +#include "imx8mm-evk-harpoon.dts" +#include "imx8mm-evk-qca-wifi.dtsi" diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi.dts index aa1a25f00f55..cba11387cb08 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi.dts +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi.dts @@ -1,128 +1,9 @@ // SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* - * Copyright 2019 NXP + * Copyright 2019, 2024 NXP */ /dts-v1/; #include "imx8mm-evk.dts" - -/ { - model = "FSL i.MX8MM LPDDR4 EVK with QCA WIFI revC board "; -}; - -/delete-node/&pmic_nxp; - -&i2c1 { - pmic_rohm: pmic@4b { - compatible = "rohm,bd71847"; - reg = <0x4b>; - pinctrl-0 = <&pinctrl_pmic>; - pinctrl-names = "default"; - interrupt-parent = <&gpio1>; - interrupts = <3 IRQ_TYPE_LEVEL_LOW>; - rohm,reset-snvs-powered; - - #clock-cells = <0>; - clocks = <&osc_32k 0>; - clock-output-names = "clk-32k-out"; - - regulators { - buck1_reg: BUCK1 { - regulator-name = "BUCK1"; - regulator-min-microvolt = <700000>; - regulator-max-microvolt = <1300000>; - regulator-boot-on; - regulator-always-on; - regulator-ramp-delay = <1250>; - }; - - buck2_reg: BUCK2 { - regulator-name = "BUCK2"; - regulator-min-microvolt = <700000>; - regulator-max-microvolt = <1300000>; - regulator-boot-on; - regulator-always-on; - regulator-ramp-delay = <1250>; - rohm,dvs-run-voltage = <1000000>; - rohm,dvs-idle-voltage = <900000>; - }; - - buck3_reg: BUCK3 { - // BUCK5 in datasheet - regulator-name = "BUCK3"; - regulator-min-microvolt = <700000>; - regulator-max-microvolt = <1350000>; - regulator-boot-on; - regulator-always-on; - }; - - buck4_reg: BUCK4 { - // BUCK6 in datasheet - regulator-name = "BUCK4"; - regulator-min-microvolt = <3000000>; - regulator-max-microvolt = <3300000>; - regulator-boot-on; - regulator-always-on; - }; - - buck5_reg: BUCK5 { - // BUCK7 in datasheet - regulator-name = "BUCK5"; - regulator-min-microvolt = <1605000>; - regulator-max-microvolt = <1995000>; - regulator-boot-on; - regulator-always-on; - }; - - buck6_reg: BUCK6 { - // BUCK8 in datasheet - regulator-name = "BUCK6"; - regulator-min-microvolt = <800000>; - regulator-max-microvolt = <1400000>; - regulator-boot-on; - regulator-always-on; - }; - - ldo1_reg: LDO1 { - regulator-name = "LDO1"; - regulator-min-microvolt = <1600000>; - regulator-max-microvolt = <3300000>; - regulator-boot-on; - regulator-always-on; - }; - - ldo2_reg: LDO2 { - regulator-name = "LDO2"; - regulator-min-microvolt = <800000>; - regulator-max-microvolt = <900000>; - regulator-boot-on; - regulator-always-on; - }; - - ldo3_reg: LDO3 { - regulator-name = "LDO3"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <3300000>; - regulator-boot-on; - regulator-always-on; - }; - - ldo4_reg: LDO4 { - regulator-name = "LDO4"; - regulator-min-microvolt = <900000>; - regulator-max-microvolt = <1800000>; - regulator-boot-on; - regulator-always-on; - }; - - ldo6_reg: LDO6 { - regulator-name = "LDO6"; - regulator-min-microvolt = <900000>; - regulator-max-microvolt = <1800000>; - regulator-boot-on; - regulator-always-on; - }; - }; - }; -}; +#include "imx8mm-evk-qca-wifi.dtsi" diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi.dtsi new file mode 100644 index 000000000000..13d3076ac94c --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-qca-wifi.dtsi @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2024 NXP + */ + +/ { + model = "FSL i.MX8MM LPDDR4 EVK with QCA WIFI revC board "; +}; + +/delete-node/&pmic_nxp; + +&i2c1 { + pmic_rohm: pmic@4b { + compatible = "rohm,bd71847"; + reg = <0x4b>; + pinctrl-0 = <&pinctrl_pmic>; + pinctrl-names = "default"; + interrupt-parent = <&gpio1>; + interrupts = <3 IRQ_TYPE_LEVEL_LOW>; + rohm,reset-snvs-powered; + + #clock-cells = <0>; + clocks = <&osc_32k 0>; + clock-output-names = "clk-32k-out"; + + regulators { + buck1_reg: BUCK1 { + regulator-name = "BUCK1"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1300000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <1250>; + }; + + buck2_reg: BUCK2 { + regulator-name = "BUCK2"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1300000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <1250>; + rohm,dvs-run-voltage = <1000000>; + rohm,dvs-idle-voltage = <900000>; + }; + + buck3_reg: BUCK3 { + // BUCK5 in datasheet + regulator-name = "BUCK3"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1350000>; + regulator-boot-on; + regulator-always-on; + }; + + buck4_reg: BUCK4 { + // BUCK6 in datasheet + regulator-name = "BUCK4"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + buck5_reg: BUCK5 { + // BUCK7 in datasheet + regulator-name = "BUCK5"; + regulator-min-microvolt = <1605000>; + regulator-max-microvolt = <1995000>; + regulator-boot-on; + regulator-always-on; + }; + + buck6_reg: BUCK6 { + // BUCK8 in datasheet + regulator-name = "BUCK6"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1400000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo1_reg: LDO1 { + regulator-name = "LDO1"; + regulator-min-microvolt = <1600000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo2_reg: LDO2 { + regulator-name = "LDO2"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <900000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo3_reg: LDO3 { + regulator-name = "LDO3"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo4_reg: LDO4 { + regulator-name = "LDO4"; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo6_reg: LDO6 { + regulator-name = "LDO6"; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg-8m-buf.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg-8m-buf.dts new file mode 100644 index 000000000000..ea5dbfd3b793 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg-8m-buf.dts @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2019-2023 NXP + */ + +/dts-v1/; +#include +#include "imx8mm-evk.dts" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + m4_reserved: m4@80000000 { + no-map; + reg = <0 0x80000000 0 0x1000000>; + }; + + vdev0vring0: vdev0vring0@b8000000 { + reg = <0 0xb8000000 0 0x100000>; + no-map; + }; + + vdev0vring1: vdev0vring1@b8100000 { + reg = <0 0xb8100000 0 0x100000>; + no-map; + }; + + rsc_table: rsc-table@b8a00000 { + reg = <0 0xb8a00000 0 0x1000>; + no-map; + }; + + vdevbuffer: vdevbuffer@b8200000 { + compatible = "shared-dma-pool"; + reg = <0 0xb8200000 0 0x800000>; + no-map; + }; + }; + + bt_sco_codec: bt_sco_codec { + status = "disabled"; + }; + + sound-bt-sco { + status = "disabled"; + }; + + sound-wm8524 { + status = "disabled"; + }; + + wm8524: audio-codec { + status = "disabled"; + }; + + rpmsg_audio: rpmsg_audio { + compatible = "fsl,imx8mm-rpmsg-audio"; + model = "ak4497-audio"; + fsl,platform = "rpmsg-audio-channel"; + fsl,enable-lpa; + fsl,rpmsg-out; + assigned-clocks = <&clk IMX8MM_CLK_SAI1>; + assigned-clock-parents = <&clk IMX8MM_AUDIO_PLL1_OUT>; + assigned-clock-rates = <49152000>; + clocks = <&clk IMX8MM_CLK_SAI1_IPG>, + <&clk IMX8MM_CLK_SAI1_ROOT>, + <&clk IMX8MM_CLK_SDMA3_ROOT>, + <&clk IMX8MM_AUDIO_PLL1_OUT>, + <&clk IMX8MM_AUDIO_PLL2_OUT>; + clock-names = "ipg", "mclk", "dma", "pll8k", "pll11k"; + status = "okay"; + }; + + uart_rpbus_0: uart-rpbus-0 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + //flags=; + flags=; + status = "okay"; + }; + + uart_rpbus_1: uart-rpbus-1 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + //flags=; + flags=; + status = "okay"; + }; + + uart_rpbus_2: uart-rpbus-2 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + //flags=; + flags=; + status = "okay"; + }; + + uart_rpbus_3: uart-rpbus-3 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + //flags=; + flags=; + status = "okay"; + }; + + uart_rpbus_4: uart-rpbus-4 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + //flags=; + flags=; + status = "okay"; + }; + + uart_rpbus_5: uart-rpbus-5 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + //flags=; + flags=; + status = "okay"; + }; + + uart_rpbus_6: uart-rpbus-6 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + //flags=; + flags=; + status = "okay"; + }; + + uart_rpbus_7: uart-rpbus-7 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + //flags=; + flags=; + status = "okay"; + }; + + uart_rpbus_8: uart-rpbus-8 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + //flags=; + flags=; + status = "okay"; + }; + + uart_rpbus_9: uart-rpbus-9 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + //flags=; + flags=; + status = "okay"; + }; + + uart_rpbus_10: uart-rpbus-10 { + compatible = "fsl,uart-rpbus"; + status = "okay"; /* mcore directly print data that receive from acore */ + }; + + imx8mm-cm4 { + compatible = "fsl,imx8mm-cm4"; + rsc-da = <0xb8000000>; + clocks = <&clk IMX8MM_CLK_M4_DIV>; + mbox-names = "tx", "rx", "rxdb"; + mboxes = <&mu 0 1 + &mu 1 1 + &mu 3 1>; + memory-region = <&vdevbuffer>, <&vdev0vring0>, <&vdev0vring1>, <&rsc_table>; + syscon = <&src>; + fsl,startup-delay-ms = <500>; + }; +}; + +&clk { + init-on-array = ; +}; + +/* + * ATTENTION: M4 may use IPs like below + * ECSPI0/ECSPI2, GPIO1/GPIO5, GPT1, I2C3, I2S3, WDOG1, UART3, UART4, PWM3, SDMA1 + */ + +&i2c3 { + status = "disabled"; +}; + +/* the uart3 is used by m4 to support srtm uart service */ +&uart3 { + status = "disabled"; +}; + +&uart4 { + status = "disabled"; +}; + +&sdma3 { + status = "disabled"; +}; + +&sai3 { + status = "disabled"; +}; + +&sai1 { + status = "disabled"; +}; + +&sai2 { + status = "disabled"; +}; + +&sai6 { + status = "disabled"; +}; + +&flexspi { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg-ca53.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg-ca53.dts new file mode 100644 index 000000000000..ef2732f6a4fc --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg-ca53.dts @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2023-2024 NXP + */ + +/dts-v1/; +#include "imx8mm-evk.dts" +#include "imx8m-rpmsg-ca53.dtsi" +#include "imx8m-rproc-ca53.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + rtos_ca53_reserved: rtos-ca53@93c00000 { + reg = <0 0x93c00000 0x0 0x24000000>; + no-map; + }; + }; +}; + +&clk { + init-on-array = ; +}; + +/* + * ATTENTION: The isolated CA53 core uses IPs below + * UART4 + */ + +&uart4 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg.dts index c59212d17112..9999e4b8d241 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg.dts +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-rpmsg.dts @@ -1,10 +1,10 @@ // SPDX-License-Identifier: (GPL-2.0 OR MIT) /* - * Copyright 2019 NXP + * Copyright 2019-2023 NXP */ /dts-v1/; - +#include #include "imx8mm-evk.dts" / { @@ -73,6 +73,91 @@ rpmsg_audio: rpmsg_audio { status = "okay"; }; + uart_rpbus_0: uart-rpbus-0 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + //flags=; + flags=; + status = "okay"; + }; + + uart_rpbus_1: uart-rpbus-1 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + //flags=; + flags=; + status = "okay"; + }; + + uart_rpbus_2: uart-rpbus-2 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + //flags=; + flags=; + status = "okay"; + }; + + uart_rpbus_3: uart-rpbus-3 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + //flags=; + flags=; + status = "okay"; + }; + + uart_rpbus_4: uart-rpbus-4 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + //flags=; + flags=; + status = "okay"; + }; + + uart_rpbus_5: uart-rpbus-5 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + //flags=; + flags=; + status = "okay"; + }; + + uart_rpbus_6: uart-rpbus-6 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + //flags=; + flags=; + status = "okay"; + }; + + uart_rpbus_7: uart-rpbus-7 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + //flags=; + flags=; + status = "okay"; + }; + + uart_rpbus_8: uart-rpbus-8 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + //flags=; + flags=; + status = "okay"; + }; + + uart_rpbus_9: uart-rpbus-9 { + compatible = "fsl,uart-rpbus"; + bus_id = <3>; /* use uart3 */ + //flags=; + flags=; + status = "okay"; + }; + + uart_rpbus_10: uart-rpbus-10 { + compatible = "fsl,uart-rpbus"; + status = "okay"; /* mcore directly print data that receive from acore */ + }; + imx8mm-cm4 { compatible = "fsl,imx8mm-cm4"; rsc-da = <0xb8000000>; @@ -101,13 +186,18 @@ IMX8MM_CLK_DISP_APB /* * ATTENTION: M4 may use IPs like below - * ECSPI0/ECSPI2, GPIO1/GPIO5, GPT1, I2C3, I2S3, WDOG1, UART4, PWM3, SDMA1 + * ECSPI0/ECSPI2, GPIO1/GPIO5, GPT1, I2C3, I2S3, WDOG1, UART3, UART4, PWM3, SDMA1 */ &i2c3 { status = "disabled"; }; +/* the uart3 is used by m4 to support srtm uart service */ +&uart3 { + status = "disabled"; +}; + &uart4 { status = "disabled"; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-net-ca53.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-net-ca53.dts new file mode 100644 index 000000000000..4e599a5ea4da --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-net-ca53.dts @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2023-2024 NXP + */ + +/dts-v1/; +#include "imx8mm-evk.dts" +#include "imx8m-generic-mbox.dtsi" +#include "imx8m-rproc-ca53.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + virtio_reserved: virtio@b8400000 { + no-map; + reg = <0 0xb8400000 0x0 0x00100000>; + }; + + /* 512MB */ + rtos_ca53_reserved: rtos-ca53@93c00000 { + no-map; + reg = <0 0x93c00000 0x0 0x20000000>; + }; + + virtio_buffer: virtio-buffer@b3c00000 { + compatible = "shared-dma-pool"; + reg = <0 0xb3c00000 0 0x4000000>; + }; + }; + + virtio_net@b8400000 { + compatible = "virtio,mmio"; + reg = <0x0 0xb8400000 0x0 0x1000>; + dma-coherent; + hypervisor_less; + memory-region = <&virtio_buffer>; + mbox-names = "mmiowr", "mmioirq"; + mboxes = <&gen_sw_mbox 0 1 1 /* TX channel with ACK */ + &gen_sw_mbox 1 1 0>; /* RX channel without ACK */ + }; +}; + +&{/busfreq} { + /* Disable busfreq to avoid Linux busfreq crash multicore virtio backend */ + status = "disabled"; +}; + +&uart4 { + status = "disabled"; +}; + +&clk { + init-on-array = ; +}; + +&iomuxc { + /* + * Used for the 2nd Linux. + * TODO: M4 may use these pins. + */ + pinctrl_uart4: uart4grp { + fsl,pins = < + MX8MM_IOMUXC_UART4_RXD_UART4_DCE_RX 0x140 + MX8MM_IOMUXC_UART4_TXD_UART4_DCE_TX 0x140 + >; + }; +}; + +&uart2 { + /* uart2 is used by the 2nd OS, so configure pin and clk */ + pinctrl-0 = <&pinctrl_uart2>, <&pinctrl_uart4>; + assigned-clocks = <&clk IMX8MM_CLK_UART4>; + assigned-clock-parents = <&clk IMX8MM_CLK_24M>; +}; + +&fec1 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-net-cm4.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-net-cm4.dts new file mode 100644 index 000000000000..f20b8fe94ef9 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-net-cm4.dts @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2023-2024 NXP + */ + +/dts-v1/; +#include "imx8mm-evk.dts" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + cm4_reserved: cm4@80000000 { + reg = <0 0x80000000 0x0 0x01000000>; + no-map; + }; + + virtio_buffer: virtio-buffer@b3c00000 { + compatible = "shared-dma-pool"; + reg = <0 0xb3c00000 0 0x4000000>; + no-map; + }; + + virtio_reserved: virtio@b8400000 { + no-map; + reg = <0 0xb8400000 0x0 0x00100000>; + }; + }; + + virtio_net@b8400000 { + compatible = "virtio,mmio"; + reg = <0x0 0xb8400000 0x0 0x1000>; + hypervisor_less; + memory-region = <&virtio_buffer>; + interrupts = ; //MU IRQ to CA53 + mbox-names = "mmiowr", "mmioirq"; + mboxes = <&mu 0 2 + &mu 1 2>; + }; + + imx8mm-cm4 { + compatible = "fsl,imx8mm-cm4"; + clocks = <&clk IMX8MM_CLK_M4_DIV>; + mbox-names = "tx", "rx", "rxdb"; + mboxes = <&mu 0 1 + &mu 1 1 + &mu 3 1>; + memory-region = <&cm4_reserved>; + syscon = <&src>; + fsl,startup-delay-ms = <500>; + status = "okay"; + }; +}; + +&{/busfreq} { + /* Disable busfreq to avoid Linux busfreq crash multicore virtio backend */ + status = "disabled"; +}; + +&uart4 { + status = "disabled"; +}; + +&clk { + init-on-array = ; +}; + +&fec1 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-perf-ca53.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-perf-ca53.dts new file mode 100644 index 000000000000..c287fc44f0a1 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-perf-ca53.dts @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2022-2024 NXP + */ + +/dts-v1/; +#include "imx8mm-evk.dts" +#include "imx8m-generic-mbox.dtsi" +#include "imx8m-rproc-ca53.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + virtio_reserved: virtio@b8400000 { + no-map; + reg = <0 0xb8400000 0x0 0x00100000>; + }; + + /* 512MB */ + rtos_ca53_reserved: rtos-ca53@93c00000 { + no-map; + reg = <0 0x93c00000 0x0 0x20000000>; + }; + + virtio_buffer: virtio-buffer@b3c00000 { + compatible = "shared-dma-pool"; + reg = <0 0xb3c00000 0 0x4000000>; + }; + }; + + virtio_trans@b8400000 { + compatible = "virtio,mmio"; + reg = <0x0 0xb8400000 0x0 0x1000>; + hypervisor_less; + dma-coherent; + memory-region = <&virtio_buffer>; + mbox-names = "mmiowr", "mmioirq"; + mboxes = <&gen_sw_mbox 0 1 1 /* TX channel with ACK */ + &gen_sw_mbox 1 1 0>; /* RX channel without ACK */ + }; +}; + +&{/busfreq} { + /* Disable busfreq to avoid Linux busfreq crash multicore virtio backend */ + status = "disabled"; +}; + +&uart4 { + status = "disabled"; +}; + +&clk { + init-on-array = ; +}; + +&iomuxc { + /* + * Used for the 2nd Linux. + * TODO: M4 may use these pins. + */ + pinctrl_uart4: uart4grp { + fsl,pins = < + MX8MM_IOMUXC_UART4_RXD_UART4_DCE_RX 0x140 + MX8MM_IOMUXC_UART4_TXD_UART4_DCE_TX 0x140 + >; + }; +}; + +&uart2 { + /* uart2 is used by the 2nd OS, so configure pin and clk */ + pinctrl-0 = <&pinctrl_uart2>, <&pinctrl_uart4>; + assigned-clocks = <&clk IMX8MM_CLK_UART4>; + assigned-clock-parents = <&clk IMX8MM_CLK_24M>; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-perf-cm4.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-perf-cm4.dts new file mode 100644 index 000000000000..7b4254c60aa9 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk-virtio-perf-cm4.dts @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2022-2024 NXP + */ + +/dts-v1/; +#include "imx8mm-evk.dts" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + cm4_reserved: cm4@80000000 { + reg = <0 0x80000000 0x0 0x01000000>; + no-map; + }; + + virtio_reserved: virtio@b8400000 { + no-map; + reg = <0 0xb8400000 0x0 0x00100000>; + }; + }; + + virtio_trans@b8400000 { + compatible = "virtio,mmio"; + reg = <0x0 0xb8400000 0x0 0x1000>; + hypervisor_less; + interrupts = ; //MU IRQ to CA53 + mbox-names = "mmiowr", "mmioirq"; + mboxes = <&mu 0 2 + &mu 1 2>; + }; + + imx8mm-cm4 { + compatible = "fsl,imx8mm-cm4"; + clocks = <&clk IMX8MM_CLK_M4_DIV>; + mbox-names = "tx", "rx", "rxdb"; + mboxes = <&mu 0 1 + &mu 1 1 + &mu 3 1>; + memory-region = <&cm4_reserved>; + syscon = <&src>; + fsl,startup-delay-ms = <500>; + status = "okay"; + }; +}; + +&{/busfreq} { + /* Disable busfreq to avoid Linux busfreq crash multicore virtio backend */ + status = "disabled"; +}; + +&uart4 { + status = "disabled"; +}; + +&clk { + init-on-array = ; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-virtio-ca53.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-virtio-ca53.dtsi new file mode 100644 index 000000000000..2202ca7b63f4 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mm-virtio-ca53.dtsi @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + virtio_reserved: virtio@b8400000 { + no-map; + reg = <0 0xb8400000 0x0 0x00100000>; + }; + + sw_mbox_reserved: gen-sw-mbox@b8500000 { + reg = <0 0xb8500000 0 0x1000>; + no-map; + }; + + virtio_buffer: virtio-buffer@b3c00000 { + compatible = "shared-dma-pool"; + reg = <0 0xb3c00000 0 0x4000000>; + }; + }; + + gen_sw_mbox: generic-software-mailbox@b8500000 { + compatible = "fsl,generic-software-mbox"; + reg = <0 0xb8500000 0 0x1000>; + #mbox-cells = <3>; + /* Use 2 unused SPI interrupts */ + interrupts = , ; + interrupt-names = "irq", "remote_irq"; + }; + + virtio_net@b8400000 { + compatible = "virtio,mmio"; + reg = <0x0 0xb8400000 0x0 0x1000>; + hypervisor_less; + memory-region = <&virtio_buffer>; + mbox-names = "mmiowr", "mmioirq"; + mboxes = <&gen_sw_mbox 0 1 1 /* TX channel with ACK */ + &gen_sw_mbox 1 1 0>; /* RX channel without ACK */ + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mm.dtsi b/arch/arm64/boot/dts/freescale/imx8mm.dtsi index abd144c2ea7d..47b640f837f3 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm.dtsi @@ -612,6 +612,17 @@ sdma3: dma-controller@302b0000 { fsl,sdma-ram-script-name = "imx/sdma/sdma-imx7d.bin"; }; + gpt1: gpt@302d0000 { + compatible = "fsl,imx8mq-gpt", "fsl,imx7d-gpt"; + reg = <0x302d0000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MM_CLK_GPT1_ROOT>, + <&clk IMX8MM_CLK_GPT1_ROOT>, + <&clk IMX8MM_CLK_GPT_3M>; + clock-names = "ipg", "per", "osc_per"; + status = "disabled"; + }; + iomuxc: pinctrl@30330000 { compatible = "fsl,imx8mm-iomuxc"; reg = <0x30330000 0x10000>; diff --git a/arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon-avb.dts b/arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon-avb.dts new file mode 100644 index 000000000000..faadcdf51e32 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon-avb.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2022-2023 NXP + */ + +#include "imx8mn-evk-harpoon.dts" +#include "imx8m-rpmsg-ca53.dtsi" + +&fec1 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon-industrial.dts b/arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon-industrial.dts new file mode 100644 index 000000000000..f3183f4e1a16 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon-industrial.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2022-2023 NXP + */ + +#include "imx8mn-evk-root.dts" +#include "imx8m-rpmsg-ca53.dtsi" + +&fec1 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon.dts b/arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon.dts new file mode 100644 index 000000000000..1581ea6e9ddc --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mn-evk-harpoon.dts @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2021-2023 NXP + */ + +#include "imx8mn-evk-root.dts" +#include "imx8m-rpmsg-ca53.dtsi" + +&i2c3 { + status = "disabled"; +}; + +&sai2 { + status = "disabled"; +}; + +&sai3 { + status = "disabled"; +}; + +&sai5 { + status = "disabled"; +}; + +&sai6 { + status = "disabled"; +}; + +&sai7 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-avb.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-avb.dts new file mode 100644 index 000000000000..fbe9b5d8554f --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-avb.dts @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2021, 2024 NXP + */ + +/dts-v1/; + +#include "imx8mp-evk.dts" + +/* AVB HW timer*/ +&gpt1 { + compatible = "fsl,avb-gpt"; + timer-channel = <1>; /* Use output compare channel 1*/ + rec-channel = <1 0 1>; // capture channel, eth port, ENET TC id + prescale = <1>; + domain = <0>; + + clocks = <&clk IMX8MP_CLK_GPT1_ROOT>, + <&clk IMX8MP_CLK_GPT1_ROOT>, + <&clk IMX8MP_AUDIO_PLL1>; + clock-names = "ipg", "per", "audio_pll"; + + /* Make the GPT clk root derive from the audio PLL*/ + assigned-clocks = <&clk IMX8MP_CLK_GPT1>; + assigned-clock-parents = <&clk IMX8MP_AUDIO_PLL1_OUT>; + assigned-clock-rates = <98304000>; + + gpt1_capin1_sel = <&gpr 0x4 2>; + + status = "okay"; +}; + +&fec { + fsl,rx-phy-delay-100-ns = <670>; + fsl,tx-phy-delay-100-ns = <670>; + fsl,rx-phy-delay-1000-ns = <0>; + fsl,tx-phy-delay-1000-ns = <0>; +}; + +ðphy1 { + eee-broken-100tx; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-baremetal.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-baremetal.dts new file mode 100644 index 000000000000..ce893a1d27c6 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-baremetal.dts @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2020-2023 NXP + */ + +/dts-v1/; + +#include "imx8mp-evk.dts" + +/ { + model = "NXP i.MX8MPlus EVK board - Baremetal"; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + rpmsg_reserved: rpmsg@55800000 { + no-map; + reg = <0 0x55800000 0 0x800000>; + }; + + /* baremetal: slave cores reserved memory */ + bm_reserved: baremetal@60000000 { + no-map; + reg = <0 0x60000000 0 0x10000000>; + }; + }; +}; + +&fec { + status = "disabled"; +}; + +&uart3 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa-enetc.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa-enetc.dts new file mode 100644 index 000000000000..29e5aeecaae9 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa-enetc.dts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2024 NXP + */ + +#include "imx8mp-evk-dsa.dts" + +&netcdsa_port3 { + label = "swp3"; + /delete-property/ ethernet; +}; + +&netcdsa_port4 { + label = "cpu"; + ethernet = <&eqos>; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa-fec-swp0.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa-fec-swp0.dts new file mode 100644 index 000000000000..11c0940c642d --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa-fec-swp0.dts @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023-2024 NXP + */ + +#include "imx8mp-evk.dts" + +/delete-node/&spidev1; + +&ecspi2 { + status = "okay"; + + netcdsa: ethernet-switch@0 { + reg = <0x0>; + #address-cells = <1>; + #size-cells = <0>; + compatible = "nxp,imxrt1180-netc"; + /* 500 kHz */ + spi-max-frequency = <500000>; + + netcdsa_ports: ports { + #address-cells = <1>; + #size-cells = <0>; + + netcdsa_port0: port@0 { + /* cpu port connected to fec */ + ethernet = <&fec>; + label = "cpu"; + reg = <0>; + phy-mode = "sgmii"; + + fixed-link { + speed = <100>; + full-duplex; + }; + }; + + netcdsa_port1: port@1 { + label = "swp1"; + reg = <1>; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + netcdsa_port2: port@2 { + label = "swp2"; + reg = <2>; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + netcdsa_port3: port@3 { + label = "swp3"; + reg = <3>; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + netcdsa_port4: port@4 { + label = "swp4"; + reg = <4>; + phy-mode = "sgmii"; + status = "disabled"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; +}; + +&pinctrl_ecspi2 { + fsl,pins = < + MX8MP_IOMUXC_ECSPI2_SCLK__ECSPI2_SCLK 0x84 + MX8MP_IOMUXC_ECSPI2_MOSI__ECSPI2_MOSI 0x84 + MX8MP_IOMUXC_ECSPI2_MISO__ECSPI2_MISO 0x84 + >; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa-fec-swp3.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa-fec-swp3.dts new file mode 100644 index 000000000000..e572cb25511a --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa-fec-swp3.dts @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023-2024 NXP + */ + +#include "imx8mp-evk.dts" + +/delete-node/&spidev1; + +&ecspi2 { + status = "okay"; + + netcdsa: ethernet-switch@0 { + reg = <0x0>; + #address-cells = <1>; + #size-cells = <0>; + compatible = "nxp,imxrt1180-netc"; + /* 500 kHz */ + spi-max-frequency = <500000>; + + netcdsa_ports: ports { + #address-cells = <1>; + #size-cells = <0>; + + netcdsa_port0: port@0 { + label = "swp0"; + reg = <0>; + phy-mode = "sgmii"; + + fixed-link { + speed = <100>; + full-duplex; + }; + }; + + netcdsa_port1: port@1 { + label = "swp1"; + reg = <1>; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + netcdsa_port2: port@2 { + label = "swp2"; + reg = <2>; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + netcdsa_port3: port@3 { + /* cpu port connected to fec */ + ethernet = <&fec>; + label = "cpu"; + reg = <3>; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + netcdsa_port4: port@4 { + label = "swp4"; + reg = <4>; + phy-mode = "sgmii"; + status = "disabled"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; +}; + +&pinctrl_ecspi2 { + fsl,pins = < + MX8MP_IOMUXC_ECSPI2_SCLK__ECSPI2_SCLK 0x84 + MX8MP_IOMUXC_ECSPI2_MOSI__ECSPI2_MOSI 0x84 + MX8MP_IOMUXC_ECSPI2_MISO__ECSPI2_MISO 0x84 + >; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa.dts new file mode 100644 index 000000000000..1d25b520c503 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-dsa.dts @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023-2024 NXP + */ + +#include "imx8mp-evk.dts" + +/delete-node/&spidev1; + +&ecspi2 { + status = "okay"; + + netcdsa: ethernet-switch@0 { + reg = <0x0>; + #address-cells = <1>; + #size-cells = <0>; + compatible = "nxp,imxrt1180-netc"; + /* 500 kHz */ + spi-max-frequency = <500000>; + + netcdsa_ports: ports { + #address-cells = <1>; + #size-cells = <0>; + + netcdsa_port0: port@0 { + label = "swp0"; + reg = <0>; + phy-mode = "sgmii"; + + fixed-link { + speed = <100>; + full-duplex; + }; + }; + + netcdsa_port1: port@1 { + label = "swp1"; + reg = <1>; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + netcdsa_port2: port@2 { + label = "swp2"; + reg = <2>; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + netcdsa_port3: port@3 { + /* cpu port connected to eqos */ + ethernet = <&eqos>; + label = "cpu"; + reg = <3>; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + netcdsa_port4: port@4 { + label = "swp4"; + reg = <4>; + phy-mode = "sgmii"; + status = "disabled"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; +}; + +&pinctrl_ecspi2 { + fsl,pins = < + MX8MP_IOMUXC_ECSPI2_SCLK__ECSPI2_SCLK 0x84 + MX8MP_IOMUXC_ECSPI2_MOSI__ECSPI2_MOSI 0x84 + MX8MP_IOMUXC_ECSPI2_MISO__ECSPI2_MISO 0x84 + >; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-ecat-userspace.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-ecat-userspace.dts new file mode 100644 index 000000000000..c60a00d8f084 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-ecat-userspace.dts @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include "imx8mp-evk.dts" + +/{ + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + /* Limit memory in 4G Bytes for user space EtherCAT */ + ecat_reserved: ecat@142000000 { + no-map; + reg = <0x1 0x42000000 0x0 0x80000000>; + }; + }; +}; + +ðphy1 { + /delete-property/ reset-gpios; + /delete-property/ reset-assert-us; + /delete-property/ reset-deassert-us; +}; + +&fec { + compatible = "fsl,imx8mm-fec-uio"; +}; + +&eqos { + compatible = "nxp,imx8mp-dwmac-eqos","snps,dwmac-5.10a"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-ecat.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-ecat.dts new file mode 100644 index 000000000000..60d6028a240a --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-ecat.dts @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2023 NXP + */ + +#include "imx8mp-evk.dts" + +&fec { + compatible = "fsl,imx8mp-fec-ecat", "fsl,imx8mq-fec-ecat", "fsl,imx6sx-fec-ecat"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-avb.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-avb.dts new file mode 100644 index 000000000000..4604d7e98644 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-avb.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2022-2023 NXP + */ + +#include "imx8mp-evk-harpoon.dts" +#include "imx8mp-rpmsg-ca53.dtsi" + +&eqos { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-industrial.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-industrial.dts new file mode 100644 index 000000000000..1bc31b6d52f7 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-industrial.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2022-2023 NXP + */ + +#include "imx8mp-evk-root.dts" +#include "imx8mp-rpmsg-ca53.dtsi" + +&eqos { + status = "disabled"; +}; + +&flexcan1 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-virtio-net.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-virtio-net.dts new file mode 100644 index 000000000000..a3b3a25477c0 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon-virtio-net.dts @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +/dts-v1/; +#include "imx8mp-evk-root.dts" +#include "imx8mp-generic-mbox.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + virtio_reserved: virtio@fc700000 { + no-map; + reg = <0 0xfc700000 0x0 0x00100000>; + }; + + virtio_buffer: virtio-buffer@f8700000 { + compatible = "shared-dma-pool"; + reg = <0 0xf8700000 0 0x4000000>; + }; + }; + + virtio_net@fc700000 { + compatible = "virtio,mmio"; + reg = <0x0 0xfc700000 0x0 0x1000>; + hypervisor_less; + memory-region = <&virtio_buffer>; + mbox-names = "mmiowr", "mmioirq"; + mboxes = <&gen_sw_mbox 0 1 1 /* TX channel with ACK */ + &gen_sw_mbox 1 1 0>; /* RX channel without ACK */ + }; +}; + +&fec { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon.dts new file mode 100644 index 000000000000..828d15424e8a --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-harpoon.dts @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2021-2023 NXP + */ + +#include "imx8mp-evk-root.dts" +#include "imx8mp-rpmsg-ca53.dtsi" + +&i2c3 { + status = "disabled"; +}; + +&sai1 { + status = "disabled"; +}; + +&sai2 { + status = "disabled"; +}; + +&sai3 { + status = "disabled"; +}; + +&sai5 { + status = "disabled"; +}; + +&sai6 { + status = "disabled"; +}; + +&sai7 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-igh.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-igh.dts new file mode 100644 index 000000000000..7f21b27837b5 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-igh.dts @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2024 NXP + */ + +#include "imx8mp-evk.dts" + +&fec { + compatible = "fsl,imx8mp-fec-igh-native", "fsl,imx8mq-fec-igh-native", "fsl,imx6sx-fec-igh-native"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-lwip.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-lwip.dts new file mode 100644 index 000000000000..06cf65278b98 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-lwip.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2023 NXP + */ + +/dts-v1/; + +#include "imx8mp-evk-multicore-rtos.dts" + +&fec { + status = "disabled"; +}; + +&eqos { + /delete-property/ clocks; + /delete-property/ clock-names; + clocks = <&clk IMX8MP_CLK_QOS_ENET_ROOT>, + <&clk IMX8MP_CLK_ENET_QOS_TIMER>, + <&clk IMX8MP_CLK_ENET_QOS>; + clock-names = "pclk", "ptp_ref", "tx"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-rpmsg.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-rpmsg.dts new file mode 100644 index 000000000000..76e3ea45786d --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-rpmsg.dts @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2023, 2025 NXP + */ + +/dts-v1/; + +#include "imx8mp-evk-multicore-rtos.dts" +#include "imx8mp-generic-mbox.dtsi" +#include "imx8mp-generic-mbox-1.dtsi" + +/ { + aliases { + i2c0 = &i2c1; + i2c1 = &i2c2; + i2c2 = &i2c_rpbus_3; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + vdev0vring0: vdev0vring0@55000000 { + reg = <0 0x55000000 0 0x8000>; + no-map; + }; + + vdev0vring1: vdev0vring1@55008000 { + reg = <0 0x55008000 0 0x8000>; + no-map; + }; + + vdevbuffer: vdevbuffer@55400000 { + compatible = "shared-dma-pool"; + reg = <0 0x55400000 0 0x100000>; + no-map; + }; + + rsc_table: rsc-table@550ff000 { + reg = <0 0x550ff000 0 0x1000>; + no-map; + }; + + vdev0vring0_ca53_2: vdev0vring0@fe100000 { + reg = <0 0xfe100000 0 0x8000>; + no-map; + }; + + vdev0vring1_ca53_2: vdev0vring1@fe108000 { + reg = <0 0xfe108000 0 0x8000>; + no-map; + }; + + rsc_table_ca53_2: rsc-table@fe1f0000 { + reg = <0 0xfe1f0000 0 0x1000>; + no-map; + }; + + vdev0buffer_ca53_2: vdev0buffer@fe200000 { + compatible = "shared-dma-pool"; + reg = <0 0xfe200000 0 0x100000>; + no-map; + }; + + vdev0vring0_ca53_3: vdev0vring0@fe110000 { + reg = <0 0xfe110000 0 0x8000>; + no-map; + }; + + vdev0vring1_ca53_3: vdev0vring1@fe118000 { + reg = <0 0xfe118000 0 0x8000>; + no-map; + }; + + rsc_table_ca53_3: rsc-table@fe1f1000 { + reg = <0 0xfe1f1000 0 0x1000>; + no-map; + }; + + vdev0buffer_ca53_3: vdev0buffer@fe300000 { + compatible = "shared-dma-pool"; + reg = <0 0xfe300000 0 0x100000>; + no-map; + }; + }; + + sound-wm8960 { + status = "disabled"; + }; + + imx8mp-cm7 { + clocks = <&clk IMX8MP_CLK_M7_DIV>, + <&audio_blk_ctrl IMX8MP_CLK_AUDIOMIX_AUDPLL_ROOT>; + clock-names = "core", "audio"; + }; +}; + +&ca53_2 { + memory-region = <&vdev0buffer_ca53_2>, <&vdev0vring0_ca53_2>, <&vdev0vring1_ca53_2>, <&rsc_table_ca53_2>, <&rtos_ca53_reserved>; + dma-coherent; + mbox-names = "tx", "rx", "rxdb"; + mboxes = <&gen_sw_mbox 0 0 1 /* Tx channel with ACK */ + &gen_sw_mbox 1 0 0 /* Rx channel without ACK */ + &gen_sw_mbox 2 0 1>; /* RXDB channel with ACK */ + fsl,startup-delay-ms = <50>; +}; + +&ca53_3 { + memory-region = <&vdev0buffer_ca53_3>, <&vdev0vring0_ca53_3>, <&vdev0vring1_ca53_3>, <&rsc_table_ca53_3>, <&rtos_ca53_reserved>; + dma-coherent; + mbox-names = "tx", "rx", "rxdb"; + mboxes = <&gen_sw_mbox_1 0 0 1 /* Tx channel with ACK */ + &gen_sw_mbox_1 1 0 0 /* Rx channel without ACK */ + &gen_sw_mbox_1 2 0 1>; /* RXDB channel with ACK */ + fsl,startup-delay-ms = <50>; +}; + +&sai3 { + status = "disabled"; +}; + +&clk { + init-on-array = ; +}; + +/delete-node/ &i2c3; + +&i2c_rpbus_3 { + compatible = "fsl,i2c-rpbus"; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + pca6416: gpio@20 { + compatible = "ti,tca6416"; + reg = <0x20>; + gpio-controller; + #gpio-cells = <2>; + }; + + ov5640_1: ov5640_mipi@3c { + compatible = "ovti,ov5640"; + reg = <0x3c>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_csi0_pwn>, <&pinctrl_csi0_rst>; + clocks = <&clk IMX8MP_CLK_IPP_DO_CLKO2>; + clock-names = "xclk"; + assigned-clocks = <&clk IMX8MP_CLK_IPP_DO_CLKO2>; + assigned-clock-parents = <&clk IMX8MP_CLK_24M>; + assigned-clock-rates = <24000000>; + csi_id = <0>; + powerdown-gpios = <&gpio4 1 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio4 0 GPIO_ACTIVE_LOW>; + mclk = <24000000>; + mclk_source = <0>; + mipi_csi; + status = "disabled"; + + port { + ov5640_mipi_1_ep: endpoint { + remote-endpoint = <&mipi_csi1_ep>; + data-lanes = <1 2>; + clock-lanes = <0>; + }; + }; + }; + + codec: wm8960@1a { + compatible = "wlf,wm8960,lpa"; + reg = <0x1a>; + wlf,shared-lrclk; + SPKVDD1-supply = <®_audio_pwr>; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-rtos.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-rtos.dts new file mode 100644 index 000000000000..66ff72d1497c --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-multicore-rtos.dts @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2023-2024 NXP + */ + +/dts-v1/; + +#include "imx8mp-evk.dts" +#include "imx8m-rproc-ca53.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* + * Reserve up to 48MB (16MB x 3) for three FreeRTOS instances running on + * three Cortex-A Cores when booting Linux on at least on Cortex-A Core. + */ + rtos_ca53_reserved: rtos-ca53@c0000000 { + no-map; + reg = <0 0xc0000000 0x0 0x3000000>; + }; + + /* Reserve 16MB for RTOS running on CM7 */ + m7_reserved: m7@80000000 { + no-map; + reg = <0 0x80000000 0 0x1000000>; + }; + + vdev0vring0: vdev0vring0@55000000 { + reg = <0 0x55000000 0 0x8000>; + no-map; + }; + + vdev0vring1: vdev0vring1@55008000 { + reg = <0 0x55008000 0 0x8000>; + no-map; + }; + + vdevbuffer: vdevbuffer@55400000 { + compatible = "shared-dma-pool"; + reg = <0 0x55400000 0 0x100000>; + no-map; + }; + + rsc_table: rsc-table@550ff000 { + reg = <0 0x550ff000 0 0x1000>; + no-map; + }; + }; + + imx8mp-cm7 { + compatible = "fsl,imx8mn-cm7"; + rsc-da = <0x55000000>; + clocks = <&clk IMX8MP_CLK_M7_DIV>; + clock-names = "core"; + mbox-names = "tx", "rx", "rxdb"; + mboxes = <&mu 0 1 + &mu 1 1 + &mu 3 1>; + memory-region = <&vdevbuffer>, <&vdev0vring0>, <&vdev0vring1>, <&rsc_table>; + status = "okay"; + fsl,startup-delay-ms = <500>; + }; +}; + +&{/busfreq} { + /* Disable busfreq to avoid Linux busfreq crash multicore virtio backend */ + status = "disabled"; +}; + +&uart4 { + status = "disabled"; +}; + +&clk { + init-on-array = ; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-rpmsg-ca53.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-rpmsg-ca53.dts new file mode 100644 index 000000000000..2ee7a41da41e --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-rpmsg-ca53.dts @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2024 NXP + */ + +/dts-v1/; + +#include "imx8mp-evk.dts" +#include "imx8mp-rpmsg-ca53.dtsi" +#include "imx8m-rproc-ca53.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + rtos_ca53_reserved: rtos-ca53@c0000000 { + reg = <0 0xc0000000 0x0 0x3000000>; + no-map; + }; + + }; +}; + +&{/busfreq} { + /* Disable busfreq to avoid Linux busfreq crash multicore virtio backend */ + status = "disabled"; +}; + +&uart3 { + status = "disabled"; +}; + +&uart4 { + status = "disabled"; +}; + +&clk { + init-on-array = ; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-net-ca53.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-net-ca53.dts new file mode 100644 index 000000000000..630df227725f --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-net-ca53.dts @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2023-2024 NXP + */ + +/dts-v1/; +#include "imx8mp-evk.dts" +#include "imx8mp-generic-mbox.dtsi" +#include "imx8m-rproc-ca53.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + virtio_reserved: virtio@fc700000 { + no-map; + reg = <0 0xfc700000 0x0 0x00100000>; + }; + + /* 512MB */ + rtos_ca53_reserved: rtos-ca53@c0000000 { + no-map; + reg = <0 0xc0000000 0x0 0x20000000>; + }; + + virtio_buffer: virtio-buffer@f8700000 { + compatible = "shared-dma-pool"; + reg = <0 0xf8700000 0 0x4000000>; + }; + }; + + virtio_net@fc700000 { + compatible = "virtio,mmio"; + reg = <0x0 0xfc700000 0x0 0x1000>; + dma-coherent; + hypervisor_less; + memory-region = <&virtio_buffer>; + mbox-names = "mmiowr", "mmioirq"; + mboxes = <&gen_sw_mbox 0 1 1 /* TX channel with ACK */ + &gen_sw_mbox 1 1 0>; /* RX channel without ACK */ + }; +}; + +&{/busfreq} { + /* Disable busfreq to avoid Linux busfreq crash multicore virtio backend */ + status = "disabled"; +}; + +&uart4 { + status = "disabled"; +}; + +&clk { + init-on-array = ; +}; + +&iomuxc { + pinctrl_uart4: uart4grp { + fsl,pins = < + MX8MP_IOMUXC_UART4_RXD__UART4_DCE_RX 0x140 + MX8MP_IOMUXC_UART4_TXD__UART4_DCE_TX 0x140 + >; + }; +}; + +&fec { + status = "disabled"; +}; + +&eqos { + /delete-property/ clocks; + /delete-property/ clock-names; + clocks = <&clk IMX8MP_CLK_QOS_ENET_ROOT>, + <&clk IMX8MP_CLK_ENET_QOS_TIMER>, + <&clk IMX8MP_CLK_ENET_QOS>; + clock-names = "pclk", "ptp_ref", "tx"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-net-cm7.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-net-cm7.dts new file mode 100644 index 000000000000..6fbb9549d143 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-net-cm7.dts @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2023-2024 NXP + */ + +/dts-v1/; +#include "imx8mp-evk.dts" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + cm7_reserved: cm7@80000000 { + reg = <0 0x80000000 0x0 0x01000000>; + no-map; + }; + + virtio_buffer: virtio-buffer@b3c00000 { + compatible = "shared-dma-pool"; + reg = <0 0xb3c00000 0 0x4000000>; + no-map; + }; + + virtio_reserved: virtio@b8400000 { + no-map; + reg = <0 0xb8400000 0x0 0x00100000>; + }; + }; + + virtio_net@b8400000 { + compatible = "virtio,mmio"; + reg = <0x0 0xb8400000 0x0 0x1000>; + hypervisor_less; + memory-region = <&virtio_buffer>; + mbox-names = "mmiowr", "mmioirq"; + mboxes = <&mu 0 2 + &mu 1 2>; + }; + + imx8mp-cm7 { + compatible = "fsl,imx8mn-cm7"; + clocks = <&clk IMX8MP_CLK_M7_DIV>; + clock-names = "core"; + mbox-names = "tx", "rx", "rxdb"; + mboxes = <&mu 0 1 + &mu 1 1 + &mu 3 1>; + memory-region = <&cm7_reserved>; + fsl,startup-delay-ms = <500>; + status = "okay"; + }; +}; + +&{/busfreq} { + /* Disable busfreq to avoid Linux busfreq crash multicore virtio backend */ + status = "disabled"; +}; + +&uart4 { + status = "disabled"; +}; + + +&fec { + status = "disabled"; +}; + +&eqos { + /delete-property/ clocks; + /delete-property/ clock-names; + clocks = <&clk IMX8MP_CLK_QOS_ENET_ROOT>, + <&clk IMX8MP_CLK_ENET_QOS_TIMER>, + <&clk IMX8MP_CLK_ENET_QOS>; + clock-names = "pclk", "ptp_ref", "tx"; +}; + +&i2c3 { + status = "disabled"; +}; + +&sai3 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-perf-ca53.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-perf-ca53.dts new file mode 100644 index 000000000000..ccf566ec5fdd --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-perf-ca53.dts @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2024 NXP + */ + +/dts-v1/; +#include "imx8mp-evk.dts" +#include "imx8mp-generic-mbox.dtsi" +#include "imx8m-rproc-ca53.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + virtio_reserved: virtio@fc700000 { + no-map; + reg = <0 0xfc700000 0x0 0x00100000>; + }; + + /* 512MB */ + rtos_ca53_reserved: rtos-ca53@c0000000 { + no-map; + reg = <0 0xc0000000 0x0 0x20000000>; + }; + + virtio_buffer: virtio-buffer@f8700000 { + compatible = "shared-dma-pool"; + reg = <0 0xf8700000 0 0x4000000>; + }; + }; + + virtio_trans@fc700000 { + compatible = "virtio,mmio"; + reg = <0x0 0xfc700000 0x0 0x1000>; + dma-coherent; + hypervisor_less; + memory-region = <&virtio_buffer>; + mbox-names = "mmiowr", "mmioirq"; + mboxes = <&gen_sw_mbox 0 1 1 /* TX channel with ACK */ + &gen_sw_mbox 1 1 0>; /* RX channel without ACK */ + }; +}; + +&{/busfreq} { + /* Disable busfreq to avoid Linux busfreq crash multicore virtio backend */ + status = "disabled"; +}; + +&uart4 { + status = "disabled"; +}; + +&clk { + init-on-array = ; +}; + +&iomuxc { + pinctrl_uart4: uart4grp { + fsl,pins = < + MX8MP_IOMUXC_UART4_RXD__UART4_DCE_RX 0x140 + MX8MP_IOMUXC_UART4_TXD__UART4_DCE_TX 0x140 + >; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-perf-cm7.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-perf-cm7.dts new file mode 100644 index 000000000000..5897c49fa04a --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-evk-virtio-perf-cm7.dts @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2024 NXP + */ + +/dts-v1/; +#include "imx8mp-evk.dts" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + linux,cma { + size = <0 0x20000000>; + alloc-ranges = <0 0x40000000 0 0x80000000>; + }; + + cm7_reserved: cm7@80000000 { + reg = <0 0x80000000 0x0 0x01000000>; + no-map; + }; + + virtio_reserved: virtio@b8400000 { + no-map; + reg = <0 0xb8400000 0x0 0x00100000>; + }; + }; + + virtio_trans@b8400000 { + compatible = "virtio,mmio"; + reg = <0x0 0xb8400000 0x0 0x1000>; + hypervisor_less; + mbox-names = "mmiowr", "mmioirq"; + mboxes = <&mu 0 2 + &mu 1 2>; + }; + + imx8mp-cm7 { + compatible = "fsl,imx8mn-cm7"; + clocks = <&clk IMX8MP_CLK_M7_DIV>; + clock-names = "core"; + mbox-names = "tx", "rx", "rxdb"; + mboxes = <&mu 0 1 + &mu 1 1 + &mu 3 1>; + memory-region = <&cm7_reserved>; + status = "okay"; + fsl,startup-delay-ms = <500>; + }; +}; + +&{/busfreq} { + /* Disable busfreq to avoid Linux busfreq crash multicore virtio backend */ + status = "disabled"; +}; + +&uart4 { + status = "disabled"; +}; + +&i2c3 { + status = "disabled"; +}; + +&sai3 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-generic-mbox-1.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-generic-mbox-1.dtsi new file mode 100644 index 000000000000..5f8ba823c0bd --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-generic-mbox-1.dtsi @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + gen_sw_mbox_1_reserved: gen-sw-mbox-1@fe001000 { + reg = <0 0xfe001000 0 0x1000>; + no-map; + }; + + }; + + gen_sw_mbox_1: generic-software-mailbox-1@fe001000 { + compatible = "fsl,generic-software-mbox"; + reg = <0 0xfe001000 0 0x1000>; + #mbox-cells = <3>; + /* Use 2 unused SPI interrupts */ + interrupts = , ; + interrupt-names = "irq", "remote_irq"; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-generic-mbox.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-generic-mbox.dtsi new file mode 100644 index 000000000000..8709e076bcc0 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-generic-mbox.dtsi @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + gen_sw_mbox_reserved: gen-sw-mbox@fe000000 { + reg = <0 0xfe000000 0 0x1000>; + no-map; + }; + + }; + + gen_sw_mbox: generic-software-mailbox@fe000000 { + compatible = "fsl,generic-software-mbox"; + reg = <0 0xfe000000 0 0x1000>; + #mbox-cells = <3>; + /* Use 2 unused SPI interrupts */ + interrupts = , ; + interrupt-names = "irq", "remote_irq"; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-rpmsg-ca53-1.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-rpmsg-ca53-1.dtsi new file mode 100644 index 000000000000..0e1f484209ff --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-rpmsg-ca53-1.dtsi @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +#include "imx8mp-generic-mbox-1.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + rpmsg_ca53_1_reserved: rpmsg-ca53-1@fe110000 { + reg = <0 0xfe110000 0 0x10000>; + no-map; + }; + + vdevbuffer_ca53_1: vdevbuffer-ca53-1@fe300000 { + compatible = "shared-dma-pool"; + reg = <0 0xfe300000 0 0x100000>; + no-map; + }; + }; + + rpmsg-ca53-1 { + compatible = "fsl,imx8mm-rpmsg"; + reg = <0x0 0xfe110000 0x0 0x10000>; /* 64K for one rpmsg instance */ + dma-coherent; + memory-region = <&vdevbuffer_ca53_1>; + mbox-names = "tx", "rx", "rxdb"; + mboxes = <&gen_sw_mbox_1 0 0 1 /* Tx channel with ACK */ + &gen_sw_mbox_1 1 0 0 /* Rx channel without ACK */ + &gen_sw_mbox_1 2 0 1>; /* RXDB channel with ACK */ + vdev-nums = <1>; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-rpmsg-ca53.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-rpmsg-ca53.dtsi new file mode 100644 index 000000000000..32e127a6d203 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mp-rpmsg-ca53.dtsi @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2022-2023 NXP + */ + +#include "imx8mp-generic-mbox.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + rpmsg_ca53_reserved: rpmsg-ca53@fe100000 { + reg = <0 0xfe100000 0 0x10000>; + no-map; + }; + + vdevbuffer_ca53: vdevbuffer-ca53@fe200000 { + compatible = "shared-dma-pool"; + reg = <0 0xfe200000 0 0x100000>; + no-map; + }; + }; + + rpmsg-ca53 { + compatible = "fsl,imx8mm-rpmsg"; + reg = <0x0 0xfe100000 0x0 0x10000>; /* 64K for one rpmsg instance */ + dma-coherent; + memory-region = <&vdevbuffer_ca53>; + mbox-names = "tx", "rx", "rxdb"; + mboxes = <&gen_sw_mbox 0 0 1 /* Tx channel with ACK */ + &gen_sw_mbox 1 0 0 /* Rx channel without ACK */ + &gen_sw_mbox 2 0 1>; /* RXDB channel with ACK */ + vdev-nums = <1>; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-avb.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-avb.dts new file mode 100644 index 000000000000..5f2f0ee80e09 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-avb.dts @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023-2024 NXP + */ + +/dts-v1/; + +#include "imx93-11x11-evk.dts" +#include "imx93-evk-avb.dtsi" diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-baremetal.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-baremetal.dts new file mode 100644 index 000000000000..84f9700437a1 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-baremetal.dts @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +/dts-v1/; + +#include "imx93-11x11-evk.dts" + +/ { + model = "NXP i.MX93 11X11 EVK board - Baremetal"; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + bm_reserved: baremetal@b0000000 { + no-map; + reg = <0 0xb0000000 0 0x10000000>; + }; + }; +}; + +&fec { + status = "disabled"; +}; + +&lpuart2 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dpdk.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dpdk.dts new file mode 100644 index 000000000000..5ac7f92b3a6b --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dpdk.dts @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include "imx93-11x11-evk.dts" + +ðphy2 { + /delete-property/ reset-gpios; + /delete-property/ reset-assert-us; + /delete-property/ reset-deassert-us; +}; + +&fec { + compatible = "fsl,imx8mm-fec-uio"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-enetc.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-enetc.dts new file mode 100644 index 000000000000..f45760d6cd3e --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-enetc.dts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2024 NXP + */ + +#include "imx93-11x11-evk-dsa.dts" + +&netcdsa_port3 { + label = "swp3"; + /delete-property/ ethernet; +}; + +&netcdsa_port4 { + label = "cpu"; + ethernet = <&eqos>; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-fec-swp0.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-fec-swp0.dts new file mode 100644 index 000000000000..64e72b7aa9de --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-fec-swp0.dts @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023-2024 NXP + */ + +#include "imx93-11x11-evk.dts" + +&flexcan2 { + status = "disabled"; +}; + +&lpuart7 { + status = "disabled"; +}; + +&lpspi3 { + fsl,spi-num-chipselects = <1>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&pinctrl_lpspi3>; + pinctrl-1 = <&pinctrl_lpspi3>; + cs-gpios = <&gpio2 8 GPIO_ACTIVE_LOW>; + pinctrl-assert-gpios = <&adp5585gpio 4 GPIO_ACTIVE_HIGH>; + status = "okay"; + + netcdsa: ethernet-switch@0 { + reg = <0x0>; + #address-cells = <1>; + #size-cells = <0>; + compatible = "nxp,imxrt1180-netc"; + /* 1 MHz */ + spi-max-frequency = <1000000>; + /* Sample data on trailing clock edge */ + + netcdsa_ports: ports { + #address-cells = <1>; + #size-cells = <0>; + + netcdsa_port0: port@0 { + /* cpu port connected to fec */ + ethernet = <&fec>; + label = "cpu"; + reg = <0>; + phy-mode = "sgmii"; + + fixed-link { + speed = <100>; + full-duplex; + }; + }; + + netcdsa_port1: port@1 { + label = "swp1"; + reg = <1>; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + netcdsa_port2: port@2 { + label = "swp2"; + reg = <2>; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + netcdsa_port3: port@3 { + label = "swp3"; + reg = <3>; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + netcdsa_port4: port@4 { + label = "swp4"; + reg = <4>; + phy-mode = "sgmii"; + status = "disabled"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; +}; + +&iomuxc { + pinctrl_lpspi3: lpspi3grp { + fsl,pins = < + MX93_PAD_GPIO_IO08__GPIO2_IO08 0x3fe + MX93_PAD_GPIO_IO09__LPSPI3_SIN 0x3fe + MX93_PAD_GPIO_IO10__LPSPI3_SOUT 0x3fe + MX93_PAD_GPIO_IO11__LPSPI3_SCK 0x3fe + >; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-fec-swp3.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-fec-swp3.dts new file mode 100644 index 000000000000..6ae448c99103 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa-fec-swp3.dts @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023-2024 NXP + */ + +#include "imx93-11x11-evk.dts" + +&flexcan2 { + status = "disabled"; +}; + +&lpuart7 { + status = "disabled"; +}; + +&lpspi3 { + fsl,spi-num-chipselects = <1>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&pinctrl_lpspi3>; + pinctrl-1 = <&pinctrl_lpspi3>; + cs-gpios = <&gpio2 8 GPIO_ACTIVE_LOW>; + pinctrl-assert-gpios = <&adp5585gpio 4 GPIO_ACTIVE_HIGH>; + status = "okay"; + + netcdsa: ethernet-switch@0 { + reg = <0x0>; + #address-cells = <1>; + #size-cells = <0>; + compatible = "nxp,imxrt1180-netc"; + /* 1 MHz */ + spi-max-frequency = <1000000>; + /* Sample data on trailing clock edge */ + + netcdsa_ports: ports { + #address-cells = <1>; + #size-cells = <0>; + + netcdsa_port0: port@0 { + label = "swp0"; + reg = <0>; + phy-mode = "sgmii"; + + fixed-link { + speed = <100>; + full-duplex; + }; + }; + + netcdsa_port1: port@1 { + label = "swp1"; + reg = <1>; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + netcdsa_port2: port@2 { + label = "swp2"; + reg = <2>; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + netcdsa_port3: port@3 { + /* cpu port connected to fec */ + ethernet = <&fec>; + label = "cpu"; + reg = <3>; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + netcdsa_port4: port@4 { + label = "swp4"; + reg = <4>; + phy-mode = "sgmii"; + status = "disabled"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; +}; + +&iomuxc { + pinctrl_lpspi3: lpspi3grp { + fsl,pins = < + MX93_PAD_GPIO_IO08__GPIO2_IO08 0x3fe + MX93_PAD_GPIO_IO09__LPSPI3_SIN 0x3fe + MX93_PAD_GPIO_IO10__LPSPI3_SOUT 0x3fe + MX93_PAD_GPIO_IO11__LPSPI3_SCK 0x3fe + >; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa.dts new file mode 100644 index 000000000000..940a7a4ac441 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dsa.dts @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023-2024 NXP + */ + +#include "imx93-11x11-evk.dts" + +&flexcan2 { + status = "disabled"; +}; + +&lpuart7 { + status = "disabled"; +}; + +&lpspi3 { + fsl,spi-num-chipselects = <1>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&pinctrl_lpspi3>; + pinctrl-1 = <&pinctrl_lpspi3>; + cs-gpios = <&gpio2 8 GPIO_ACTIVE_LOW>; + pinctrl-assert-gpios = <&adp5585gpio 4 GPIO_ACTIVE_HIGH>; + status = "okay"; + + netcdsa: ethernet-switch@0 { + reg = <0x0>; + #address-cells = <1>; + #size-cells = <0>; + compatible = "nxp,imxrt1180-netc"; + /* 1 MHz */ + spi-max-frequency = <1000000>; + /* Sample data on trailing clock edge */ + + netcdsa_ports: ports { + #address-cells = <1>; + #size-cells = <0>; + + netcdsa_port0: port@0 { + label = "swp0"; + reg = <0>; + phy-mode = "sgmii"; + + fixed-link { + speed = <100>; + full-duplex; + }; + }; + + netcdsa_port1: port@1 { + label = "swp1"; + reg = <1>; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + netcdsa_port2: port@2 { + label = "swp2"; + reg = <2>; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + netcdsa_port3: port@3 { + /* cpu port connected to eqos */ + ethernet = <&eqos>; + label = "cpu"; + reg = <3>; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + netcdsa_port4: port@4 { + label = "swp4"; + reg = <4>; + phy-mode = "sgmii"; + status = "disabled"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; +}; + +&iomuxc { + pinctrl_lpspi3: lpspi3grp { + fsl,pins = < + MX93_PAD_GPIO_IO08__GPIO2_IO08 0x3fe + MX93_PAD_GPIO_IO09__LPSPI3_SIN 0x3fe + MX93_PAD_GPIO_IO10__LPSPI3_SOUT 0x3fe + MX93_PAD_GPIO_IO11__LPSPI3_SCK 0x3fe + >; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-ecat-userspace.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-ecat-userspace.dts new file mode 100644 index 000000000000..5ac7f92b3a6b --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-ecat-userspace.dts @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include "imx93-11x11-evk.dts" + +ðphy2 { + /delete-property/ reset-gpios; + /delete-property/ reset-assert-us; + /delete-property/ reset-deassert-us; +}; + +&fec { + compatible = "fsl,imx8mm-fec-uio"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-ecat.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-ecat.dts new file mode 100644 index 000000000000..3626356fb9b6 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-ecat.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2023 NXP + */ + +#include "imx93-11x11-evk.dts" + + +&fec { + compatible = "fsl,imx93-fec-ecat", "fsl,imx8mp-fec-ecat", "fsl,imx8mq-fec-ecat"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-avb.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-avb.dts new file mode 100644 index 000000000000..95a4d0578fcb --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-avb.dts @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +#include "imx93-11x11-evk-harpoon.dts" + +&eqos { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-industrial.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-industrial.dts new file mode 100644 index 000000000000..d0a18a1dd6dd --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-industrial.dts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +#include "imx93-11x11-evk-root.dts" +#include "imx93-rpmsg-ca55.dtsi" + +&eqos { + status = "disabled"; +}; + +&flexcan2 { + status = "disabled"; +}; + +®_can2_stby { + status = "disabled"; +}; \ No newline at end of file diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-virtio-net.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-virtio-net.dts new file mode 100644 index 000000000000..75ff20e30ab6 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon-virtio-net.dts @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +/dts-v1/; +#include "imx93-11x11-evk-root.dts" +#include "imx93-generic-mbox.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + virtio_reserved: virtio@fc700000 { + no-map; + reg = <0 0xfc700000 0x0 0x00100000>; + }; + + /* 512MB */ + freertos_reserved: inmate@d0000000 { + no-map; + reg = <0 0xd0000000 0x0 0x20000000>; + }; + + virtio_buffer: virtio-buffer@f8700000 { + compatible = "shared-dma-pool"; + reg = <0 0xf8700000 0 0x4000000>; + }; + }; + + virtio_net@fc700000 { + compatible = "virtio,mmio"; + reg = <0x0 0xfc700000 0x0 0x1000>; + hypervisor_less; + memory-region = <&virtio_buffer>; + mbox-names = "mmiowr", "mmioirq"; + mboxes = <&gen_sw_mbox 0 1 1 /* TX channel with ACK */ + &gen_sw_mbox 1 1 0>; /* RX channel without ACK */ + }; +}; + +&fec { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon.dts new file mode 100644 index 000000000000..2f3ba10e7033 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-harpoon.dts @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +#include "imx93-11x11-evk-root.dts" +#include "imx93-rpmsg-ca55.dtsi" + +&dsi { + status = "disabled"; +}; + +&lpi2c1 { + status = "disabled"; +}; + +&lpi2c4 { + status = "disabled"; +}; + +&sai1 { + status = "disabled"; +}; + +&sai2 { + status = "disabled"; +}; + +&sai3 { + status = "disabled"; +}; + +&flexcan2 { + status = "disabled"; +}; \ No newline at end of file diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-igh.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-igh.dts new file mode 100644 index 000000000000..c2648d5b2662 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-igh.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2024 NXP + */ + +#include "imx93-11x11-evk.dts" + + +&fec { + compatible = "fsl,imx93-fec-igh-native", "fsl,imx8mp-fec-igh-native", "fsl,imx8mq-fec-igh-native"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rpmsg.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rpmsg.dts new file mode 100644 index 000000000000..0b5bdeb94367 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rpmsg.dts @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2025 NXP + */ + +/dts-v1/; +#include "imx93-11x11-evk-multicore-rtos.dts" +#include "imx93-generic-mbox.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + vdev0vring0_ca55: vdev0vring0@fe100000 { + reg = <0 0xfe100000 0 0x8000>; + no-map; + }; + + vdev0vring1_ca55: vdev0vring1@fe108000 { + reg = <0 0xfe108000 0 0x8000>; + no-map; + }; + + rsc_table_ca55: rsc-table@fe1f0000 { + reg = <0 0xfe1f0000 0 0x1000>; + no-map; + }; + + vdev0buffer_ca55: vdev0buffer@fe200000 { + compatible = "shared-dma-pool"; + reg = <0 0xfe200000 0 0x100000>; + no-map; + }; + }; +}; + +&ca55_1 { + memory-region = <&vdev0buffer_ca55>, <&vdev0vring0_ca55>, <&vdev0vring1_ca55>, <&rsc_table_ca55>, <&rtos_ca55_reserved>; + dma-coherent; + mbox-names = "tx", "rx", "rxdb"; + mboxes = <&gen_sw_mbox 0 0 1 /* Tx channel with ACK */ + &gen_sw_mbox 1 0 0 /* Rx channel without ACK */ + &gen_sw_mbox 2 0 1>; /* RXDB channel with ACK */ + fsl,startup-delay-ms = <50>; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rtos.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rtos.dts new file mode 100644 index 000000000000..13db26b61ad1 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-multicore-rtos.dts @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2023-2024 NXP + */ + +/dts-v1/; +#include "imx93-11x11-evk.dts" +#include "imx93-rproc-ca55.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* + * Reserve up to 16MB for one possible FreeRTOS instances running on + * one Cortex-A Cores when booting Linux on at least on Cortex-A Core. + */ + rtos_ca55_reserved: rtos-ca55@d0000000 { + no-map; + reg = <0 0xd0000000 0x0 0x1000000>; + }; + + /* Reserve 16MB for FreeRTOS on M33 */ + m33_reserved: m33@a5000000 { + no-map; + reg = <0 0xa5000000 0 0x1000000>; + }; + }; +}; + +&lpuart2 { + status = "disabled"; +}; + +&clk { + init-on-array = ; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-uart-sharing-cm33.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-uart-sharing-cm33.dts new file mode 100644 index 000000000000..e55552658d09 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-uart-sharing-cm33.dts @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +/dts-v1/; + +#include +#include "imx93-11x11-evk.dts" + +/ { + uart_rpbus_0: uart-rpbus-0 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_1: uart-rpbus-1 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_2: uart-rpbus-2 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_3: uart-rpbus-3 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_4: uart-rpbus-4 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_5: uart-rpbus-5 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_6: uart-rpbus-6 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_7: uart-rpbus-7 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_8: uart-rpbus-8 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_9: uart-rpbus-9 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_10: uart-rpbus-10 { + compatible = "fsl,uart-rpbus"; + status = "okay"; /* mcore directly print data that receive from acore */ + }; +}; + +/* + * ATTENTION: M33 may use IPs like below + * LPUART5 + */ + +/* the lpuart5 is used by m33 to support srtm uart service */ +&lpuart5 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-virtio-net-ca55.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-virtio-net-ca55.dts new file mode 100644 index 000000000000..0783e2e025ca --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-virtio-net-ca55.dts @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2023-2024 NXP + */ + +/dts-v1/; +#include "imx93-11x11-evk.dts" +#include "imx93-generic-mbox.dtsi" +#include "imx93-rproc-ca55.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + virtio_reserved: virtio@fc700000 { + no-map; + reg = <0 0xfc700000 0x0 0x00100000>; + }; + + /* 512MB */ + rtos_ca55_reserved: rtos-ca55@d0000000 { + no-map; + reg = <0 0xd0000000 0x0 0x20000000>; + }; + + virtio_buffer: virtio-buffer@f8700000 { + compatible = "shared-dma-pool"; + reg = <0 0xf8700000 0 0x4000000>; + }; + }; + + virtio_net@fc700000 { + compatible = "virtio,mmio"; + reg = <0x0 0xfc700000 0x0 0x1000>; + dma-coherent; + hypervisor_less; + memory-region = <&virtio_buffer>; + mbox-names = "mmiowr", "mmioirq"; + mboxes = <&gen_sw_mbox 0 1 1 /* TX channel with ACK */ + &gen_sw_mbox 1 1 0>; /* RX channel without ACK */ + }; +}; + +&lpuart2 { + status = "disabled"; +}; + +&clk { + init-on-array = ; +}; + +&fec { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-virtio-net-cm33.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-virtio-net-cm33.dts new file mode 100644 index 000000000000..52a650c3ec25 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-virtio-net-cm33.dts @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2023-2024 NXP + */ + +/dts-v1/; +#include "imx93-11x11-evk.dts" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + cm33_reserved: cm33@a0000000 { + reg = <0 0xa0000000 0x0 0x01000000>; + no-map; + }; + + virtio_buffer: virtio-buffer@a1000000 { + compatible = "shared-dma-pool"; + reg = <0 0xa1000000 0 0x3000000>; + no-map; + }; + + virtio_reserved: virtio@a8400000 { + reg = <0 0xa8400000 0x0 0x00100000>; + no-map; + }; + }; + + virtio_net@a8400000 { + compatible = "virtio,mmio"; + reg = <0x0 0xa8400000 0x0 0x1000>; + hypervisor_less; + memory-region = <&virtio_buffer>; + mbox-names = "mmiowr", "mmioirq"; + mboxes = <&mu1 0 2 + &mu1 1 2>; + }; +}; + +&clk { + init-on-array = ; +}; + +&lpuart2 { + status = "disabled"; +}; + +&fec { + status = "disabled"; +}; + +&cm33 { + memory-region = <&cm33_reserved>; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts index 4d1aa281f33e..1d022b134f38 100644 --- a/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts @@ -324,7 +324,7 @@ &xcvr { assigned-clock-rates = <12288000>, <200000000>; status = "okay"; }; - + &adc1 { vref-supply = <®_vref_1v8>; status = "okay"; @@ -381,6 +381,9 @@ &eqos { pinctrl-1 = <&pinctrl_eqos_sleep>; phy-mode = "rgmii-id"; phy-handle = <ðphy1>; + snps,force_thresh_dma_mode; + snps,mtl-tx-config = <&mtl_tx_setup>; + snps,mtl-rx-config = <&mtl_rx_setup>; status = "okay"; mdio { @@ -397,6 +400,61 @@ ethphy1: ethernet-phy@1 { realtek,clkout-disable; }; }; + + mtl_tx_setup: tx-queues-config { + snps,tx-queues-to-use = <5>; + snps,tx-sched-sp; + queue0 { + snps,dcb-algorithm; + snps,priority = <0x1>; + }; + queue1 { + snps,dcb-algorithm; + snps,priority = <0x2>; + }; + queue2 { + snps,dcb-algorithm; + snps,priority = <0x4>; + }; + queue3 { + snps,dcb-algorithm; + snps,priority = <0x8>; + }; + queue4 { + snps,dcb-algorithm; + snps,priority = <0xf0>; + }; + }; + + mtl_rx_setup: rx-queues-config { + snps,rx-queues-to-use = <5>; + snps,rx-sched-sp; + queue0 { + snps,dcb-algorithm; + snps,priority = <0x1>; + snps,map-to-dma-channel = <0>; + }; + queue1 { + snps,dcb-algorithm; + snps,priority = <0x2>; + snps,map-to-dma-channel = <1>; + }; + queue2 { + snps,dcb-algorithm; + snps,priority = <0x4>; + snps,map-to-dma-channel = <2>; + }; + queue3 { + snps,dcb-algorithm; + snps,priority = <0x8>; + snps,map-to-dma-channel = <3>; + }; + queue4 { + snps,dcb-algorithm; + snps,priority = <0xf0>; + snps,map-to-dma-channel = <4>; + }; + }; }; &fec { diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-aud-hat-avb.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-aud-hat-avb.dts new file mode 100644 index 000000000000..332495ce5320 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-aud-hat-avb.dts @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2024 NXP + */ + +/dts-v1/; + +#include "imx93-14x14-evk-aud-hat.dts" +#include "imx93-evk-avb.dtsi" diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-aud-hat.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-aud-hat.dts index 8c4ede5931db..470641838b78 100644 --- a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-aud-hat.dts +++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-aud-hat.dts @@ -209,7 +209,7 @@ cs42448: audio-codec@48 { VA-supply = <®_audio_pwr>; VD-supply = <®_audio_pwr>; VLS-supply = <®_audio_pwr>; - VLC-supply = <®_audio_pwr>; + VLC-supply = <®_exp_sel>; reset-gpio = <&gpio2 25 GPIO_ACTIVE_LOW>; status = "okay"; }; diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-imxai2eth-ath.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-imxai2eth-ath.dts new file mode 100644 index 000000000000..84a9a16a06e3 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-imxai2eth-ath.dts @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2024 NXP + */ + +#include "imx93-14x14-evk.dts" + +&lpi2c2 { + max7322: gpio@68 { + compatible = "maxim,max7322"; + reg = <0x68>; + gpio-controller; + #gpio-cells = <2>; + reset-gpios = <&pcal6524 15 GPIO_ACTIVE_LOW>; + }; +}; + +&eqos { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_eqos_rgmii>; + phy-mode = "rgmii-id"; + phy-handle = <&eqosphy>; + pinctrl-assert-gpios = <&max7322 0 GPIO_ACTIVE_HIGH>; + snps,force_thresh_dma_mode; + snps,mtl-tx-config = <&mtl_tx_setup>; + snps,mtl-rx-config = <&mtl_rx_setup>; + + snps,clk-csr = <5>; + status = "okay"; + + mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <2500000>; + + eqosphy: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + qca,disable-smarteee; + + vddio-supply = <&vddio1>; + + vddio1: vddio-regulator { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + }; + }; + + mtl_tx_setup: tx-queues-config { + snps,tx-queues-to-use = <5>; + snps,tx-sched-sp; + queue0 { + snps,dcb-algorithm; + snps,priority = <0x1>; + }; + queue1 { + snps,dcb-algorithm; + snps,priority = <0x2>; + }; + queue2 { + snps,dcb-algorithm; + snps,priority = <0x4>; + }; + queue3 { + snps,dcb-algorithm; + snps,priority = <0x8>; + }; + queue4 { + snps,dcb-algorithm; + snps,priority = <0xf0>; + }; + }; + + mtl_rx_setup: rx-queues-config { + snps,rx-queues-to-use = <5>; + snps,rx-sched-sp; + queue0 { + snps,dcb-algorithm; + snps,priority = <0x1>; + snps,map-to-dma-channel = <0>; + }; + queue1 { + snps,dcb-algorithm; + snps,priority = <0x2>; + snps,map-to-dma-channel = <1>; + }; + queue2 { + snps,dcb-algorithm; + snps,priority = <0x4>; + snps,map-to-dma-channel = <2>; + }; + queue3 { + snps,dcb-algorithm; + snps,priority = <0x8>; + snps,map-to-dma-channel = <3>; + }; + queue4 { + snps,dcb-algorithm; + snps,priority = <0xf0>; + snps,map-to-dma-channel = <4>; + }; + }; +}; + +&iomuxc { + pinctrl_eqos_rgmii: eqosrgmiigrp { + fsl,pins = < + MX93_PAD_ENET1_MDC__ENET_QOS_MDC 0x57e + MX93_PAD_ENET1_MDIO__ENET_QOS_MDIO 0x57e + MX93_PAD_ENET1_RD0__ENET_QOS_RGMII_RD0 0x57e + MX93_PAD_ENET1_RD1__ENET_QOS_RGMII_RD1 0x57e + MX93_PAD_ENET1_RD2__ENET_QOS_RGMII_RD2 0x57e + MX93_PAD_ENET1_RD3__ENET_QOS_RGMII_RD3 0x57e + MX93_PAD_ENET1_RXC__CCM_ENET_QOS_CLOCK_GENERATE_RX_CLK 0x57e + MX93_PAD_ENET1_RX_CTL__ENET_QOS_RGMII_RX_CTL 0x57e + MX93_PAD_ENET1_TD0__ENET_QOS_RGMII_TD0 0x57e + MX93_PAD_ENET1_TD1__ENET_QOS_RGMII_TD1 0x57e + MX93_PAD_ENET1_TD2__ENET_QOS_RGMII_TD2 0x57e + MX93_PAD_ENET1_TD3__ENET_QOS_RGMII_TD3 0x57e + MX93_PAD_ENET1_TXC__CCM_ENET_QOS_CLOCK_GENERATE_TX_CLK 0x57e + MX93_PAD_ENET1_TX_CTL__ENET_QOS_RGMII_TX_CTL 0x57e + >; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-mqs.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-mqs.dts index e3920484ae87..50e5b35b2c75 100644 --- a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-mqs.dts +++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-mqs.dts @@ -1,73 +1,7 @@ // SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* - * Copyright 2023 NXP + * Copyright 2023-2024 NXP */ #include "imx93-14x14-evk.dts" - -/ { - reg_mqs_switch: regulator-mqs-switch { - compatible = "regulator-fixed"; - regulator-name = "mqs-switch"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&pcal6524_2 2 GPIO_ACTIVE_HIGH>; - enable-active-high; - regulator-always-on; - }; - - reg_mqs_amp_en: regulator-mqs-amp-en { - compatible = "regulator-fixed"; - regulator-name = "mqs-amp-en"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&pcal6524_2 1 GPIO_ACTIVE_HIGH>; - enable-active-high; - regulator-always-on; - }; - - sound-mqs { - compatible = "fsl,imx6sx-sdb-mqs", - "fsl,imx-audio-mqs"; - model = "mqs-audio"; - audio-cpu = <&sai1>; - audio-codec = <&mqs1>; - }; - - sound-bt-sco { - status = "disabled"; - }; -}; - -&flexcan1 { - status = "disabled"; -}; - -&sai1 { - #sound-dai-cells = <0>; - clocks = <&clk IMX93_CLK_SAI1_IPG>, <&clk IMX93_CLK_DUMMY>, - <&clk IMX93_CLK_SAI1_GATE>, <&clk IMX93_CLK_DUMMY>, - <&clk IMX93_CLK_DUMMY>, <&clk IMX93_CLK_AUDIO_PLL>; - clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3", "pll8k"; - assigned-clocks = <&clk IMX93_CLK_SAI1>; - assigned-clock-parents = <&clk IMX93_CLK_AUDIO_PLL>; - assigned-clock-rates = <24576000>; - status = "okay"; -}; - -&mqs1 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_mqs1>; - clocks = <&clk IMX93_CLK_MQS1_GATE>; - clock-names = "mclk"; - status = "okay"; -}; - -&iomuxc { - pinctrl_mqs1: mqs1grp { - fsl,pins = < - MX93_PAD_PDM_CLK__MQS1_LEFT 0x31e - MX93_PAD_PDM_BIT_STREAM0__MQS1_RIGHT 0x31e - >; - }; -}; +#include "imx93-14x14-evk-mqs.dtsi" diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-mqs.dtsi b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-mqs.dtsi new file mode 100644 index 000000000000..4cb9a3280128 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-mqs.dtsi @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2024 NXP + */ + +/ { + reg_mqs_switch: regulator-mqs-switch { + compatible = "regulator-fixed"; + regulator-name = "mqs-switch"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&pcal6524_2 2 GPIO_ACTIVE_HIGH>; + enable-active-high; + regulator-always-on; + }; + + reg_mqs_amp_en: regulator-mqs-amp-en { + compatible = "regulator-fixed"; + regulator-name = "mqs-amp-en"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&pcal6524_2 1 GPIO_ACTIVE_HIGH>; + enable-active-high; + regulator-always-on; + }; + + sound-mqs { + compatible = "fsl,imx6sx-sdb-mqs", + "fsl,imx-audio-mqs"; + model = "mqs-audio"; + audio-cpu = <&sai1>; + audio-codec = <&mqs1>; + }; + + sound-bt-sco { + status = "disabled"; + }; +}; + +&flexcan1 { + status = "disabled"; +}; + +&sai1 { + #sound-dai-cells = <0>; + clocks = <&clk IMX93_CLK_SAI1_IPG>, <&clk IMX93_CLK_DUMMY>, + <&clk IMX93_CLK_SAI1_GATE>, <&clk IMX93_CLK_DUMMY>, + <&clk IMX93_CLK_DUMMY>, <&clk IMX93_CLK_AUDIO_PLL>; + clock-names = "bus", "mclk0", "mclk1", "mclk2", "mclk3", "pll8k"; + assigned-clocks = <&clk IMX93_CLK_SAI1>; + assigned-clock-parents = <&clk IMX93_CLK_AUDIO_PLL>; + assigned-clock-rates = <24576000>; + status = "okay"; +}; + +&mqs1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_mqs1>; + clocks = <&clk IMX93_CLK_MQS1_GATE>; + clock-names = "mclk"; + status = "okay"; +}; + +&iomuxc { + pinctrl_mqs1: mqs1grp { + fsl,pins = < + MX93_PAD_PDM_CLK__MQS1_LEFT 0x31e + MX93_PAD_PDM_BIT_STREAM0__MQS1_RIGHT 0x31e + >; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-multicore-rpmsg.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-multicore-rpmsg.dts new file mode 100644 index 000000000000..d6d07c82a766 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-multicore-rpmsg.dts @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2025 NXP + */ + +/dts-v1/; +#include "imx93-14x14-evk-multicore-rtos.dts" +#include "imx93-generic-mbox.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + vdev0vring0_ca55: vdev0vring0@fe100000 { + reg = <0 0xfe100000 0 0x8000>; + no-map; + }; + + vdev0vring1_ca55: vdev0vring1@fe108000 { + reg = <0 0xfe108000 0 0x8000>; + no-map; + }; + + rsc_table_ca55: rsc-table@fe1f0000 { + reg = <0 0xfe1f0000 0 0x1000>; + no-map; + }; + + vdev0buffer_ca55: vdev0buffer@fe200000 { + compatible = "shared-dma-pool"; + reg = <0 0xfe200000 0 0x100000>; + no-map; + }; + }; +}; + +&ca55_1 { + memory-region = <&vdev0buffer_ca55>, <&vdev0vring0_ca55>, <&vdev0vring1_ca55>, <&rsc_table_ca55>, <&rtos_ca55_reserved>; + dma-coherent; + mbox-names = "tx", "rx", "rxdb"; + mboxes = <&gen_sw_mbox 0 0 1 /* Tx channel with ACK */ + &gen_sw_mbox 1 0 0 /* Rx channel without ACK */ + &gen_sw_mbox 2 0 1>; /* RXDB channel with ACK */ + fsl,startup-delay-ms = <50>; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-multicore-rtos.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-multicore-rtos.dts new file mode 100644 index 000000000000..7bed244c703c --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-multicore-rtos.dts @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2024 NXP + */ + +/dts-v1/; +#include "imx93-14x14-evk.dts" +#include "imx93-rproc-ca55.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* + * Reserve up to 16MB for one possible FreeRTOS instances running on + * one Cortex-A Cores when booting Linux on at least on Cortex-A Core. + */ + rtos_ca55_reserved: rtos-ca55@d0000000 { + no-map; + reg = <0 0xd0000000 0x0 0x1000000>; + }; + + /* Reserve 16MB for FreeRTOS on M33 */ + m33_reserved: m33@a5000000 { + no-map; + reg = <0 0xa5000000 0 0x1000000>; + }; + }; +}; + +&lpuart2 { + status = "disabled"; +}; + +&clk { + init-on-array = ; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-sja1105-avb.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-sja1105-avb.dts new file mode 100644 index 000000000000..693684a10a23 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-sja1105-avb.dts @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2024 NXP + */ + +#include "imx93-14x14-evk-sja1105.dts" +#include "imx93-14x14-evk-mqs.dtsi" +#include "imx93-evk-avb.dtsi" diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-sja1105.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-sja1105.dts index 24bd885cd2fd..9da91fe35d74 100644 --- a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-sja1105.dts +++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-sja1105.dts @@ -29,7 +29,7 @@ &eqos { phy-mode = "rgmii-rxid"; phy-handle = <&fixed0>; pinctrl-assert-gpios = <&max7322 0 GPIO_ACTIVE_LOW>; - snps,clk-csr = <6>; + snps,clk-csr = <5>; status = "okay"; fixed0: fixed-link { @@ -90,6 +90,7 @@ sja1105@0 { spi-cpha; clocks = <&clock_sja1105>; pinctrl-assert-gpios = <&max7322 0 GPIO_ACTIVE_LOW>; + hostprio = <5>; ports { #address-cells = <1>; diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-tja1103.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-tja1103.dts index 7e049848c4c7..91bb3eec6fe2 100644 --- a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-tja1103.dts +++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-tja1103.dts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* - * Copyright 2023 NXP + * Copyright 2023-2024 NXP */ #include "imx93-14x14-evk.dts" @@ -10,13 +10,17 @@ &eqos { pinctrl-0 = <&pinctrl_eqos_rmii>; phy-mode = "rmii"; phy-handle = <&eqosphy>; + snps,force_thresh_dma_mode; + snps,mtl-tx-config = <&mtl_tx_setup>; + snps,mtl-rx-config = <&mtl_rx_setup>; assigned-clocks = <&clk IMX93_CLK_ENET_TIMER2>, <&clk IMX93_CLK_ENET>; assigned-clock-parents = <&clk IMX93_CLK_SYS_PLL_PFD1_DIV2>, <&clk IMX93_CLK_SYS_PLL_PFD1_DIV2>; assigned-clock-rates = <100000000>, <50000000>; - snps,clk-csr = <6>; + + snps,clk-csr = <5>; status = "okay"; mdio { @@ -32,6 +36,61 @@ eqosphy: ethernet-phy@2 { reg = <2>; }; }; + + mtl_tx_setup: tx-queues-config { + snps,tx-queues-to-use = <5>; + snps,tx-sched-sp; + queue0 { + snps,dcb-algorithm; + snps,priority = <0x1>; + }; + queue1 { + snps,dcb-algorithm; + snps,priority = <0x2>; + }; + queue2 { + snps,dcb-algorithm; + snps,priority = <0x4>; + }; + queue3 { + snps,dcb-algorithm; + snps,priority = <0x8>; + }; + queue4 { + snps,dcb-algorithm; + snps,priority = <0xf0>; + }; + }; + + mtl_rx_setup: rx-queues-config { + snps,rx-queues-to-use = <5>; + snps,rx-sched-sp; + queue0 { + snps,dcb-algorithm; + snps,priority = <0x1>; + snps,map-to-dma-channel = <0>; + }; + queue1 { + snps,dcb-algorithm; + snps,priority = <0x2>; + snps,map-to-dma-channel = <1>; + }; + queue2 { + snps,dcb-algorithm; + snps,priority = <0x4>; + snps,map-to-dma-channel = <2>; + }; + queue3 { + snps,dcb-algorithm; + snps,priority = <0x8>; + snps,map-to-dma-channel = <3>; + }; + queue4 { + snps,dcb-algorithm; + snps,priority = <0xf0>; + snps,map-to-dma-channel = <4>; + }; + }; }; &iomuxc { diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-uart-sharing-cm33.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-uart-sharing-cm33.dts new file mode 100644 index 000000000000..7ff128fcf2b9 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-uart-sharing-cm33.dts @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2024 NXP + */ + +/dts-v1/; + +#include +#include "imx93-14x14-evk.dts" + +/ { + uart_rpbus_0: uart-rpbus-0 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_1: uart-rpbus-1 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_2: uart-rpbus-2 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_3: uart-rpbus-3 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_4: uart-rpbus-4 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_5: uart-rpbus-5 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_6: uart-rpbus-6 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_7: uart-rpbus-7 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_8: uart-rpbus-8 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_9: uart-rpbus-9 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_10: uart-rpbus-10 { + compatible = "fsl,uart-rpbus"; + status = "okay"; /* mcore directly print data that receive from acore */ + }; +}; + +/* + * ATTENTION: M33 may use IPs like below + * LPUART5 + */ + +/* the lpuart5 is used by m33 to support srtm uart service */ +&lpuart5 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-virtio-net-ca55.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-virtio-net-ca55.dts new file mode 100644 index 000000000000..2dca2b07a972 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-virtio-net-ca55.dts @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2024 NXP + */ + +/dts-v1/; +#include "imx93-14x14-evk.dts" +#include "imx93-generic-mbox.dtsi" +#include "imx93-rproc-ca55.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + virtio_reserved: virtio@fc700000 { + no-map; + reg = <0 0xfc700000 0x0 0x00100000>; + }; + + /* 512MB */ + rtos_ca55_reserved: rtos-ca55@d0000000 { + no-map; + reg = <0 0xd0000000 0x0 0x20000000>; + }; + + virtio_buffer: virtio-buffer@f8700000 { + compatible = "shared-dma-pool"; + reg = <0 0xf8700000 0 0x4000000>; + }; + }; + + virtio_net@fc700000 { + compatible = "virtio,mmio"; + reg = <0x0 0xfc700000 0x0 0x1000>; + dma-coherent; + hypervisor_less; + memory-region = <&virtio_buffer>; + mbox-names = "mmiowr", "mmioirq"; + mboxes = <&gen_sw_mbox 0 1 1 /* TX channel with ACK */ + &gen_sw_mbox 1 1 0>; /* RX channel without ACK */ + }; +}; + +&lpuart2 { + status = "disabled"; +}; + +&clk { + init-on-array = ; +}; + +&fec { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-14x14-evk-virtio-net-cm33.dts b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-virtio-net-cm33.dts new file mode 100644 index 000000000000..8d699f7c8389 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-14x14-evk-virtio-net-cm33.dts @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2024 NXP + */ + +/dts-v1/; +#include "imx93-14x14-evk.dts" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + cm33_reserved: cm33@a0000000 { + reg = <0 0xa0000000 0x0 0x01000000>; + no-map; + }; + + virtio_buffer: virtio-buffer@a1000000 { + compatible = "shared-dma-pool"; + reg = <0 0xa1000000 0 0x3000000>; + no-map; + }; + + virtio_reserved: virtio@a8400000 { + reg = <0 0xa8400000 0x0 0x00100000>; + no-map; + }; + }; + + virtio_net@a8400000 { + compatible = "virtio,mmio"; + reg = <0x0 0xa8400000 0x0 0x1000>; + hypervisor_less; + memory-region = <&virtio_buffer>; + mbox-names = "mmiowr", "mmioirq"; + mboxes = <&mu1 0 2 + &mu1 1 2>; + }; +}; + +&clk { + init-on-array = ; +}; + +&lpuart2 { + status = "disabled"; +}; + +&fec { + status = "disabled"; +}; + +&cm33 { + memory-region = <&cm33_reserved>; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-baremetal.dts b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-baremetal.dts new file mode 100644 index 000000000000..8b4cbd366ae6 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-baremetal.dts @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +/dts-v1/; + +#include "imx93-9x9-qsb.dts" + +/ { + model = "NXP i.MX93 9X9 QSB board - Baremetal"; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + bm_reserved: baremetal@b0000000 { + no-map; + reg = <0 0xb0000000 0 0x10000000>; + }; + }; +}; + +&fec { + status = "disabled"; +}; + +&lpuart2 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-inmate.dts b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-inmate.dts new file mode 100644 index 000000000000..cd1f0e0cc401 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-inmate.dts @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +/dts-v1/; + +#include + +/ { + model = "NXP i.MX93 9x9 QSB"; + compatible = "fsl,imx93-9x9-qsb", "fsl,imx93"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + aliases { + mmc0 = &usdhc1; + serial1 = &lpuart2; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + A55_0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + reg = <0x0>; + enable-method = "psci"; + #cooling-cells = <2>; + }; + }; + + psci { + compatible = "arm,psci-1.0"; + method = "smc"; + }; + + gic: interrupt-controller@48000000 { + compatible = "arm,gic-v3"; + reg = <0 0x48000000 0 0x10000>, + <0 0x48040000 0 0xc0000>; + #interrupt-cells = <3>; + interrupt-controller; + interrupts = ; + interrupt-parent = <&gic>; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , + ; + clock-frequency = <24000000>; + }; + + clk_dummy: clock-dummy { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "clk_dummy"; + }; + + clk_400m: clock-400m { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <200000000>; + clock-output-names = "200m"; + }; + + osc_24m: clock-osc-24m { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24000000>; + clock-output-names = "osc_24m"; + }; + + pci@fd700000 { + compatible = "pci-host-ecam-generic"; + device_type = "pci"; + bus-range = <0 0>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &gic GIC_SPI 227 IRQ_TYPE_EDGE_RISING>, + <0 0 0 2 &gic GIC_SPI 228 IRQ_TYPE_EDGE_RISING>, + <0 0 0 3 &gic GIC_SPI 229 IRQ_TYPE_EDGE_RISING>, + <0 0 0 4 &gic GIC_SPI 230 IRQ_TYPE_EDGE_RISING>; + reg = <0x0 0xfd700000 0x0 0x100000>; + ranges = <0x02000000 0x00 0x10000000 0x0 0x10000000 0x00 0x10000>; + }; + + soc@0 { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x0 0x0 0x80000000>, + <0x28000000 0x0 0x28000000 0x10000000>; + + aips1: bus@44000000 { + compatible = "fsl,aips-bus", "simple-bus"; + reg = <0x44000000 0x800000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + lpuart2: serial@44390000 { + compatible = "fsl,imx93-lpuart", "fsl,imx8ulp-lpuart", "fsl,imx7ulp-lpuart"; + reg = <0x44390000 0x1000>; + interrupts = ; + status = "disabled"; + }; + }; + + aips3: bus@42800000 { + compatible = "fsl,aips-bus", "simple-bus"; + reg = <0x42800000 0x800000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + usdhc1: mmc@42850000 { + compatible = "fsl,imx93-usdhc", "fsl,imx8mm-usdhc"; + reg = <0x42850000 0x10000>; + interrupts = ; + fsl,tuning-start-tap = <20>; + fsl,tuning-step= <2>; + status = "disabled"; + }; + }; + }; +}; + +&lpuart2 { + clocks = <&osc_24m>; + clock-names = "ipg"; + status = "okay"; +}; + +&usdhc1 { + clocks = <&clk_dummy>, + <&clk_dummy>, + <&clk_400m>; + clock-names = "ipg", "ahb", "per"; + bus-width = <8>; + non-removable; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-root.dts b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-root.dts new file mode 100644 index 000000000000..e61f46be93b7 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-root.dts @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +/dts-v1/; + +#include "imx93-9x9-qsb.dts" + +/{ + interrupt-parent = <&gic>; + + resmem: reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + }; +}; + +&clk { + init-on-array = ; +}; + +&iomuxc { + pinctrl_uart2: uart2grp { + fsl,pins = < + MX93_PAD_UART2_TXD__LPUART2_TX 0x31e + MX93_PAD_UART2_RXD__LPUART2_RX 0x31e + >; + }; +}; + +&lpuart2 { + /delete-property/ dmas; + /delete-property/ dma-names; + status = "disabled"; +}; + +&lpuart1 { + pinctrl-0 = <&pinctrl_uart1>, <&pinctrl_uart2>; + assigned-clocks = <&clk IMX93_CLK_LPUART2>; + assigned-clock-parents = <&clk IMX93_CLK_24M>; +}; + +&usdhc1 { + status = "disabled"; +}; + +&usdhc2 { + pinctrl-0 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_gpio>, <&pinctrl_usdhc1>; + pinctrl-1 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_gpio>, <&pinctrl_usdhc1>; + pinctrl-2 = <&pinctrl_usdhc2>, <&pinctrl_usdhc2_gpio>, <&pinctrl_usdhc1>; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-uart-sharing-cm33.dts b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-uart-sharing-cm33.dts new file mode 100644 index 000000000000..c897b9e9ced2 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb-uart-sharing-cm33.dts @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +/dts-v1/; + +#include +#include "imx93-9x9-qsb.dts" + +/ { + uart_rpbus_0: uart-rpbus-0 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_1: uart-rpbus-1 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_2: uart-rpbus-2 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_3: uart-rpbus-3 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_4: uart-rpbus-4 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_5: uart-rpbus-5 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_6: uart-rpbus-6 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_7: uart-rpbus-7 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_8: uart-rpbus-8 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_9: uart-rpbus-9 { + compatible = "fsl,uart-rpbus"; + bus_id = <5>; /* use lpuart5 */ + flags=; + status = "okay"; + }; + + uart_rpbus_10: uart-rpbus-10 { + compatible = "fsl,uart-rpbus"; + status = "okay"; /* mcore directly print data that receive from acore */ + }; +}; + +/* + * ATTENTION: M33 may use IPs like below + * LPUART5 + */ + +/* the lpuart5 is used by m33 to support srtm uart service */ +&lpuart5 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts index 7798271da63b..9534747b6a09 100644 --- a/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts +++ b/arch/arm64/boot/dts/freescale/imx93-9x9-qsb.dts @@ -224,6 +224,9 @@ &eqos { pinctrl-0 = <&pinctrl_eqos>; phy-mode = "rgmii-id"; phy-handle = <ðphy1>; + snps,force_thresh_dma_mode; + snps,mtl-tx-config = <&mtl_tx_setup>; + snps,mtl-rx-config = <&mtl_rx_setup>; status = "okay"; mdio { @@ -241,6 +244,61 @@ ethphy1: ethernet-phy@1 { realtek,clkout-disable; }; }; + + mtl_tx_setup: tx-queues-config { + snps,tx-queues-to-use = <5>; + snps,tx-sched-sp; + queue0 { + snps,dcb-algorithm; + snps,priority = <0x1>; + }; + queue1 { + snps,dcb-algorithm; + snps,priority = <0x2>; + }; + queue2 { + snps,dcb-algorithm; + snps,priority = <0x4>; + }; + queue3 { + snps,dcb-algorithm; + snps,priority = <0x8>; + }; + queue4 { + snps,dcb-algorithm; + snps,priority = <0xf0>; + }; + }; + + mtl_rx_setup: rx-queues-config { + snps,rx-queues-to-use = <5>; + snps,rx-sched-sp; + queue0 { + snps,dcb-algorithm; + snps,priority = <0x1>; + snps,map-to-dma-channel = <0>; + }; + queue1 { + snps,dcb-algorithm; + snps,priority = <0x2>; + snps,map-to-dma-channel = <1>; + }; + queue2 { + snps,dcb-algorithm; + snps,priority = <0x4>; + snps,map-to-dma-channel = <2>; + }; + queue3 { + snps,dcb-algorithm; + snps,priority = <0x8>; + snps,map-to-dma-channel = <3>; + }; + queue4 { + snps,dcb-algorithm; + snps,priority = <0xf0>; + snps,map-to-dma-channel = <4>; + }; + }; }; &lpm { diff --git a/arch/arm64/boot/dts/freescale/imx93-evk-avb.dtsi b/arch/arm64/boot/dts/freescale/imx93-evk-avb.dtsi new file mode 100644 index 000000000000..76bcb8a7979e --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-evk-avb.dtsi @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2024 NXP + */ + +/* AVB HW timer*/ +&tpm4 { + compatible = "fsl,avb-tpm"; + timer-channel = <1>; /* Use output compare channel 1*/ + prescale = <1>; + domain = <0>; + interrupts = ; + + clocks = <&clk IMX93_CLK_BUS_WAKEUP>, <&clk IMX93_CLK_TPM4_GATE>, <&clk IMX93_CLK_AUDIO_PLL>; + clock-names = "ipg", "per", "audio_pll"; + + /* Set TPM4 clock root to Audio PLL. */ + assigned-clocks = <&clk IMX93_CLK_TPM4>; + assigned-clock-parents = <&clk IMX93_CLK_AUDIO_PLL>; + assigned-clock-rates = <49152000>; + + /* Enble SW sampling for media clock recovery on port 0 */ + sw-recovery = <0>; + + status = "okay"; +}; + +&fec { + fsl,rx-phy-delay-100-ns = <670>; + fsl,tx-phy-delay-100-ns = <670>; + fsl,rx-phy-delay-1000-ns = <0>; + fsl,tx-phy-delay-1000-ns = <0>; +}; + +ðphy2 { + eee-broken-100tx; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-generic-mbox.dtsi b/arch/arm64/boot/dts/freescale/imx93-generic-mbox.dtsi new file mode 100644 index 000000000000..5e6e1723a05d --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-generic-mbox.dtsi @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + gen_sw_mbox_reserved: gen-sw-mbox@fe000000 { + reg = <0 0xfe000000 0 0x1000>; + no-map; + }; + + }; + + gen_sw_mbox: generic-software-mailbox@fe000000 { + compatible = "fsl,generic-software-mbox"; + reg = <0 0xfe000000 0 0x1000>; + #mbox-cells = <3>; + /* Use 2 unused SPI interrupts */ + interrupts = , ; + interrupt-names = "irq", "remote_irq"; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-rpmsg-ca55.dtsi b/arch/arm64/boot/dts/freescale/imx93-rpmsg-ca55.dtsi new file mode 100644 index 000000000000..5db78b628c44 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-rpmsg-ca55.dtsi @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2023 NXP + */ + +#include "imx93-generic-mbox.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + rpmsg_ca55_reserved: rpmsg-ca55@fe100000 { + reg = <0 0xfe100000 0 0x10000>; + no-map; + }; + + vdevbuffer_ca55: vdevbuffer-ca55@fe200000 { + compatible = "shared-dma-pool"; + reg = <0 0xfe200000 0 0x100000>; + no-map; + }; + }; + + rpmsg-ca55 { + compatible = "fsl,imx8mm-rpmsg"; + reg = <0x0 0xfe100000 0x0 0x10000>; /* 64K for one rpmsg instance */ + dma-coherent; + memory-region = <&vdevbuffer_ca55>; + mbox-names = "tx", "rx", "rxdb"; + mboxes = <&gen_sw_mbox 0 0 1 /* Tx channel with ACK */ + &gen_sw_mbox 1 0 0 /* Rx channel without ACK */ + &gen_sw_mbox 2 0 1>; /* RXDB channel with ACK */ + vdev-nums = <1>; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx93-rproc-ca55.dtsi b/arch/arm64/boot/dts/freescale/imx93-rproc-ca55.dtsi new file mode 100644 index 000000000000..4cc9342cf9a4 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx93-rproc-ca55.dtsi @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2024 NXP + */ + +/ { + ca55_1: remoteproc-ca55-1 { + compatible = "fsl,imx-rproc-psci"; + /* bitmask:0b10, assign A55 Core 1 */ + fsl,cpus-bits = <0x2>; + memory-region = <&rtos_ca55_reserved>; + }; +}; + diff --git a/arch/arm64/boot/dts/freescale/imx95-15x15-evk-harpoon-industrial.dts b/arch/arm64/boot/dts/freescale/imx95-15x15-evk-harpoon-industrial.dts new file mode 100644 index 000000000000..e3a6ce1bbff4 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-15x15-evk-harpoon-industrial.dts @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2025 NXP + */ + +#include "imx95-15x15-evk-root.dts" +#include "imx95-rpmsg-ca55.dtsi" + +&lpuart3 { + /* To use this, needs dedicated SM to allow RTOS access lpuart3 and lpuart3 pinmux */ + status = "disabled"; +}; + +/* This Message Unit is used for communication between the RTOS cell and the SM */ +&mu3 { + status = "disabled"; +}; + +&flexcan2 { + status = "disabled"; +}; + +®_can2_stby { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-15x15-evk-harpoon.dts b/arch/arm64/boot/dts/freescale/imx95-15x15-evk-harpoon.dts new file mode 100644 index 000000000000..8838cd44871c --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-15x15-evk-harpoon.dts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2024 NXP + */ + +/dts-v1/; + +#include "imx95-15x15-evk-root.dts" +#include "imx95-rpmsg-ca55.dtsi" + +&lpuart3 { + /* To use this, needs dedicated SM to allow RTOS access lpuart3 and lpuart3 pinmux */ + status = "disabled"; +}; + +/* This Message Unit is used for communication between the RTOS cell and the SM */ +&mu3 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-15x15-evk-multicore-rtos.dts b/arch/arm64/boot/dts/freescale/imx95-15x15-evk-multicore-rtos.dts new file mode 100644 index 000000000000..359a54275e9c --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-15x15-evk-multicore-rtos.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2025 NXP + */ + +/dts-v1/; +#include "imx95-15x15-evk.dts" +#include "imx95-rproc-ca55.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* + * Reserve up to 3 x 16MB for three possible FreeRTOS instances running on + * Cortex-A Cores when booting Linux. + */ + rtos_ca55_reserved: rtos-ca55@d0000000 { + no-map; + reg = <0 0xd0000000 0x0 0x3000000>; + }; + }; +}; + +&lpuart3 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-19x19-evk-harpoon-industrial.dts b/arch/arm64/boot/dts/freescale/imx95-19x19-evk-harpoon-industrial.dts new file mode 100644 index 000000000000..59fde8aa0a65 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-19x19-evk-harpoon-industrial.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2024 NXP + */ + +#include "imx95-19x19-evk-root.dts" +#include "imx95-rpmsg-ca55.dtsi" + +&lpuart3 { + /* To use this, needs dedicated SM to allow RTOS access lpuart3 and lpuart3 pinmux */ + status = "disabled"; +}; + +/* This Message Unit is used for communication between the RTOS cell and the SM */ +&mu3 { + status = "disabled"; +}; + +&flexcan2 { + status = "disabled"; +}; + +®_can2_en { + status = "disabled"; +}; + +®_can2_stby { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-19x19-evk-harpoon.dts b/arch/arm64/boot/dts/freescale/imx95-19x19-evk-harpoon.dts new file mode 100644 index 000000000000..8f0e6f8c38e1 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-19x19-evk-harpoon.dts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2024 NXP + */ + +/dts-v1/; + +#include "imx95-19x19-evk-root.dts" +#include "imx95-rpmsg-ca55.dtsi" + +&lpuart3 { + /* To use this, needs dedicated SM to allow RTOS access lpuart3 and lpuart3 pinmux */ + status = "disabled"; +}; + +/* This Message Unit is used for communication between the RTOS cell and the SM */ +&mu3 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-19x19-evk-multicore-rtos.dts b/arch/arm64/boot/dts/freescale/imx95-19x19-evk-multicore-rtos.dts new file mode 100644 index 000000000000..b28f9d22dbc2 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-19x19-evk-multicore-rtos.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2024 NXP + */ + +/dts-v1/; +#include "imx95-19x19-evk.dts" +#include "imx95-rproc-ca55.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* + * Reserve up to 3 x 16MB for three possible FreeRTOS instances running on + * Cortex-A Cores when booting Linux. + */ + rtos_ca55_reserved: rtos-ca55@d0000000 { + no-map; + reg = <0 0xd0000000 0x0 0x3000000>; + }; + }; +}; + +&lpuart3 { + status = "disabled"; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-generic-mbox.dtsi b/arch/arm64/boot/dts/freescale/imx95-generic-mbox.dtsi new file mode 100644 index 000000000000..81fd2a34faaf --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-generic-mbox.dtsi @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2024 NXP + */ + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + gen_sw_mbox_reserved: gen-sw-mbox@c0000000 { + reg = <0 0xc0000000 0 0x1000>; + no-map; + }; + + }; + + gen_sw_mbox: generic-software-mailbox@c0000000 { + compatible = "fsl,generic-software-mbox"; + reg = <0 0xc0000000 0 0x1000>; + #mbox-cells = <3>; + /* Use 2 unused SPI interrupts */ + interrupts = , ; + interrupt-names = "irq", "remote_irq"; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-rpmsg-ca55.dtsi b/arch/arm64/boot/dts/freescale/imx95-rpmsg-ca55.dtsi new file mode 100644 index 000000000000..fc72fab7b791 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-rpmsg-ca55.dtsi @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2024 NXP + */ + +#include "imx95-generic-mbox.dtsi" + +/ { + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + rpmsg_ca55_reserved: rpmsg-ca55@c0100000 { + reg = <0 0xc0100000 0 0x10000>; + no-map; + }; + + vdevbuffer_ca55: vdevbuffer-ca55@c0200000 { + compatible = "shared-dma-pool"; + reg = <0 0xc0200000 0 0x100000>; + no-map; + }; + }; + + rpmsg-ca55 { + compatible = "fsl,imx8mm-rpmsg"; + reg = <0x0 0xc0100000 0x0 0x10000>; /* 64K for one rpmsg instance */ + dma-coherent; + memory-region = <&vdevbuffer_ca55>; + mbox-names = "tx", "rx", "rxdb"; + mboxes = <&gen_sw_mbox 0 0 1 /* Tx channel with ACK */ + &gen_sw_mbox 1 0 0 /* Rx channel without ACK */ + &gen_sw_mbox 2 0 1>; /* RXDB channel with ACK */ + vdev-nums = <1>; + }; +}; diff --git a/arch/arm64/boot/dts/freescale/imx95-rproc-ca55.dtsi b/arch/arm64/boot/dts/freescale/imx95-rproc-ca55.dtsi new file mode 100644 index 000000000000..94c5d9db84ad --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx95-rproc-ca55.dtsi @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2024 NXP + */ + +/ { + ca55_1: remoteproc-ca55-1 { + compatible = "fsl,imx-rproc-psci"; + /* bitmask:0b000010, assign A55 Core 1 */ + fsl,cpus-bits = <0x2>; + memory-region = <&rtos_ca55_reserved>; + }; + + ca55_2: remoteproc-ca55-2 { + compatible = "fsl,imx-rproc-psci"; + /* bitmask:0b000100, assign A55 Core 2 */ + fsl,cpus-bits = <0x4>; + memory-region = <&rtos_ca55_reserved>; + }; + + ca55_3: remoteproc-ca55-3 { + compatible = "fsl,imx-rproc-psci"; + /* bitmask:0b001000, assign A55 Core 3 */ + fsl,cpus-bits = <0x8>; + memory-region = <&rtos_ca55_reserved>; + }; + + ca55_4: remoteproc-ca55-4 { + compatible = "fsl,imx-rproc-psci"; + /* bitmask:0b010000, assign A55 Core 4 */ + fsl,cpus-bits = <0x10>; + memory-region = <&rtos_ca55_reserved>; + }; + + ca55_5: remoteproc-ca55-5 { + compatible = "fsl,imx-rproc-psci"; + /* bitmask:0b100000, assign A55 Core 5 */ + fsl,cpus-bits = <0x20>; + memory-region = <&rtos_ca55_reserved>; + }; + + ca55_4_5: remoteproc-ca55-4-5 { + compatible = "fsl,imx-rproc-psci"; + /* bitmask:0b110000, assign A55 Core 4 and 5 */ + fsl,cpus-bits = <0x30>; + memory-region = <&rtos_ca55_reserved>; + }; +}; diff --git a/arch/arm64/configs/imx_avb.config b/arch/arm64/configs/imx_avb.config new file mode 100644 index 000000000000..1c8ba04c0381 --- /dev/null +++ b/arch/arm64/configs/imx_avb.config @@ -0,0 +1,8 @@ +CONFIG_EXPERT=y +CONFIG_PREEMPT_RT=y +CONFIG_AVB_SUPPORT=y +CONFIG_NET_SWITCHDEV=y +CONFIG_DEBUG_INFO=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y diff --git a/arch/arm64/configs/imx_v8_defconfig b/arch/arm64/configs/imx_v8_defconfig index f076bf99e0cd..f083f63a6ac9 100644 --- a/arch/arm64/configs/imx_v8_defconfig +++ b/arch/arm64/configs/imx_v8_defconfig @@ -6,6 +6,8 @@ CONFIG_NO_HZ_IDLE=y CONFIG_HIGH_RES_TIMERS=y CONFIG_BPF_SYSCALL=y CONFIG_BPF_JIT=y +CONFIG_BPF_JIT_ALWAYS_ON=y +CONFIG_XDP_SOCKETS=y CONFIG_PREEMPT=y CONFIG_IRQ_TIME_ACCOUNTING=y CONFIG_BSD_PROCESS_ACCT=y @@ -137,6 +139,10 @@ CONFIG_BRIDGE_NF_EBTABLES=m CONFIG_BRIDGE=y CONFIG_BRIDGE_VLAN_FILTERING=y CONFIG_NET_DSA=m +CONFIG_NET_DSA_SJA1105=m +CONFIG_NET_DSA_SJA1105_PTP=y +CONFIG_NET_DSA_SJA1105_TAS=y +CONFIG_NET_DSA_SJA1105_VL=y CONFIG_VLAN_8021Q_GVRP=y CONFIG_VLAN_8021Q_MVRP=y CONFIG_LLC2=y @@ -152,17 +158,22 @@ CONFIG_NET_CLS_U32=y CONFIG_NET_CLS_FLOWER=y CONFIG_NET_CLS_ACT=y CONFIG_NET_ACT_GACT=m -CONFIG_NET_ACT_MIRRED=m +CONFIG_NET_ACT_MIRRED=y CONFIG_NET_ACT_SKBEDIT=y +CONFIG_NET_ACT_FRER=y CONFIG_NET_ACT_GATE=y CONFIG_NET_ACT_POLICE=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_EMATCH_META=y CONFIG_TSN=y CONFIG_VSOCKETS=y CONFIG_VIRTIO_VSOCKETS=y CONFIG_QRTR=m CONFIG_QRTR_SMD=m CONFIG_QRTR_TUN=m -CONFIG_NET_PKTGEN=m +CONFIG_NET_PKTGEN=y CONFIG_CAN=m CONFIG_BT=y CONFIG_BT_RFCOMM=y @@ -292,8 +303,10 @@ CONFIG_MACVLAN=m CONFIG_MACVTAP=m CONFIG_TUN=y CONFIG_VETH=m -CONFIG_VIRTIO_NET=y +CONFIG_VIRTIO_NET=m CONFIG_NET_DSA_MSCC_FELIX=m +CONFIG_NET_DSA_NETC=m +CONFIG_NET_DSA_NETC_PTP=y CONFIG_NET_DSA_SJA1105=m CONFIG_NET_DSA_SJA1105_PTP=y CONFIG_NET_DSA_SJA1105_TAS=y @@ -306,6 +319,7 @@ CONFIG_SYSTEMPORT=m CONFIG_MACB=y CONFIG_THUNDER_NIC_PF=y CONFIG_FEC=y +CONFIG_FEC_ECAT=y CONFIG_FEC_UIO=y CONFIG_FSL_FMAN=y CONFIG_FSL_DPAA_ETH=y @@ -931,7 +945,7 @@ CONFIG_VFIO_FSL_MC=y CONFIG_VIRTIO_PCI=y CONFIG_VIRTIO_BALLOON=y CONFIG_VIRTIO_INPUT=y -CONFIG_VIRTIO_MMIO=y +CONFIG_VIRTIO_MMIO=m CONFIG_VIRTIO_IVSHMEM=y CONFIG_VHOST_VSOCK=y CONFIG_XEN_GNTDEV=y @@ -1205,3 +1219,11 @@ CONFIG_MEMTEST=y CONFIG_VHOST_NET=m CONFIG_IMX_ISPMU=m CONFIG_IMX_ISP_REMOTEPROC=m + +# Preempt RT, depend on CONFIG_EXPERT +CONFIG_EXPERT=y +CONFIG_PREEMPT_RT=y + +CONFIG_RPMSG_TTY=m +CONFIG_GENERIC_SOFTWARE_MAILBOX=y +CONFIG_VIRTIO_TRANS=m diff --git a/arch/arm64/configs/linux-dpaa-ethercat.config b/arch/arm64/configs/linux-dpaa-ethercat.config new file mode 100644 index 000000000000..15e4b306bc3d --- /dev/null +++ b/arch/arm64/configs/linux-dpaa-ethercat.config @@ -0,0 +1 @@ +CONFIG_FSL_DPAA_ETHERCAT=y diff --git a/arch/arm64/configs/linux-rpmsg-8m-buf.config b/arch/arm64/configs/linux-rpmsg-8m-buf.config new file mode 100644 index 000000000000..d7780b2b8299 --- /dev/null +++ b/arch/arm64/configs/linux-rpmsg-8m-buf.config @@ -0,0 +1 @@ +CONFIG_RPMSG_8M_BUF=y diff --git a/arch/arm64/configs/lsdk.config b/arch/arm64/configs/lsdk.config index d97ef5280ed2..f17f9aef7868 100644 --- a/arch/arm64/configs/lsdk.config +++ b/arch/arm64/configs/lsdk.config @@ -84,6 +84,10 @@ CONFIG_BRIDGE_NF_EBTABLES=y CONFIG_BRIDGE_EBT_T_NAT=y CONFIG_BRIDGE_EBT_DNAT=y CONFIG_BRIDGE_EBT_SNAT=y +CONFIG_BRIDGE_EBT_BROUTE=y +CONFIG_BRIDGE_EBT_802_3=y +CONFIG_BRIDGE_EBT_IP=y +CONFIG_BRIDGE_EBT_IP6=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y CONFIG_SFP=y CONFIG_PHY_FSL_LYNX_10G=y @@ -92,6 +96,15 @@ CONFIG_MTIP_BACKPLANE_PHY=y CONFIG_BPF_SYSCALL=y CONFIG_BPF_JIT_ALWAYS_ON=y CONFIG_XDP_SOCKETS=y +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +CONFIG_NET_ACT_GATE=y +CONFIG_NET_CLS_MATCHALL=y +CONFIG_NET_PKTGEN=y + +# Enable felix switch TSN +CONFIG_MSCC_FELIX_SWITCH_TSN=y # GPU CONFIG_DRM=y @@ -99,7 +112,6 @@ CONFIG_MXC_GPU_VIV=y # disable unneeded options and override default options set by defconfig to deduce the size of modules # CONFIG_NET_VENDOR_STMICRO is not set -# CONFIG_MEDIA_SUPPORT is not set # CONFIG_BACKLIGHT_GENERIC is not set # CONFIG_TEGRA_HOST1X is not set # CONFIG_BT is not set @@ -183,6 +195,7 @@ CONFIG_NET_CLS_FLOWER=y CONFIG_NET_EMATCH=y CONFIG_NET_EMATCH_STACK=32 CONFIG_NET_EMATCH_U32=y +CONFIG_NET_EMATCH_META=y CONFIG_NET_CLS_ACT=y CONFIG_NET_ACT_PEDIT=y CONFIG_NET_ACT_SKBEDIT=y @@ -194,10 +207,18 @@ CONFIG_USB_RTL8152=y CONFIG_USB_USBNET=y CONFIG_USB_NET_AX8817X=y CONFIG_USB_NET_AX88179_178A=y -# CONFIG_NET_PKTGEN is not set #build-in drivers CONFIG_FSL_DPAA2_SWITCH=y CONFIG_QORIQ_THERMAL=y CONFIG_FSL_IFC=y CONFIG_MTD_NAND_FSL_IFC=y + +# Preempt RT, depend on CONFIG_EXPERT +CONFIG_PREEMPT_RT=y + +CONFIG_NFC_NXP_PN5XX=m + +# IVSHMEM +CONFIG_IVSHMEM_NET=y +CONFIG_UIO_IVSHMEM=y diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index be87f2f7f77c..1ad60cee856f 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -53,6 +53,10 @@ #include +#ifdef CONFIG_BAREMETAL +#include +#endif + DEFINE_PER_CPU_READ_MOSTLY(int, cpu_number); EXPORT_PER_CPU_SYMBOL(cpu_number); @@ -73,6 +77,13 @@ enum ipi_msg_type { IPI_TIMER, IPI_IRQ_WORK, IPI_WAKEUP, +#ifdef CONFIG_BAREMETAL +#if defined(CONFIG_IMX8M_BAREMETAL) || defined(CONFIG_IMX93_BAREMETAL) + IPI_BAREMETAL_COMM = 9, +#else + IPI_BAREMETAL_COMM = 8, +#endif +#endif NR_IPI }; @@ -767,6 +778,9 @@ static const char *ipi_types[NR_IPI] __tracepoint_string = { [IPI_TIMER] = "Timer broadcast interrupts", [IPI_IRQ_WORK] = "IRQ work interrupts", [IPI_WAKEUP] = "CPU wake-up interrupts", +#ifdef CONFIG_BAREMETAL + [IPI_BAREMETAL_COMM] = "Baremetal inter-core interrupts", +#endif }; static void smp_cross_call(const struct cpumask *target, unsigned int ipinr); @@ -907,6 +921,17 @@ static void do_handle_IPI(int ipinr) break; #endif +#ifdef CONFIG_BAREMETAL + case IPI_BAREMETAL_COMM: { + /* FIXME: use the fixed source coreID from core1 */ + int irqsrc = 1; + /*linux core is 0 core, so iterate from 1 core.*/ + for(irqsrc = 1; irqsrc < CONFIG_MAX_CPUS; irqsrc++) + ipi_baremetal_handle(ipinr, irqsrc); + } + break; +#endif + default: pr_crit("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr); break; diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 61fb49dca45b..3c61d0541c99 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -166,6 +166,7 @@ config PPC select ARCH_STACKWALK select ARCH_SUPPORTS_ATOMIC_RMW select ARCH_SUPPORTS_DEBUG_PAGEALLOC if PPC_BOOK3S || PPC_8xx || 40x + select ARCH_SUPPORTS_RT if HAVE_POSIX_CPU_TIMERS_TASK_WORK select ARCH_USE_BUILTIN_BSWAP select ARCH_USE_CMPXCHG_LOCKREF if PPC64 select ARCH_USE_MEMTEST @@ -268,6 +269,7 @@ config PPC select HAVE_PERF_USER_STACK_DUMP select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_RELIABLE_STACKTRACE + select HAVE_POSIX_CPU_TIMERS_TASK_WORK if !KVM select HAVE_RSEQ select HAVE_SETUP_PER_CPU_AREA if PPC64 select HAVE_SOFTIRQ_ON_OWN_STACK diff --git a/arch/powerpc/include/asm/stackprotector.h b/arch/powerpc/include/asm/stackprotector.h index 283c34647856..4727f40052dd 100644 --- a/arch/powerpc/include/asm/stackprotector.h +++ b/arch/powerpc/include/asm/stackprotector.h @@ -19,8 +19,13 @@ */ static __always_inline void boot_init_stack_canary(void) { - unsigned long canary = get_random_canary(); + unsigned long canary; +#ifndef CONFIG_PREEMPT_RT + canary = get_random_canary(); +#else + canary = ((unsigned long)&canary) & CANARY_MASK; +#endif current->stack_canary = canary; #ifdef CONFIG_PPC64 get_paca()->canary = canary; diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 2de7f6dcd32b..739f5b179a7f 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -261,12 +261,17 @@ static char *get_mmu_str(void) static int __die(const char *str, struct pt_regs *regs, long err) { + const char *pr = ""; + printk("Oops: %s, sig: %ld [#%d]\n", str, err, ++die_counter); + if (IS_ENABLED(CONFIG_PREEMPTION)) + pr = IS_ENABLED(CONFIG_PREEMPT_RT) ? " PREEMPT_RT" : " PREEMPT"; + printk("%s PAGE_SIZE=%luK%s%s%s%s%s%s %s\n", IS_ENABLED(CONFIG_CPU_LITTLE_ENDIAN) ? "LE" : "BE", PAGE_SIZE / 1024, get_mmu_str(), - IS_ENABLED(CONFIG_PREEMPT) ? " PREEMPT" : "", + pr, IS_ENABLED(CONFIG_SMP) ? " SMP" : "", IS_ENABLED(CONFIG_SMP) ? (" NR_CPUS=" __stringify(NR_CPUS)) : "", debug_pagealloc_enabled() ? " DEBUG_PAGEALLOC" : "", diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig index 902611954200..2f188137f830 100644 --- a/arch/powerpc/kvm/Kconfig +++ b/arch/powerpc/kvm/Kconfig @@ -224,6 +224,7 @@ config KVM_E500MC config KVM_MPIC bool "KVM in-kernel MPIC emulation" depends on KVM && PPC_E500 + depends on !PREEMPT_RT select HAVE_KVM_IRQCHIP select HAVE_KVM_IRQFD select HAVE_KVM_IRQ_ROUTING diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig index 4ebf2ef2845d..381c3be3bec1 100644 --- a/arch/powerpc/platforms/pseries/Kconfig +++ b/arch/powerpc/platforms/pseries/Kconfig @@ -2,6 +2,7 @@ config PPC_PSERIES depends on PPC64 && PPC_BOOK3S bool "IBM pSeries & new (POWER5-based) iSeries" + select GENERIC_ALLOCATOR select HAVE_PCSPKR_PLATFORM select MPIC select OF_DYNAMIC diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index b1e6d275cda9..9a8d3970da3c 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -206,7 +207,13 @@ static int tce_build_pSeriesLP(unsigned long liobn, long tcenum, long tceshift, return ret; } -static DEFINE_PER_CPU(__be64 *, tce_page); +struct tce_page { + __be64 * page; + local_lock_t lock; +}; +static DEFINE_PER_CPU(struct tce_page, tce_page) = { + .lock = INIT_LOCAL_LOCK(lock), +}; static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages, unsigned long uaddr, @@ -229,9 +236,10 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, direction, attrs); } - local_irq_save(flags); /* to protect tcep and the page behind it */ + /* to protect tcep and the page behind it */ + local_lock_irqsave(&tce_page.lock, flags); - tcep = __this_cpu_read(tce_page); + tcep = __this_cpu_read(tce_page.page); /* This is safe to do since interrupts are off when we're called * from iommu_alloc{,_sg}() @@ -240,12 +248,12 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, tcep = (__be64 *)__get_free_page(GFP_ATOMIC); /* If allocation fails, fall back to the loop implementation */ if (!tcep) { - local_irq_restore(flags); + local_unlock_irqrestore(&tce_page.lock, flags); return tce_build_pSeriesLP(tbl->it_index, tcenum, tceshift, npages, uaddr, direction, attrs); } - __this_cpu_write(tce_page, tcep); + __this_cpu_write(tce_page.page, tcep); } rpn = __pa(uaddr) >> tceshift; @@ -275,7 +283,7 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, tcenum += limit; } while (npages > 0 && !rc); - local_irq_restore(flags); + local_unlock_irqrestore(&tce_page.lock, flags); if (unlikely(rc == H_NOT_ENOUGH_RESOURCES)) { ret = (int)rc; @@ -459,16 +467,17 @@ static int tce_setrange_multi_pSeriesLP(unsigned long start_pfn, DMA_BIDIRECTIONAL, 0); } - local_irq_disable(); /* to protect tcep and the page behind it */ - tcep = __this_cpu_read(tce_page); + /* to protect tcep and the page behind it */ + local_lock_irq(&tce_page.lock); + tcep = __this_cpu_read(tce_page.page); if (!tcep) { tcep = (__be64 *)__get_free_page(GFP_ATOMIC); if (!tcep) { - local_irq_enable(); + local_unlock_irq(&tce_page.lock); return -ENOMEM; } - __this_cpu_write(tce_page, tcep); + __this_cpu_write(tce_page.page, tcep); } proto_tce = TCE_PCI_READ | TCE_PCI_WRITE; @@ -511,7 +520,7 @@ static int tce_setrange_multi_pSeriesLP(unsigned long start_pfn, /* error cleanup: caller will clear whole range */ - local_irq_enable(); + local_unlock_irq(&tce_page.lock); return rc; } diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index fe30b24d52e1..2bdf6a7d0f8c 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -49,6 +49,7 @@ config RISCV select ARCH_SUPPORTS_HUGETLBFS if MMU select ARCH_SUPPORTS_PAGE_TABLE_CHECK if MMU select ARCH_SUPPORTS_PER_VMA_LOCK if MMU + select ARCH_SUPPORTS_RT select ARCH_USE_MEMTEST select ARCH_USE_QUEUED_RWLOCKS select ARCH_USES_CFI_TRAPS if CFI_CLANG @@ -136,6 +137,7 @@ config RISCV select HAVE_PERF_USER_STACK_DUMP select HAVE_POSIX_CPU_TIMERS_TASK_WORK select HAVE_PREEMPT_DYNAMIC_KEY if !XIP_KERNEL + select HAVE_PREEMPT_AUTO select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_RETHOOK if !XIP_KERNEL select HAVE_RSEQ diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h index d0345bd659c9..23fed53b8815 100644 --- a/arch/riscv/include/asm/cpufeature.h +++ b/arch/riscv/include/asm/cpufeature.h @@ -30,6 +30,4 @@ DECLARE_PER_CPU(long, misaligned_access_speed); /* Per-cpu ISA extensions. */ extern struct riscv_isainfo hart_isa[NR_CPUS]; -void check_unaligned_access(int cpu); - #endif diff --git a/arch/riscv/include/asm/thread_info.h b/arch/riscv/include/asm/thread_info.h index d18ce0113ca1..e18710fe51f0 100644 --- a/arch/riscv/include/asm/thread_info.h +++ b/arch/riscv/include/asm/thread_info.h @@ -82,6 +82,7 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src); * - pending work-to-be-done flags are in lowest half-word * - other flags in upper half-word(s) */ +#define TIF_ARCH_RESCHED_LAZY 0 /* Lazy rescheduling */ #define TIF_NOTIFY_RESUME 1 /* callback before returning to user */ #define TIF_SIGPENDING 2 /* signal pending */ #define TIF_NEED_RESCHED 3 /* rescheduling necessary */ @@ -96,6 +97,7 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src); #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) #define _TIF_NOTIFY_SIGNAL (1 << TIF_NOTIFY_SIGNAL) #define _TIF_UPROBE (1 << TIF_UPROBE) +#define _TIF_ARCH_RESCHED_LAZY (1 << TIF_ARCH_RESCHED_LAZY) #define _TIF_WORK_MASK \ (_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | _TIF_NEED_RESCHED | \ diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c index e39a905aca24..dd118773e717 100644 --- a/arch/riscv/kernel/cpufeature.c +++ b/arch/riscv/kernel/cpufeature.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -30,6 +31,7 @@ #define MISALIGNED_ACCESS_JIFFIES_LG2 1 #define MISALIGNED_BUFFER_SIZE 0x4000 +#define MISALIGNED_BUFFER_ORDER get_order(MISALIGNED_BUFFER_SIZE) #define MISALIGNED_COPY_SIZE ((MISALIGNED_BUFFER_SIZE / 2) - 0x80) unsigned long elf_hwcap __read_mostly; @@ -571,14 +573,15 @@ unsigned long riscv_get_elf_hwcap(void) return hwcap; } -void check_unaligned_access(int cpu) +static int check_unaligned_access(void *param) { + int cpu = smp_processor_id(); u64 start_cycles, end_cycles; u64 word_cycles; u64 byte_cycles; int ratio; unsigned long start_jiffies, now; - struct page *page; + struct page *page = param; void *dst; void *src; long speed = RISCV_HWPROBE_MISALIGNED_SLOW; @@ -587,12 +590,6 @@ void check_unaligned_access(int cpu) if (per_cpu(misaligned_access_speed, cpu) != RISCV_HWPROBE_MISALIGNED_UNKNOWN) return; - page = alloc_pages(GFP_NOWAIT, get_order(MISALIGNED_BUFFER_SIZE)); - if (!page) { - pr_warn("Can't alloc pages to measure memcpy performance"); - return; - } - /* Make an unaligned destination buffer. */ dst = (void *)((unsigned long)page_address(page) | 0x1); /* Unalign src as well, but differently (off by 1 + 2 = 3). */ @@ -645,7 +642,7 @@ void check_unaligned_access(int cpu) pr_warn("cpu%d: rdtime lacks granularity needed to measure unaligned access speed\n", cpu); - goto out; + return 0; } if (word_cycles < byte_cycles) @@ -659,18 +656,83 @@ void check_unaligned_access(int cpu) (speed == RISCV_HWPROBE_MISALIGNED_FAST) ? "fast" : "slow"); per_cpu(misaligned_access_speed, cpu) = speed; + return 0; +} -out: - __free_pages(page, get_order(MISALIGNED_BUFFER_SIZE)); +static void check_unaligned_access_nonboot_cpu(void *param) +{ + unsigned int cpu = smp_processor_id(); + struct page **pages = param; + + if (smp_processor_id() != 0) + check_unaligned_access(pages[cpu]); } -static int check_unaligned_access_boot_cpu(void) +static int riscv_online_cpu(unsigned int cpu) { - check_unaligned_access(0); + static struct page *buf; + + /* We are already set since the last check */ + if (per_cpu(misaligned_access_speed, cpu) != RISCV_HWPROBE_MISALIGNED_UNKNOWN) + return 0; + + buf = alloc_pages(GFP_KERNEL, MISALIGNED_BUFFER_ORDER); + if (!buf) { + pr_warn("Allocation failure, not measuring misaligned performance\n"); + return -ENOMEM; + } + + check_unaligned_access(buf); + __free_pages(buf, MISALIGNED_BUFFER_ORDER); + return 0; +} + +/* Measure unaligned access on all CPUs present at boot in parallel. */ +static int check_unaligned_access_all_cpus(void) +{ + unsigned int cpu; + unsigned int cpu_count = num_possible_cpus(); + struct page **bufs = kzalloc(cpu_count * sizeof(struct page *), + GFP_KERNEL); + + if (!bufs) { + pr_warn("Allocation failure, not measuring misaligned performance\n"); + return 0; + } + + /* + * Allocate separate buffers for each CPU so there's no fighting over + * cache lines. + */ + for_each_cpu(cpu, cpu_online_mask) { + bufs[cpu] = alloc_pages(GFP_KERNEL, MISALIGNED_BUFFER_ORDER); + if (!bufs[cpu]) { + pr_warn("Allocation failure, not measuring misaligned performance\n"); + goto out; + } + } + + /* Check everybody except 0, who stays behind to tend jiffies. */ + on_each_cpu(check_unaligned_access_nonboot_cpu, bufs, 1); + + /* Check core 0. */ + smp_call_on_cpu(0, check_unaligned_access, bufs[0], true); + + /* Setup hotplug callback for any new CPUs that come online. */ + cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "riscv:online", + riscv_online_cpu, NULL); + +out: + for_each_cpu(cpu, cpu_online_mask) { + if (bufs[cpu]) + __free_pages(bufs[cpu], MISALIGNED_BUFFER_ORDER); + } + + kfree(bufs); return 0; } -arch_initcall(check_unaligned_access_boot_cpu); +arch_initcall(check_unaligned_access_all_cpus); #ifdef CONFIG_RISCV_ALTERNATIVE /* diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c index 1b8da4e40a4d..2cb5e651412c 100644 --- a/arch/riscv/kernel/smpboot.c +++ b/arch/riscv/kernel/smpboot.c @@ -246,7 +246,6 @@ asmlinkage __visible void smp_callin(void) numa_add_cpu(curr_cpuid); set_cpu_online(curr_cpuid, 1); - check_unaligned_access(curr_cpuid); if (has_vector()) { if (riscv_v_setup_vsize()) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 82d12c93feab..7bbf835fe725 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -118,6 +118,7 @@ config X86 select ARCH_USES_CFI_TRAPS if X86_64 && CFI_CLANG select ARCH_SUPPORTS_LTO_CLANG select ARCH_SUPPORTS_LTO_CLANG_THIN + select ARCH_SUPPORTS_RT select ARCH_USE_BUILTIN_BSWAP select ARCH_USE_MEMTEST select ARCH_USE_QUEUED_RWLOCKS @@ -272,6 +273,7 @@ config X86 select HAVE_STATIC_CALL select HAVE_STATIC_CALL_INLINE if HAVE_OBJTOOL select HAVE_PREEMPT_DYNAMIC_CALL + select HAVE_PREEMPT_AUTO select HAVE_RSEQ select HAVE_RUST if X86_64 select HAVE_SYSCALL_TRACEPOINTS diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index d63b02940747..1ff38ebbd588 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -81,8 +81,9 @@ struct thread_info { #define TIF_NOTIFY_RESUME 1 /* callback before returning to user */ #define TIF_SIGPENDING 2 /* signal pending */ #define TIF_NEED_RESCHED 3 /* rescheduling necessary */ -#define TIF_SINGLESTEP 4 /* reenable singlestep on user return*/ -#define TIF_SSBD 5 /* Speculative store bypass disable */ +#define TIF_ARCH_RESCHED_LAZY 4 /* Lazy rescheduling */ +#define TIF_SINGLESTEP 5 /* reenable singlestep on user return*/ +#define TIF_SSBD 6 /* Speculative store bypass disable */ #define TIF_SPEC_IB 9 /* Indirect branch speculation mitigation */ #define TIF_SPEC_L1D_FLUSH 10 /* Flush L1D on mm switches (processes) */ #define TIF_USER_RETURN_NOTIFY 11 /* notify kernel of userspace return */ @@ -104,6 +105,7 @@ struct thread_info { #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) +#define _TIF_ARCH_RESCHED_LAZY (1 << TIF_ARCH_RESCHED_LAZY) #define _TIF_SINGLESTEP (1 << TIF_SINGLESTEP) #define _TIF_SSBD (1 << TIF_SSBD) #define _TIF_SPEC_IB (1 << TIF_SPEC_IB) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 831fa4a12159..5af3ebec0f74 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -107,7 +107,7 @@ static const struct dmi_system_id processor_power_dmi_table[] = { */ static void __cpuidle acpi_safe_halt(void) { - if (!tif_need_resched()) { + if (!need_resched()) { raw_safe_halt(); raw_local_irq_disable(); } diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 2b8fd6bb7da0..0125f92bb805 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -199,7 +199,7 @@ source "drivers/base/regmap/Kconfig" config DMA_SHARED_BUFFER bool - default n + default y select IRQ_WORK help This option enables the framework for buffer-sharing between diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 06673c6ca255..a5d0f7c06342 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -57,6 +57,41 @@ static void zram_free_page(struct zram *zram, size_t index); static int zram_read_page(struct zram *zram, struct page *page, u32 index, struct bio *parent); +#ifdef CONFIG_PREEMPT_RT +static void zram_meta_init_table_locks(struct zram *zram, size_t num_pages) +{ + size_t index; + + for (index = 0; index < num_pages; index++) + spin_lock_init(&zram->table[index].lock); +} + +static int zram_slot_trylock(struct zram *zram, u32 index) +{ + int ret; + + ret = spin_trylock(&zram->table[index].lock); + if (ret) + __set_bit(ZRAM_LOCK, &zram->table[index].flags); + return ret; +} + +static void zram_slot_lock(struct zram *zram, u32 index) +{ + spin_lock(&zram->table[index].lock); + __set_bit(ZRAM_LOCK, &zram->table[index].flags); +} + +static void zram_slot_unlock(struct zram *zram, u32 index) +{ + __clear_bit(ZRAM_LOCK, &zram->table[index].flags); + spin_unlock(&zram->table[index].lock); +} + +#else + +static void zram_meta_init_table_locks(struct zram *zram, size_t num_pages) { } + static int zram_slot_trylock(struct zram *zram, u32 index) { return bit_spin_trylock(ZRAM_LOCK, &zram->table[index].flags); @@ -71,6 +106,7 @@ static void zram_slot_unlock(struct zram *zram, u32 index) { bit_spin_unlock(ZRAM_LOCK, &zram->table[index].flags); } +#endif static inline bool init_done(struct zram *zram) { @@ -1245,6 +1281,7 @@ static bool zram_meta_alloc(struct zram *zram, u64 disksize) if (!huge_class_size) huge_class_size = zs_huge_class_size(zram->mem_pool); + zram_meta_init_table_locks(zram, num_pages); return true; } diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index ca7a15bd4845..e64eb607eb45 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -69,6 +69,9 @@ struct zram_table_entry { unsigned long element; }; unsigned long flags; +#ifdef CONFIG_PREEMPT_RT + spinlock_t lock; +#endif #ifdef CONFIG_ZRAM_MEMORY_TRACKING ktime_t ac_time; #endif diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile index 9e62d3dd2e34..aab46cfd41cd 100644 --- a/drivers/clk/imx/Makefile +++ b/drivers/clk/imx/Makefile @@ -16,6 +16,7 @@ mxc-clk-objs += clk-gate-93.o mxc-clk-objs += clk-gate-exclusive.o mxc-clk-objs += clk-pfd.o mxc-clk-objs += clk-pfdv2.o +mxc-clk-objs += clk-pll.o mxc-clk-objs += clk-pllv1.o mxc-clk-objs += clk-pllv2.o mxc-clk-objs += clk-pllv3.o diff --git a/drivers/clk/imx/clk-frac-pll.c b/drivers/clk/imx/clk-frac-pll.c index c703056fae85..2085d8e91ca8 100644 --- a/drivers/clk/imx/clk-frac-pll.c +++ b/drivers/clk/imx/clk-frac-pll.c @@ -17,6 +17,7 @@ #include #include "clk.h" +#include "clk-pll.h" #define PLL_CFG0 0x0 #define PLL_CFG1 0x4 @@ -35,7 +36,11 @@ #define PLL_FRAC_ACK_TIMEOUT 500000 struct clk_frac_pll { + u32 orig_divff; + u32 orig_divfi; + u32 orig_pllcfg0; struct clk_hw hw; + struct clk_imx_pll imx_pll; void __iomem *base; }; @@ -202,6 +207,126 @@ static const struct clk_ops clk_frac_pll_ops = { .set_rate = clk_pll_set_rate, }; +/* This function fetches the original PLL parameters to use + * them later for ppb adjustment + */ +static void imx_frac_pll_init(struct clk_imx_pll *pll) +{ + struct clk_frac_pll *frac_pll; + u32 val; + + frac_pll = (struct clk_frac_pll *) container_of(pll, + struct clk_frac_pll, imx_pll); + + val = readl_relaxed(frac_pll->base + PLL_CFG1); + + frac_pll->orig_divff = (val >> 7) & PLL_FRAC_DIV_MASK; + frac_pll->orig_divfi = val & PLL_INT_DIV_MASK; + + frac_pll->orig_pllcfg0 = readl_relaxed(frac_pll->base + PLL_CFG0); +} + +/** + * imx_frac_pll_adjust - Adjust the Audio pll by ppb. + * + * This function adjust the audio pll by ppb (part per billion) and returns + * the exact number of ppb adjusted. + * The adjustment is done by only modifying the Fractional Divide part + * of the audio PLL. + * Since the pllout = parent_rate * 8 / 2 * (1 + DIVFI + DIVFF / 2^24) + * and the adjusted value is + * pllout_new = pllout * (1 + ppb/1e9) which equals: + * parent_rate * 8 / 2 * (1 + DIVFI + DIVFF_new / 2^24) + * The new divff is calculated as the following: + * DIVFF_new = ((1 + DIVFI) * ppb * 2^24 + DIVFF * 1e9 + DIVFF * ppb) / (1e9) + */ + +static int imx_frac_pll_adjust(struct clk_imx_pll *pll, int *ppb) +{ + u64 temp64; + u32 val; + s64 applied_ppb; + struct clk_frac_pll *frac_pll; + int rc = IMX_CLK_PLL_SUCCESS; + + int req_ppb = *ppb; + + frac_pll = (struct clk_frac_pll *) container_of(pll, + struct clk_frac_pll, imx_pll); + + /*Calcultate the new PLL Numerator*/ + temp64 = ((u64) frac_pll->orig_divfi + 1) * PLL_FRAC_DENOM * req_ppb + + (u64) frac_pll->orig_divff * 1000000000 + + (u64) frac_pll->orig_divff * req_ppb; + + do_div(temp64, 1000000000); + + if (temp64 >= PLL_FRAC_DENOM) { + rc = -IMX_CLK_PLL_PREC_ERR; + goto exit; + } + + /* clear the NEW_DIV_VAL */ + val = frac_pll->orig_pllcfg0; + val &= ~PLL_NEWDIV_VAL; + writel_relaxed(val, frac_pll->base + PLL_CFG0); + + /* Write the PLL control settings with the new DIVFF + * NOTE: This sets the reserved bit (bit 31) to zero + */ + + val = 0; + val |= (((u32)temp64 << 7) | frac_pll->orig_divfi); + writel_relaxed(val, frac_pll->base + PLL_CFG1); + + /* Set the NEW_DIV_VAL to reload the DIVFI and DIVFF */ + val = frac_pll->orig_pllcfg0; + val |= PLL_NEWDIV_VAL; + writel_relaxed(val, frac_pll->base + PLL_CFG0); + + /*Calculate and return the actual applied ppb*/ + applied_ppb = div64_s64((s64) (temp64 - frac_pll->orig_divff) * 1000000000, + frac_pll->orig_divff + ((s64) frac_pll->orig_divfi + 1) * PLL_FRAC_DENOM); + + *ppb = (int) applied_ppb; + + exit: + return rc; +} + +static unsigned long imx_frac_pll_get_rate(struct clk_imx_pll *pll, + unsigned long parent_rate) +{ + struct clk_frac_pll *frac_pll; + + frac_pll = (struct clk_frac_pll *) container_of(pll, + struct clk_frac_pll, imx_pll); + + return clk_pll_recalc_rate(&frac_pll->hw, parent_rate); +} + +static int imx_frac_pll_set_rate(struct clk_imx_pll *pll, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_frac_pll *frac_pll; + int rc = IMX_CLK_PLL_SUCCESS; + + frac_pll = (struct clk_frac_pll *) container_of(pll, + struct clk_frac_pll, imx_pll); + + if (clk_pll_set_rate(&frac_pll->hw, rate, parent_rate) < 0) + rc = -IMX_CLK_PLL_INVALID_PARAM; + + return rc; +} + +static const struct clk_imx_pll_ops imx_clk_frac_pll_ops = { + .set_rate = imx_frac_pll_set_rate, + .get_rate = imx_frac_pll_get_rate, + .adjust = imx_frac_pll_adjust, + .init = imx_frac_pll_init, +}; + struct clk_hw *imx_clk_hw_frac_pll(const char *name, const char *parent_name, void __iomem *base) @@ -232,6 +357,11 @@ struct clk_hw *imx_clk_hw_frac_pll(const char *name, return ERR_PTR(ret); } + pll->imx_pll.ops = &imx_clk_frac_pll_ops; + + if (imx_pll_register(&pll->imx_pll, name) < 0) + pr_warn("Failed to register %s into imx pll\n", name); + return hw; } EXPORT_SYMBOL_GPL(imx_clk_hw_frac_pll); diff --git a/drivers/clk/imx/clk-fracn-gppll.c b/drivers/clk/imx/clk-fracn-gppll.c index e8fa9170c68f..07a23f3fff19 100644 --- a/drivers/clk/imx/clk-fracn-gppll.c +++ b/drivers/clk/imx/clk-fracn-gppll.c @@ -13,6 +13,7 @@ #include #include "clk.h" +#include "clk-pll.h" #define PLL_CTRL 0x0 #define HW_CTRL_SEL BIT(16) @@ -63,10 +64,20 @@ .odiv = (_odiv), \ } +/* MFN : Numerator of the fractional part of divider (30-bits, signed) */ +#define MFN_MAX_VALUE ((s32)(GENMASK(29, 0) >> 1)) +#define MFN_MIN_VALUE ((s32)(-MFN_MAX_VALUE - 1)) + struct clk_fracn_gppll { struct clk_hw hw; void __iomem *base; const struct imx_fracn_gppll_rate_table *rate_table; + u32 orig_mfd; + u32 orig_mfi; + u32 orig_odiv; + u32 orig_rdiv; + s32 orig_mfn; + struct clk_imx_pll imx_pll; int rate_count; u32 flags; }; @@ -87,7 +98,7 @@ static const struct imx_fracn_gppll_rate_table fracn_tbl[] = { PLL_FRACN_GP(484000000U, 121, 0, 1, 0, 6), PLL_FRACN_GP(445333333U, 167, 0, 1, 0, 9), PLL_FRACN_GP(400000000U, 200, 0, 1, 0, 12), - PLL_FRACN_GP(393216000U, 163, 84, 100, 0, 10), + PLL_FRACN_GP(393216000U, 163, 1680000, 2000000, 0, 10), PLL_FRACN_GP(300000000U, 150, 0, 1, 0, 12) }; @@ -150,18 +161,19 @@ static long clk_fracn_gppll_round_rate(struct clk_hw *hw, unsigned long rate, return rate_table[pll->rate_count - 1].rate; } -static unsigned long clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +/* This function calculates the actual rate based on the configured PLL registers */ +static unsigned long __clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw); - const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table; u32 pll_numerator, pll_denominator, pll_div; - u32 mfi, mfn, mfd, rdiv, odiv; + u32 mfi, mfd, rdiv, odiv; + s32 mfn; u64 fvco = parent_rate; - long rate = 0; - int i; pll_numerator = readl_relaxed(pll->base + PLL_NUMERATOR); - mfn = FIELD_GET(PLL_MFN_MASK, pll_numerator); + + /* Numerator is 30-bits signed value */ + mfn = sign_extend32(FIELD_GET(PLL_MFN_MASK, pll_numerator), 29); pll_denominator = readl_relaxed(pll->base + PLL_DENOMINATOR); mfd = FIELD_GET(PLL_MFD_MASK, pll_denominator); @@ -172,22 +184,6 @@ static unsigned long clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned lon rdiv = FIELD_GET(PLL_RDIV_MASK, pll_div); odiv = FIELD_GET(PLL_ODIV_MASK, pll_div); - /* - * Sometimes, the recalculated rate has deviation due to - * the frac part. So find the accurate pll rate from the table - * first, if no match rate in the table, use the rate calculated - * from the equation below. - */ - for (i = 0; i < pll->rate_count; i++) { - if (rate_table[i].mfn == mfn && rate_table[i].mfi == mfi && - rate_table[i].mfd == mfd && rate_table[i].rdiv == rdiv && - rate_table[i].odiv == odiv) - rate = rate_table[i].rate; - } - - if (rate) - return (unsigned long)rate; - if (!rdiv) rdiv = rdiv + 1; @@ -208,13 +204,50 @@ static unsigned long clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned lon do_div(fvco, rdiv * odiv); } else { /* Fvco = (Fref / rdiv) * (MFI + MFN / MFD) */ - fvco = fvco * mfi * mfd + fvco * mfn; + fvco *= (mfi * mfd + mfn); do_div(fvco, mfd * rdiv * odiv); } return (unsigned long)fvco; } +static unsigned long clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw); + const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table; + u32 pll_numerator, pll_denominator, pll_div; + u32 mfi, mfn, mfd, rdiv, odiv; + long rate = 0; + int i; + + pll_numerator = readl_relaxed(pll->base + PLL_NUMERATOR); + mfn = FIELD_GET(PLL_MFN_MASK, pll_numerator); + + pll_denominator = readl_relaxed(pll->base + PLL_DENOMINATOR); + mfd = FIELD_GET(PLL_MFD_MASK, pll_denominator); + + pll_div = readl_relaxed(pll->base + PLL_DIV); + mfi = FIELD_GET(PLL_MFI_MASK, pll_div); + + rdiv = FIELD_GET(PLL_RDIV_MASK, pll_div); + odiv = FIELD_GET(PLL_ODIV_MASK, pll_div); + + /* + * Sometimes, the recalculated rate has deviation due to + * the frac part. So find the accurate pll rate from the table + * first, if no match rate in the table, use the rate calculated + * from the equation below. + */ + for (i = 0; i < pll->rate_count; i++) { + if (rate_table[i].mfn == mfn && rate_table[i].mfi == mfi && + rate_table[i].mfd == mfd && rate_table[i].rdiv == rdiv && + rate_table[i].odiv == odiv) + rate = rate_table[i].rate; + } + + return rate ? (unsigned long) rate : __clk_fracn_gppll_recalc_rate(hw, parent_rate); +} + static int clk_fracn_gppll_wait_lock(struct clk_fracn_gppll *pll) { u32 val; @@ -232,6 +265,11 @@ static int clk_fracn_gppll_set_rate(struct clk_hw *hw, unsigned long drate, int ret; rate = imx_get_pll_settings(pll, drate); + if (!rate) { + pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, + drate, clk_hw_get_name(hw)); + return -EINVAL; + } /* Hardware control select disable. PLL is control by register */ tmp = readl_relaxed(pll->base + PLL_CTRL); @@ -350,6 +388,122 @@ static const struct clk_ops clk_fracn_gppll_ops = { .set_rate = clk_fracn_gppll_set_rate, }; +/* This function fetches the original PLL parameters to use + * them later for ppb adjustment + */ +static void imx_fracn_gppll_init(struct clk_imx_pll *pll) +{ + struct clk_fracn_gppll *fracn_pll; + u32 pll_numerator, pll_denominator, pll_div, mfn; + + fracn_pll = (struct clk_fracn_gppll *) container_of(pll, + struct clk_fracn_gppll, imx_pll); + + pll_numerator = readl_relaxed(fracn_pll->base + PLL_NUMERATOR); + mfn = FIELD_GET(PLL_MFN_MASK, pll_numerator); + + /* Numerator is 30-bits signed value */ + fracn_pll->orig_mfn = sign_extend32(mfn, 29); + + pll_denominator = readl_relaxed(fracn_pll->base + PLL_DENOMINATOR); + fracn_pll->orig_mfd = FIELD_GET(PLL_MFD_MASK, pll_denominator); + + pll_div = readl_relaxed(fracn_pll->base + PLL_DIV); + fracn_pll->orig_mfi = FIELD_GET(PLL_MFI_MASK, pll_div); + + fracn_pll->orig_rdiv = FIELD_GET(PLL_RDIV_MASK, pll_div); + fracn_pll->orig_odiv = FIELD_GET(PLL_ODIV_MASK, pll_div); +} + +/** + * imx_fracn_gppll_adjust - Adjust the Audio pll by ppb. + * + * This function adjusts the audio pll by ppb (part per billion) and returns + * the exact number of ppb adjusted. + * The adjustment is done by only modifying the numerator of the fractional part + * of the audio PLL. + * Since the pllout = (ref * (mfi + mfn/mfd)) / (rdiv * odiv) + * and the adjusted value is + * pllout_new = pllout * (1 + ppb/1e9) which equals: + * (ref * (mfi + mfn_new/mfd)) / (rdiv * odiv) + * The new numerator (mfn_new) is calculated as following: + * mfn_new = (1e9 * mfn + (mfi * mfd + mfn) * ppb) / (1e9) + */ + +static int imx_fracn_gppll_adjust(struct clk_imx_pll *pll, int *ppb) +{ + s64 temp64; + s64 applied_ppb; + struct clk_fracn_gppll *fracn_pll; + int rc = IMX_CLK_PLL_SUCCESS; + + int req_ppb = *ppb; + + fracn_pll = (struct clk_fracn_gppll *) container_of(pll, + struct clk_fracn_gppll, imx_pll); + + /* Calculate the new numerator value */ + temp64 = ((s64) fracn_pll->orig_mfn * 1000000000) + + ((s64) fracn_pll->orig_mfi * fracn_pll->orig_mfd + (s64) fracn_pll->orig_mfn) * req_ppb; + + temp64 = div_s64(temp64, 1000000000); + + /* Sanity check on the new numerator value: + * - the value is inside the 30-bits signed values range + * - mfn/mfd should be in the range [-2, 2] + */ + if (temp64 > MFN_MAX_VALUE || temp64 < MFN_MIN_VALUE || (abs(temp64) > (2 * fracn_pll->orig_mfd))) { + rc = -IMX_CLK_PLL_PREC_ERR; + goto exit; + } + + /* Write the PLL control settings with the new numerator */ + + writel_relaxed(FIELD_PREP(PLL_MFN_MASK, temp64), fracn_pll->base + PLL_NUMERATOR); + + /* Calculate and return the actual applied ppb */ + applied_ppb = div64_s64((s64) (temp64 - fracn_pll->orig_mfn) * 1000000000, + fracn_pll->orig_mfn + fracn_pll->orig_mfd * (s64) fracn_pll->orig_mfi); + + *ppb = (int) applied_ppb; + + exit: + return rc; +} + +static unsigned long imx_fracn_gppll_get_rate(struct clk_imx_pll *pll, + unsigned long parent_rate) +{ + struct clk_fracn_gppll *fracn_pll; + + fracn_pll = (struct clk_fracn_gppll *) container_of(pll, + struct clk_fracn_gppll, imx_pll); + + return __clk_fracn_gppll_recalc_rate(&fracn_pll->hw, parent_rate); +} + +static int imx_fracn_gppll_set_rate(struct clk_imx_pll *pll, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_fracn_gppll *fracn_pll; + int rc = IMX_CLK_PLL_SUCCESS; + + fracn_pll = (struct clk_fracn_gppll *) container_of(pll, + struct clk_fracn_gppll, imx_pll); + + if (clk_fracn_gppll_set_rate(&fracn_pll->hw, rate, parent_rate) < 0) + rc = -IMX_CLK_PLL_INVALID_PARAM; + + return rc; +} + +static const struct clk_imx_pll_ops imx_clk_fracn_gppll_ops = { + .set_rate = imx_fracn_gppll_set_rate, + .get_rate = imx_fracn_gppll_get_rate, + .adjust = imx_fracn_gppll_adjust, + .init = imx_fracn_gppll_init, +}; + static struct clk_hw *_imx_clk_fracn_gppll(const char *name, const char *parent_name, void __iomem *base, const struct imx_fracn_gppll_clk *pll_clk, @@ -385,6 +539,13 @@ static struct clk_hw *_imx_clk_fracn_gppll(const char *name, const char *parent_ return ERR_PTR(ret); } + if (pll_flags & CLK_FRACN_GPPLL_FRACN) { + pll->imx_pll.ops = &imx_clk_fracn_gppll_ops; + + if (imx_pll_register(&pll->imx_pll, name) < 0) + pr_warn("%s: failed to register %s into imx pll\n", __func__, name); + } + return hw; } diff --git a/drivers/clk/imx/clk-imx8-acm.c b/drivers/clk/imx/clk-imx8-acm.c index 1c95ae905eec..e8ad426cd844 100644 --- a/drivers/clk/imx/clk-imx8-acm.c +++ b/drivers/clk/imx/clk-imx8-acm.c @@ -235,11 +235,20 @@ static const struct clk_parent_data imx8dxl_mclk_sels[] = { { .fw_name = "acm_aud_clk1_sel" }, }; +static const struct clk_parent_data imx8dxl_gpt_mux_clk_sels[] = { + { .fw_name = "aud_pll_div_clk0_lpcg_clk", .name = "aud_pll_div_clk0_lpcg_clk" }, + { .fw_name = "aud_pll_div_clk1_lpcg_clk", .name = "aud_pll_div_clk1_lpcg_clk" }, + { .fw_name = "acm_aud_clk0_sel", .name = "acm_aud_clk0_sel" }, + { .fw_name = "acm_aud_clk1_sel", .name = "acm_aud_clk1_sel" }, + { .index = -1 }, +}; + static struct clk_imx8_acm_sel imx8dxl_sels[] = { { "acm_aud_clk0_sel", IMX_ADMA_ACM_AUD_CLK0_SEL, imx8dxl_aud_clk_sels, ARRAY_SIZE(imx8dxl_aud_clk_sels), 0x000000, 0, 5 }, { "acm_aud_clk1_sel", IMX_ADMA_ACM_AUD_CLK1_SEL, imx8dxl_aud_clk_sels, ARRAY_SIZE(imx8dxl_aud_clk_sels), 0x010000, 0, 5 }, { "acm_mclkout0_sel", IMX_ADMA_ACM_MCLKOUT0_SEL, imx8dxl_mclk_out_sels, ARRAY_SIZE(imx8dxl_mclk_out_sels), 0x020000, 0, 3 }, { "acm_mclkout1_sel", IMX_ADMA_ACM_MCLKOUT1_SEL, imx8dxl_mclk_out_sels, ARRAY_SIZE(imx8dxl_mclk_out_sels), 0x030000, 0, 3 }, + { "acm_gpt0_mux_clk_sel", IMX_ADMA_ACM_GPT0_MUX_CLK_SEL, imx8dxl_gpt_mux_clk_sels, ARRAY_SIZE(imx8dxl_gpt_mux_clk_sels), 0x080000, 0, 3 }, { "acm_sai0_mclk_sel", IMX_ADMA_ACM_SAI0_MCLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x0E0000, 0, 2 }, { "acm_sai1_mclk_sel", IMX_ADMA_ACM_SAI1_MCLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x0F0000, 0, 2 }, { "acm_sai2_mclk_sel", IMX_ADMA_ACM_SAI2_MCLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x100000, 0, 2 }, @@ -326,6 +335,41 @@ static int clk_imx_acm_detach_pm_domains(struct device *dev, return 0; } +/* The number of cells for the GPT capture device tree attribute */ +#define OF_GPT_CAPTURE_CELLS_NB 2 + +static void clk_imx_acm_gpt_input_mux(struct device_node *np, struct imx8_acm_priv *priv) +{ + u32 len, reg_offset, event_sel_control, num_capture_select; + int i, offset; + + if (!of_get_property(np, "gpt-capture-select", &len)) + return; + + num_capture_select = len / (sizeof(u32) * OF_GPT_CAPTURE_CELLS_NB); + + for (i = 0; i < num_capture_select; i++) { + offset = i * OF_GPT_CAPTURE_CELLS_NB; + if (of_property_read_u32_index(np, + "gpt-capture-select", + offset, ®_offset)) { + pr_err("failed to read gpt register offset cell at offset %d\n", + offset); + return; + } + + if (of_property_read_u32_index(np, + "gpt-capture-select", + offset + 1, &event_sel_control)) { + pr_err("failed to read gpt event select control cell at offset %d\n", + offset + 1); + return; + } + + writel_relaxed(event_sel_control, priv->reg + reg_offset); + } +} + static int imx8_acm_clk_probe(struct platform_device *pdev) { struct clk_hw_onecell_data *clk_hw_data; @@ -386,6 +430,8 @@ static int imx8_acm_clk_probe(struct platform_device *pdev) goto err_clk_register; } + clk_imx_acm_gpt_input_mux(dev->of_node, priv); + pm_runtime_put_sync(&pdev->dev); return 0; diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c index 10a4bfb825e3..8432477370e4 100644 --- a/drivers/clk/imx/clk-imx8mq.c +++ b/drivers/clk/imx/clk-imx8mq.c @@ -217,7 +217,7 @@ static const char * const imx8mq_pwm4_sels[] = {"osc_25m", "sys2_pll_100m", "sys "sys3_pll_out", "clk_ext2", "sys1_pll_80m", "video_pll1_out", }; static const char * const imx8mq_gpt1_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_400m", "sys1_pll_40m", - "sys1_pll_80m", "audio_pll1_out", "clk_ext1", }; + "video_pll1_out", "sys1_pll_80m", "audio_pll1_out", "clk_ext1", }; static const char * const imx8mq_wdog_sels[] = {"osc_25m", "sys1_pll_133m", "sys1_pll_160m", "vpu_pll_out", "sys2_pll_125m", "sys3_pll_out", "sys1_pll_80m", "sys2_pll_166m", }; diff --git a/drivers/clk/imx/clk-pll.c b/drivers/clk/imx/clk-pll.c new file mode 100644 index 000000000000..b77375a11add --- /dev/null +++ b/drivers/clk/imx/clk-pll.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 NXP + * + */ + +#include +#include +#include +#include +#include +#include "clk-pll.h" + + +static struct clk_imx_pll *clk_imx_pll_array[MAX_IMX_PLL_NUM] = { NULL }; + +struct clk_imx_pll *clk_imx_pll_get_by_name(const char *name) +{ + int i; + struct clk_imx_pll *pll = NULL; + + for (i = 0; i < MAX_IMX_PLL_NUM; i++) { + if (clk_imx_pll_array[i] && clk_imx_pll_array[i]->ops + && !strncmp(clk_imx_pll_array[i]->pll_name, name, MAX_PLL_NAME_SIZE)) { + + pll = clk_imx_pll_array[i]; + + /* Init all PLL original parameters*/ + if (pll && pll->ops && pll->ops->init) + pll->ops->init(pll); + + break; + } + } + + return pll; +} +EXPORT_SYMBOL(clk_imx_pll_get_by_name); + +int clk_imx_pll_adjust(struct clk_imx_pll *pll, int *ppb) +{ + if (pll && pll->ops && pll->ops->adjust) + return pll->ops->adjust(pll, ppb); + + return -IMX_CLK_PLL_INVALID_PARAM; +} +EXPORT_SYMBOL(clk_imx_pll_adjust); + +unsigned long clk_imx_pll_get_rate(struct clk_imx_pll *pll, + unsigned long parent_rate) +{ + if (pll && pll->ops && pll->ops->get_rate) + return pll->ops->get_rate(pll, parent_rate); + + return 0; +} +EXPORT_SYMBOL(clk_imx_pll_get_rate); + +int clk_imx_pll_set_rate(struct clk_imx_pll *pll, unsigned long rate, + unsigned long parent_rate) +{ + if (pll && pll->ops && pll->ops->set_rate) + return pll->ops->set_rate(pll, rate, parent_rate); + + return -IMX_CLK_PLL_INVALID_PARAM; +} +EXPORT_SYMBOL(clk_imx_pll_set_rate); + +int imx_pll_register(struct clk_imx_pll *pll, const char *name) +{ + int i; + + strncpy(pll->pll_name, name, MAX_PLL_NAME_SIZE); + + for (i = 0; i < MAX_IMX_PLL_NUM; i++) { + if (!clk_imx_pll_array[i]) { + clk_imx_pll_array[i] = pll; + return 0; + } + } + + return -EINVAL; +} diff --git a/drivers/clk/imx/clk-pll.h b/drivers/clk/imx/clk-pll.h new file mode 100644 index 000000000000..f12167842059 --- /dev/null +++ b/drivers/clk/imx/clk-pll.h @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 NXP + * + */ + +#ifndef __IMX_CLK_PLL_H +#define __IMX_CLK_PLL_H + +#include + +#define MAX_IMX_PLL_NUM 16 +#define MAX_PLL_NAME_SIZE 64 + +struct clk_imx_pll; + +struct clk_imx_pll_ops { + int (*set_rate)(struct clk_imx_pll *pll, unsigned long rate, + unsigned long parent_rate); + unsigned long (*get_rate)(struct clk_imx_pll *pll, + unsigned long parent_rate); + int (*adjust)(struct clk_imx_pll *pll, int *ppb); + void (*init)(struct clk_imx_pll *pll); +}; + +struct clk_imx_pll { + char pll_name[MAX_PLL_NAME_SIZE]; + const struct clk_imx_pll_ops *ops; +}; + +int imx_pll_register(struct clk_imx_pll *pll, const char *name); + +#endif /*__IMX_CLK_PLL_H*/ diff --git a/drivers/clk/imx/clk-pll14xx.c b/drivers/clk/imx/clk-pll14xx.c index 6c17786ecb9f..e68030c4a24f 100644 --- a/drivers/clk/imx/clk-pll14xx.c +++ b/drivers/clk/imx/clk-pll14xx.c @@ -16,6 +16,7 @@ #include #include "clk.h" +#include "clk-pll.h" #define GNRL_CTL 0x0 #define DIV_CTL0 0x4 @@ -37,8 +38,13 @@ struct clk_pll14xx { struct clk_hw hw; void __iomem *base; + u32 orig_mdiv; + u32 orig_pdiv; + u32 orig_sdiv; + short int orig_kdiv; enum imx_pll14xx_type type; const struct imx_pll14xx_rate_table *rate_table; + struct clk_imx_pll imx_pll; int rate_count; }; @@ -536,6 +542,113 @@ static const struct clk_ops clk_pll1443x_ops = { .set_rate = clk_pll1443x_set_rate, }; +/* This function fetches the original PLL parameters to use + * them later for ppb adjustment + */ +static void imx_pll1443x_init(struct clk_imx_pll *pll) +{ + struct clk_pll14xx *pll14xx; + u32 pll_div_ctl0, pll_div_ctl1; + + pll14xx = (struct clk_pll14xx *) container_of(pll, + struct clk_pll14xx, imx_pll); + + pll_div_ctl0 = readl_relaxed(pll14xx->base + DIV_CTL0); + pll_div_ctl1 = readl_relaxed(pll14xx->base + DIV_CTL1); + + pll14xx->orig_kdiv = FIELD_GET(KDIV_MASK, pll_div_ctl1); + pll14xx->orig_mdiv = FIELD_GET(MDIV_MASK, pll_div_ctl0); + pll14xx->orig_pdiv = FIELD_GET(PDIV_MASK, pll_div_ctl0); + pll14xx->orig_sdiv = FIELD_GET(SDIV_MASK, pll_div_ctl0); +} + +/** + * imx_pll1443x_adjust - Adjust the Audio pll by ppb. + * + * This function adjust the audio pll by ppb (part per billion) and returns + * the exact number of ppb adjusted. + * The adjustment is done by only modifying the delta-sigma modulator(DSM) part + * of the audio PLL. + * Since the pllout = (parent_rate * (m + k/65536)) / (p * 2^s) + * and the adjusted value is + * pllout_new = pllout * (1 + ppb/1e9) which equals: + * (parent_rate * (m + k_new/65536)) / (p * 2^s) + * The new DSM (k_new) is calculated as the following: + * k_new = (1e9 * k + (m * 65536 + k) * ppb) / (1e9) + */ + +static int imx_pll1443x_adjust(struct clk_imx_pll *pll, int *ppb) +{ + s64 temp64; + s64 applied_ppb; + struct clk_pll14xx *pll14xx; + int rc = IMX_CLK_PLL_SUCCESS; + + int req_ppb = *ppb; + + pll14xx = (struct clk_pll14xx *) container_of(pll, + struct clk_pll14xx, imx_pll); + + /* Calcultate the new DSM value */ + temp64 = ((s64) pll14xx->orig_kdiv * 1000000000) + + ((s64) pll14xx->orig_mdiv * 65536 + (s64) pll14xx->orig_kdiv) * req_ppb; + + temp64 = div_s64(temp64, 1000000000); + + if (temp64 > KDIV_MAX || temp64 < KDIV_MIN) { + rc = -IMX_CLK_PLL_PREC_ERR; + goto exit; + } + + /* Write the PLL control settings with the new DSM + */ + + writel_relaxed(FIELD_PREP(KDIV_MASK, temp64), pll14xx->base + DIV_CTL1); + + /* Calculate and return the actual applied ppb */ + applied_ppb = div64_s64((s64) (temp64 - pll14xx->orig_kdiv) * 1000000000, + pll14xx->orig_kdiv + 65536 * (s64) pll14xx->orig_mdiv); + + *ppb = (int) applied_ppb; + + exit: + return rc; +} + +static unsigned long imx_pll1443x_get_rate(struct clk_imx_pll *pll, + unsigned long parent_rate) +{ + struct clk_pll14xx *pll14xx; + + pll14xx = (struct clk_pll14xx *) container_of(pll, + struct clk_pll14xx, imx_pll); + + return clk_pll14xx_recalc_rate(&pll14xx->hw, parent_rate); +} + +static int imx_pll1443x_set_rate(struct clk_imx_pll *pll, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_pll14xx *pll14xx; + int rc = IMX_CLK_PLL_SUCCESS; + + pll14xx = (struct clk_pll14xx *) container_of(pll, + struct clk_pll14xx, imx_pll); + + if (clk_pll1443x_set_rate(&pll14xx->hw, rate, parent_rate) < 0) + rc = -IMX_CLK_PLL_INVALID_PARAM; + + return rc; +} + +static const struct clk_imx_pll_ops imx_clk_pll1443x_ops = { + .set_rate = imx_pll1443x_set_rate, + .get_rate = imx_pll1443x_get_rate, + .adjust = imx_pll1443x_adjust, + .init = imx_pll1443x_init, +}; + + struct clk_hw *imx_dev_clk_hw_pll14xx(struct device *dev, const char *name, const char *parent_name, void __iomem *base, const struct imx_pll14xx_clk *pll_clk) @@ -590,6 +703,13 @@ struct clk_hw *imx_dev_clk_hw_pll14xx(struct device *dev, const char *name, return ERR_PTR(ret); } + if (pll_clk->type == PLL_1443X) { + pll->imx_pll.ops = &imx_clk_pll1443x_ops; + + if (imx_pll_register(&pll->imx_pll, name) < 0) + pr_warn("Failed to register %s into imx pll\n", name); + } + return hw; } EXPORT_SYMBOL_GPL(imx_dev_clk_hw_pll14xx); diff --git a/drivers/clk/imx/clk-pllv3.c b/drivers/clk/imx/clk-pllv3.c index 6f6d4589f7eb..b74b48f3fc90 100644 --- a/drivers/clk/imx/clk-pllv3.c +++ b/drivers/clk/imx/clk-pllv3.c @@ -15,6 +15,7 @@ #include #include #include "clk.h" +#include "clk-pll.h" #define PLL_NUM_OFFSET 0x10 #define PLL_DENOM_OFFSET 0x20 @@ -48,6 +49,10 @@ */ struct clk_pllv3 { struct clk_hw hw; + struct clk_imx_pll imx_pll; + u32 orig_num; + u32 orig_denom; + u32 orig_div; void __iomem *base; u32 power_bit; bool powerup_set; @@ -441,6 +446,106 @@ static const struct clk_ops clk_pllv3_vf610_ops = { .set_rate = clk_pllv3_vf610_set_rate, }; +/** + * imx_pllv3_av_adjust - Adjust the Audio pll by ppb. + * + * This function adjust the audio pll by ppb (part per billion) and returns + * the exact number of ppb adjusted. + * The adjustment is done by only modifying the Audio PLL Numerator. + * Since the pllout = parent * (div + num/denom) and the adjusted Value is + * pllout_new = PLL_out * (1 + ppb/1e9) + * Also pllout_new = parent * (div + new_num/denom) + * The new numerator is calculated as the following: + * new_num = (div * ppb * denom + num * 1e9 + num * ppb) / (1e9) + */ + +static int imx_pllv3_av_adjust(struct clk_imx_pll *pll, int *ppb) +{ + struct clk_pllv3 *av_pll; + u64 temp64; + s32 applied_ppb; + int rc = IMX_CLK_PLL_SUCCESS; + + int req_ppb = *ppb; + + av_pll = (struct clk_pllv3 *) container_of(pll, struct clk_pllv3, + imx_pll); + + /*Calcultate the new PLL Numerator*/ + temp64 = (u64) av_pll->orig_denom * av_pll->orig_div * req_ppb + + (u64) av_pll->orig_num * 1000000000 + + (u64) av_pll->orig_num * req_ppb; + + do_div(temp64, 1000000000); + + if (temp64 >= av_pll->orig_denom) { + rc = -IMX_CLK_PLL_PREC_ERR; + goto exit; + } + + /*Write the new PLL num*/ + writel_relaxed((u32) temp64, av_pll->base + av_pll->num_offset); + + /*Calculate and return the actual applied ppb*/ + applied_ppb = div64_s64((s64) (temp64 - av_pll->orig_num) * 1000000000, + av_pll->orig_num + + (s64) av_pll->orig_denom * av_pll->orig_div); + + *ppb = (int) applied_ppb; + +exit: + return rc; + +} + +/* This function fetches the original PLL parameters to use + * them later for ppb adjustment + */ +static void imx_pllv3_av_init(struct clk_imx_pll *pll) +{ + struct clk_pllv3 *av_pll; + + av_pll = (struct clk_pllv3 *) container_of(pll, struct clk_pllv3, + imx_pll); + + av_pll->orig_num = readl_relaxed(av_pll->base + av_pll->num_offset); + av_pll->orig_denom = readl_relaxed(av_pll->base + av_pll->denom_offset); + av_pll->orig_div = readl_relaxed(av_pll->base) & av_pll->div_mask; +} + +static unsigned long imx_pllv3_av_get_rate(struct clk_imx_pll *pll, + unsigned long parent_rate) +{ + struct clk_pllv3 *av_pll; + + av_pll = (struct clk_pllv3 *) container_of(pll, struct clk_pllv3, + imx_pll); + + return clk_pllv3_av_recalc_rate(&av_pll->hw, parent_rate); +} + +static int imx_pllv3_av_set_rate(struct clk_imx_pll *pll, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_pllv3 *av_pll; + int rc = IMX_CLK_PLL_SUCCESS; + + av_pll = (struct clk_pllv3 *) container_of(pll, struct clk_pllv3, + imx_pll); + + if (clk_pllv3_av_set_rate(&av_pll->hw, rate, parent_rate) < 0) + rc = -IMX_CLK_PLL_INVALID_PARAM; + + return rc; +} + +static const struct clk_imx_pll_ops imx_clk_pllv3_av_ops = { + .set_rate = imx_pllv3_av_set_rate, + .get_rate = imx_pllv3_av_get_rate, + .adjust = imx_pllv3_av_adjust, + .init = imx_pllv3_av_init, +}; + static unsigned long clk_pllv3_enet_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -496,6 +601,7 @@ struct clk_hw *imx_clk_hw_pllv3(enum imx_pllv3_type type, const char *name, fallthrough; case IMX_PLLV3_AV: ops = &clk_pllv3_av_ops; + pll->imx_pll.ops = &imx_clk_pllv3_av_ops; break; case IMX_PLLV3_ENET_IMX7: pll->power_bit = IMX7_ENET_PLL_POWER; @@ -533,6 +639,9 @@ struct clk_hw *imx_clk_hw_pllv3(enum imx_pllv3_type type, const char *name, return ERR_PTR(ret); } + if (imx_pll_register(&pll->imx_pll, name) < 0) + pr_warn("Failed to register %s into imx pll\n", name); + return hw; } EXPORT_SYMBOL_GPL(imx_clk_hw_pllv3); diff --git a/drivers/clk/imx/clk-scu.c b/drivers/clk/imx/clk-scu.c index c790e82c24c8..d0765cf9c3bc 100644 --- a/drivers/clk/imx/clk-scu.c +++ b/drivers/clk/imx/clk-scu.c @@ -18,6 +18,7 @@ #include #include "clk-scu.h" +#include "clk-pll.h" #define IMX_SIP_CPUFREQ 0xC2000001 #define IMX_SIP_SET_CPUFREQ 0x00 @@ -56,6 +57,8 @@ struct clk_scu { u8 parent_index; bool is_enabled; u32 rate; + unsigned long original_rate; + struct clk_imx_pll imx_pll; }; /* @@ -466,6 +469,82 @@ static const struct clk_ops clk_scu_pi_ops = { .set_rate = clk_scu_set_rate, }; +static void imx_scu_init(struct clk_imx_pll *pll) +{ + struct clk_scu *clk; + + clk = (struct clk_scu *) container_of(pll, struct clk_scu, imx_pll); + + clk->original_rate = clk_scu_recalc_rate(&clk->hw, 0); + + pr_info("SCU clock %p for PLL API init with original rate %lu\n", + clk, clk->original_rate); + + return; +} + +/* SCU is stating possible variation of +/-250Khz which gives around 300 ppm @ 786MHz + * Set a maximum adjustement at 250 ppm + */ +#define IMX_SCU_MAX_PPB_ADJUSTEMENT 250000 + +static int imx_scu_adjust(struct clk_imx_pll *pll, int *ppb) +{ + struct clk_scu * clk = (struct clk_scu *) container_of(pll, + struct clk_scu, imx_pll); + unsigned long new_rate; + int delta; + + if (!clk->original_rate) { + pr_warn_once("failed to get original rate for clock\n"); + return -IMX_CLK_PLL_LOCK_ERR; + } + + if (abs(*ppb) > IMX_SCU_MAX_PPB_ADJUSTEMENT) { + return -IMX_CLK_PLL_PREC_ERR; + } + + if (*ppb < 0) + delta = -1; + else + delta = 1; + + delta *= (int)div64_u64((u64)clk->original_rate * (u64)abs(*ppb), + 1000000000); + + new_rate = clk->original_rate + delta; + + if (clk_scu_set_rate(&clk->hw, new_rate, 0) < 0) + return -IMX_CLK_PLL_INVALID_PARAM; + + return IMX_CLK_PLL_SUCCESS; +} + +static int imx_scu_set_rate(struct clk_imx_pll *pll, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_scu * clk = (struct clk_scu *) container_of(pll, + struct clk_scu, imx_pll); + + return clk_scu_set_rate(&clk->hw, rate, 0); +} + +static unsigned long imx_scu_get_rate(struct clk_imx_pll *pll, + unsigned long parent_rate) +{ + struct clk_scu * clk = (struct clk_scu *) container_of(pll, + struct clk_scu, imx_pll); + + return clk_scu_recalc_rate(&clk->hw, 0); +} + +static const struct clk_imx_pll_ops imx_clk_scu_ops = { + .set_rate = imx_scu_set_rate, + .get_rate = imx_scu_get_rate, + .adjust = imx_scu_adjust, + .init = imx_scu_init, +}; + struct clk_hw *__imx_clk_scu(struct device *dev, const char *name, const char * const *parents, int num_parents, u32 rsrc_id, u8 clk_type) @@ -514,6 +593,13 @@ struct clk_hw *__imx_clk_scu(struct device *dev, const char *name, if (dev) dev_set_drvdata(dev, clk); + if (rsrc_id == IMX_SC_R_AUDIO_PLL_0 && clk_type == IMX_SC_PM_CLK_PLL) { + clk->imx_pll.ops = &imx_clk_scu_ops; + + if (imx_pll_register(&clk->imx_pll, name) < 0) + pr_warn("%s: failed to register %s into imx pll\n", __func__, name); + } + return hw; } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c b/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c index 172aa10a8800..4ae4720535a5 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c @@ -60,11 +60,9 @@ static DEFINE_PER_CPU(int, fpu_recursion_depth); */ inline void dc_assert_fp_enabled(void) { - int *pcpu, depth = 0; + int depth; - pcpu = get_cpu_ptr(&fpu_recursion_depth); - depth = *pcpu; - put_cpu_ptr(&fpu_recursion_depth); + depth = __this_cpu_read(fpu_recursion_depth); ASSERT(depth >= 1); } @@ -84,33 +82,28 @@ inline void dc_assert_fp_enabled(void) */ void dc_fpu_begin(const char *function_name, const int line) { - int *pcpu; + int depth; - pcpu = get_cpu_ptr(&fpu_recursion_depth); - *pcpu += 1; + WARN_ON_ONCE(!in_task()); + preempt_disable(); + depth = __this_cpu_inc_return(fpu_recursion_depth); - if (*pcpu == 1) { + if (depth == 1) { #if defined(CONFIG_X86) || defined(CONFIG_LOONGARCH) - migrate_disable(); kernel_fpu_begin(); #elif defined(CONFIG_PPC64) - if (cpu_has_feature(CPU_FTR_VSX_COMP)) { - preempt_disable(); + if (cpu_has_feature(CPU_FTR_VSX_COMP)) enable_kernel_vsx(); - } else if (cpu_has_feature(CPU_FTR_ALTIVEC_COMP)) { - preempt_disable(); + else if (cpu_has_feature(CPU_FTR_ALTIVEC_COMP)) enable_kernel_altivec(); - } else if (!cpu_has_feature(CPU_FTR_FPU_UNAVAILABLE)) { - preempt_disable(); + else if (!cpu_has_feature(CPU_FTR_FPU_UNAVAILABLE)) enable_kernel_fp(); - } #elif defined(CONFIG_ARM64) kernel_neon_begin(); #endif } - TRACE_DCN_FPU(true, function_name, line, *pcpu); - put_cpu_ptr(&fpu_recursion_depth); + TRACE_DCN_FPU(true, function_name, line, depth); } /** @@ -125,30 +118,26 @@ void dc_fpu_begin(const char *function_name, const int line) */ void dc_fpu_end(const char *function_name, const int line) { - int *pcpu; + int depth; - pcpu = get_cpu_ptr(&fpu_recursion_depth); - *pcpu -= 1; - if (*pcpu <= 0) { + depth = __this_cpu_dec_return(fpu_recursion_depth); + if (depth == 0) { #if defined(CONFIG_X86) || defined(CONFIG_LOONGARCH) kernel_fpu_end(); - migrate_enable(); #elif defined(CONFIG_PPC64) - if (cpu_has_feature(CPU_FTR_VSX_COMP)) { + if (cpu_has_feature(CPU_FTR_VSX_COMP)) disable_kernel_vsx(); - preempt_enable(); - } else if (cpu_has_feature(CPU_FTR_ALTIVEC_COMP)) { + else if (cpu_has_feature(CPU_FTR_ALTIVEC_COMP)) disable_kernel_altivec(); - preempt_enable(); - } else if (!cpu_has_feature(CPU_FTR_FPU_UNAVAILABLE)) { + else if (!cpu_has_feature(CPU_FTR_FPU_UNAVAILABLE)) disable_kernel_fp(); - preempt_enable(); - } #elif defined(CONFIG_ARM64) kernel_neon_end(); #endif + } else { + WARN_ON_ONCE(depth < 0); } - TRACE_DCN_FPU(false, function_name, line, *pcpu); - put_cpu_ptr(&fpu_recursion_depth); + TRACE_DCN_FPU(false, function_name, line, depth); + preempt_enable(); } diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c index d587f807dfd7..5036a3e60832 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c @@ -2141,9 +2141,17 @@ bool dcn20_validate_bandwidth(struct dc *dc, struct dc_state *context, bool fast_validate) { bool voltage_supported; + display_e2e_pipe_params_st *pipes; + + pipes = kcalloc(dc->res_pool->pipe_count, sizeof(display_e2e_pipe_params_st), GFP_KERNEL); + if (!pipes) + return false; + DC_FP_START(); - voltage_supported = dcn20_validate_bandwidth_fp(dc, context, fast_validate); + voltage_supported = dcn20_validate_bandwidth_fp(dc, context, fast_validate, pipes); DC_FP_END(); + + kfree(pipes); return voltage_supported; } diff --git a/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c b/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c index d1a25fe6c44f..5674c3450fc3 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c @@ -953,9 +953,17 @@ static bool dcn21_validate_bandwidth(struct dc *dc, struct dc_state *context, bool fast_validate) { bool voltage_supported; + display_e2e_pipe_params_st *pipes; + + pipes = kcalloc(dc->res_pool->pipe_count, sizeof(display_e2e_pipe_params_st), GFP_KERNEL); + if (!pipes) + return false; + DC_FP_START(); - voltage_supported = dcn21_validate_bandwidth_fp(dc, context, fast_validate); + voltage_supported = dcn21_validate_bandwidth_fp(dc, context, fast_validate, pipes); DC_FP_END(); + + kfree(pipes); return voltage_supported; } diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c index 8a5a038fd855..68970d6cf031 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c @@ -2018,7 +2018,7 @@ void dcn20_patch_bounding_box(struct dc *dc, struct _vcs_dpi_soc_bounding_box_st } static bool dcn20_validate_bandwidth_internal(struct dc *dc, struct dc_state *context, - bool fast_validate) + bool fast_validate, display_e2e_pipe_params_st *pipes) { bool out = false; @@ -2027,7 +2027,6 @@ static bool dcn20_validate_bandwidth_internal(struct dc *dc, struct dc_state *co int vlevel = 0; int pipe_split_from[MAX_PIPES]; int pipe_cnt = 0; - display_e2e_pipe_params_st *pipes = kzalloc(dc->res_pool->pipe_count * sizeof(display_e2e_pipe_params_st), GFP_ATOMIC); DC_LOGGER_INIT(dc->ctx->logger); BW_VAL_TRACE_COUNT(); @@ -2062,16 +2061,14 @@ static bool dcn20_validate_bandwidth_internal(struct dc *dc, struct dc_state *co out = false; validate_out: - kfree(pipes); BW_VAL_TRACE_FINISH(); return out; } -bool dcn20_validate_bandwidth_fp(struct dc *dc, - struct dc_state *context, - bool fast_validate) +bool dcn20_validate_bandwidth_fp(struct dc *dc, struct dc_state *context, + bool fast_validate, display_e2e_pipe_params_st *pipes) { bool voltage_supported = false; bool full_pstate_supported = false; @@ -2090,11 +2087,11 @@ bool dcn20_validate_bandwidth_fp(struct dc *dc, ASSERT(context != dc->current_state); if (fast_validate) { - return dcn20_validate_bandwidth_internal(dc, context, true); + return dcn20_validate_bandwidth_internal(dc, context, true, pipes); } // Best case, we support full UCLK switch latency - voltage_supported = dcn20_validate_bandwidth_internal(dc, context, false); + voltage_supported = dcn20_validate_bandwidth_internal(dc, context, false, pipes); full_pstate_supported = context->bw_ctx.bw.dcn.clk.p_state_change_support; if (context->bw_ctx.dml.soc.dummy_pstate_latency_us == 0 || @@ -2106,7 +2103,8 @@ bool dcn20_validate_bandwidth_fp(struct dc *dc, // Fallback: Try to only support G6 temperature read latency context->bw_ctx.dml.soc.dram_clock_change_latency_us = context->bw_ctx.dml.soc.dummy_pstate_latency_us; - voltage_supported = dcn20_validate_bandwidth_internal(dc, context, false); + memset(pipes, 0, dc->res_pool->pipe_count * sizeof(display_e2e_pipe_params_st)); + voltage_supported = dcn20_validate_bandwidth_internal(dc, context, false, pipes); dummy_pstate_supported = context->bw_ctx.bw.dcn.clk.p_state_change_support; if (voltage_supported && (dummy_pstate_supported || !(context->stream_count))) { @@ -2311,9 +2309,8 @@ static void dcn21_calculate_wm(struct dc *dc, struct dc_state *context, &context->bw_ctx.dml, pipes, pipe_cnt); } -bool dcn21_validate_bandwidth_fp(struct dc *dc, - struct dc_state *context, - bool fast_validate) +bool dcn21_validate_bandwidth_fp(struct dc *dc, struct dc_state *context, + bool fast_validate, display_e2e_pipe_params_st *pipes) { bool out = false; @@ -2322,7 +2319,6 @@ bool dcn21_validate_bandwidth_fp(struct dc *dc, int vlevel = 0; int pipe_split_from[MAX_PIPES]; int pipe_cnt = 0; - display_e2e_pipe_params_st *pipes = kzalloc(dc->res_pool->pipe_count * sizeof(display_e2e_pipe_params_st), GFP_ATOMIC); DC_LOGGER_INIT(dc->ctx->logger); BW_VAL_TRACE_COUNT(); @@ -2362,7 +2358,6 @@ bool dcn21_validate_bandwidth_fp(struct dc *dc, out = false; validate_out: - kfree(pipes); BW_VAL_TRACE_FINISH(); diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.h b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.h index c51badf7b68a..b6c34198ddc8 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.h +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.h @@ -61,9 +61,8 @@ void dcn20_update_bounding_box(struct dc *dc, unsigned int num_states); void dcn20_patch_bounding_box(struct dc *dc, struct _vcs_dpi_soc_bounding_box_st *bb); -bool dcn20_validate_bandwidth_fp(struct dc *dc, - struct dc_state *context, - bool fast_validate); +bool dcn20_validate_bandwidth_fp(struct dc *dc, struct dc_state *context, + bool fast_validate, display_e2e_pipe_params_st *pipes); void dcn20_fpu_set_wm_ranges(int i, struct pp_smu_wm_range_sets *ranges, struct _vcs_dpi_soc_bounding_box_st *loaded_bb); @@ -77,9 +76,8 @@ int dcn21_populate_dml_pipes_from_context(struct dc *dc, struct dc_state *context, display_e2e_pipe_params_st *pipes, bool fast_validate); -bool dcn21_validate_bandwidth_fp(struct dc *dc, - struct dc_state *context, - bool fast_validate); +bool dcn21_validate_bandwidth_fp(struct dc *dc, struct dc_state *context, bool + fast_validate, display_e2e_pipe_params_st *pipes); void dcn21_update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_params); void dcn21_clk_mgr_set_bw_params_wm_table(struct clk_bw_params *bw_params); diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index ce397a8797f7..98c3f532822d 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -3,7 +3,6 @@ config DRM_I915 tristate "Intel 8xx/9xx/G3x/G4x/HD Graphics" depends on DRM depends on X86 && PCI - depends on !PREEMPT_RT select INTEL_GTT if X86 select INTERVAL_TREE # we need shmfs for the swappable backing store, and in particular diff --git a/drivers/gpu/drm/i915/display/intel_crtc.c b/drivers/gpu/drm/i915/display/intel_crtc.c index cfbfbfed3f5e..da2becfbc86c 100644 --- a/drivers/gpu/drm/i915/display/intel_crtc.c +++ b/drivers/gpu/drm/i915/display/intel_crtc.c @@ -562,7 +562,8 @@ void intel_pipe_update_start(struct intel_atomic_state *state, */ intel_psr_wait_for_idle_locked(new_crtc_state); - local_irq_disable(); + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) + local_irq_disable(); crtc->debug.min_vbl = min; crtc->debug.max_vbl = max; @@ -587,11 +588,13 @@ void intel_pipe_update_start(struct intel_atomic_state *state, break; } - local_irq_enable(); + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) + local_irq_enable(); timeout = schedule_timeout(timeout); - local_irq_disable(); + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) + local_irq_disable(); } finish_wait(wq, &wait); @@ -624,7 +627,8 @@ void intel_pipe_update_start(struct intel_atomic_state *state, return; irq_disable: - local_irq_disable(); + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) + local_irq_disable(); } #if IS_ENABLED(CONFIG_DRM_I915_DEBUG_VBLANK_EVADE) @@ -728,7 +732,8 @@ void intel_pipe_update_end(struct intel_atomic_state *state, */ intel_vrr_send_push(new_crtc_state); - local_irq_enable(); + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) + local_irq_enable(); if (intel_vgpu_active(dev_priv)) return; diff --git a/drivers/gpu/drm/i915/display/intel_vblank.c b/drivers/gpu/drm/i915/display/intel_vblank.c index f5659ebd08eb..5b6d2f55528d 100644 --- a/drivers/gpu/drm/i915/display/intel_vblank.c +++ b/drivers/gpu/drm/i915/display/intel_vblank.c @@ -294,7 +294,8 @@ static bool i915_get_crtc_scanoutpos(struct drm_crtc *_crtc, */ spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); - /* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */ + if (IS_ENABLED(CONFIG_PREEMPT_RT)) + preempt_disable(); /* Get optional system timestamp before query. */ if (stime) @@ -358,7 +359,8 @@ static bool i915_get_crtc_scanoutpos(struct drm_crtc *_crtc, if (etime) *etime = ktime_get(); - /* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */ + if (IS_ENABLED(CONFIG_PREEMPT_RT)) + preempt_enable(); spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); diff --git a/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c b/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c index f2973cd1a8aa..aa77f8601b8a 100644 --- a/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c +++ b/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c @@ -315,7 +315,12 @@ void __intel_breadcrumbs_park(struct intel_breadcrumbs *b) return; /* Kick the work once more to drain the signalers, and disarm the irq */ - irq_work_queue(&b->irq_work); + irq_work_sync(&b->irq_work); + while (READ_ONCE(b->irq_armed) && !atomic_read(&b->active)) { + irq_work_queue(&b->irq_work); + cond_resched(); + irq_work_sync(&b->irq_work); + } } void intel_breadcrumbs_free(struct kref *kref) diff --git a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c index 2065be5a196b..73d815fc514b 100644 --- a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c +++ b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c @@ -1303,7 +1303,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) * and context switches) submission. */ - spin_lock(&sched_engine->lock); + spin_lock_irq(&sched_engine->lock); /* * If the queue is higher priority than the last @@ -1403,7 +1403,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) * Even if ELSP[1] is occupied and not worthy * of timeslices, our queue might be. */ - spin_unlock(&sched_engine->lock); + spin_unlock_irq(&sched_engine->lock); return; } } @@ -1429,7 +1429,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) if (last && !can_merge_rq(last, rq)) { spin_unlock(&ve->base.sched_engine->lock); - spin_unlock(&engine->sched_engine->lock); + spin_unlock_irq(&engine->sched_engine->lock); return; /* leave this for another sibling */ } @@ -1591,7 +1591,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) */ sched_engine->queue_priority_hint = queue_prio(sched_engine); i915_sched_engine_reset_on_empty(sched_engine); - spin_unlock(&sched_engine->lock); + spin_unlock_irq(&sched_engine->lock); /* * We can skip poking the HW if we ended up with exactly the same set @@ -1617,13 +1617,6 @@ static void execlists_dequeue(struct intel_engine_cs *engine) } } -static void execlists_dequeue_irq(struct intel_engine_cs *engine) -{ - local_irq_disable(); /* Suspend interrupts across request submission */ - execlists_dequeue(engine); - local_irq_enable(); /* flush irq_work (e.g. breadcrumb enabling) */ -} - static void clear_ports(struct i915_request **ports, int count) { memset_p((void **)ports, NULL, count); @@ -2478,7 +2471,7 @@ static void execlists_submission_tasklet(struct tasklet_struct *t) } if (!engine->execlists.pending[0]) { - execlists_dequeue_irq(engine); + execlists_dequeue(engine); start_timeslice(engine); } diff --git a/drivers/gpu/drm/i915/gt/intel_reset.c b/drivers/gpu/drm/i915/gt/intel_reset.c index 13fb8e5042c5..b51fb0c97772 100644 --- a/drivers/gpu/drm/i915/gt/intel_reset.c +++ b/drivers/gpu/drm/i915/gt/intel_reset.c @@ -164,13 +164,13 @@ static int i915_do_reset(struct intel_gt *gt, /* Assert reset for at least 20 usec, and wait for acknowledgement. */ pci_write_config_byte(pdev, I915_GDRST, GRDOM_RESET_ENABLE); udelay(50); - err = wait_for_atomic(i915_in_reset(pdev), 50); + err = _wait_for_atomic(i915_in_reset(pdev), 50, 0); /* Clear the reset request. */ pci_write_config_byte(pdev, I915_GDRST, 0); udelay(50); if (!err) - err = wait_for_atomic(!i915_in_reset(pdev), 50); + err = _wait_for_atomic(!i915_in_reset(pdev), 50, 0); return err; } @@ -190,7 +190,7 @@ static int g33_do_reset(struct intel_gt *gt, struct pci_dev *pdev = to_pci_dev(gt->i915->drm.dev); pci_write_config_byte(pdev, I915_GDRST, GRDOM_RESET_ENABLE); - return wait_for_atomic(g4x_reset_complete(pdev), 50); + return _wait_for_atomic(g4x_reset_complete(pdev), 50, 0); } static int g4x_do_reset(struct intel_gt *gt, @@ -207,7 +207,7 @@ static int g4x_do_reset(struct intel_gt *gt, pci_write_config_byte(pdev, I915_GDRST, GRDOM_MEDIA | GRDOM_RESET_ENABLE); - ret = wait_for_atomic(g4x_reset_complete(pdev), 50); + ret = _wait_for_atomic(g4x_reset_complete(pdev), 50, 0); if (ret) { GT_TRACE(gt, "Wait for media reset failed\n"); goto out; @@ -215,7 +215,7 @@ static int g4x_do_reset(struct intel_gt *gt, pci_write_config_byte(pdev, I915_GDRST, GRDOM_RENDER | GRDOM_RESET_ENABLE); - ret = wait_for_atomic(g4x_reset_complete(pdev), 50); + ret = _wait_for_atomic(g4x_reset_complete(pdev), 50, 0); if (ret) { GT_TRACE(gt, "Wait for render reset failed\n"); goto out; @@ -785,9 +785,7 @@ int __intel_gt_reset(struct intel_gt *gt, intel_engine_mask_t engine_mask) reset_mask = wa_14015076503_start(gt, engine_mask, !retry); GT_TRACE(gt, "engine_mask=%x\n", reset_mask); - preempt_disable(); ret = reset(gt, reset_mask, retry); - preempt_enable(); wa_14015076503_end(gt, reset_mask); } diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.h b/drivers/gpu/drm/i915/gt/uc/intel_guc.h index 8dc291ff0093..5b8d084c9c58 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc.h +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.h @@ -317,7 +317,7 @@ static inline int intel_guc_send_busy_loop(struct intel_guc *guc, { int err; unsigned int sleep_period_ms = 1; - bool not_atomic = !in_atomic() && !irqs_disabled(); + bool not_atomic = !in_atomic() && !irqs_disabled() && !rcu_preempt_depth(); /* * FIXME: Have caller pass in if we are in an atomic context to avoid diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c index f59081066a19..014d02029a41 100644 --- a/drivers/gpu/drm/i915/i915_request.c +++ b/drivers/gpu/drm/i915/i915_request.c @@ -609,7 +609,6 @@ bool __i915_request_submit(struct i915_request *request) RQ_TRACE(request, "\n"); - GEM_BUG_ON(!irqs_disabled()); lockdep_assert_held(&engine->sched_engine->lock); /* @@ -718,7 +717,6 @@ void __i915_request_unsubmit(struct i915_request *request) */ RQ_TRACE(request, "\n"); - GEM_BUG_ON(!irqs_disabled()); lockdep_assert_held(&engine->sched_engine->lock); /* diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index ce1cbee1b39d..3c51620d011b 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -6,6 +6,10 @@ #if !defined(_I915_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) #define _I915_TRACE_H_ +#ifdef CONFIG_PREEMPT_RT +#define NOTRACE +#endif + #include #include #include @@ -322,7 +326,7 @@ DEFINE_EVENT(i915_request, i915_request_add, TP_ARGS(rq) ); -#if defined(CONFIG_DRM_I915_LOW_LEVEL_TRACEPOINTS) +#if defined(CONFIG_DRM_I915_LOW_LEVEL_TRACEPOINTS) && !defined(NOTRACE) DEFINE_EVENT(i915_request, i915_request_guc_submit, TP_PROTO(struct i915_request *rq), TP_ARGS(rq) diff --git a/drivers/gpu/drm/i915/i915_utils.h b/drivers/gpu/drm/i915/i915_utils.h index c61066498bf2..48e19e55d6b0 100644 --- a/drivers/gpu/drm/i915/i915_utils.h +++ b/drivers/gpu/drm/i915/i915_utils.h @@ -288,7 +288,7 @@ wait_remaining_ms_from_jiffies(unsigned long timestamp_jiffies, int to_wait_ms) #define wait_for(COND, MS) _wait_for((COND), (MS) * 1000, 10, 1000) /* If CONFIG_PREEMPT_COUNT is disabled, in_atomic() always reports false. */ -#if defined(CONFIG_DRM_I915_DEBUG) && defined(CONFIG_PREEMPT_COUNT) +#if defined(CONFIG_DRM_I915_DEBUG) && defined(CONFIG_PREEMPT_COUNT) && !defined(CONFIG_PREEMPT_RT) # define _WAIT_FOR_ATOMIC_CHECK(ATOMIC) WARN_ON_ONCE((ATOMIC) && !in_atomic()) #else # define _WAIT_FOR_ATOMIC_CHECK(ATOMIC) do { } while (0) diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index e575048af139..fa58ec612b67 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -689,4 +689,8 @@ config SUNPLUS_SP7021_INTC chained controller, routing all interrupt source in P-Chip to the primary controller on C-Chip. +config BAREMETAL + bool + depends on ARM_GIC || ARM_GIC_V3 + endmenu diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index ffd945fe71aa..4d6fef2c9aee 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -120,3 +120,4 @@ obj-$(CONFIG_IRQ_IDT3243X) += irq-idt3243x.o obj-$(CONFIG_APPLE_AIC) += irq-apple-aic.o obj-$(CONFIG_MCHP_EIC) += irq-mchp-eic.o obj-$(CONFIG_SUNPLUS_SP7021_INTC) += irq-sp7021-intc.o +obj-$(CONFIG_BAREMETAL) += ipi-baremetal.o \ No newline at end of file diff --git a/drivers/irqchip/ipi-baremetal.c b/drivers/irqchip/ipi-baremetal.c new file mode 100644 index 000000000000..58f88546b6f0 --- /dev/null +++ b/drivers/irqchip/ipi-baremetal.c @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * IPI for inter-core communiction for NXP Layerscape baremetal + * + * Copyright 2018-2023 NXP + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int pid; +int mycoreid; + +#undef IPI_BAREMETAL_SIGNAL + +#define DEVICE_NAME "ipi_bm" + +/* + * choose 50 as baremetal signal number. + * real-time signals are in the range of 33 to 64. + */ +#define SIG_BM 50 + +#ifndef IPI_BAREMETAL_SIGNAL +void __iomem *share_base; +#ifdef CONFIG_SOC_IMX6Q_BAREMETAL +#define GICD_BASE 0x00A01000 +#define GICD_SIZE 0x1000 +#define GICC_BASE 0x00A00100 +#define GICC_SIZE 0x1000 +#define GIC_DIST_IGROUP 0x080 +#define GIC_DIST_CTRL 0x000 +#define GIC_CPU_CTRL 0x00 +#define GICD_ENABLE 0x3 +#define GICC_ENABLE 0x7 +#endif +#if defined(CONFIG_SOC_IMX6Q_BAREMETAL) +#define CONFIG_SYS_DDR_SDRAM_BASE 0x10000000UL +#define CONFIG_SYS_DDR_SDRAM_SLAVE_SIZE (128 * 1024 * 1024) +#define CONFIG_SYS_DDR_SDRAM_MASTER_SIZE (512 * 1024 * 1024) +#elif defined(CONFIG_LX2160A_BAREMETAL) +#define CONFIG_SYS_DDR_SDRAM_BASE 0x80000000UL +#define CONFIG_SYS_DDR_SDRAM_SLAVE_SIZE (64 * 1024 * 1024) +#define CONFIG_SYS_DDR_SDRAM_MASTER_SIZE (512 * 1024 * 1024) +#elif defined(CONFIG_IMX8M_BAREMETAL) +#define CONFIG_SYS_DDR_SDRAM_SLAVE_ADDR (0x60000000) +#define CONFIG_SYS_DDR_SDRAM_SLAVE_SIZE (32 * 1024 * 1024) +#define CONFIG_SYS_DDR_SDRAM_SLAVE_RESERVE_SIZE (32 * 1024 *1024) +#elif defined(CONFIG_IMX93_BAREMETAL) +#define CONFIG_SYS_DDR_SDRAM_SLAVE_ADDR (0xb0000000) +#define CONFIG_SYS_DDR_SDRAM_SLAVE_SIZE (32 * 1024 * 1024) +#define CONFIG_SYS_DDR_SDRAM_SLAVE_RESERVE_SIZE (32 * 1024 *1024) +#else +#define CONFIG_SYS_DDR_SDRAM_BASE 0x80000000UL +#define CONFIG_SYS_DDR_SDRAM_SLAVE_SIZE (256 * 1024 * 1024) +#define CONFIG_SYS_DDR_SDRAM_MASTER_SIZE (512 * 1024 * 1024) +#endif + +#if defined(CONFIG_IMX8M_BAREMETAL) || defined(CONFIG_IMX93_BAREMETAL) +#define CONFIG_SYS_DDR_SDRAM_SHARE_BASE (CONFIG_SYS_DDR_SDRAM_SLAVE_ADDR \ + + CONFIG_SYS_DDR_SDRAM_SLAVE_SIZE*(CONFIG_MAX_CPUS-1)) +#define CONFIG_SYS_DDR_SDRAM_SHARE_RESERVE_SIZE (4 * 1024 * 1024) +#else +#define CONFIG_SYS_DDR_SDRAM_SHARE_BASE \ + (CONFIG_SYS_DDR_SDRAM_BASE + CONFIG_SYS_DDR_SDRAM_MASTER_SIZE \ + + CONFIG_SYS_DDR_SDRAM_SLAVE_SIZE * (CONFIG_MAX_CPUS - 1)) + +#define CONFIG_SYS_DDR_SDRAM_SHARE_RESERVE_SIZE (16 * 1024 * 1024) +#endif + +#if defined(CONFIG_SOC_IMX6Q_BAREMETAL) +#define CONFIG_SYS_DDR_SDRAM_SHARE_SIZE \ + ((128 * 1024 * 1024) - CONFIG_SYS_DDR_SDRAM_SHARE_RESERVE_SIZE) +#elif defined(CONFIG_LX2160A_BAREMETAL) +#define CONFIG_SYS_DDR_SDRAM_SHARE_SIZE \ + ((64 * 1024 * 1024) - CONFIG_SYS_DDR_SDRAM_SHARE_RESERVE_SIZE) +#elif defined(CONFIG_IMX8M_BAREMETAL) || defined(CONFIG_IMX93_BAREMETAL) +#define CONFIG_SYS_DDR_SDRAM_SHARE_SIZE (CONFIG_SYS_DDR_SDRAM_SLAVE_RESERVE_SIZE \ + - CONFIG_SYS_DDR_SDRAM_SHARE_RESERVE_SIZE) +#else +#define CONFIG_SYS_DDR_SDRAM_SHARE_SIZE \ + ((256 * 1024 * 1024) - CONFIG_SYS_DDR_SDRAM_SHARE_RESERVE_SIZE) +#endif +#define CONFIG_SYS_DDR_SDRAM_SHARE_RESERVE_BASE \ + (CONFIG_SYS_DDR_SDRAM_SHARE_BASE + CONFIG_SYS_DDR_SDRAM_SHARE_SIZE) + +/* number of descriptor for each ring */ +#define ICC_RING_ENTRY 128 +/* size of each block */ +#define ICC_BLOCK_UNIT_SIZE (4 * 1024) +/* 2M space for core's ring and desc struct */ +#define ICC_RING_DESC_SPACE (2 * 1024 * 1024) + +/* share memory size for each core icc */ +#define ICC_CORE_MEM_SPACE (CONFIG_SYS_DDR_SDRAM_SHARE_SIZE / CONFIG_MAX_CPUS) +/* share memory base for core x */ +#define ICC_CORE_MEM_BASE_PHY(x) \ + (CONFIG_SYS_DDR_SDRAM_SHARE_BASE + (x) * ICC_CORE_MEM_SPACE) +/* share memory base for core x */ +#define ICC_CORE_MEM_BASE(x) \ + ((unsigned long)share_base + (x) * ICC_CORE_MEM_SPACE) +/* the ring struct addr of core x ring y */ +#define ICC_CORE_RING_BASE(x, y) \ + (ICC_CORE_MEM_BASE(x) + (y) * sizeof(struct icc_ring)) +/* the desc struct addr of core x */ +#define ICC_CORE_DESC_BASE_PHY(x) \ + (ICC_CORE_MEM_BASE_PHY(x) + CONFIG_MAX_CPUS * sizeof(struct icc_ring)) +/* + * The core x block memory base addr for icc data transfer. + * The beginning 2M space of core x icc memory is for + * core x ring and desc struct. + */ +#define ICC_CORE_BLOCK_BASE_PHY(x) \ + (ICC_CORE_MEM_BASE_PHY(x) + ICC_RING_DESC_SPACE) +#define ICC_CORE_BLOCK_BASE(x) (ICC_CORE_MEM_BASE(x) + ICC_RING_DESC_SPACE) +#define ICC_CORE_BLOCK_END_PHY(x) \ + (ICC_CORE_MEM_BASE_PHY(x) + ICC_CORE_MEM_SPACE) +#define ICC_CORE_BLOCK_END(x) (ICC_CORE_MEM_BASE(x) + ICC_CORE_MEM_SPACE) +#define ICC_CORE_BLOCK_COUNT \ + ((ICC_CORE_MEM_SPACE - ICC_RING_DESC_SPACE)/ICC_BLOCK_UNIT_SIZE) + +#define ICC_PHY2VIRT(x) \ + (((void *)x - ICC_CORE_MEM_BASE_PHY(mycoreid)) \ + + ICC_CORE_MEM_BASE(mycoreid)) +#define ICC_VIRT2PHY(x) \ + (((void *)x - ICC_CORE_MEM_BASE(mycoreid)) \ + + ICC_CORE_MEM_BASE_PHY(mycoreid)) + +#define IPIDEV_IOCIRQ 1 + +#define ICC_CMD_TX_DATA 0x00 +#define ICC_CMD_DUMP_TIME 0x01 + +struct icc_desc { + unsigned long block_addr; /* block address */ + unsigned int byte_count; /* available bytes */ + unsigned int option_mode; /* option mode for this icc irq */ +}; + +struct icc_ring { + unsigned int src_coreid; /* which core created the ring */ + unsigned int dest_coreid; /* which core the ring sends SGI to */ + unsigned int interrupt; /* which interrupt (SGI) be used */ + unsigned int desc_num; /* number of descriptor */ + struct icc_desc *desc; /* pointer of the first descriptor */ + unsigned int desc_head; /* modified by producer */ + unsigned int desc_tail; /* modified by consumer */ + unsigned long busy_counts; /* statistic: ring full */ + unsigned long interrupt_counts; /* statistic: total sent number */ + unsigned int irq_status; /* status of the ring, set by producer, reset by consumer */ +}; +#endif + +int ipi_baremetal_open(struct inode *inode, struct file *filp) +{ + return 0; +} + +ssize_t ipi_baremetal_read(struct file *file, + char __user *buff, size_t count, loff_t *offp) +{ + return 0; +} + +ssize_t ipi_baremetal_write(struct file *file, + const char __user *buff, size_t count, loff_t *offp) +{ + char mybuf[10]; + int ret; + + /* read the value from user space */ + if (count > 10) + return -EINVAL; + copy_from_user(mybuf, buff, count); + ret = kstrtoint(mybuf, 0, &pid); + if (ret) + return -EINVAL; + + return 0; +} + +static int ipi_baremetal_release(struct inode *inode, struct file *file) +{ + return 0; +} + +#ifndef CONFIG_LS1021A_BAREMETAL +static long ipi_baremetal_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + unsigned long val; + unsigned long __user *argp = (unsigned long __user *)arg; + int err; +#if defined(CONFIG_LX2160A_BAREMETAL) + unsigned long i, cluster, mask; +#endif + + err = copy_from_user(&val, argp, sizeof(val)); + if (err) + return -EFAULT; + + val |= ((unsigned long)1 << 40); + +#if defined(CONFIG_LX2160A_BAREMETAL) + val = *(unsigned long *)arg; + + for (i = 0; i < 16; i++) { + if ((val >> i) & 0x1) { + cluster = i / 2; + mask = i % 2; + val |= (1 << mask) | (cluster << 16); + } + } +#endif + switch (cmd) { + case IPIDEV_IOCIRQ: + write_sysreg_s(val, SYS_ICC_SGI1R_EL1); + break; + default: + return -EINVAL; + } + return 0; +} +#endif + +static int icc_ring_empty(struct icc_ring *ring) +{ + if (ring->desc_tail == ring->desc_head) + return 1; + return 0; +} + +/* how many rx blocks are valid waiting to be handled */ +static int icc_ring_valid(struct icc_ring *ring) +{ + int valid; + + if (icc_ring_empty(ring)) + return 0; + + if (ring->desc_head > ring->desc_tail) + valid = ring->desc_head - ring->desc_tail; + else + valid = ring->desc_num - ring->desc_tail + ring->desc_head; + return valid; +} + +int ipi_baremetal_handle(u32 irqnr, u32 irqsrc) +{ +#ifdef IPI_BAREMETAL_SIGNAL + struct siginfo info; + struct task_struct *t; + int si_data; + int ret; + + if (!pid) + return 0; + + si_data = (irqnr << 16) | irqsrc; + /* send the signal */ + memset(&info, 0, sizeof(struct siginfo)); + info.si_signo = SIG_BM; + info.si_code = SI_QUEUE; + info.si_int = si_data; + + rcu_read_lock(); + /* find the task_struct associated with this pid */ + t = find_task_by_vpid(pid); + if (t == NULL) { + pr_info("no such pid\n"); + rcu_read_unlock(); + return -ENODEV; + } + rcu_read_unlock(); + + /* send the signal */ + ret = send_sig_info(SIG_BM, &info, t); + if (ret < 0) { + pr_info("error sending signal\n"); + return ret; + } +#else + struct icc_ring *ring; + struct icc_desc *desc; + struct icc_desc *desc_phy; + unsigned long block_addr; + unsigned int byte_count, option_mode; + int i, valid; + int hw_irq, src_coreid; + + hw_irq = irqnr; + src_coreid = irqsrc; + + if (src_coreid == mycoreid) { + pr_err("Do not support self-icc now!\n"); + return -1; + } + + /* get the ring for this core from source core */ + ring = (struct icc_ring *)ICC_CORE_RING_BASE(src_coreid, mycoreid); + valid = icc_ring_valid(ring); + for (i = 0; i < valid; i++) { + desc_phy = ring->desc + ring->desc_tail; + desc = ICC_PHY2VIRT(desc_phy); + option_mode = desc->option_mode; + + if (option_mode & ICC_CMD_DUMP_TIME) { + pr_info("Get the SGI from CoreID: %d\n", src_coreid); + } else { + block_addr = desc->block_addr; + byte_count = desc->byte_count; + + if ((*(char *)ICC_PHY2VIRT(block_addr)) != 0x5a) + pr_info("Get the ICC from core %d; block: 0x%lx, bytes: %d, value: 0x%x\n", + src_coreid, block_addr, byte_count, + (*(char *)ICC_PHY2VIRT(block_addr))); + } + + /* add desc_tail */ + ring->desc_tail = (ring->desc_tail + 1) % ring->desc_num; + } +#endif + return 0; +} + +static const struct vm_operations_struct shd_mmap_mem_ops = { +#ifdef CONFIG_HAVE_IOREMAP_PROT + .access = generic_access_phys +#endif +}; + +static int shd_mmap_mem(struct file *file, struct vm_area_struct *vma) +{ + size_t size = vma->vm_end - vma->vm_start; + +#if defined(CONFIG_LS1021A_BAREMETAL) || defined(CONFIG_SOC_IMX6Q_BAREMETAL) + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); +#else + vma->vm_page_prot = pgprot_cached(vma->vm_page_prot); +#endif + vma->vm_ops = &shd_mmap_mem_ops; + + /* Remap-pfn-range will mark the range VM_IO */ + if (remap_pfn_range(vma, + vma->vm_start, + vma->vm_pgoff, + size, + vma->vm_page_prot)) { + return -EAGAIN; + } + return 0; +} + +const struct file_operations ipi_bm_ops = { + .owner = THIS_MODULE, + .open = ipi_baremetal_open, + .release = ipi_baremetal_release, + .read = ipi_baremetal_read, + .write = ipi_baremetal_write, + .mmap = shd_mmap_mem, +#ifndef CONFIG_LS1021A_BAREMETAL + .unlocked_ioctl = ipi_baremetal_ioctl, +#endif +}; + +static struct miscdevice ipi_bm_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = DEVICE_NAME, + .fops = &ipi_bm_ops, +}; + +#ifdef CONFIG_SOC_IMX6Q_BAREMETAL +void gic_enable_dist(void) +{ + void __iomem *gicd_base, *gicc_base; + + gicd_base = ioremap((phys_addr_t)GICD_BASE, GICD_SIZE); + if (!gicd_base) { + pr_err("failed to remap gicd base for ICC\n"); + return -ENOMEM; + } + gicc_base = ioremap((phys_addr_t)GICC_BASE, GICC_SIZE); + if (!gicc_base) { + pr_err("failed to remap gicc base for ICC\n"); + return -ENOMEM; + } + /* set the SGI interrupts for this core to group 1 */ + writel(0xffffffff, gicd_base + GIC_DIST_IGROUP); + writel(GICD_ENABLE, gicd_base + GIC_DIST_CTRL); + writel(GICC_ENABLE, gicc_base + GIC_CPU_CTRL); + iounmap(gicd_base); + iounmap(gicc_base); +} +#endif + +static int __init ipi_baremetal_init(void) +{ + int ret; + + pr_info("NXP inter-core communiction IRQ driver\n"); +#ifndef IPI_BAREMETAL_SIGNAL +#if defined(CONFIG_LS1021A_BAREMETAL) || defined(CONFIG_SOC_IMX6Q_BAREMETAL) + share_base = ioremap((phys_addr_t)CONFIG_SYS_DDR_SDRAM_SHARE_BASE, + CONFIG_SYS_DDR_SDRAM_SHARE_SIZE); +#else + share_base = ioremap_cache((phys_addr_t)CONFIG_SYS_DDR_SDRAM_SHARE_BASE, + CONFIG_SYS_DDR_SDRAM_SHARE_SIZE); +#endif + if (!share_base) { + pr_err("failed to remap share base (%lu/%u) for ICC\n", + CONFIG_SYS_DDR_SDRAM_SHARE_BASE, + CONFIG_SYS_DDR_SDRAM_SHARE_SIZE); + return -ENOMEM; + } + mycoreid = 0; +#ifdef CONFIG_SOC_IMX6Q_BAREMETAL + gic_enable_dist(); +#endif +#endif + ret = misc_register(&ipi_bm_misc); + if (ret < 0) { + pr_info("Register ipi_bm error! ret: %d\n", ret); + return ret; + } + pr_info("ipi_bm device created!\n"); + return 0; +} + +static void __exit ipi_baremetal_exit(void) +{ + pid = 0; +#ifndef IPI_BAREMETAL_SIGNAL + iounmap(share_base); +#endif + misc_deregister(&ipi_bm_misc); + pr_info("ipi_bm device deleted!\n"); +} + +module_init(ipi_baremetal_init); +module_exit(ipi_baremetal_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("NXP"); +MODULE_DESCRIPTION("NXP inter-core communiction IPI driver"); diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index a09883e5006c..7069a2bdab7e 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -28,6 +28,10 @@ #include #include +#ifdef CONFIG_BAREMETAL +#include +#endif + #include #include #include @@ -1430,12 +1434,12 @@ static void __init gic_smp_init(void) "irqchip/arm/gicv3:starting", gic_starting_cpu, NULL); - /* Register all 8 non-secure SGIs */ - base_sgi = irq_domain_alloc_irqs(gic_data.domain, 8, NUMA_NO_NODE, &sgi_fwspec); + /* Register all 16 non-secure SGIs */ + base_sgi = irq_domain_alloc_irqs(gic_data.domain, 16, NUMA_NO_NODE, &sgi_fwspec); if (WARN_ON(base_sgi <= 0)) return; - set_smp_ipi_range(base_sgi, 8); + set_smp_ipi_range(base_sgi, 16); } static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 412196a7dad5..5448b1441577 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -868,11 +868,11 @@ static __init void gic_smp_init(void) "irqchip/arm/gic:starting", gic_starting_cpu, NULL); - base_sgi = irq_domain_alloc_irqs(gic_data[0].domain, 8, NUMA_NO_NODE, &sgi_fwspec); + base_sgi = irq_domain_alloc_irqs(gic_data[0].domain, 16, NUMA_NO_NODE, &sgi_fwspec); if (WARN_ON(base_sgi <= 0)) return; - set_smp_ipi_range(base_sgi, 8); + set_smp_ipi_range(base_sgi, 16); } #else #define gic_smp_init() do { } while(0) diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index bc2e265cb02d..84158265ae08 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -295,4 +295,13 @@ config QCOM_IPCC acts as an interrupt controller for receiving interrupts from clients. Say Y here if you want to build this driver. +config GENERIC_SOFTWARE_MAILBOX + tristate "Generic software Mailbox driver" + depends on OF + select GENERIC_IRQ_INJECTION + select GIC_GENTLE_CONFIG if ARM_GIC_V3 + help + This driver leverages unused interrupt line and shared memory to + implement a mailbox, which is used to send messages between different + OSes. endif diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index fc9376117111..38d6994df5e1 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -62,3 +62,5 @@ obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox.o obj-$(CONFIG_QCOM_IPCC) += qcom-ipcc.o obj-$(CONFIG_APPLE_MAILBOX) += apple-mailbox.o + +obj-$(CONFIG_GENERIC_SOFTWARE_MAILBOX) += generic-software-mailbox.o diff --git a/drivers/mailbox/generic-software-mailbox.c b/drivers/mailbox/generic-software-mailbox.c new file mode 100644 index 000000000000..1938f45de6ec --- /dev/null +++ b/drivers/mailbox/generic-software-mailbox.c @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2022-2023 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Generic software Registers: + * + * TX_STATUS[n]: TX channel n status + * RX_STATUS[n]: RX channel n status + * 0: indicates message in T/RX_CH[n] is invalid and channel ready. + * 1: indicates message in T/RX_CH[n] is valid and channel busy. + * 2: indicates message in T/RX_CH[n] has been received by the peer. + * RXDB_STATUS[n]: RX doorbell channel n status + * 0: indicates channel ready. + * 1: indicates channel busy. + * 2: indicates channel doorbell has been received by the peer. + * TX_CH[n]: Transmit data register for channel n + * RX_CH[n]: Receive data register for channel n + * + * To send a message: + * Update the data register TX_CH[n] with the message, then set the + * TX_STATUS[n] to 1, inject a interrupt to remote side; after the + * transmission done set the TX_STATUS[n] back to 0. + * + * When received a message: + * Get the received data from RX_CH[n] and then set the RX_STATUS[n] to + * 2 and inject a interrupt to notify the remote side transmission done. + */ + +#define MBOX_TX_CHAN (4) +#define MBOX_RX_CHAN (4) +#define MBOX_RXDB_CHAN (4) +#define RX_CHAN_SHFT (MBOX_TX_CHAN) +#define RXDB_CHAN_SHFT (MBOX_TX_CHAN + MBOX_RX_CHAN) +#define MBOX_CHAN_MAX (MBOX_TX_CHAN + MBOX_RX_CHAN + MBOX_RXDB_CHAN) + +struct sw_mbox_reg { + uint32_t tx_status[MBOX_TX_CHAN]; + uint32_t rx_status[MBOX_RX_CHAN]; + uint32_t rxdb_status[MBOX_RXDB_CHAN]; + uint32_t tx_ch[MBOX_TX_CHAN]; + uint32_t rx_ch[MBOX_RX_CHAN]; + uint32_t rxdb_ch[MBOX_RX_CHAN]; + uint32_t ch_ack_flags; /*from bit0 each bit for each channel(tx_ch, rx_ch, rxdb_ch), 1: ack, 0:noack */ +}; + +enum sw_mbox_channel_status { + S_READY, + S_BUSY, + S_DONE, +}; + +enum sw_mbox_type { + SW_TYPE_TX, /* Tx */ + SW_TYPE_RX, /* Rx */ + SW_TYPE_RXDB, /* Rx doorbell */ +}; + +struct sw_mbox_con_priv { + uint32_t idx; + enum sw_mbox_type type; + struct sw_mbox *priv; +}; + +struct sw_mbox { + struct device *dev; + struct sw_mbox_reg __iomem *base; + struct sw_mbox_con_priv cp[MBOX_CHAN_MAX]; + struct mbox_chan chan[MBOX_CHAN_MAX]; + struct mbox_controller controller; + int irq; + int remote_irq; +}; + +static int sw_mbox_send_data(struct mbox_chan *chan, void *msg) +{ + struct sw_mbox_con_priv *cp = chan->con_priv; + struct sw_mbox *mbox = cp->priv; + uint32_t idx = cp->idx; + uint32_t *data = msg; + int ret; + + if (cp->type != SW_TYPE_TX) { + dev_err(mbox->dev, "Channel type error\n"); + return -EINVAL; + } + + writel(*data, &mbox->base->tx_ch[idx]); + writel(S_BUSY, &mbox->base->tx_status[idx]); + ret = irq_set_irqchip_state(mbox->remote_irq, IRQCHIP_STATE_PENDING, + true); + if (ret) { + dev_err(mbox->dev, "Fail to inject IRQ\n"); + return ret; + } + + return 0; +} + +static irqreturn_t sw_mbox_interrupt(int irq, void *dev_id) +{ + struct sw_mbox *mbox = dev_id; + irqreturn_t ret = IRQ_NONE; + uint32_t rxdb_status; + uint32_t rx_status; + uint32_t tx_status; + uint32_t rx_ch; + int i; + + for (i = 0; i < MBOX_TX_CHAN; i++) { + tx_status = readl(&mbox->base->tx_status[i]); + if (tx_status == S_DONE) { + writel(S_READY, &mbox->base->tx_status[i]); + mbox_chan_txdone(&mbox->chan[i], 0); + ret = IRQ_HANDLED; + } + } + + for (i = 0; i < MBOX_RX_CHAN; i++) { + rx_status = readl(&mbox->base->rx_status[i]); + if (rx_status == S_BUSY) { + rx_ch = readl(&mbox->base->rx_ch[i]); + mbox_chan_received_data(&mbox->chan[i + RX_CHAN_SHFT], + (void *)&rx_ch); + if (mbox->base->ch_ack_flags & (1 << (i + RX_CHAN_SHFT))) { + /* Sender need ACK */ + writel(S_DONE, &mbox->base->rx_status[i]); + irq_set_irqchip_state(mbox->remote_irq, + IRQCHIP_STATE_PENDING, true); + } else { + /* set status to be ready if sender doesn't need ACK */ + writel(S_READY, &mbox->base->rx_status[i]); + } + ret = IRQ_HANDLED; + } + } + + for (i = 0; i < MBOX_RXDB_CHAN; i++) { + rxdb_status = readl(&mbox->base->rxdb_status[i]); + if (rxdb_status == S_BUSY) { + mbox_chan_received_data(&mbox->chan[i + RXDB_CHAN_SHFT], + NULL); + if (mbox->base->ch_ack_flags & (1 << (i + RXDB_CHAN_SHFT))) { + /* Sender need ACK */ + writel(S_DONE, &mbox->base->rxdb_status[i]); + irq_set_irqchip_state(mbox->remote_irq, + IRQCHIP_STATE_PENDING, true); + } else { + /* set status to be ready if sender doesn't need ACK */ + writel(S_READY, &mbox->base->rxdb_status[i]); + } + ret = IRQ_HANDLED; + } + } + + return ret; +} + +static int sw_mbox_startup(struct mbox_chan *chan) +{ + return 0; +} + +static void sw_mbox_shutdown(struct mbox_chan *chan) +{ +} + +static const struct mbox_chan_ops sw_mbox_ops = { + .send_data = sw_mbox_send_data, + .startup = sw_mbox_startup, + .shutdown = sw_mbox_shutdown, +}; + + +static struct mbox_chan *sw_mbox_xlate(struct mbox_controller *mbox, + const struct of_phandle_args *sp) +{ + struct mbox_chan *chan; + struct sw_mbox_con_priv *cp; + struct sw_mbox *sw_mb; + uint32_t type, idx, chan_idx, ack; + + if (sp->args_count != 3) { + dev_err(mbox->dev, "Invalid argument count %d\n", + sp->args_count); + return ERR_PTR(-EINVAL); + } + + type = sp->args[0]; + idx = sp->args[1]; + ack = sp->args[2]; + + switch (type) { + case SW_TYPE_TX: + chan_idx = idx; + break; + case SW_TYPE_RX: + chan_idx = RX_CHAN_SHFT + idx; + break; + case SW_TYPE_RXDB: + chan_idx = RXDB_CHAN_SHFT + idx; + break; + default: + dev_err(mbox->dev, "Invalid chan type: %d\n", type); + return ERR_PTR(-EINVAL); + } + + if (chan_idx >= MBOX_CHAN_MAX) { + dev_err(mbox->dev, "Not supported channel number: %d. (type: %d, idx: %d)\n", + chan_idx, type, idx); + return ERR_PTR(-EINVAL); + } + + chan = &mbox->chans[chan_idx]; + cp = chan->con_priv; + sw_mb = cp->priv; + if (ack) + sw_mb->base->ch_ack_flags |= 1 << chan_idx; + else + sw_mb->base->ch_ack_flags &= ~(1 << chan_idx); + + return chan; +} + +static const struct of_device_id sw_mbox_of_match[] = { + { .compatible = "fsl,generic-software-mbox", }, + {}, +}; +MODULE_DEVICE_TABLE(of, sw_mbox_of_match); + +static int sw_mailbox_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sw_mbox *mbox; + int err, i; + + mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); + if (!mbox) + return -ENOMEM; + mbox->dev = dev; + + mbox->irq = platform_get_irq_byname(pdev, "irq"); + if (mbox->irq <= 0) { + dev_err(dev, "Failed to get irq\n"); + return mbox->irq; + } + mbox->remote_irq = platform_get_irq_byname(pdev, "remote_irq"); + if (mbox->remote_irq <= 0) { + dev_err(dev, "Failed to get remote irq\n"); + return mbox->remote_irq; + } + + err = devm_request_irq(dev, mbox->irq, sw_mbox_interrupt, IRQF_SHARED, + pdev->name, mbox); + if (err) + return err; + + mbox->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mbox->base)) + return PTR_ERR(mbox->base); + + memset_io(mbox->base->tx_status, 0, 4 * MBOX_TX_CHAN); + memset_io(mbox->base->rx_status, 0, 4 * MBOX_RX_CHAN); + memset_io(mbox->base->rxdb_status, 0, 4 * MBOX_RXDB_CHAN); + + mbox->controller.dev = dev; + mbox->controller.chans = mbox->chan; + mbox->controller.num_chans = MBOX_CHAN_MAX; + mbox->controller.ops = &sw_mbox_ops; + mbox->controller.of_xlate = sw_mbox_xlate; + mbox->controller.txdone_irq = true; + + for (i = 0; i < MBOX_CHAN_MAX; i++) { + mbox->chan[i].con_priv = &mbox->cp[i]; + mbox->cp[i].priv = mbox; + mbox->cp[i].idx = i; + } + + err = devm_mbox_controller_register(dev, &mbox->controller); + if (err) { + dev_err(dev, "Failed to register mailbox %d\n", err); + return err; + } + + platform_set_drvdata(pdev, mbox); + + return 0; +} + +static struct platform_driver sw_mbox_driver = { + .driver = { + .name = "generic-software-mailbox", + .of_match_table = sw_mbox_of_match, + }, + .probe = sw_mailbox_probe, +}; + +static int __init sw_mbox_init(void) +{ + return platform_driver_register(&sw_mbox_driver); +} + +static void __exit sw_mbox_exit(void) +{ + platform_driver_unregister(&sw_mbox_driver); +} + +module_init(sw_mbox_init); +module_exit(sw_mbox_exit); + +MODULE_DESCRIPTION("Generic Software mailbox driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c index ebff3baf3045..3b6f153e1523 100644 --- a/drivers/mailbox/mailbox.c +++ b/drivers/mailbox/mailbox.c @@ -159,7 +159,7 @@ static enum hrtimer_restart txdone_hrtimer(struct hrtimer *hrtimer) void mbox_chan_received_data(struct mbox_chan *chan, void *mssg) { /* No buffering the received data */ - if (chan->cl->rx_callback) + if (chan->cl && chan->cl->rx_callback) chan->cl->rx_callback(chan->cl, mssg); } EXPORT_SYMBOL_GPL(mbox_chan_received_data); diff --git a/drivers/mxc/ipu3/ipu_common.c b/drivers/mxc/ipu3/ipu_common.c index 4ba7b6df7986..145fc0f1b4f9 100644 --- a/drivers/mxc/ipu3/ipu_common.c +++ b/drivers/mxc/ipu3/ipu_common.c @@ -2923,8 +2923,9 @@ static irqreturn_t ipu_sync_irq_handler(int irq, void *desc) uint32_t line, bit, int_stat, int_ctrl; irqreturn_t result = IRQ_NONE; const int int_reg[] = { 1, 2, 3, 4, 11, 12, 13, 14, 15, 0 }; + unsigned long flags; - spin_lock(&ipu->int_reg_spin_lock); + spin_lock_irqsave(&ipu->int_reg_spin_lock, flags); for (i = 0; int_reg[i] != 0; i++) { int_stat = ipu_cm_read(ipu, @@ -2949,7 +2950,7 @@ static irqreturn_t ipu_sync_irq_handler(int irq, void *desc) } } - spin_unlock(&ipu->int_reg_spin_lock); + spin_unlock_irqrestore(&ipu->int_reg_spin_lock, flags); return result; } @@ -2960,8 +2961,9 @@ static irqreturn_t ipu_err_irq_handler(int irq, void *desc) int i; uint32_t int_stat; const int err_reg[] = { 5, 6, 9, 10, 0 }; + unsigned long flags; - spin_lock(&ipu->int_reg_spin_lock); + spin_lock_irqsave(&ipu->int_reg_spin_lock, flags); for (i = 0; err_reg[i] != 0; i++) { int_stat = ipu_cm_read(ipu, @@ -2980,7 +2982,7 @@ static irqreturn_t ipu_err_irq_handler(int irq, void *desc) } } - spin_unlock(&ipu->int_reg_spin_lock); + spin_unlock_irqrestore(&ipu->int_reg_spin_lock, flags); return IRQ_HANDLED; } diff --git a/drivers/mxc/ipu3/ipu_device.c b/drivers/mxc/ipu3/ipu_device.c index 925aaffc5d95..d9f521174ce0 100644 --- a/drivers/mxc/ipu3/ipu_device.c +++ b/drivers/mxc/ipu3/ipu_device.c @@ -3249,7 +3249,7 @@ static int ipu_task_thread(void *argv) uint32_t size; unsigned long flags; unsigned int cpu; - struct cpumask cpu_mask; + struct cpumask cpu_mask = CPU_MASK_NONE; struct ipu_thread_data *data = (struct ipu_thread_data *)argv; thread_id++; diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index f8c1d73b251d..6365e73296cf 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -89,6 +89,8 @@ source "drivers/net/dsa/xrs700x/Kconfig" source "drivers/net/dsa/realtek/Kconfig" +source "drivers/net/dsa/netc/Kconfig" + config NET_DSA_RZN1_A5PSW tristate "Renesas RZ/N1 A5PSW Ethernet switch support" depends on OF && ARCH_RZN1 diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile index cb9a97340e58..f641c13c533c 100644 --- a/drivers/net/dsa/Makefile +++ b/drivers/net/dsa/Makefile @@ -26,3 +26,4 @@ obj-y += qca/ obj-y += realtek/ obj-y += sja1105/ obj-y += xrs700x/ +obj-y += netc/ \ No newline at end of file diff --git a/drivers/net/dsa/netc/Kconfig b/drivers/net/dsa/netc/Kconfig new file mode 100644 index 000000000000..a665ab225e2b --- /dev/null +++ b/drivers/net/dsa/netc/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config NET_DSA_NETC + tristate "NXP NETC Ethernet switch family support" + depends on NET_DSA && SPI + select NET_DSA_TAG_NETC + help + This is the driver for the NXP ENTC Ethernet switch family. + These are managed over an SPI interface. Probing is handled + based on OF bindings and so is the linkage to PHYLINK. + +config NET_DSA_NETC_PTP + bool "Support for the PTP clock on the NXP NETC Ethernet switch" + depends on NET_DSA_NETC + depends on PTP_1588_CLOCK + help + This enables support for timestamping and PTP clock manipulations in + the NETC DSA driver. \ No newline at end of file diff --git a/drivers/net/dsa/netc/Makefile b/drivers/net/dsa/netc/Makefile new file mode 100644 index 000000000000..f9eb97417999 --- /dev/null +++ b/drivers/net/dsa/netc/Makefile @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_NET_DSA_NETC) += netcdsa.o + +netcdsa-objs := \ + netc_spi.o \ + netc_config.o \ + netc_ethtool.o \ + netc_devlink.o \ + netc_main.o + +ifdef CONFIG_NET_DSA_NETC_PTP +netcdsa-objs += netc_ptp.o +endif \ No newline at end of file diff --git a/drivers/net/dsa/netc/netc.h b/drivers/net/dsa/netc/netc.h new file mode 100644 index 000000000000..3c7a3f4a17a4 --- /dev/null +++ b/drivers/net/dsa/netc/netc.h @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2023 NXP + */ + +#ifndef _NETC_H +#define _NETC_H + +#include +#include +#include +#include +#include +#include + +#include "netc_config.h" +#include "netc_ptp.h" + +struct netc_private; + +enum { + NETC_SPEED_AUTO, + NETC_SPEED_10MBPS, + NETC_SPEED_100MBPS, + NETC_SPEED_1000MBPS, + NETC_SPEED_2500MBPS, + NETC_SPEED_MAX, +}; + +enum netc_internal_phy_t { + NETC_NO_PHY = 0, +}; + +struct netc_info { + const char *name; + int device_id; + int num_ports; + enum dsa_tag_protocol tag_proto; + int ptp_ts_bits; + bool multiple_cascade_ports; + bool can_limit_mcast_flood; +}; + +struct netc_psfp_list { + struct list_head stream_list; +#define MAX_SSIDS 512 + uint16_t ssids[MAX_SSIDS]; + int num_ssids; + /* Serialize access to the lists */ + struct mutex lock; +}; + +struct netc_private { + const struct netc_info *info; + struct netc_config config; + int cpu_port; + phy_interface_t phy_mode[NETC_MAX_NUM_PORTS]; + bool fixed_link[NETC_MAX_NUM_PORTS]; + unsigned long ucast_egress_floods; + unsigned long bcast_egress_floods; + + size_t max_xfer_len; + struct spi_device *spidev; + struct dsa_switch *ds; + u16 bridge_pvid[NETC_MAX_NUM_PORTS]; + u16 tag_8021q_pvid[NETC_MAX_NUM_PORTS]; + /* Serializes transmission of management frames so that + * the switch doesn't confuse them with one another. + */ + struct mutex mgmt_lock; + /* Serializes accesses to the FDB */ + struct mutex fdb_lock; + + struct devlink_region **regions; + + /* PTP two-step TX timestamp ID, and its serialization lock */ + spinlock_t ts_id_lock; + u32 ts_id; + unsigned long hwts_tx_en; + unsigned long hwts_rx_en; + struct netc_ptp_data ptp_data; + + struct netc_psfp_list psfp; +}; + +int netc_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, + struct netlink_ext_ack *extack); +void netc_frame_memory_partitioning(struct netc_private *priv); + +/* From netc_devlink.c */ +int netc_devlink_setup(struct dsa_switch *ds); +void netc_devlink_teardown(struct dsa_switch *ds); +int netc_devlink_info_get(struct dsa_switch *ds, + struct devlink_info_req *req, + struct netlink_ext_ack *extack); + +/* From netc_spi.c */ +int netc_xfer_cmd(const struct netc_private *priv, + enum netc_spi_rw_mode rw, enum netc_cmd cmd, + void *param, size_t param_len, + void *resp, size_t resp_len, + struct ptp_system_timestamp *ptp_sts); +int netc_xfer_set_cmd(const struct netc_private *priv, + enum netc_cmd cmd, + void *param, size_t param_len); +int netc_xfer_get_cmd(const struct netc_private *priv, + enum netc_cmd cmd, uint32_t id, + void *resp, size_t resp_len); + +int netc_xfer_write_reg(const struct netc_private *priv, + uint32_t reg, uint32_t value); +int netc_xfer_read_reg(const struct netc_private *priv, + uint32_t reg, uint32_t *value); +int netc_xfer_write_u64(const struct netc_private *priv, + enum netc_cmd cmd, uint64_t value, + struct ptp_system_timestamp *ptp_sts); +int netc_xfer_read_u64(const struct netc_private *priv, + enum netc_cmd cmd, uint64_t *value, + struct ptp_system_timestamp *ptp_sts); + +/* From netc_ethtool.c */ +void netc_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data); +void netc_get_strings(struct dsa_switch *ds, int port, + uint32_t stringset, uint8_t *data); +int netc_get_sset_count(struct dsa_switch *ds, int port, int sset); + +/* From netc_ptp.c */ +void netc_ptp_txtstamp_skb(struct dsa_switch *ds, int port, + struct sk_buff *skb); + +#endif /* _NETC_H */ diff --git a/drivers/net/dsa/netc/netc_config.c b/drivers/net/dsa/netc/netc_config.c new file mode 100644 index 000000000000..e2bd3c1b29dc --- /dev/null +++ b/drivers/net/dsa/netc/netc_config.c @@ -0,0 +1,696 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2023-2024 NXP + */ + +#include +#include +#include +#include +#include "netc.h" + +int netc_get_devinfo(struct netc_private *priv, struct netc_config *config) +{ + struct netc_cmd_sysinfo info; + int rc; + + rc = netc_xfer_get_cmd(priv, NETC_CMD_SYS_INFO_GET, 0, + &info, sizeof(info)); + if (rc < 0) + return rc; + + config->device_id = info.device_id; + config->vendor_id = info.vendor_id; + config->version_major = info.version_major; + config->version_minor = info.version_minor; + config->version_revision = info.version_revision; + config->cpu_port_mode = info.cpu_port; + + return 0; +} + +int netc_port_mtu_set(struct netc_private *priv, int port, int mtu) +{ + struct netc_cmd_port_mtu mtu_cmd = {0}; + + mtu_cmd.port = (uint8_t)port; + mtu_cmd.mtu = (uint16_t)mtu; + + return netc_xfer_set_cmd(priv, NETC_CMD_PORT_MTU_SET, + &mtu_cmd, sizeof(mtu_cmd)); +} + +int netc_port_mtu_get(struct netc_private *priv, int port, int *mtu) +{ + int rc; + struct netc_cmd_port_mtu mtu_resp = {0}; + + rc = netc_xfer_get_cmd(priv, NETC_CMD_PORT_MTU_GET, port, + &mtu_resp, sizeof(mtu_resp)); + + if (rc != 0) + return rc; + + *mtu = mtu_resp.mtu; + + return 0; +} + +/* Set link speed in the MAC configuration for a specific port. */ +int netc_port_phylink_mode_set(struct netc_private *priv, + struct netc_mac_config *mac) +{ + struct device *dev = priv->ds->dev; + struct netc_cmd_port_phylink_mode phylink_mode = {0}; + int rc; + + phylink_mode.port = mac->port; + phylink_mode.duplex = mac->duplex; + phylink_mode.speed = mac->speed; + + rc = netc_xfer_set_cmd(priv, NETC_CMD_PORT_PHYLINK_MODE_SET, + &phylink_mode, sizeof(phylink_mode)); + if (rc < 0) { + dev_err(dev, "Failed to write phylink_mode: %d\n", rc); + return rc; + } + + return 0; +} + +/* Get link speed in the MAC configuration for a specific port. */ +int netc_port_phylink_status_get(struct netc_private *priv, + struct netc_mac_config *mac) +{ + struct device *dev = priv->ds->dev; + struct netc_cmd_port_phylink_status phylink_status = {0}; + int rc; + + rc = netc_xfer_get_cmd(priv, NETC_CMD_PORT_PHYLINK_STATUS_GET, + mac->port, + &phylink_status, sizeof(phylink_status)); + if (rc < 0) { + dev_err(dev, "Failed to get phylink status: %d\n", rc); + return rc; + } + + mac->link = phylink_status.link; + mac->speed = phylink_status.speed; + mac->duplex = phylink_status.duplex; + + return 0; +} + +int netc_port_pvid_set(struct netc_private *priv, int port, uint16_t pvid) +{ + int rc = 0; + struct netc_cmd_port_pvid cmd_pvid = {0}; + + cmd_pvid.port = (uint8_t)port; + cmd_pvid.pvid = pvid; + + rc = netc_xfer_set_cmd(priv, NETC_CMD_PORT_PVID_SET, + &cmd_pvid, sizeof(cmd_pvid)); + + return rc; +} + +int netc_port_link_set(struct netc_private *priv, int port, bool up) +{ + int rc = 0; + struct netc_cmd_port_link egress = {0}; + + egress.port = (uint8_t)port; + egress.link = up; + + rc = netc_xfer_set_cmd(priv, NETC_CMD_PORT_LINK_SET, + &egress, sizeof(egress)); + + return rc; +} + +int netc_port_dropuntag_set(struct netc_private *priv, int port, bool drop) +{ + int rc = 0; + struct netc_cmd_port_dropuntag dropuntag = {0}; + + dropuntag.port = (uint8_t)port; + dropuntag.drop = (uint16_t)drop; + + rc = netc_xfer_set_cmd(priv, NETC_CMD_PORT_DROPUNTAG_SET, + &dropuntag, sizeof(dropuntag)); + + return rc; +} + +int netc_port_dsa_add(struct netc_private *priv, int cpu_port, + int slave_port, const unsigned char *mac_addr) +{ + int rc = 0; + struct netc_cmd_port_dsa_add dsa_add = {0}; + + dsa_add.cpu_port = (uint8_t)cpu_port; + dsa_add.slave_port = (uint8_t)slave_port; + ether_addr_copy(dsa_add.mac_addr, mac_addr); + + rc = netc_xfer_set_cmd(priv, NETC_CMD_PORT_DSA_ADD, + &dsa_add, sizeof(dsa_add)); + + return rc; +} + +int netc_port_dsa_del(struct netc_private *priv, int slave_port) +{ + int rc = 0; + struct netc_cmd_port_dsa_del dsa_del = {0}; + + dsa_del.slave_port = (uint8_t)slave_port; + + rc = netc_xfer_set_cmd(priv, NETC_CMD_PORT_DSA_DEL, + &dsa_del, sizeof(dsa_del)); + + return rc; +} + +int netc_vlan_entry_add(struct netc_private *priv, + uint16_t vid, int port, bool untagged) +{ + struct device *dev = priv->ds->dev; + struct netc_cmd_vlan cmd_vlan = {0}; + int rc; + + cmd_vlan.vid = vid; + cmd_vlan.port = (uint8_t)port; + cmd_vlan.untagged = untagged; + + rc = netc_xfer_set_cmd(priv, NETC_CMD_VLAN_ADD, + &cmd_vlan, sizeof(cmd_vlan)); + if (rc < 0) { + dev_err(dev, "Failed to add vlan entry: %d\n", rc); + return rc; + } + + return 0; +} + +int netc_vlan_entry_del(struct netc_private *priv, uint16_t vid, int port) +{ + struct device *dev = priv->ds->dev; + struct netc_cmd_vlan cmd_vlan = {0}; + int rc; + + cmd_vlan.vid = vid; + cmd_vlan.port = (uint8_t)port; + + rc = netc_xfer_set_cmd(priv, NETC_CMD_VLAN_DEL, + &cmd_vlan, sizeof(cmd_vlan)); + if (rc < 0) { + dev_err(dev, "Failed to add vlan entry: %d\n", rc); + return rc; + } + + return 0; +} + +int netc_vlan_entry_read(struct netc_private *priv, + struct netc_vlan_entry *vlan, + uint32_t entry_id, uint32_t *next_id) +{ + struct device *dev = priv->ds->dev; + struct netc_cmd_vlan_dump vlan_dump = {0}; + int rc; + + rc = netc_xfer_get_cmd(priv, NETC_CMD_VLAN_DUMP, entry_id, + &vlan_dump, sizeof(vlan_dump)); + if (rc < 0) { + dev_err(dev, "Failed to read vlan entry 0x%08x: %d\n", + entry_id, rc); + return rc; + } + + vlan->entry_id = entry_id; + vlan->vid = vlan_dump.vid; + vlan->port_map = vlan_dump.port_map; + *next_id = vlan_dump.resume_entry_id; + + return 0; +} + +int netc_fdb_entry_add(struct netc_private *priv, + const unsigned char *mac_addr, + uint16_t vid, int port) +{ + struct device *dev = priv->ds->dev; + struct netc_cmd_fdb fdb_add = {0}; + int rc; + + ether_addr_copy(fdb_add.mac_addr, mac_addr); + fdb_add.vid = vid; + fdb_add.port = (uint8_t)port; + + rc = netc_xfer_set_cmd(priv, NETC_CMD_FDB_ADD, + &fdb_add, sizeof(fdb_add)); + if (rc < 0) { + dev_err(dev, "Failed to add fdb: %d\n", rc); + return rc; + } + + return 0; +} + +int netc_fdb_entry_del(struct netc_private *priv, + const unsigned char *mac_addr, + uint16_t vid, int port) +{ + struct device *dev = priv->ds->dev; + struct netc_cmd_fdb fdb_del = {0}; + int rc; + + ether_addr_copy(fdb_del.mac_addr, mac_addr); + fdb_del.vid = vid; + fdb_del.port = (uint8_t)port;; + + rc = netc_xfer_set_cmd(priv, NETC_CMD_FDB_DEL, + &fdb_del, sizeof(fdb_del)); + if (rc < 0) { + dev_err(dev, "Failed to delete fdb: %d\n", rc); + return rc; + } + + return 0; +} + +int netc_fdb_entry_get(struct netc_private *priv, struct netc_fdb_entry *fdb, + uint32_t entry_id, uint32_t *next_id) +{ + struct device *dev = priv->ds->dev; + struct netc_cmd_fdb_dump fdb_dump = {0}; + int rc; + + rc = netc_xfer_get_cmd(priv, NETC_CMD_FDB_DUMP, entry_id, + &fdb_dump, sizeof(fdb_dump)); + if (rc < 0) { + dev_err(dev, "Failed to get fdb entry: %d\n", rc); + return rc; + } + + *next_id = fdb_dump.resume_entry_id; + + ether_addr_copy(fdb->mac_addr, fdb_dump.mac_addr); + fdb->vid = fdb_dump.vid; + fdb->port_map = fdb_dump.port_map; + fdb->dynamic = fdb_dump.dynamic; + + return 0; +} + +int netc_streamid_set(struct netc_private *priv, int port_mask, uint16_t handle, + const unsigned char *mac, uint16_t vid, tsn_cb_streamid_type type) +{ + struct device *dev = priv->ds->dev; + struct netc_cmd_nullstreamid streamid = {0}; + int rc; + + streamid.type = type; + streamid.handle = handle; + streamid.vid = vid; + streamid.port_mask = port_mask; + ether_addr_copy(streamid.mac_addr, mac); + + rc = netc_xfer_set_cmd(priv, NETC_CMD_STREAMID_SET, &streamid, sizeof(streamid)); + if (rc < 0) { + dev_err(dev, "failed to add streamid: %d\n", handle); + return rc; + } + + return 0; +} + +int netc_streamid_del(struct netc_private *priv, uint16_t stream_handle) +{ + struct device *dev = priv->ds->dev; + int rc; + + rc = netc_xfer_set_cmd(priv, NETC_CMD_STREAMID_DEL, &stream_handle, sizeof(stream_handle)); + if (rc < 0) { + dev_err(dev, "failed to delete streamid: %d\n", stream_handle); + return rc; + } + + return 0; +} + +int netc_port_priority_map(struct netc_private *priv, int port, uint8_t *map) +{ + struct netc_cmd_priority_map prio_map; + int rc; + + prio_map.port = port; + memcpy(prio_map.map, map, sizeof(prio_map.map)); + + rc = netc_xfer_set_cmd(priv, NETC_CMD_PRIORITY_MAP_SET, &prio_map, sizeof(prio_map)); + + return rc; +} + +int netc_qbv_set(struct netc_private *priv, int port, int enable, + struct tc_taprio_qopt_offload *taprio) +{ + struct device *dev = priv->ds->dev; + struct netc_cmd_qbv_set_p1 qbvconf_p1 = {0}; + struct netc_cmd_qbv_set_p2 qbvconf_p2 = {0}; + struct netc_cmd_qbv_gcl qbvconf_gcl = {0}; + uint8_t buffer[16]; + uint8_t offset; + int rc; + + qbvconf_p1.port = port; + qbvconf_p1.enabled = enable; + qbvconf_p1.base_time = taprio->base_time; + qbvconf_p1.cycle_time = taprio->cycle_time; + qbvconf_p1.gcl_len = taprio->num_entries; + if (enable) + qbvconf_p1.gcl_len = taprio->num_entries; + else + qbvconf_p1.gcl_len = 0; + + if (qbvconf_p1.gcl_len > NETC_QBV_LIST_MAX_ENTRIES) + return -EINVAL; + + rc = netc_xfer_set_cmd(priv, NETC_CMD_QBV_SET_P1, &qbvconf_p1, sizeof(qbvconf_p1)); + if (rc < 0) + goto err; + + qbvconf_p2.cycle_time_ext = taprio->cycle_time_extension; + rc = netc_xfer_set_cmd(priv, NETC_CMD_QBV_SET_P2, &qbvconf_p2, sizeof(qbvconf_p2)); + if (rc < 0) + goto err; + + offset = 0; + memset(buffer, 0, sizeof(buffer)); + for (int i = 0; i < qbvconf_p1.gcl_len; i++) { + qbvconf_gcl.interval = taprio->entries[i].interval; + qbvconf_gcl.gate_mask = taprio->entries[i].gate_mask; + qbvconf_gcl.operation = taprio->entries[i].command; + memcpy(&buffer[offset], &qbvconf_gcl, sizeof(qbvconf_gcl)); + if (offset) { + rc = netc_xfer_set_cmd(priv, NETC_CMD_QBV_SET_GCL, buffer, sizeof(buffer)); + if (rc < 0) + goto err; + + offset = 0; + memset(buffer, 0, sizeof(buffer)); + } else { + offset = sizeof(qbvconf_gcl); + } + } + + if (offset) { + rc = netc_xfer_set_cmd(priv, NETC_CMD_QBV_SET_GCL, buffer, sizeof(buffer)); + if (rc < 0) + goto err; + } + + return 0; +err: + dev_err(dev, "failed to set qbv on port: %d\n", port); + + return rc; +} + +int netc_port_set_preemptible_tcs(struct dsa_switch *ds, int port, + unsigned long preemptible_tcs) +{ + struct netc_private *priv = ds->priv; + struct netc_cmd_qbu_set qbu_conf; + + memset(&qbu_conf, 0, sizeof(qbu_conf)); + qbu_conf.port = port; + qbu_conf.preemption_mask = (uint8_t)preemptible_tcs; + + return netc_xfer_set_cmd(priv, NETC_CMD_QBU_SET, + &qbu_conf, sizeof(qbu_conf)); +} + +int netc_port_set_mm(struct dsa_switch *ds, int port, + struct ethtool_mm_cfg *cfg, + struct netlink_ext_ack *extack) +{ + struct netc_private *priv = ds->priv; + struct netc_cmd_set_get_mm mm_conf; + u32 add_frag_size = 0; + int rc = 0; + + memset(&mm_conf, 0, sizeof(mm_conf)); + mm_conf.verify_time = (uint32_t)cfg->verify_time; + mm_conf.verify_enabled = cfg->verify_enabled ? 1 : 0; + mm_conf.tx_enabled = cfg->tx_enabled ? 1 : 0; + mm_conf.pmac_enabled = cfg->pmac_enabled ? 1 : 0; + mm_conf.port = (uint8_t)port; + + rc = ethtool_mm_frag_size_min_to_add((u32)cfg->tx_min_frag_size, + &add_frag_size, extack); + if (rc) + return rc; + + mm_conf.add_frag_size = (int32_t)add_frag_size; + + return netc_xfer_set_cmd(priv, NETC_CMD_MM_SET, &mm_conf, sizeof(mm_conf)); +} + +int netc_port_get_mm(struct dsa_switch *ds, int port, + struct ethtool_mm_state *state) +{ + struct netc_private *priv = ds->priv; + struct netc_cmd_set_get_mm mm_conf; + int rc; + + memset(&mm_conf, 0, sizeof(mm_conf)); + mm_conf.port = (uint8_t)port; + rc = netc_xfer_get_cmd(priv, NETC_CMD_MM_GET, port, &mm_conf, sizeof(mm_conf)); + if (rc) + return rc; + + state->verify_time = (u32)mm_conf.verify_time; + state->max_verify_time = NETC_GET_MM_MAX_VERIFY_TIME; + state->verify_status = (enum ethtool_mm_verify_status)mm_conf.verify_status; + state->tx_enabled = !!mm_conf.tx_enabled; + state->tx_active = !!mm_conf.tx_active; + state->pmac_enabled = !!mm_conf.pmac_enabled; + state->verify_enabled = !!mm_conf.verify_enabled; + state->tx_min_frag_size = + ethtool_mm_frag_size_add_to_min(mm_conf.add_frag_size); + state->rx_min_frag_size = state->tx_min_frag_size; + + return 0; +} + +static int netc_qci_sg_set(struct netc_private *priv, struct netc_stream_filter *filter) +{ + struct action_gate_entry *entry = NULL; + struct netc_cmd_psfp_sg_p1 sg_p1 = {0}; + struct netc_cmd_psfp_sg_p2 sg_p2 = {0}; + struct netc_cmd_psfp_sgl sgl = {0}; + int rc; + + sg_p1.index = filter->stream_handle; + sg_p1.base_time = filter->qci.gate.basetime; + sg_p1.cycle_time = filter->qci.gate.cycletime; + sg_p1.gcl_len = filter->qci.gate.num_entries; + rc = netc_xfer_set_cmd(priv, NETC_CMD_QCI_SG_SET_P1, &sg_p1, sizeof(sg_p1)); + if (rc < 0) + return rc; + + sg_p2.cycle_time_ext = filter->qci.gate.cycletimeext; + sg_p2.prio = filter->qci.gate.prio; + rc = netc_xfer_set_cmd(priv, NETC_CMD_QCI_SG_SET_P2, &sg_p2, sizeof(sg_p2)); + if (rc < 0) + return rc; + + for (int i = 0; i < filter->qci.gate.num_entries; i++) { + entry = &filter->qci.gate.entries[i]; + sgl.interval = entry->interval; + sgl.maxoctets = entry->maxoctets; + sgl.ipv = entry->ipv; + sgl.gate_state = entry->gate_state; + rc = netc_xfer_set_cmd(priv, NETC_CMD_QCI_SG_SET_GCL, &sgl, sizeof(sgl)); + if (rc < 0) + return rc; + } + + return 0; +} + +static int netc_qci_fm_set(struct netc_private *priv, struct netc_stream_filter *filter) +{ + struct netc_cmd_psfp_fm fm = {0}; + int rc; + + fm.index = filter->stream_handle; + fm.rate = filter->qci.police.rate; + fm.burst = filter->qci.police.burst; + rc = netc_xfer_set_cmd(priv, NETC_CMD_QCI_FM_SET, &fm, sizeof(fm)); + if (rc < 0) + return rc; + + return 0; +} + +int netc_qci_set(struct netc_private *priv, struct netc_stream_filter *filter, int port) +{ + struct device *dev = priv->ds->dev; + struct netc_cmd_psfp_sf sf = {0}; + int rc; + + sf.stream_handle = filter->stream_handle; + sf.priority_spec = filter->qci.priority_spec; + sf.port = port; + sf.maxsdu = filter->qci.maxsdu; + if (filter->qci.gate.num_entries) { + sf.sg_enable = 1; + rc = netc_qci_sg_set(priv, filter); + if (rc < 0) + return rc; + } + + if (filter->qci.police.rate) { + sf.fm_enable = 1; + rc = netc_qci_fm_set(priv, filter); + if (rc < 0) + return rc; + } + + rc = netc_xfer_set_cmd(priv, NETC_CMD_QCI_SF_SET, + &sf, sizeof(sf)); + if (rc < 0) { + dev_err(dev, "failed to set Qci setting: %d\n", rc); + return rc; + } + + return 0; +} + +int netc_qci_del(struct netc_private *priv, uint16_t handle, + uint32_t port) +{ + struct device *dev = priv->ds->dev; + int rc; + + rc = netc_xfer_set_cmd(priv, NETC_CMD_QCI_DEL, + &handle, sizeof(handle)); + if (rc < 0) { + dev_err(dev, "failed to delete Qci setting: %d\n", rc); + return rc; + } + + return 0; +} + +int netc_qci_get(struct netc_private *priv, uint16_t handle, struct flow_stats *stats) +{ + struct netc_cmd_psfp_response psfp_resp = {0}; + int rc; + + rc = netc_xfer_get_cmd(priv, NETC_CMD_QCI_GET, handle, + &psfp_resp, sizeof(psfp_resp)); + + if (rc != 0) + return rc; + + stats->pkts = psfp_resp.pkts; + stats->drops = psfp_resp.drops; + + return 0; +} + +int netc_frer_seqgen(struct netc_private *priv, struct netc_stream_filter *filter) +{ + struct device *dev = priv->ds->dev; + struct netc_cmd_frer_sg frer_sg = {0}; + int rc; + + frer_sg.stream_handle = filter->stream_handle; + frer_sg.iport = filter->seqgen.iport; + frer_sg.encap = filter->seqgen.enc; + + rc = netc_xfer_set_cmd(priv, NETC_CMD_FRER_SG_SET, + &frer_sg, sizeof(frer_sg)); + if (rc < 0) { + dev_err(dev, "failed to set FRER sequence generation: %d\n", rc); + return rc; + } + + return 0; +} + +int netc_frer_seqrec(struct netc_private *priv, struct netc_stream_filter *filter) +{ + struct device *dev = priv->ds->dev; + struct netc_cmd_frer_sr frer_sr = {0}; + int rc; + + frer_sr.stream_handle = filter->stream_handle; + frer_sr.eport = filter->seqrec.eport; + frer_sr.encap = filter->seqrec.enc; + frer_sr.alg = filter->seqrec.alg; + frer_sr.his_len = filter->seqrec.his_len; + frer_sr.reset_timeout = filter->seqrec.reset_timeout; + frer_sr.rtag_pop_en = filter->seqrec.rtag_pop_en; + + rc = netc_xfer_set_cmd(priv, NETC_CMD_FRER_SR_SET, + &frer_sr, sizeof(frer_sr)); + if (rc < 0) { + dev_err(dev, "failed to set FRER sequence recovery: %d\n", rc); + return rc; + } + + return 0; +} + +int netc_frer_sg_del(struct netc_private *priv, uint16_t stream_handle, uint32_t port) +{ + struct device *dev = priv->ds->dev; + int rc; + + rc = netc_xfer_set_cmd(priv, NETC_CMD_FRER_SG_DEL, + &stream_handle, sizeof(stream_handle)); + if (rc < 0) { + dev_err(dev, "failed to delete FRER setting: %d\n", rc); + return rc; + } + + return 0; +} + +int netc_frer_sr_del(struct netc_private *priv, uint16_t stream_handle, uint32_t port) +{ + struct device *dev = priv->ds->dev; + int rc; + + rc = netc_xfer_set_cmd(priv, NETC_CMD_FRER_SR_DEL, + &stream_handle, sizeof(stream_handle)); + if (rc < 0) { + dev_err(dev, "failed to delete FRER setting: %d\n", rc); + return rc; + } + + return 0; +} + +int netc_config_setup(struct netc_config *config) +{ + if (config->vlan_max_count) { + config->vlan = kcalloc(config->vlan_max_count, + sizeof(*config->vlan), + GFP_KERNEL); + if (!config->vlan) + return -ENOMEM; + } + + return 0; +} + +void netc_config_free(struct netc_config *config) +{ + kfree(config->vlan); +} diff --git a/drivers/net/dsa/netc/netc_config.h b/drivers/net/dsa/netc/netc_config.h new file mode 100644 index 000000000000..8bd64a1e7142 --- /dev/null +++ b/drivers/net/dsa/netc/netc_config.h @@ -0,0 +1,557 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2023-2024 NXP + */ + +#ifndef _NETC_CONFIG_H +#define _NETC_CONFIG_H + +#include +#include +#include +#include + +#define NETC_RT1180_DEVICE_ID 0xe001 +#define NETC_NUM_PORTS 5 +#define NETC_MAX_NUM_PORTS NETC_NUM_PORTS +#define NETC_NUM_TC 8 + +#define NETC_ETHTOOL_STATS_NUM_MAX 120 +#define NETC_QBV_LIST_MAX_ENTRIES 256 + +#define NETC_SPI_WORD_BITS 8 +#define NETC_SPI_MSG_WORD_BYTES 4 +#define NETC_SPI_MSG_HEADER_SIZE 20 +#define NETC_SPI_MSG_PARAM_SIZE 16 +#define NETC_SPI_MSG_MAXLEN 4096 +#define NETC_SPI_MSG_RESPONSE_TIME 1000 /* us */ + +#define NETC_CMD_DIR_SHIFT 31 +#define NETC_CMD_LEN_SHIFT 16 + +#define NETC_GET_MM_MAX_VERIFY_TIME (128U) + +enum netc_spi_rw_mode { + SPI_READ = 0, + SPI_WRITE = 1, +}; + +struct netc_cmd_hdr { + uint32_t cmd; + uint8_t param[NETC_SPI_MSG_PARAM_SIZE]; +}; + +/* Command */ +enum netc_cmd { + /* port related command */ + NETC_CMD_SYS_INFO_GET = 0x1, + NETC_CMD_PORT_DSA_ADD, + NETC_CMD_PORT_DSA_DEL, + NETC_CMD_PORT_MTU_SET, + NETC_CMD_PORT_MTU_GET, + NETC_CMD_PORT_PHYLINK_MODE_SET, + NETC_CMD_PORT_PHYLINK_STATUS_GET, + NETC_CMD_PORT_ETHTOOL_STATS_GET, + NETC_CMD_PORT_PVID_SET, + NETC_CMD_PORT_LINK_SET, + NETC_CMD_PORT_DROPUNTAG_SET, + + NETC_CMD_FDB_ADD = 0x1000, + NETC_CMD_FDB_DEL, + NETC_CMD_FDB_DUMP, + NETC_CMD_VLAN_ADD, + NETC_CMD_VLAN_DEL, + NETC_CMD_VLAN_DUMP, + NETC_CMD_FORWARD_MASK_SET, + + NETC_CMD_PTP_SYNC_SET = 0x2000, + NETC_CMD_TIMER_CUR_SET, + NETC_CMD_TIMER_CUR_GET, + NETC_CMD_TIMER_RATE_SET, + NETC_CMD_TIMER_RATE_GET, + NETC_CMD_TIMER_ADJTIME_SET, + NETC_CMD_TIMER_ADJFINE_SET, + NETC_CMD_TIMER_PPS_START, + NETC_CMD_TIMER_PPS_STOP, + NETC_CMD_TIMER_EXTTS_START, + NETC_CMD_TIMER_EXTTS_STOP, + + NETC_CMD_QBV_SET_P1 = 0x3000, + NETC_CMD_QBV_SET_P2, + NETC_CMD_QBV_SET_GCL, + NETC_CMD_QBU_SET, + NETC_CMD_MM_SET, + NETC_CMD_MM_GET, + NETC_CMD_QCI_SF_SET, + NETC_CMD_QCI_SG_SET_P1, + NETC_CMD_QCI_SG_SET_P2, + NETC_CMD_QCI_SG_SET_GCL, + NETC_CMD_QCI_FM_SET, + NETC_CMD_QCI_DEL, + NETC_CMD_QCI_GET, + NETC_CMD_FRER_SG_SET, + NETC_CMD_FRER_SG_DEL, + NETC_CMD_FRER_SR_SET, + NETC_CMD_FRER_SR_DEL, + NETC_CMD_STREAMID_SET, + NETC_CMD_STREAMID_DEL, + NETC_CMD_PRIORITY_MAP_SET, + + NETC_CMD_REG_SET = 0x4000, + NETC_CMD_REG_GET, + NETC_CMD_MAX_NUM, +}; + +struct netc_cmd_sysinfo { + uint16_t device_id; + uint16_t vendor_id; + uint8_t version_major; + uint8_t version_minor; + uint8_t version_revision; + uint8_t cpu_port; +}; + +/* command data for NETC_CMD_PORT_DSA_ADD */ +struct netc_cmd_port_dsa_add { + uint8_t cpu_port; /* switch port 0, 1, 2 or 3 */ + uint8_t slave_port; /* switch port 0, 1, 2 or 3 */ + uint8_t mac_addr[ETH_ALEN]; /* MAC address of master interface */ +}; + +/* command data for NETC_CMD_PORT_DSA_DEL */ +struct netc_cmd_port_dsa_del { + uint8_t slave_port; /* switch port 0, 1, 2 or 3 */ + uint8_t reserved[3]; +}; + +/* command data for NETC_CMD_PORT_MTU_SET */ +struct netc_cmd_port_mtu { + uint8_t port; /* switch port 0, 1, 2 or 3 */ + uint8_t reserved; + uint16_t mtu; +}; + +/* command data for NETC_CMD_PORT_PHYLINK_MODE_SET */ +struct netc_cmd_port_phylink_mode { + uint8_t port; /* switch port 0, 1, 2 or 3 */ + bool duplex; /* 0: half duplex; 1: full duplex */ + uint16_t speed; /* 10: 10Mbps ; 100: 100Mbps ; 1000: 1000Mbps */ +}; + +/* command data for NETC_CMD_PORT_PVID_SET */ +struct netc_cmd_port_pvid { + uint8_t port; /* switch port 0, 1, 2 or 3 */ + uint8_t reserved; + uint16_t pvid; +}; + +/* command data for netc_cmd_port_link */ +struct netc_cmd_port_link { + uint8_t port; /* switch port 0, 1, 2 or 3 */ + bool link; /* 0: down; 1: up */ + uint8_t reserved[2]; +}; + +/* command data for netc_cmd_port_dropuntag */ +struct netc_cmd_port_dropuntag { + uint8_t port; /* switch port 0, 1, 2 or 3 */ + uint8_t reserved; + uint16_t drop; +}; + +/* command data for NETC_CMD_FDB_ADD */ +struct netc_cmd_fdb { + uint8_t mac_addr[ETH_ALEN]; + uint16_t vid; + uint8_t port; /* switch port 0, 1, 2 or 3 */ + uint8_t reserved[3]; +}; + +/* command data for NETC_CMD_VLAN_ADD */ +struct netc_cmd_vlan { + uint16_t vid; + uint8_t port; /* switch port 0, 1, 2 or 3 */ + bool untagged; +}; + +/* data returned for NETC_CMD_PORT_PHYLINK_STATUS_GET */ +struct netc_cmd_port_phylink_status { + uint8_t port; /* switch port 0, 1, 2 or 3 */ + bool link; + uint16_t speed; + bool duplex; /* 0: down; 1: up */ + uint8_t reserved[3]; +}; + +/* command param */ +struct netc_cmd_read_param { + uint32_t id; +}; + +/* command data for NETC_CMD_REG_SET */ +struct netc_cmd_reg_cmd { + uint32_t reg; + uint32_t value; +}; + +/* data returned for NETC_CMD_FDB_DUMP */ +struct netc_cmd_fdb_dump { + uint8_t mac_addr[ETH_ALEN]; + uint16_t vid; + /* bit 0: switch port 0 etc. */ + uint32_t port_map; + bool dynamic; + uint8_t reserved[3]; + /* non-zero means there are remaining entries, 0 means no more entries */ + uint32_t resume_entry_id; +}; + +/* data returned for NETC_CMD_VLAN_DUMP */ +struct netc_cmd_vlan_dump { + uint16_t vid; + bool untagged; + uint8_t reserved; + /* bit 0: switch port 0 etc. */ + uint32_t port_map; + /* non-zero means there are remaining entries, 0 means no more entries */ + uint32_t resume_entry_id; +}; + +/* command param for NETC_CMD_TIMER_PPS_START */ +struct netc_cmd_timer_pps { + uint64_t pin_start; + uint32_t pin_duration32; +}; + +/* command param for NETC PTP */ +struct netc_ptp_ctl_param { + union { + uint64_t ns; + int64_t offset; + int64_t ppb; + }; + uint8_t clock_id; +}; + +/* stream identification */ +typedef enum { + STREAMID_RESERVED = 0, + /* Null Stream identification */ + STREAMID_NULL, + /* Source MAC and VLAN Stream identification */ + STREAMID_SMAC_VLAN, + /* Active Destination MAC and VLAN stream identification */ + STREAMID_DMAC_VLAN, + /* IP stream identification */ + STREAMID_IP, +} tsn_cb_streamid_type; + +enum netc_action_type { + NETC_STREAM_NULL, + NETC_STREAM_FRER_SEQGEN, + NETC_STREAM_FRER_SEQREC, + NETC_STREAM_QCI, +}; + +struct netc_stream { + struct list_head list; + unsigned long id; + int port_mask; + uint8_t mac[ETH_ALEN]; + uint16_t vid; + tsn_cb_streamid_type type; + enum netc_action_type action; + uint16_t handle; + s8 prio; + bool update; +}; + +typedef enum { + NETC_SEQI_RTAG = 1, + NETC_SEQI_HSR_SEQ_TAG, + NETC_SEQI_PRP_SEQ_TRAILER, +} netc_encapsulation_t; + +typedef enum { + NETC_SEQR_VECTOR = 0, + NETC_SEQR_MATCH, +} netc_seqr_algorithm_t; + +struct netc_stream_seqgen { + netc_encapsulation_t enc; + uint8_t iport; +}; + +struct netc_stream_seqrec { + netc_encapsulation_t enc; + netc_seqr_algorithm_t alg; + uint16_t reset_timeout; + uint8_t his_len; + uint8_t rtag_pop_en; + uint8_t eport; +}; + +enum tc_frer_tag_action { + FRER_TAG_NULL, + FRER_TAG_PUSH, + FRER_TAG_POP, +}; + +struct netc_stream_qci { + uint32_t maxsdu; + int8_t priority_spec; + struct { + int32_t prio; + uint64_t basetime; + uint32_t cycletime; + uint32_t cycletimeext; + uint16_t num_entries; + struct action_gate_entry *entries; + } gate; + struct { + uint32_t burst; + uint64_t rate; + } police; +}; + +struct netc_stream_filter { + struct list_head list; + uint16_t stream_handle; + union { + struct netc_stream_seqgen seqgen; + struct netc_stream_seqrec seqrec; + struct netc_stream_qci qci; + }; +}; + +/* command data for NETC_CMD_STREAMID_SET */ +struct netc_cmd_nullstreamid { + uint8_t mac_addr[ETH_ALEN]; + uint16_t vid; + uint16_t handle; + uint8_t type; + uint8_t port_mask; +}; + +/* command data for NETC_CMD_FRER_SG_SET */ +struct netc_cmd_frer_sg { + uint16_t stream_handle; + uint8_t encap; + uint8_t iport; +}; + +/* command data for NETC_CMD_FRER_SR_SET */ +struct netc_cmd_frer_sr { + uint16_t stream_handle; + uint16_t reset_timeout; + uint8_t his_len; + uint8_t encap; + uint8_t alg; + uint8_t rtag_pop_en; + uint8_t eport; + uint8_t reserved[3]; +}; + +struct netc_cmd_psfp_sg_p1 { + uint64_t base_time; + uint32_t cycle_time; + uint16_t gcl_len; + uint16_t index; +}; + +struct netc_cmd_psfp_sg_p2 { + uint32_t cycle_time_ext; + int32_t prio; + uint8_t reserved[8]; +}; + +struct netc_cmd_psfp_sgl { + uint32_t interval; + int32_t maxoctets; + int32_t ipv; + uint8_t gate_state; + uint8_t reserved[3]; +}; + +struct netc_cmd_psfp_fm { + uint64_t rate; + uint32_t burst; + uint16_t index; + uint8_t reserved[2]; +}; + +struct netc_cmd_psfp_sf { + uint32_t maxsdu; + uint16_t stream_handle; + int8_t priority_spec; + uint8_t sg_enable; + uint8_t fm_enable; + uint8_t port; + uint8_t reserved[6]; +}; + +struct netc_cmd_psfp_response { + uint64_t pkts; + uint64_t drops; +}; + +struct netc_cmd_priority_map { + uint8_t port; + uint8_t map[8]; + uint8_t reserved[7]; +}; + +struct netc_cmd_qbv_gcl { + uint32_t interval; + uint16_t gate_mask; + uint16_t operation; +}; + +struct netc_cmd_qbv_set_p1 { + uint64_t base_time; + uint32_t cycle_time; + uint16_t gcl_len; + uint8_t enabled; + uint8_t port; +}; + +struct netc_cmd_qbv_set_p2 { + uint32_t cycle_time_ext; + uint8_t reserved[12]; +}; + +struct netc_cmd_set_get_mm { + uint32_t verify_time; + uint32_t add_frag_size; + uint8_t verify_enabled; + uint8_t verify_status; + uint8_t tx_enabled; + uint8_t pmac_enabled; + uint8_t tx_active; + uint8_t port; + uint8_t reserved[2]; +}; + +struct netc_cmd_qbu_set { + uint8_t preemption_mask; + uint8_t port; + uint8_t reserved[2]; +}; + +struct netc_cmd_port_ethtool_stats { + uint64_t values[NETC_ETHTOOL_STATS_NUM_MAX]; +}; + +struct netc_mac_config { + uint8_t port; + uint16_t speed; + uint16_t vlanid; + bool link; + bool egress; + bool ingress; + bool duplex; + bool drptag; + bool drpuntag; + bool retag; +}; + +struct netc_fdb_entry { + uint8_t mac_addr[ETH_ALEN]; + uint16_t vid; + uint32_t port_map; /* bit 0: switch port 0 etc. */ + bool dynamic; +}; + +struct netc_vlan_entry { + uint16_t vid; + uint16_t port; + uint32_t port_map; + uint32_t tag_ports; + uint32_t entry_id; +}; + +struct netc_config { + uint16_t device_id; + uint16_t vendor_id; + uint8_t version_major; + uint8_t version_minor; + uint8_t version_revision; + uint8_t cpu_port_mode; + uint16_t tpid; + uint16_t tpid2; + struct netc_mac_config mac[NETC_MAX_NUM_PORTS]; + int cpu_port; + int vlan_count; + int vlan_max_count; + struct netc_vlan_entry *vlan; +}; + +struct netc_private; + +int netc_get_devinfo(struct netc_private *priv, struct netc_config *config); + +int netc_port_phylink_mode_set(struct netc_private *priv, + struct netc_mac_config *mac); +int netc_port_phylink_stats_get(struct netc_private *priv, + struct netc_mac_config *mac); +int netc_port_pvid_set(struct netc_private *priv, int port, uint16_t pvid); +int netc_port_link_set(struct netc_private *priv, int port, bool up); +int netc_port_dropuntag_set(struct netc_private *priv, int port, bool drop); + +int netc_port_mtu_set(struct netc_private *priv, int port, int mtu); +int netc_port_mtu_get(struct netc_private *priv, int port, int *mtu); + +int netc_port_pvid_set(struct netc_private *priv, int port, uint16_t pvid); + +int netc_port_dsa_add(struct netc_private *priv, int cpu_port, + int slave_port, const unsigned char *mac_addr); +int netc_port_dsa_del(struct netc_private *priv, int slave_port); + +int netc_fdb_entry_add(struct netc_private *priv, + const unsigned char *mac_addr, + uint16_t vid, int port); +int netc_fdb_entry_del(struct netc_private *priv, + const unsigned char *mac_addr, + uint16_t vid, int port); +int netc_fdb_entry_get(struct netc_private *priv, + struct netc_fdb_entry *fdb, + uint32_t entry_id, uint32_t *next_id); + +int netc_vlan_entry_add(struct netc_private *priv, + uint16_t vid, int port, bool untagged); +int netc_vlan_entry_del(struct netc_private *priv, uint16_t vid, int port); +int netc_vlan_entry_get(struct netc_private *priv, + struct netc_vlan_entry *vlan, + uint32_t entry_id, uint32_t *next_id); + +int netc_config_setup(struct netc_config *config); +void netc_config_free(struct netc_config *config); + +int netc_streamid_set(struct netc_private *priv, int port_mask, uint16_t handle, + const unsigned char *mac, uint16_t vid, tsn_cb_streamid_type type); +int netc_streamid_del(struct netc_private *priv, uint16_t handle); + +int netc_frer_seqgen(struct netc_private *priv, + struct netc_stream_filter *filter); +int netc_frer_seqrec(struct netc_private *priv, + struct netc_stream_filter *filter); +int netc_frer_sg_del(struct netc_private *priv, uint16_t handle, uint32_t port); +int netc_frer_sr_del(struct netc_private *priv, uint16_t handle, uint32_t port); + +int netc_qci_set(struct netc_private *priv, + struct netc_stream_filter *filter, int port); +int netc_qci_del(struct netc_private *priv, + uint16_t handle, uint32_t port); +int netc_qci_get(struct netc_private *priv, uint16_t handle, struct flow_stats *stats); +int netc_qbv_set(struct netc_private *priv, int port, int enable, + struct tc_taprio_qopt_offload *taprio); +int netc_port_priority_map(struct netc_private *priv, int port, uint8_t *map); + +int netc_port_set_preemptible_tcs(struct dsa_switch *ds, int port, + unsigned long preemptible_tcs); +int netc_port_set_mm(struct dsa_switch *ds, int port, + struct ethtool_mm_cfg *cfg, + struct netlink_ext_ack *extack); +int netc_port_get_mm(struct dsa_switch *ds, int port, + struct ethtool_mm_state *state); +#endif /* _NETC_CONFIG_H */ diff --git a/drivers/net/dsa/netc/netc_devlink.c b/drivers/net/dsa/netc/netc_devlink.c new file mode 100644 index 000000000000..963a6fd8775c --- /dev/null +++ b/drivers/net/dsa/netc/netc_devlink.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2023 NXP + */ + +#include "netc.h" + +static size_t netc_config_get_size(struct netc_private *priv) +{ + return sizeof(struct netc_config); +} + +static int +netc_region_config_snapshot(struct devlink *dl, + const struct devlink_region_ops *ops, + struct netlink_ext_ack *extack, + u8 **data) +{ + struct dsa_switch *ds = dsa_devlink_to_ds(dl); + struct netc_private *priv = ds->priv; + size_t len; + + len = netc_config_get_size(priv); + *data = kcalloc(len, sizeof(u8), GFP_KERNEL); + if (!*data) + return -ENOMEM; + + return netc_xfer_get_cmd(priv, NETC_CMD_SYS_INFO_GET, 0, *data, len); +} + +static struct devlink_region_ops netc_region_config_ops = { + .name = "config", + .snapshot = netc_region_config_snapshot, + .destructor = kfree, +}; + +enum netc_region_id { + NETC_REGION_CONFIG = 0, +}; + +struct netc_region { + const struct devlink_region_ops *ops; + size_t (*get_size)(struct netc_private *priv); +}; + +static struct netc_region netc_regions[] = { + [NETC_REGION_CONFIG] = { + .ops = &netc_region_config_ops, + .get_size = netc_config_get_size, + }, +}; + +int netc_devlink_info_get(struct dsa_switch *ds, + struct devlink_info_req *req, + struct netlink_ext_ack *extack) +{ + struct netc_private *priv = ds->priv; + int rc; + + rc = devlink_info_version_fixed_put(req, + DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, + priv->info->name); + return rc; +} + +int netc_devlink_setup(struct dsa_switch *ds) +{ + int i, num_regions = ARRAY_SIZE(netc_regions); + struct netc_private *priv = ds->priv; + const struct devlink_region_ops *ops; + struct devlink_region *region; + u64 size; + + priv->regions = kcalloc(num_regions, sizeof(struct devlink_region *), + GFP_KERNEL); + if (!priv->regions) + return -ENOMEM; + + for (i = 0; i < num_regions; i++) { + size = netc_regions[i].get_size(priv); + ops = netc_regions[i].ops; + + region = dsa_devlink_region_create(ds, ops, 1, size); + if (IS_ERR(region)) { + while (--i >= 0) + dsa_devlink_region_destroy(priv->regions[i]); + + kfree(priv->regions); + return PTR_ERR(region); + } + + priv->regions[i] = region; + } + + return 0; +} + +void netc_devlink_teardown(struct dsa_switch *ds) +{ + int i, num_regions = ARRAY_SIZE(netc_regions); + struct netc_private *priv = ds->priv; + + for (i = 0; i < num_regions; i++) + dsa_devlink_region_destroy(priv->regions[i]); + + kfree(priv->regions); +} diff --git a/drivers/net/dsa/netc/netc_ethtool.c b/drivers/net/dsa/netc/netc_ethtool.c new file mode 100644 index 000000000000..7e6ab46ffee6 --- /dev/null +++ b/drivers/net/dsa/netc/netc_ethtool.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2023 NXP + */ + +#include "netc.h" + +enum netc_stat_index { + /* RX stats */ + NETC_STAT_RX_BYTES, + NETC_STAT_RX_VALID_BYTES, + NETC_STAT_RX_PAUSE_FRAMES, + NETC_STAT_RX_VALID_FRAMES, + NETC_STAT_RX_VLAN_FRAMES, + NETC_STAT_RX_UC_FRAMES, + NETC_STAT_RX_MC_FRAMES, + NETC_STAT_RX_BC_FRAMES, + NETC_STAT_RX_FRAMES, + NETC_STAT_RX_MIN_FRAMES, + NETC_STAT_RX_64_FRAMES, + NETC_STAT_RX_65_127_FRAMES, + NETC_STAT_RX_128_255_FRAMES, + NETC_STAT_RX_256_511_FRAMES, + NETC_STAT_RX_512_1023_FRAMES, + NETC_STAT_RX_1024_1522_FRAMES, + NETC_STAT_RX_1523_MAX_FRAMES, + NETC_STAT_RX_CONTROL_FRAMES, + + /* TX stats */ + NETC_STAT_TX_BYTES, + NETC_STAT_TX_VALID_BYTES, + NETC_STAT_TX_PAUSE_FRAMES, + NETC_STAT_TX_VALID_FRAMES, + NETC_STAT_TX_VLAN_FRAMES, + NETC_STAT_TX_UC_FRAMES, + NETC_STAT_TX_MC_FRAMES, + NETC_STAT_TX_BC_FRAMES, + NETC_STAT_TX_FRAMES, + NETC_STAT_TX_MIN_FRAMES, + NETC_STAT_TX_64_FRAMES, + NETC_STAT_TX_65_127_FRAMES, + NETC_STAT_TX_128_255_FRAMES, + NETC_STAT_TX_256_511_FRAMES, + NETC_STAT_TX_512_1023_FRAMES, + NETC_STAT_TX_1024_1522_FRAMES, + NETC_STAT_TX_1523_MAX_FRAMES, + NETC_STAT_TX_CONTROL_FRAMES, + + NETC_STAT_RX_VALID_REASSEMBLED_FRAMES, + NETC_STAT_RX_ADDITIONAL_MPACKETS, + NETC_STAT_RX_ERROR_FRAME_REASSEMBLY, + NETC_STAT_RX_ERROR_FRAME_SMD, + NETC_STAT_TX_ADDITIONAL_MPACKETS, + NETC_STAT_TX_HOLD_TRANSITIONS, + + /* Error stats */ + NETC_STAT_RX_ERROR, + NETC_STAT_RX_ERROR_UNDERSIZE, + NETC_STAT_RX_ERROR_OVERSIZE, + NETC_STAT_RX_ERROR_FCS, + NETC_STAT_RX_ERROR_FRAGMENT, + NETC_STAT_RX_ERROR_JABBER, + NETC_STAT_RX_ERROR_DISCARD, + NETC_STAT_RX_ERROR_NO_TRUNCATED, + NETC_STAT_TX_ERROR_FCS, + NETC_STAT_TX_ERROR_UNDERSIZE, + + /* Discard stats */ + NETC_STAT_RX_DISCARD_COUNT, + NETC_STAT_RX_DISCARD_REASON0, + NETC_STAT_RX_DISCARD_TABLE_ID, + NETC_STAT_RX_DISCARD_ENTRY_ID, + NETC_STAT_TX_DISCARD_COUNT, + NETC_STAT_TX_DISCARD_REASON0, + NETC_STAT_TX_DISCARD_TABLE_ID, + NETC_STAT_TX_DISCARD_ENTRY_ID, + NETC_STAT_BRIDGE_DISCARD_COUNT, + NETC_STAT_BRIDGE_DISCARD_REASON0, + NETC_STAT_BRIDGE_DISCARD_TABLE_ID, + NETC_STAT_BRIDGE_DISCARD_ENTRY_ID, + + /* Q0 stats */ + NETC_STAT_Q0_REJECTED_BYTES, + NETC_STAT_Q0_REJECTED_FRAMES, + NETC_STAT_Q0_DEQUEUE_BYTES, + NETC_STAT_Q0_DEQUEUE_FRAMES, + NETC_STAT_Q0_DROPPED_BYTES, + NETC_STAT_Q0_DROPPED_FRAMES, + NETC_STAT_Q0_FRAMES, + + /* Q1 stats */ + NETC_STAT_Q1_REJECTED_BYTES, + NETC_STAT_Q1_REJECTED_FRAMES, + NETC_STAT_Q1_DEQUEUE_BYTES, + NETC_STAT_Q1_DEQUEUE_FRAMES, + NETC_STAT_Q1_DROPPED_BYTES, + NETC_STAT_Q1_DROPPED_FRAMES, + NETC_STAT_Q1_FRAMES, + + /* Q2 stats */ + NETC_STAT_Q2_REJECTED_BYTES, + NETC_STAT_Q2_REJECTED_FRAMES, + NETC_STAT_Q2_DEQUEUE_BYTES, + NETC_STAT_Q2_DEQUEUE_FRAMES, + NETC_STAT_Q2_DROPPED_BYTES, + NETC_STAT_Q2_DROPPED_FRAMES, + NETC_STAT_Q2_FRAMES, + + /* Q3 stats */ + NETC_STAT_Q3_REJECTED_BYTES, + NETC_STAT_Q3_REJECTED_FRAMES, + NETC_STAT_Q3_DEQUEUE_BYTES, + NETC_STAT_Q3_DEQUEUE_FRAMES, + NETC_STAT_Q3_DROPPED_BYTES, + NETC_STAT_Q3_DROPPED_FRAMES, + NETC_STAT_Q3_FRAMES, + + /* Q4 stats */ + NETC_STAT_Q4_REJECTED_BYTES, + NETC_STAT_Q4_REJECTED_FRAMES, + NETC_STAT_Q4_DEQUEUE_BYTES, + NETC_STAT_Q4_DEQUEUE_FRAMES, + NETC_STAT_Q4_DROPPED_BYTES, + NETC_STAT_Q4_DROPPED_FRAMES, + NETC_STAT_Q4_FRAMES, + + /* Q5 stats */ + NETC_STAT_Q5_REJECTED_BYTES, + NETC_STAT_Q5_REJECTED_FRAMES, + NETC_STAT_Q5_DEQUEUE_BYTES, + NETC_STAT_Q5_DEQUEUE_FRAMES, + NETC_STAT_Q5_DROPPED_BYTES, + NETC_STAT_Q5_DROPPED_FRAMES, + NETC_STAT_Q5_FRAMES, + + /* Q6 stats */ + NETC_STAT_Q6_REJECTED_BYTES, + NETC_STAT_Q6_REJECTED_FRAMES, + NETC_STAT_Q6_DEQUEUE_BYTES, + NETC_STAT_Q6_DEQUEUE_FRAMES, + NETC_STAT_Q6_DROPPED_BYTES, + NETC_STAT_Q6_DROPPED_FRAMES, + NETC_STAT_Q6_FRAMES, + + /* Q7 stats */ + NETC_STAT_Q7_REJECTED_BYTES, + NETC_STAT_Q7_REJECTED_FRAMES, + NETC_STAT_Q7_DEQUEUE_BYTES, + NETC_STAT_Q7_DEQUEUE_FRAMES, + NETC_STAT_Q7_DROPPED_BYTES, + NETC_STAT_Q7_DROPPED_FRAMES, + NETC_STAT_Q7_FRAMES, + NETC_STAT_NUM, +}; + +char netc_stat_name[][ETH_GSTRING_LEN] = { + /* RX stats */ + [NETC_STAT_RX_BYTES] = "in-bytes", + [NETC_STAT_RX_VALID_BYTES] = "in-valid-bytes", + [NETC_STAT_RX_PAUSE_FRAMES] = "in-pause-frames", + [NETC_STAT_RX_VALID_FRAMES] = "in-valid-frames", + [NETC_STAT_RX_VLAN_FRAMES] = "in-vlan-frames", + [NETC_STAT_RX_UC_FRAMES] = "in-uc-frames", + [NETC_STAT_RX_MC_FRAMES] = "in-mc-frames", + [NETC_STAT_RX_BC_FRAMES] = "in-bc-frames", + [NETC_STAT_RX_FRAMES] = "in-frames", + [NETC_STAT_RX_MIN_FRAMES] = "in-min-frames", + [NETC_STAT_RX_64_FRAMES] = "in-64-frames", + [NETC_STAT_RX_65_127_FRAMES] = "in-65-127-frames", + [NETC_STAT_RX_128_255_FRAMES] = "in-128-255-frames", + [NETC_STAT_RX_256_511_FRAMES] = "in-256-511-frames", + [NETC_STAT_RX_512_1023_FRAMES] = "in-512-1023-frames", + [NETC_STAT_RX_1024_1522_FRAMES] = "in-1024-1522-frames", + [NETC_STAT_RX_1523_MAX_FRAMES] = "in-1523-max-frames", + [NETC_STAT_RX_CONTROL_FRAMES] = "in-control-frames", + + /* TX stats */ + [NETC_STAT_TX_BYTES] = "out-bytes", + [NETC_STAT_TX_VALID_BYTES] = "out-valid-bytes", + [NETC_STAT_TX_PAUSE_FRAMES] = "out-pause-frames", + [NETC_STAT_TX_VALID_FRAMES] = "out-valid-frames", + [NETC_STAT_TX_VLAN_FRAMES] = "out-vlan-frames", + [NETC_STAT_TX_UC_FRAMES] = "out-uc-frames", + [NETC_STAT_TX_MC_FRAMES] = "out-mc-frames", + [NETC_STAT_TX_BC_FRAMES] = "out-bc-frames", + [NETC_STAT_TX_FRAMES] = "out-frames", + [NETC_STAT_TX_MIN_FRAMES] = "out-min-frames", + [NETC_STAT_TX_64_FRAMES] = "out-64-frames", + [NETC_STAT_TX_65_127_FRAMES] = "out-65-127-frames", + [NETC_STAT_TX_128_255_FRAMES] = "out-128-255-frames", + [NETC_STAT_TX_256_511_FRAMES] = "out-256-511-frames", + [NETC_STAT_TX_512_1023_FRAMES] = "out-512-1023-frames", + [NETC_STAT_TX_1024_1522_FRAMES] = "out-1024-1522-frames", + [NETC_STAT_TX_1523_MAX_FRAMES] = "out-1523-max-frames", + [NETC_STAT_TX_CONTROL_FRAMES] = "out-control-frames", + + [NETC_STAT_RX_VALID_REASSEMBLED_FRAMES] = "in-valid-reassembled-frames", + [NETC_STAT_RX_ADDITIONAL_MPACKETS] = "in-additional-mPackets", + [NETC_STAT_RX_ERROR_FRAME_REASSEMBLY] = "in-error-frame-reassembly", + [NETC_STAT_RX_ERROR_FRAME_SMD] = "in-error-frame-smd", + [NETC_STAT_TX_ADDITIONAL_MPACKETS] = "out-additional-mPackets", + [NETC_STAT_TX_HOLD_TRANSITIONS] = "out-hold-transitions", + + /* Error stats */ + [NETC_STAT_RX_ERROR] = "in-error", + [NETC_STAT_RX_ERROR_UNDERSIZE] = "in-error-undersize", + [NETC_STAT_RX_ERROR_OVERSIZE] = "in-error-oversize", + [NETC_STAT_RX_ERROR_FCS] = "in-error-fcs", + [NETC_STAT_RX_ERROR_FRAGMENT] = "in-error-fragment", + [NETC_STAT_RX_ERROR_JABBER] = "in-error-jabber", + [NETC_STAT_RX_ERROR_DISCARD] = "in-error-discard", + [NETC_STAT_RX_ERROR_NO_TRUNCATED] = "in-error-dicard-no-truncated", + [NETC_STAT_TX_ERROR_FCS] = "out-error-fcs", + [NETC_STAT_TX_ERROR_UNDERSIZE] = "out-error-undersize", + + /* Discard stats */ + [NETC_STAT_RX_DISCARD_COUNT] = "in-discard-count", + [NETC_STAT_RX_DISCARD_REASON0] = "in-discard-reason0", + [NETC_STAT_RX_DISCARD_TABLE_ID] = "in-discard-table-id", + [NETC_STAT_RX_DISCARD_ENTRY_ID] = "in-discard-entry-id", + [NETC_STAT_TX_DISCARD_COUNT] = "out-discard-count", + [NETC_STAT_TX_DISCARD_REASON0] = "out-discard-reason0", + [NETC_STAT_TX_DISCARD_TABLE_ID] = "out-discard-table-id", + [NETC_STAT_TX_DISCARD_ENTRY_ID] = "out-discard-entry-id", + [NETC_STAT_BRIDGE_DISCARD_COUNT] = "bridge-discard-count", + [NETC_STAT_BRIDGE_DISCARD_REASON0] = "bridge-discard-reason0", + [NETC_STAT_BRIDGE_DISCARD_TABLE_ID] = "bridge-discard-table-id", + [NETC_STAT_BRIDGE_DISCARD_ENTRY_ID] = "bridge-discard-entry-id", + + /* Q0 stats */ + [NETC_STAT_Q0_REJECTED_BYTES] = "q0-rejected-bytes", + [NETC_STAT_Q0_REJECTED_FRAMES] = "q0-rejected-frames", + [NETC_STAT_Q0_DEQUEUE_BYTES] = "q0-dequeue-bytes", + [NETC_STAT_Q0_DEQUEUE_FRAMES] = "q0-dequeue-frames", + [NETC_STAT_Q0_DROPPED_BYTES] = "q0-dropped-bytes", + [NETC_STAT_Q0_DROPPED_FRAMES] = "q0-dropped-frames", + [NETC_STAT_Q0_FRAMES] = "q0-frames", + + /* Q1 stats */ + [NETC_STAT_Q1_REJECTED_BYTES] = "q1-rejected-bytes", + [NETC_STAT_Q1_REJECTED_FRAMES] = "q1-rejected-frames", + [NETC_STAT_Q1_DEQUEUE_BYTES] = "q1-dequeue-bytes", + [NETC_STAT_Q1_DEQUEUE_FRAMES] = "q1-dequeue-frames", + [NETC_STAT_Q1_DROPPED_BYTES] = "q1-dropped-bytes", + [NETC_STAT_Q1_DROPPED_FRAMES] = "q1-dropped-frames", + [NETC_STAT_Q1_FRAMES] = "q1-frames", + + /* Q2 stats */ + [NETC_STAT_Q2_REJECTED_BYTES] = "q2-rejected-bytes", + [NETC_STAT_Q2_REJECTED_FRAMES] = "q2-rejected-frames", + [NETC_STAT_Q2_DEQUEUE_BYTES] = "q2-dequeue-bytes", + [NETC_STAT_Q2_DEQUEUE_FRAMES] = "q2-dequeue-frames", + [NETC_STAT_Q2_DROPPED_BYTES] = "q2-dropped-bytes", + [NETC_STAT_Q2_DROPPED_FRAMES] = "q2-dropped-frames", + [NETC_STAT_Q2_FRAMES] = "q2-frames", + + /* Q3 stats */ + [NETC_STAT_Q3_REJECTED_BYTES] = "q3-rejected-bytes", + [NETC_STAT_Q3_REJECTED_FRAMES] = "q3-rejected-frames", + [NETC_STAT_Q3_DEQUEUE_BYTES] = "q3-dequeue-bytes", + [NETC_STAT_Q3_DEQUEUE_FRAMES] = "q3-dequeue-frames", + [NETC_STAT_Q3_DROPPED_BYTES] = "q3-dropped-bytes", + [NETC_STAT_Q3_DROPPED_FRAMES] = "q3-dropped-frames", + [NETC_STAT_Q3_FRAMES] = "q3-frames", + + /* Q4 stats */ + [NETC_STAT_Q4_REJECTED_BYTES] = "q4-rejected-bytes", + [NETC_STAT_Q4_REJECTED_FRAMES] = "q4-rejected-frames", + [NETC_STAT_Q4_DEQUEUE_BYTES] = "q4-dequeue-bytes", + [NETC_STAT_Q4_DEQUEUE_FRAMES] = "q4-dequeue-frames", + [NETC_STAT_Q4_DROPPED_BYTES] = "q4-dropped-bytes", + [NETC_STAT_Q4_DROPPED_FRAMES] = "q4-dropped-frames", + [NETC_STAT_Q4_FRAMES] = "q4-frames", + + /* Q5 stats */ + [NETC_STAT_Q5_REJECTED_BYTES] = "q5-rejected-bytes", + [NETC_STAT_Q5_REJECTED_FRAMES] = "q5-rejected-frames", + [NETC_STAT_Q5_DEQUEUE_BYTES] = "q5-dequeue-bytes", + [NETC_STAT_Q5_DEQUEUE_FRAMES] = "q5-dequeue-frames", + [NETC_STAT_Q5_DROPPED_BYTES] = "q5-dropped-bytes", + [NETC_STAT_Q5_DROPPED_FRAMES] = "q5-dropped-frames", + [NETC_STAT_Q5_FRAMES] = "q5-frames", + + /* Q6 stats */ + [NETC_STAT_Q6_REJECTED_BYTES] = "q6-rejected-bytes", + [NETC_STAT_Q6_REJECTED_FRAMES] = "q6-rejected-frames", + [NETC_STAT_Q6_DEQUEUE_BYTES] = "q6-dequeue-bytes", + [NETC_STAT_Q6_DEQUEUE_FRAMES] = "q6-dequeue-frames", + [NETC_STAT_Q6_DROPPED_BYTES] = "q6-dropped-bytes", + [NETC_STAT_Q6_DROPPED_FRAMES] = "q6-dropped-frames", + [NETC_STAT_Q6_FRAMES] = "q6-frames", + + /* Q7 stats */ + [NETC_STAT_Q7_REJECTED_BYTES] = "q7-rejected-bytes", + [NETC_STAT_Q7_REJECTED_FRAMES] = "q7-rejected-frames", + [NETC_STAT_Q7_DEQUEUE_BYTES] = "q7-dequeue-bytes", + [NETC_STAT_Q7_DEQUEUE_FRAMES] = "q7-dequeue-frames", + [NETC_STAT_Q7_DROPPED_BYTES] = "q7-dropped-bytes", + [NETC_STAT_Q7_DROPPED_FRAMES] = "q7-dropped-frames", + [NETC_STAT_Q7_FRAMES] = "q7-frames", +}; + +void netc_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data) +{ + struct netc_private *priv = ds->priv; + struct netc_cmd_port_ethtool_stats stats; + int rc; + enum netc_stat_index i; + + rc = netc_xfer_get_cmd(priv, NETC_CMD_PORT_ETHTOOL_STATS_GET, + port, &stats, sizeof(stats)); + + if (rc) { + dev_err(ds->dev, + "Failed to get port %d stats\n", port); + return; + } + + for (i = 0; i < NETC_STAT_NUM; i++) + data[i] = stats.values[i]; +} + +void netc_get_strings(struct dsa_switch *ds, int port, + u32 stringset, u8 *data) +{ + enum netc_stat_index i; + char *p = data; + + if (stringset != ETH_SS_STATS) + return; + + for (i = 0; i < NETC_STAT_NUM; i++) { + strscpy(p, netc_stat_name[i], ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } +} + +int netc_get_sset_count(struct dsa_switch *ds, int port, int sset) +{ + if (sset != ETH_SS_STATS) + return -EOPNOTSUPP; + + return NETC_STAT_NUM; +} diff --git a/drivers/net/dsa/netc/netc_main.c b/drivers/net/dsa/netc/netc_main.c new file mode 100644 index 000000000000..5aa93b3b295c --- /dev/null +++ b/drivers/net/dsa/netc/netc_main.c @@ -0,0 +1,1439 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2023-2024 NXP + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "netc.h" + +uint8_t netc_default_priority_map[] = {0, 1, 2, 3, 4, 5, 6, 7}; + +int netc_is_vlan_configured(struct netc_private *priv, uint16_t vid) +{ + struct netc_vlan_entry *vlan; + int count, i; + + vlan = priv->config.vlan; + count = priv->config.vlan_count; + + for (i = 0; i < count; i++) { + if (vlan[i].vid == vid) + return i; + } + + /* Return an invalid entry index if not found */ + return -1; +} + +static bool vid_is_netc_dsa_8021q(struct dsa_switch *ds, u16 vid) +{ + int port; + struct dsa_port *dp; + unsigned int bridge_num; + u16 standalone_vid, bridge_vid; + + for (port = 0; port < ds->num_ports; port++) { + dp = dsa_to_port(ds, port); + standalone_vid = dsa_tag_8021q_standalone_vid(dp); + + if (vid == standalone_vid) + return true; + + if (dp->bridge) { + bridge_num = dsa_port_bridge_num_get(dp); + bridge_vid = dsa_tag_8021q_bridge_vid(bridge_num); + + if (vid == bridge_vid) + return true; + } + } + + return false; +} + +static int netc_drop_untagged(struct dsa_switch *ds, int port, bool drop) +{ + struct netc_private *priv = ds->priv; + struct netc_mac_config *mac; + + mac = &priv->config.mac[port]; + if (mac->drpuntag == drop) + return 0; + + mac->drpuntag = drop; + + return netc_port_dropuntag_set(priv, port, drop); +} + +static int netc_pvid_apply(struct netc_private *priv, int port, uint16_t pvid) +{ + struct netc_mac_config *mac; + + mac = &priv->config.mac[port]; + if (mac->vlanid == pvid) + return 0; + + mac->vlanid = pvid; + + return netc_port_pvid_set(priv, port, pvid); +} + +static int netc_commit_pvid(struct dsa_switch *ds, int port) +{ + struct dsa_port *dp = dsa_to_port(ds, port); + struct net_device *br = dsa_port_bridge_dev_get(dp); + struct netc_private *priv = ds->priv; + bool drop_untagged = false; + int rc; + uint16_t pvid; + + if (br && br_vlan_enabled(br)) + pvid = priv->bridge_pvid[port]; + else + pvid = priv->tag_8021q_pvid[port]; + + rc = netc_pvid_apply(priv, port, pvid); + if (rc) + return rc; + + /* + * Only force dropping of untagged packets when the port is under a + * VLAN-aware bridge. When the tag_8021q pvid is used, we are + * deliberately removing the RX VLAN from the port's VMEMB_PORT list, + * to prevent DSA tag spoofing from the link partner. Untagged packets + * are the only ones that should be received with tag_8021q, so + * definitely don't drop them. + */ + if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) + drop_untagged = true; + + return netc_drop_untagged(ds, port, drop_untagged); +} + +static int netc_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, uint16_t vid, + struct dsa_db db) +{ + struct netc_private *priv = ds->priv; + int rc; + + if (!vid) { + switch (db.type) { + case DSA_DB_PORT: + vid = dsa_tag_8021q_standalone_vid(db.dp); + break; + case DSA_DB_BRIDGE: + vid = dsa_tag_8021q_bridge_vid(db.bridge.num); + break; + default: + return -EOPNOTSUPP; + } + } + + /* Allow enough time between consecutive calls for adding FDB entry */ + usleep_range(NETC_SPI_MSG_RESPONSE_TIME, + NETC_SPI_MSG_RESPONSE_TIME * 10); + + mutex_lock(&priv->fdb_lock); + rc = netc_fdb_entry_add(priv, addr, vid, port); + mutex_unlock(&priv->fdb_lock); + + return rc; +} + +static int netc_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, uint16_t vid, + struct dsa_db db) +{ + struct netc_private *priv = ds->priv; + int rc; + + if (!vid) { + switch (db.type) { + case DSA_DB_PORT: + vid = dsa_tag_8021q_standalone_vid(db.dp); + break; + case DSA_DB_BRIDGE: + vid = dsa_tag_8021q_bridge_vid(db.bridge.num); + break; + default: + return -EOPNOTSUPP; + } + } + + mutex_lock(&priv->fdb_lock); + rc = netc_fdb_entry_del(priv, addr, vid, port); + mutex_unlock(&priv->fdb_lock); + + return rc; +} + +static int netc_fdb_dump(struct dsa_switch *ds, int port, + dsa_fdb_dump_cb_t *cb, void *data) +{ + struct netc_private *priv = ds->priv; + struct device *dev = ds->dev; + u32 entry_id = 0, next_id = 0; + int rc; + + while (1) { + struct netc_fdb_entry fdb = {0}; + + rc = netc_fdb_entry_get(priv, &fdb, entry_id, &next_id); + /* No fdb entry at i, not an issue */ + if (rc) { + dev_err(dev, "Failed to dump FDB: %d\n", rc); + return rc; + } + + if (next_id == 0) /* This entry is empty */ + return 0; + + /* + * FDB dump callback is per port. This means we have to + * disregard a valid entry if it's not for this port, even if + * only to revisit it later. This is inefficient because the + * 1024-sized FDB table needs to be traversed 4 times through + * SPI during a 'bridge fdb show' command. + */ + if (fdb.port_map & BIT(port)) { + /* Need to hide the dsa_8021q VLANs from the user. */ + if (vid_is_netc_dsa_8021q(ds, fdb.vid)) + fdb.vid = 0; + + rc = cb(fdb.mac_addr, fdb.vid, fdb.dynamic, data); + if (rc) + return rc; + } + + entry_id = next_id; + + if (entry_id == 0 || entry_id == 0xffffffff) + break; + } + + return 0; +} + +static int netc_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + return netc_fdb_add(ds, port, mdb->addr, mdb->vid, db); +} + +static int netc_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + return netc_fdb_del(ds, port, mdb->addr, mdb->vid, db); +} + +static int netc_parse_ports_node(struct netc_private *priv, + struct device_node *ports_node) +{ + struct device *dev = &priv->spidev->dev; + struct device_node *child; + + for_each_available_child_of_node(ports_node, child) { + struct device_node *phy_node; + phy_interface_t phy_mode; + u32 index; + int err; + + /* Get switch port number from DT */ + if (of_property_read_u32(child, "reg", &index) < 0) { + dev_err(dev, "Port number not defined in device tree\n"); + of_node_put(child); + return -ENODEV; + } + + /* Get PHY mode from DT */ + err = of_get_phy_mode(child, &phy_mode); + if (err) { + dev_err(dev, "Failed to read phy-mode or phy-interface-type %d\n", + index); + of_node_put(child); + return -ENODEV; + } + + phy_node = of_parse_phandle(child, "phy-handle", 0); + if (!phy_node) { + if (!of_phy_is_fixed_link(child)) { + dev_err(dev, "phy-handle or fixed-link properties missing!\n"); + of_node_put(child); + return -ENODEV; + } + /* phy-handle is missing, but fixed-link isn't. + * So it's a fixed link. Default to PHY role. + */ + priv->fixed_link[index] = true; + } else { + of_node_put(phy_node); + } + + priv->phy_mode[index] = phy_mode; + } + + return 0; +} + +static int netc_parse_dt(struct netc_private *priv) +{ + struct device *dev = &priv->spidev->dev; + struct device_node *switch_node = dev->of_node; + struct device_node *ports_node; + int rc; + + ports_node = of_get_child_by_name(switch_node, "ports"); + if (!ports_node) + ports_node = of_get_child_by_name(switch_node, "ethernet-ports"); + if (!ports_node) { + dev_err(dev, "Incorrect bindings: absent \"ports\" node\n"); + return -ENODEV; + } + + rc = netc_parse_ports_node(priv, ports_node); + of_node_put(ports_node); + + return rc; +} + +static void netc_mac_link_down(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface) +{ + struct netc_private *priv = ds->priv; + struct netc_mac_config *mac; + + mac = &priv->config.mac[port]; + + mac->egress = false; + + netc_port_link_set(priv, port, false); +} + +static void netc_mac_link_up(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface, + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + struct netc_private *priv = ds->priv; + struct netc_mac_config *mac; + + mac = &priv->config.mac[port]; + + mac->speed = speed; + mac->egress = true; + + netc_port_phylink_mode_set(priv, mac); + netc_port_link_set(priv, port, true); +} + +static void netc_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) +{ + struct netc_private *priv = ds->priv; + phy_interface_t phy_mode; + + phy_mode = priv->phy_mode[port]; + __set_bit(phy_mode, config->supported_interfaces); + + /* + * The MAC does not support pause frames, and also doesn't + * support half-duplex traffic modes. + */ + config->mac_capabilities = MAC_10FD | MAC_100FD; + config->mac_capabilities |= MAC_1000FD; +} + +static int netc_bridge_member(struct dsa_switch *ds, int port, + struct dsa_bridge bridge, bool member) +{ + int rc; + + rc = netc_commit_pvid(ds, port); + if (rc) + return rc; + + return 0; +} + +static int netc_bridge_join(struct dsa_switch *ds, int port, + struct dsa_bridge bridge, + bool *tx_fwd_offload, + struct netlink_ext_ack *extack) +{ + int rc; + + rc = netc_bridge_member(ds, port, bridge, true); + if (rc) + return rc; + + rc = dsa_tag_8021q_bridge_join(ds, port, bridge); + if (rc) { + netc_bridge_member(ds, port, bridge, false); + return rc; + } + + *tx_fwd_offload = true; + + return 0; +} + +static void netc_bridge_leave(struct dsa_switch *ds, int port, + struct dsa_bridge bridge) +{ + dsa_tag_8021q_bridge_leave(ds, port, bridge); + netc_bridge_member(ds, port, bridge, false); +} + +static enum dsa_tag_protocol +netc_get_tag_protocol(struct dsa_switch *ds, int port, + enum dsa_tag_protocol mp) +{ + struct netc_private *priv = ds->priv; + + return priv->info->tag_proto; +} + +int netc_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, + struct netlink_ext_ack *extack) +{ + struct netc_private *priv = ds->priv; + struct netc_config *config = &priv->config; + int rc; + + if (enabled) { + /* Enable VLAN filtering. */ + config->tpid = ETH_P_8021Q; + config->tpid2 = ETH_P_8021AD; + } else { + /* Disable VLAN filtering. */ + config->tpid = ETH_P_8021Q; + config->tpid2 = ETH_P_NETC; + } + + for (port = 0; port < ds->num_ports; port++) { + if (dsa_is_unused_port(ds, port)) + continue; + + rc = netc_commit_pvid(ds, port); + if (rc) + return rc; + } + + return 0; +} + +static int netc_bridge_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) +{ + struct netc_private *priv = ds->priv; + uint16_t flags = vlan->flags; + bool untagged = false; + int rc; + + /* Be sure to deny the configuration done by tag_8021q. */ + if (vid_is_netc_dsa_8021q(ds, vlan->vid)) { + NL_SET_ERR_MSG_MOD(extack, + "VLAN ID 3072-3076 & 3088 reserved for dsa_8021q operation"); + return -EBUSY; + } + + /* Always install bridge VLANs as egress-tagged on CPU and DSA ports */ + if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) + flags = 0; + + if (flags & BRIDGE_VLAN_INFO_UNTAGGED) + untagged = true; + + rc = netc_vlan_entry_add(priv, vlan->vid, port, untagged); + if (rc) + return rc; + + if (vlan->flags & BRIDGE_VLAN_INFO_PVID) + priv->bridge_pvid[port] = vlan->vid; + + /* Allow enough time between adding VLAN entry and setting PVID */ + usleep_range(NETC_SPI_MSG_RESPONSE_TIME, + NETC_SPI_MSG_RESPONSE_TIME * 10); + + return netc_commit_pvid(ds, port); +} + +static int netc_bridge_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + struct netc_private *priv = ds->priv; + int rc; + + rc = netc_vlan_entry_del(priv, vlan->vid, port); + if (rc) + return rc; + + /* + * In case the pvid was deleted, make sure that untagged packets will + * be dropped. + */ + return netc_commit_pvid(ds, port); +} + +static int netc_8021q_vlan_add(struct dsa_switch *ds, int port, + uint16_t vid, uint16_t flags) +{ + struct netc_private *priv = ds->priv; + int rc; + + rc = netc_vlan_entry_add(priv, vid, port, false); + if (rc) + return rc; + + if (flags & BRIDGE_VLAN_INFO_PVID) + priv->tag_8021q_pvid[port] = vid; + + /* Allow enough time between adding VLAN entry and setting PVID */ + usleep_range(NETC_SPI_MSG_RESPONSE_TIME, + NETC_SPI_MSG_RESPONSE_TIME * 10); + + return netc_commit_pvid(ds, port); +} + +static int netc_8021q_vlan_del(struct dsa_switch *ds, int port, uint16_t vid) +{ + struct netc_private *priv = ds->priv; + + return netc_vlan_entry_del(priv, vid, port); +} + +static int netc_prechangeupper(struct dsa_switch *ds, int port, + struct netdev_notifier_changeupper_info *info) +{ + struct netlink_ext_ack *extack = info->info.extack; + struct net_device *upper = info->upper_dev; + struct dsa_switch_tree *dst = ds->dst; + struct dsa_port *dp; + + if (is_vlan_dev(upper)) { + NL_SET_ERR_MSG_MOD(extack, "8021q uppers are not supported"); + return -EBUSY; + } + + if (netif_is_bridge_master(upper)) { + list_for_each_entry(dp, &dst->ports, list) { + struct net_device *br = dsa_port_bridge_dev_get(dp); + + if (br && br != upper && br_vlan_enabled(br)) { + NL_SET_ERR_MSG_MOD(extack, + "Only one VLAN-aware bridge is supported"); + return -EBUSY; + } + } + } + + return 0; +} + +static int netc_connect_tag_protocol(struct dsa_switch *ds, + enum dsa_tag_protocol proto) +{ + struct netc_private *priv = ds->priv; + struct netc_tagger_data *tagger_data; + + if (proto != priv->info->tag_proto) + return -EPROTONOSUPPORT; + + tagger_data = netc_tagger_data(ds); + tagger_data->meta_tstamp_handler = netc_process_meta_tstamp; + + return 0; +} + +static int netc_stream_identify(struct flow_cls_offload *f, struct netc_stream *stream) +{ + struct flow_rule *rule = flow_cls_offload_flow_rule(f); + struct flow_dissector *dissector = rule->match.dissector; + + if (dissector->used_keys & + ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) | + BIT(FLOW_DISSECTOR_KEY_BASIC) | + BIT(FLOW_DISSECTOR_KEY_VLAN) | + BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS))) + return -EOPNOTSUPP; + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { + struct flow_match_eth_addrs match; + + flow_rule_match_eth_addrs(rule, &match); + if (is_zero_ether_addr(match.mask->src) && + !is_zero_ether_addr(match.mask->dst)) { + ether_addr_copy(stream->mac, match.key->dst); + stream->type = STREAMID_NULL; + } else if (!is_zero_ether_addr(match.mask->src) && + is_zero_ether_addr(match.mask->dst)) { + ether_addr_copy(stream->mac, match.key->src); + stream->type = STREAMID_SMAC_VLAN; + } else + return -EOPNOTSUPP; + } else { + return -EOPNOTSUPP; + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) { + struct flow_match_vlan match; + + flow_rule_match_vlan(rule, &match); + if (match.mask->vlan_priority) + stream->prio = match.key->vlan_priority; + else + stream->prio = -1; + + if (!match.mask->vlan_id) + return -EOPNOTSUPP; + stream->vid = match.key->vlan_id; + } else { + stream->vid = 0; + } + + stream->id = f->cookie; + + return 0; +} + +static struct netc_stream * +netc_stream_table_lookup(struct list_head *stream_list, + struct netc_stream *stream) +{ + struct netc_stream *tmp; + + list_for_each_entry(tmp, stream_list, list) + if (ether_addr_equal(tmp->mac, stream->mac) && + tmp->vid == stream->vid && tmp->port_mask == stream->port_mask && + tmp->type == stream->type) + return tmp; + + return NULL; +} + +static int netc_stream_handle_alloc(struct netc_private *priv) +{ + int i; + for (i = 0; i < MAX_SSIDS; i++) { + if (priv->psfp.ssids[i] == 0) { + priv->psfp.ssids[i] = 1; + priv->psfp.num_ssids++; + return i; + } + } + + return -EINVAL; +} + +static int netc_stream_handle_del(struct netc_private *priv, u32 handle) +{ + if (handle < 0 || handle > MAX_SSIDS) + return -EINVAL; + + if (priv->psfp.ssids[handle] == 1) { + priv->psfp.ssids[handle] = 0; + priv->psfp.num_ssids--; + } + + return 0; +} + +static int netc_stream_table_add(struct netc_private *priv, struct list_head *stream_list, + struct netc_stream *stream, struct netlink_ext_ack *extack) +{ + struct netc_stream *stream_entry; + int rc; + + stream_entry = kmemdup(stream, sizeof(*stream_entry), GFP_KERNEL); + if (!stream_entry) + return -ENOMEM; + + if (stream->update) { + rc = netc_streamid_set(priv, stream_entry->port_mask, stream_entry->handle, + stream_entry->mac, stream_entry->vid, stream_entry->type); + if (rc) { + kfree(stream_entry); + return rc; + } + } + + list_add_tail(&stream_entry->list, stream_list); + + return 0; +} + +static struct netc_stream * +netc_stream_table_get(struct list_head *stream_list, unsigned long id) +{ + struct netc_stream *tmp; + + list_for_each_entry(tmp, stream_list, list) + if (tmp->id == id) + return tmp; + + return NULL; +} + +static int netc_cls_flower_add(struct dsa_switch *ds, int port, + struct flow_cls_offload *f, bool ingress) +{ + struct dsa_port *dp = dsa_to_port(ds, port); + struct netc_private *priv = ds->priv; + struct netlink_ext_ack *extack = f->common.extack; + const struct flow_action_entry *a; + struct netc_stream stream = {.action = NETC_STREAM_NULL}; + struct netc_stream *stream_entry; + struct netc_psfp_list *psfp; + struct netc_stream_filter filter = {0}; + int cpu_port = dp->cpu_dp->index; + uint64_t rate; + int i, rc; + uint32_t handle; + bool set_stream = false; + + psfp = &priv->psfp; + + rc = netc_stream_identify(f, &stream); + if (rc) { + NL_SET_ERR_MSG_MOD(extack, "Only can match on VID and dest MAC"); + return rc; + } + + mutex_lock(&psfp->lock); + + flow_action_for_each(i, a, &f->rule->action) { + switch (a->id) { + case FLOW_ACTION_FRER: + if ((a->frer.recover && a->frer.tag_action == FRER_TAG_PUSH) || + (!a->frer.recover && a->frer.tag_action != FRER_TAG_PUSH)) { + NL_SET_ERR_MSG_MOD(extack, + "Non-supported tag action"); + rc = -EOPNOTSUPP; + goto err; + } + + if (a->frer.recover) { + stream.action = NETC_STREAM_FRER_SEQREC; + filter.seqrec.enc = a->frer.tag_type; + filter.seqrec.alg = a->frer.rcvy_alg; + filter.seqrec.his_len = a->frer.rcvy_history_len; + filter.seqrec.reset_timeout = a->frer.rcvy_reset_msec; + filter.seqrec.rtag_pop_en = + (a->frer.tag_action == FRER_TAG_POP) ? 1 : 0; + if (ingress) { + stream.port_mask = 0xF & ~BIT(cpu_port); + filter.seqrec.eport = cpu_port; + } else { + stream.port_mask = 0xF & ~BIT(port); + filter.seqrec.eport = port; + } + } else { + stream.action = NETC_STREAM_FRER_SEQGEN; + filter.seqgen.enc = a->frer.tag_type; + if (ingress) { + filter.seqgen.iport = port; + stream.port_mask = BIT(port); + } else { + filter.seqgen.iport = cpu_port; + stream.port_mask = BIT(cpu_port); + } + } + set_stream = true; + break; + + case FLOW_ACTION_GATE: + stream.port_mask = BIT(port); + stream.action = NETC_STREAM_QCI; + filter.qci.gate.prio = a->gate.prio; + filter.qci.gate.basetime = a->gate.basetime; + filter.qci.gate.cycletime = a->gate.cycletime; + filter.qci.gate.cycletimeext = a->gate.cycletimeext; + filter.qci.gate.num_entries = a->gate.num_entries; + filter.qci.gate.entries = a->gate.entries; + set_stream = true; + break; + + case FLOW_ACTION_POLICE: + stream.port_mask = BIT(port); + stream.action = NETC_STREAM_QCI; + if (a->police.mtu < 0) { + NL_SET_ERR_MSG_MOD(extack, + "invalided maxsdu size"); + rc = -EINVAL; + goto err; + } + filter.qci.maxsdu = a->police.mtu; + + rate = a->police.rate_bytes_ps; + if (rate) { + filter.qci.police.burst = a->police.burst; + filter.qci.police.rate = rate * 8; + } + set_stream = true; + break; + + case FLOW_ACTION_MIRRED: + if (stream.type != STREAMID_NULL) { + NL_SET_ERR_MSG_MOD(extack, + "Only support destination MAC"); + rc = -EOPNOTSUPP; + goto err; + } + dp = dsa_port_from_netdev(a->dev); + if (IS_ERR(dp)) { + rc = -EINVAL; + goto err; + } + if (netc_fdb_entry_add(priv, stream.mac, stream.vid, dp->index) < 0) { + rc = -EINVAL; + goto err; + } + break; + + default: + rc = -EOPNOTSUPP; + goto err; + } + } + + if (!set_stream) + goto exit; + + stream_entry = netc_stream_table_lookup(&psfp->stream_list, &stream); + if (stream_entry) { + stream.handle = stream_entry->handle; + stream.update = false; + } else { + handle = netc_stream_handle_alloc(priv); + stream.handle = handle; + stream.update = true; + } + + rc = netc_stream_table_add(priv, &psfp->stream_list, + &stream, extack); + if (rc) { + NL_SET_ERR_MSG_MOD(extack, "Failed to add new stream table"); + goto err; + } + + filter.stream_handle = stream.handle; + + switch (stream.action) { + case NETC_STREAM_FRER_SEQGEN: + rc = netc_frer_seqgen(priv, &filter); + if (rc) { + goto err; + } + break; + case NETC_STREAM_FRER_SEQREC: + rc = netc_frer_seqrec(priv, &filter); + if (rc) { + goto err; + } + break; + case NETC_STREAM_QCI: + filter.qci.priority_spec = stream.prio; + rc = netc_qci_set(priv, &filter, port); + if (rc) { + goto err; + } + break; + default: + mutex_unlock(&psfp->lock); + return -EOPNOTSUPP; + } + +exit: + mutex_unlock(&psfp->lock); + + return 0; +err: + mutex_unlock(&psfp->lock); + + return rc; +} + +static int netc_cls_flower_del(struct dsa_switch *ds, int port, + struct flow_cls_offload *cls, bool ingress) +{ + struct netc_private *priv = ds->priv; + struct netc_stream *stream, *tmp; + struct netc_psfp_list *psfp; + u32 stream_handle; + int rc; + + psfp = &priv->psfp; + + mutex_lock(&psfp->lock); + + stream = netc_stream_table_get(&psfp->stream_list, cls->cookie); + if (!stream) { + mutex_unlock(&psfp->lock); + return 0; + } + + stream_handle = stream->handle; + + switch (stream->action) { + case NETC_STREAM_FRER_SEQGEN: + rc = netc_frer_sg_del(priv, stream_handle, port); + if (rc) + goto err; + break; + case NETC_STREAM_FRER_SEQREC: + rc = netc_frer_sr_del(priv, stream_handle, port); + if (rc) + goto err; + break; + case NETC_STREAM_QCI: + rc = netc_qci_del(priv, stream_handle, port); + if (rc) + goto err; + break; + default: + mutex_unlock(&psfp->lock); + return -EOPNOTSUPP; + } + + list_del(&stream->list); + + tmp = netc_stream_table_lookup(&psfp->stream_list, stream); + if (!tmp) { + rc = netc_streamid_del(priv, stream->handle); + if (rc) + goto err; + rc = netc_stream_handle_del(priv, stream->handle); + if (rc) + goto err; + } + + kfree(stream); + mutex_unlock(&psfp->lock); + + return 0; + +err: + mutex_unlock(&psfp->lock); + return rc; +} + +static int netc_cls_flower_stats(struct dsa_switch *ds, int port, + struct flow_cls_offload *cls, bool ingress) +{ + struct netc_private *priv = ds->priv; + struct netc_psfp_list *psfp; + struct netc_stream *stream; + struct flow_stats stats; + int rc; + + psfp = &priv->psfp; + + mutex_lock(&psfp->lock); + + stream = netc_stream_table_get(&psfp->stream_list, cls->cookie); + if (!stream) { + mutex_unlock(&psfp->lock); + return 0; + } + mutex_unlock(&psfp->lock); + + rc = netc_qci_get(priv, stream->handle, &stats); + if (rc < 0) + return rc; + + flow_stats_update(&cls->stats, 0x0, stats.pkts, stats.drops, 0x0, + FLOW_ACTION_HW_STATS_IMMEDIATE); + + return 0; +} + +static int netc_port_mqprio_set(struct dsa_switch *ds, int port, + struct tc_mqprio_qopt_offload *mqprio) +{ + struct netc_private *priv = ds->priv; + struct tc_mqprio_qopt *qopt = &mqprio->qopt; + struct dsa_port *dp; + uint8_t *map; + int rc; + + dp = dsa_to_port(ds, port); + + if (dp->bridge) + map = qopt->prio_tc_map; + else + map = netc_default_priority_map; + + if (!qopt->num_tc) + map = netc_default_priority_map; + + rc = netc_port_set_preemptible_tcs(ds, port, mqprio->preemptible_tcs); + if (rc) + return rc; + + return netc_port_priority_map(priv, port, map); +} + +static int netc_port_taprio_set(struct dsa_switch *ds, int port, + struct tc_taprio_qopt_offload *taprio) +{ + struct netc_private *priv = ds->priv; + int enable = 1; + int rc; + + if (taprio->cmd == TAPRIO_CMD_DESTROY) { + enable = 0; + } else if (taprio->cmd != TAPRIO_CMD_REPLACE) { + rc = -EOPNOTSUPP; + return rc; + } + + netc_port_mqprio_set(ds, port, &taprio->mqprio); + + rc = netc_qbv_set(priv, port, enable, taprio); + + return rc; +} + +static int netc_qos_query_caps(struct tc_query_caps_base *base) +{ + switch (base->type) { + case TC_SETUP_QDISC_TAPRIO: { + struct tc_taprio_caps *caps = base->caps; + + caps->supports_queue_max_sdu = true; + + return 0; + } + default: + return -EOPNOTSUPP; + } +} + + +static int netc_port_setup_tc(struct dsa_switch *ds, int port, + enum tc_setup_type type, + void *type_data) +{ + switch (type) { + case TC_QUERY_CAPS: + return netc_qos_query_caps(type_data); + case TC_SETUP_QDISC_TAPRIO: + return netc_port_taprio_set(ds, port, type_data); + case TC_SETUP_QDISC_CBS: + dev_info(ds->dev, "TC_SETUP_QDISC_CBS not support yet!\n"); + return -EOPNOTSUPP; + case TC_SETUP_QDISC_MQPRIO: + return netc_port_mqprio_set(ds, port, type_data); + default: + return -EOPNOTSUPP; + } +} + +static int netc_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +{ + struct netc_private *priv = ds->priv; + int maxlen = new_mtu + ETH_HLEN + ETH_FCS_LEN; + + if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) + maxlen += VLAN_HLEN; + + return netc_port_mtu_set(priv, port, maxlen); +} + +static int netc_get_max_mtu(struct dsa_switch *ds, int port) +{ + return 2000 - VLAN_ETH_HLEN - ETH_FCS_LEN; +} + +static int netc_mac_init(struct netc_private *priv) +{ + struct netc_mac_config *mac; + struct dsa_switch *ds = priv->ds; + struct dsa_port *dp; + + mac = priv->config.mac; + + dsa_switch_for_each_port(dp, ds) { + mac[dp->index].port = dp->index; + mac[dp->index].speed = 1000; + mac[dp->index].vlanid = 1; + mac[dp->index].drpuntag = false; + mac[dp->index].retag = false; + + if (dsa_port_is_dsa(dp)) + dp->learning = true; + + /* Disallow untagged packets from being received on the + * CPU and DSA ports. + */ + if (dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)) + mac[dp->index].drpuntag = true; + } + + return 0; +} + +static int netc_dsa_init(struct netc_private *priv) +{ + struct dsa_switch *ds = priv->ds; + struct dsa_port *dp, *cpu_dp = NULL; + const u8 *mac; + int port; + + for (port = 0; port < ds->num_ports; port++) { + if (dsa_is_cpu_port(ds, port)) { + cpu_dp = dsa_to_port(ds, port); + break; + } + } + + if (!cpu_dp) { + dev_err(ds->dev, "Failed to find cpu port\n"); + return -ENODEV; + } + + if (!is_zero_ether_addr(cpu_dp->mac)) + mac = cpu_dp->mac; + else + mac = cpu_dp->master->dev_addr; + + pr_info("NETC DSA: cpu port:%d master:%s\n", + cpu_dp->index, cpu_dp->master->name); + + for (port = 0; port < ds->num_ports; port++) { + dp = dsa_to_port(ds, port); + + if (dsa_port_is_unused(dp)) + continue; + if (dsa_port_is_cpu(dp)) + continue; + + pr_info("NETC DSA: add switch port:%d\n", port); + + netc_port_dsa_add(priv, cpu_dp->index, port, mac); + } + + return 0; +} + +static int netc_setup(struct dsa_switch *ds) +{ + struct netc_private *priv = ds->priv; + int port; + int rc; + + rc = netc_config_setup(&priv->config); + if (rc < 0) { + dev_err(ds->dev, "Failed to setup config: %d\n", rc); + return rc; + } + + netc_mac_init(priv); + netc_dsa_init(priv); + + for (port = 0; port < ds->num_ports; port++) { + priv->tag_8021q_pvid[port] = NETC_DEFAULT_VLAN; + priv->bridge_pvid[port] = NETC_DEFAULT_VLAN; + } + + rc = netc_ptp_clock_register(ds); + if (rc < 0) { + dev_err(ds->dev, "Failed to register PTP clock: %d\n", rc); + goto out_config_free; + } + + rc = netc_devlink_setup(ds); + if (rc < 0) + goto out_ptp_teardown; + + rtnl_lock(); + rc = dsa_tag_8021q_register(ds, htons(ETH_P_8021Q)); + rtnl_unlock(); + if (rc) + goto out_devlink_teardown; + + /* + * On netc, VLAN filtering per se is always enabled in hardware. + * The only thing we can do to disable it is lie about what the 802.1Q + * EtherType is. + * So it will still try to apply VLAN filtering, but all ingress + * traffic (except frames received with EtherType of ETH_P_NETC) + * will be internally tagged with a distorted VLAN header where the + * TPID is ETH_P_NETC, and the VLAN ID is the port pvid. + */ + ds->vlan_filtering_is_global = true; + ds->untag_bridge_pvid = true; + ds->fdb_isolation = true; + /* tag_8021q has 3 bits for the VBID, and the value 0 is reserved */ + ds->max_num_bridges = 7; + + /* Advertise the 8 egress queues */ + ds->num_tx_queues = NETC_NUM_TC; + + ds->mtu_enforcement_ingress = true; + ds->assisted_learning_on_cpu_port = true; + + return 0; + +out_devlink_teardown: + netc_devlink_teardown(ds); +out_ptp_teardown: + netc_ptp_clock_unregister(ds); +out_config_free: + netc_config_free(&priv->config); + + return rc; +} + +static void netc_teardown(struct dsa_switch *ds) +{ + struct netc_private *priv = ds->priv; + + rtnl_lock(); + dsa_tag_8021q_unregister(ds); + rtnl_unlock(); + + netc_devlink_teardown(ds); + netc_ptp_clock_unregister(ds); + netc_config_free(&priv->config); +} + +static const struct dsa_switch_ops netc_switch_ops = { + .get_tag_protocol = netc_get_tag_protocol, + .connect_tag_protocol = netc_connect_tag_protocol, + .setup = netc_setup, + .teardown = netc_teardown, + .port_change_mtu = netc_change_mtu, + .port_max_mtu = netc_get_max_mtu, + .phylink_get_caps = netc_phylink_get_caps, + .phylink_mac_link_up = netc_mac_link_up, + .phylink_mac_link_down = netc_mac_link_down, + .get_strings = netc_get_strings, + .get_ethtool_stats = netc_get_ethtool_stats, + .get_sset_count = netc_get_sset_count, + .port_fdb_dump = netc_fdb_dump, + .port_fdb_add = netc_fdb_add, + .port_fdb_del = netc_fdb_del, + .port_mdb_add = netc_mdb_add, + .port_mdb_del = netc_mdb_del, + .port_bridge_join = netc_bridge_join, + .port_bridge_leave = netc_bridge_leave, + .port_vlan_filtering = netc_vlan_filtering, + .port_vlan_add = netc_bridge_vlan_add, + .port_vlan_del = netc_bridge_vlan_del, + .port_hwtstamp_get = netc_hwtstamp_get, + .port_hwtstamp_set = netc_hwtstamp_set, + .port_rxtstamp = netc_port_rxtstamp, + .port_txtstamp = netc_port_txtstamp, + .get_ts_info = netc_get_ts_info, + .devlink_info_get = netc_devlink_info_get, + .tag_8021q_vlan_add = netc_8021q_vlan_add, + .tag_8021q_vlan_del = netc_8021q_vlan_del, + .port_prechangeupper = netc_prechangeupper, + .cls_flower_add = netc_cls_flower_add, + .cls_flower_del = netc_cls_flower_del, + .cls_flower_stats = netc_cls_flower_stats, + .port_setup_tc = netc_port_setup_tc, + .set_mm = netc_port_set_mm, + .get_mm = netc_port_get_mm, +}; + +static const struct of_device_id netc_dt_ids[]; +static int netc_check_device_id(struct netc_private *priv) +{ + struct device *dev = &priv->spidev->dev; + struct netc_config *config = &priv->config; + int rc; + + rc = netc_get_devinfo(priv, config); + if (rc < 0) + return rc; + + if (config->device_id != priv->info->device_id) { + dev_err(dev, "Device tree specifies device ID 0x%x, but found 0x%x please fix it!\n", + priv->info->device_id, config->device_id); + return -ENODEV; + } + + return 0; +} + +static int netc_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct netc_private *priv; + struct dsa_switch *ds; + size_t max_xfer, max_msg; + int rc; + + if (!dev->of_node) { + dev_err(dev, "No DTS bindings for netc driver\n"); + return -EINVAL; + } + + priv = devm_kzalloc(dev, sizeof(struct netc_private), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* + * Populate our driver private structure (priv) based on + * the device tree node that was probed (spi) + */ + priv->spidev = spi; + spi_set_drvdata(spi, priv); + + /* Configure the SPI bus */ + spi->bits_per_word = NETC_SPI_WORD_BITS; + rc = spi_setup(spi); + if (rc < 0) { + dev_err(dev, "Could not init SPI\n"); + return rc; + } + + max_xfer = spi_max_transfer_size(spi); + max_msg = spi_max_message_size(spi); + + /* + * We need to send at least one 64-bit word of SPI payload per message + * in order to be able to make useful progress. + */ + if (max_msg < NETC_SPI_MSG_HEADER_SIZE + 8) { + dev_err(dev, "SPI master cannot send large enough buffers, aborting\n"); + return -EINVAL; + } + + priv->max_xfer_len = NETC_SPI_MSG_MAXLEN; + if (priv->max_xfer_len > max_xfer) + priv->max_xfer_len = max_xfer; + if (priv->max_xfer_len > max_msg - NETC_SPI_MSG_HEADER_SIZE) + priv->max_xfer_len = max_msg - NETC_SPI_MSG_HEADER_SIZE; + + priv->info = of_device_get_match_data(dev); + + /* Detect hardware device */ + rc = netc_check_device_id(priv); + if (rc < 0) { + dev_err(dev, "Device ID check failed: %d\n", rc); + return rc; + } + + dev_info(dev, "Probed switch chip:%s ID:0x%x firmware:%d.%d.%d\n", + priv->info->name, + priv->config.device_id, + priv->config.version_major, + priv->config.version_minor, + priv->config.version_revision); + + ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); + if (!ds) + return -ENOMEM; + + ds->dev = dev; + ds->num_ports = priv->info->num_ports; + ds->ops = &netc_switch_ops; + ds->priv = priv; + priv->ds = ds; + + mutex_init(&priv->mgmt_lock); + mutex_init(&priv->fdb_lock); + spin_lock_init(&priv->ts_id_lock); + + rc = netc_parse_dt(priv); + if (rc < 0) { + dev_err(ds->dev, "Failed to parse DT: %d\n", rc); + return rc; + } + + //for tc filter + INIT_LIST_HEAD(&priv->psfp.stream_list); + memset(priv->psfp.ssids, 0, sizeof(priv->psfp.ssids)); + priv->psfp.num_ssids = 0; + mutex_init(&priv->psfp.lock); + + return dsa_register_switch(priv->ds); +} + +static void netc_remove(struct spi_device *spi) +{ + struct netc_private *priv = spi_get_drvdata(spi); + + if (!priv) + return; + + dsa_unregister_switch(priv->ds); +} + +static void netc_shutdown(struct spi_device *spi) +{ + struct netc_private *priv = spi_get_drvdata(spi); + + if (!priv) + return; + + dsa_switch_shutdown(priv->ds); + + spi_set_drvdata(spi, NULL); +} + +const struct netc_info netc_info = { + .device_id = NETC_RT1180_DEVICE_ID, + .tag_proto = DSA_TAG_PROTO_NETC_VALUE, + .can_limit_mcast_flood = false, + .num_ports = NETC_NUM_PORTS, + .name = "netc", +}; + +static const struct of_device_id netc_dt_ids[] = { + { .compatible = "nxp,imxrt1180-netc", .data = &netc_info}, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, netc_dt_ids); + +static const struct spi_device_id netc_spi_ids[] = { + { "imxrt1180-netc" }, + { }, +}; +MODULE_DEVICE_TABLE(spi, netc_spi_ids); + +static struct spi_driver netc_driver = { + .driver = { + .name = "netc-spi", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(netc_dt_ids), + }, + .id_table = netc_spi_ids, + .probe = netc_probe, + .remove = netc_remove, + .shutdown = netc_shutdown, +}; + +module_spi_driver(netc_driver); + +MODULE_AUTHOR("Minghuan Lian "); + +MODULE_DESCRIPTION("NETC DSA Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/dsa/netc/netc_ptp.c b/drivers/net/dsa/netc/netc_ptp.c new file mode 100644 index 000000000000..3e03641b7ae9 --- /dev/null +++ b/drivers/net/dsa/netc/netc_ptp.c @@ -0,0 +1,375 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2023-2024 NXP + */ + +#include "netc.h" + +#define extts_to_data(t) \ + container_of((t), struct netc_ptp_data, extts_timer) +#define ptp_caps_to_data(d) \ + container_of((d), struct netc_ptp_data, caps) +#define ptp_data_to_netc(d) \ + container_of((d), struct netc_private, ptp_data) + +int netc_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr) +{ + struct netc_private *priv = ds->priv; + struct hwtstamp_config config; + + if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) + return -EFAULT; + + switch (config.tx_type) { + case HWTSTAMP_TX_OFF: + priv->hwts_tx_en &= ~BIT(port); + break; + case HWTSTAMP_TX_ON: + priv->hwts_tx_en |= BIT(port); + break; + default: + return -ERANGE; + } + + switch (config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + priv->hwts_rx_en &= ~BIT(port); + break; + default: + priv->hwts_rx_en |= BIT(port); + break; + } + + if (copy_to_user(ifr->ifr_data, &config, sizeof(config))) + return -EFAULT; + return 0; +} + +int netc_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr) +{ + struct netc_private *priv = ds->priv; + struct hwtstamp_config config; + + config.flags = 0; + if (priv->hwts_tx_en & BIT(port)) + config.tx_type = HWTSTAMP_TX_ON; + else + config.tx_type = HWTSTAMP_TX_OFF; + if (priv->hwts_rx_en & BIT(port)) + config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; + else + config.rx_filter = HWTSTAMP_FILTER_NONE; + + return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? + -EFAULT : 0; +} + +int netc_get_ts_info(struct dsa_switch *ds, int port, + struct ethtool_ts_info *info) +{ + struct netc_private *priv = ds->priv; + struct netc_ptp_data *ptp_data = &priv->ptp_data; + + /* Called during cleanup */ + if (!ptp_data->clock) + return -ENODEV; + + info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + info->tx_types = (1 << HWTSTAMP_TX_OFF) | + (1 << HWTSTAMP_TX_ON); + info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | + (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT); + info->phc_index = ptp_clock_index(ptp_data->clock); + + return 0; +} + +/* Called from dsa_skb_defer_rx_timestamp */ +bool netc_port_rxtstamp(struct dsa_switch *ds, int port, + struct sk_buff *skb, unsigned int type) +{ + struct skb_shared_hwtstamps *shwt = skb_hwtstamps(skb); + u64 ts = NETC_SKB_CB(skb)->tstamp; + + *shwt = (struct skb_shared_hwtstamps) {0}; + + shwt->hwtstamp = ns_to_ktime(ts); + + /* Don't defer */ + return false; +} + +void netc_process_meta_tstamp(struct dsa_switch *ds, int port, + u32 ts_id, u64 tstamp) +{ + struct netc_private *priv = ds->priv; + struct netc_ptp_data *ptp_data = &priv->ptp_data; + struct sk_buff *skb, *skb_tmp, *skb_match = NULL; + struct skb_shared_hwtstamps shwt = {0}; + + spin_lock(&ptp_data->skb_txtstamp_queue.lock); + + skb_queue_walk_safe(&ptp_data->skb_txtstamp_queue, skb, skb_tmp) { + if (NETC_SKB_CB(skb)->ts_id != ts_id) + continue; + + __skb_unlink(skb, &ptp_data->skb_txtstamp_queue); + skb_match = skb; + + break; + } + + spin_unlock(&ptp_data->skb_txtstamp_queue.lock); + + if (WARN_ON(!skb_match)) + return; + + shwt.hwtstamp = ns_to_ktime(tstamp); + skb_complete_tx_timestamp(skb_match, &shwt); +} + +/* Called from dsa_skb_tx_timestamp. This callback is just to clone + * the skb and have it available in NETC_SKB_CB in the .port_deferred_xmit + * callback, where we will timestamp it synchronously. + */ +void netc_port_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb) +{ + struct netc_private *priv = ds->priv; + struct sk_buff *clone; + struct netc_ptp_data *ptp_data = &priv->ptp_data; + u32 ts_id; + + if (!(priv->hwts_tx_en & BIT(port))) + return; + + clone = skb_clone_sk(skb); + if (!clone) + return; + + NETC_SKB_CB(skb)->clone = clone; + + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + + spin_lock(&priv->ts_id_lock); + + ts_id = priv->ts_id; + /* Deal automatically with 8-bit wraparound */ + priv->ts_id++; + + NETC_SKB_CB(clone)->ts_id = ts_id; + + spin_unlock(&priv->ts_id_lock); + + skb_queue_tail(&ptp_data->skb_txtstamp_queue, clone); +} + +static int netc_ptp_reset(struct dsa_switch *ds) +{ + struct netc_private *priv = ds->priv; + struct netc_ptp_data *ptp_data = &priv->ptp_data; + int rc; + u64 data = 1; + + dev_dbg(ds->dev, "Resetting PTP clock\n"); + + mutex_lock(&ptp_data->lock); + + rc = netc_xfer_set_cmd(priv, NETC_CMD_PTP_SYNC_SET, + &data, sizeof(data)); + + mutex_unlock(&ptp_data->lock); + + return rc; +} + +static int netc_ptp_gettimex(struct ptp_clock_info *ptp, + struct timespec64 *ts, + struct ptp_system_timestamp *ptp_sts) +{ + struct netc_ptp_data *ptp_data = ptp_caps_to_data(ptp); + struct netc_private *priv = ptp_data_to_netc(ptp_data); + u64 now = 0; + int rc; + + mutex_lock(&ptp_data->lock); + + rc = netc_xfer_read_u64(priv, NETC_CMD_TIMER_CUR_GET, &now, ptp_sts); + + mutex_unlock(&ptp_data->lock); + + *ts = ns_to_timespec64(now); + + if (rc < 0) + dev_err(priv->ds->dev, "Failed to read PTP clock: %d\n", rc); + + return rc; +} + +static int netc_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + struct netc_ptp_data *ptp_data = ptp_caps_to_data(ptp); + struct netc_private *priv = ptp_data_to_netc(ptp_data); + struct netc_ptp_ctl_param param; + int rc; + + param.ns = timespec64_to_ns(ts); + param.clock_id = 0; + + mutex_lock(&ptp_data->lock); + + rc = netc_xfer_set_cmd(priv, NETC_CMD_TIMER_CUR_SET, + ¶m, sizeof(param)); + + mutex_unlock(&ptp_data->lock); + + return rc; +} + +static int netc_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + struct netc_ptp_data *ptp_data = ptp_caps_to_data(ptp); + struct netc_private *priv = ptp_data_to_netc(ptp_data); + struct netc_ptp_ctl_param param; + int rc; + + param.ppb = scaled_ppm_to_ppb(scaled_ppm);; + param.clock_id = 0; + + mutex_lock(&ptp_data->lock); + + rc = netc_xfer_set_cmd(priv, NETC_CMD_TIMER_ADJFINE_SET, + ¶m, sizeof(param)); + + mutex_unlock(&ptp_data->lock); + + return rc; +} + +static int netc_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct netc_ptp_data *ptp_data = ptp_caps_to_data(ptp); + struct netc_private *priv = ptp_data_to_netc(ptp_data); + struct netc_ptp_ctl_param param; + int rc; + + param.offset = delta; + param.clock_id = 0; + + mutex_lock(&ptp_data->lock); + + rc = netc_xfer_set_cmd(priv, NETC_CMD_TIMER_ADJTIME_SET, + ¶m, sizeof(param)); + + mutex_unlock(&ptp_data->lock); + + return rc; +} + +static int netc_per_out_enable(struct netc_private *priv, + struct ptp_perout_request *perout, + bool on) +{ + struct netc_ptp_data *ptp_data = &priv->ptp_data; + struct netc_cmd_timer_pps param; + int rc; + + /* We only support one channel */ + if (perout->index != 0) + return -EOPNOTSUPP; + + /* Reject requests with unsupported flags */ + if (perout->flags) + return -EOPNOTSUPP; + + mutex_lock(&ptp_data->lock); + + if (on) { + struct timespec64 pin_duration_ts = { + .tv_sec = perout->period.sec, + .tv_nsec = perout->period.nsec, + }; + struct timespec64 pin_start_ts = { + .tv_sec = perout->start.sec, + .tv_nsec = perout->start.nsec, + }; + u64 pin_duration = timespec64_to_ns(&pin_duration_ts); + if (pin_duration > U32_MAX) { + rc = -ERANGE; + goto _out; + } + param.pin_duration32 = (u32) pin_duration; + param.pin_start = timespec64_to_ns(&pin_start_ts); + + rc = netc_xfer_set_cmd(priv, NETC_CMD_TIMER_PPS_START, + ¶m, sizeof(param)); + } else + rc = netc_xfer_set_cmd(priv, NETC_CMD_TIMER_PPS_STOP, + NULL, 0); + +_out: + mutex_unlock(&ptp_data->lock); + + return rc; +} + +static int netc_ptp_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *req, int on) +{ + struct netc_ptp_data *ptp_data = ptp_caps_to_data(ptp); + struct netc_private *priv = ptp_data_to_netc(ptp_data); + int rc = -EOPNOTSUPP; + + if (req->type == PTP_CLK_REQ_PEROUT) + rc = netc_per_out_enable(priv, &req->perout, on); + + return rc; +} + +struct ptp_clock_info netc_clock_caps = { + .owner = THIS_MODULE, + .name = "NETC PHC", + .max_adj = 1000000, + .n_alarm = 2, + .n_ext_ts = 2, + .n_per_out = 3, + .n_pins = 0, + .pps = 1, + .adjfine = netc_ptp_adjfine, + .adjtime = netc_ptp_adjtime, + .gettimex64 = netc_ptp_gettimex, + .settime64 = netc_ptp_settime, + .enable = netc_ptp_enable, +}; + +int netc_ptp_clock_register(struct dsa_switch *ds) +{ + struct netc_private *priv = ds->priv; + struct netc_ptp_data *ptp_data = &priv->ptp_data; + + skb_queue_head_init(&ptp_data->skb_txtstamp_queue); + + ptp_data->caps = netc_clock_caps; + ptp_data->clock = ptp_clock_register(&ptp_data->caps, ds->dev); + if (IS_ERR_OR_NULL(ptp_data->clock)) + return PTR_ERR(ptp_data->clock); + + return netc_ptp_reset(ds); +} + +void netc_ptp_clock_unregister(struct dsa_switch *ds) +{ + struct netc_private *priv = ds->priv; + struct netc_ptp_data *ptp_data = &priv->ptp_data; + + if (IS_ERR_OR_NULL(ptp_data->clock)) + return; + + del_timer_sync(&ptp_data->extts_timer); + ptp_cancel_worker_sync(ptp_data->clock); + skb_queue_purge(&ptp_data->skb_txtstamp_queue); + ptp_clock_unregister(ptp_data->clock); + ptp_data->clock = NULL; +} diff --git a/drivers/net/dsa/netc/netc_ptp.h b/drivers/net/dsa/netc/netc_ptp.h new file mode 100644 index 000000000000..efdb15e90223 --- /dev/null +++ b/drivers/net/dsa/netc/netc_ptp.h @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2024 NXP + */ + +#ifndef _NETC_PTP_H +#define _NETC_PTP_H + +#include + +#if IS_ENABLED(CONFIG_NET_DSA_NETC_PTP) + +struct netc_ptp_data { + struct timer_list extts_timer; + /* Used on NETC where meta frames are generated only for + * 2-step TX timestamps + */ + struct sk_buff_head skb_txtstamp_queue; + struct ptp_clock *clock; + struct ptp_clock_info caps; + /* Serializes all operations on the PTP hardware clock */ + struct mutex lock; + bool extts_enabled; + u64 ptpsyncts; +}; + +int netc_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr); + +int netc_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr); + +void netc_process_meta_tstamp(struct dsa_switch *ds, int port, + u32 ts_id, u64 tstamp); + +int netc_get_ts_info(struct dsa_switch *ds, int port, + struct ethtool_ts_info *ts); + +bool netc_port_rxtstamp(struct dsa_switch *ds, int port, + struct sk_buff *skb, unsigned int type); + +void netc_port_txtstamp(struct dsa_switch *ds, int port, + struct sk_buff *skb); + +int netc_ptp_clock_register(struct dsa_switch *ds); + +void netc_ptp_clock_unregister(struct dsa_switch *ds); + + +#else + +struct netc_ptp_data { + struct mutex lock; +}; + +static inline int netc_ptp_clock_register(struct dsa_switch *ds) +{ + return 0; +} + +static inline void netc_ptp_clock_unregister(struct dsa_switch *ds) +{ +} + +#define netc_get_ts_info NULL + +#define netc_port_rxtstamp NULL + +#define netc_port_txtstamp NULL + +#define netc_hwtstamp_get NULL + +#define netc_hwtstamp_set NULL + +#define netc_process_meta_tstamp NULL + +#endif /* IS_ENABLED(CONFIG_NET_DSA_NETC_PTP) */ + +#endif /* _NETC_PTP_H */ diff --git a/drivers/net/dsa/netc/netc_spi.c b/drivers/net/dsa/netc/netc_spi.c new file mode 100644 index 000000000000..41cfbc3686ad --- /dev/null +++ b/drivers/net/dsa/netc/netc_spi.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2023 NXP + */ + +#include +#include "netc.h" + +int netc_xfer_cmd(const struct netc_private *priv, + enum netc_spi_rw_mode rw, enum netc_cmd cmd, + void *param, size_t param_len, + void *resp, size_t resp_len, + struct ptp_system_timestamp *ptp_sts) +{ + struct netc_cmd_hdr hdr = {0}; + struct spi_device *spi = priv->spidev; + struct spi_transfer hdr_xfer, resp_xfer; + int rc; + + if (!IS_ALIGNED(resp_len, NETC_SPI_MSG_WORD_BYTES)) { + dev_err(&spi->dev, "netc cmd %d data size should be a multiple of 4 : %ld", + cmd, resp_len); + return -EINVAL; + } + + if (resp_len > priv->max_xfer_len) { + dev_err(&spi->dev, "netc cmd %d data size is too large\n", + cmd); + return -EINVAL; + } + + if (param_len > NETC_SPI_MSG_PARAM_SIZE) { + dev_err(&spi->dev, "netc cmd %d param size is too large\n", + cmd); + return -EINVAL; + } + + hdr.cmd = (rw << NETC_CMD_DIR_SHIFT) | + ((resp_len / NETC_SPI_MSG_WORD_BYTES) << + NETC_CMD_LEN_SHIFT) | + cmd; + if (param) + memcpy(hdr.param, param, param_len); + + hdr_xfer.tx_buf = &hdr; + hdr_xfer.len = NETC_SPI_MSG_HEADER_SIZE; + hdr_xfer.ptp_sts_word_pre = hdr_xfer.len - 1; + hdr_xfer.ptp_sts_word_post = hdr_xfer.len - 1; + hdr_xfer.ptp_sts = ptp_sts; + + rc = spi_sync_transfer(spi, &hdr_xfer, 1); + if (rc < 0) { + dev_err(&spi->dev, "netc cmd %d SPI transfer failed: %d\n", + cmd, rc); + return rc; + } + + usleep_range(NETC_SPI_MSG_RESPONSE_TIME, + NETC_SPI_MSG_RESPONSE_TIME * 10); + + if (!resp) + return 0; + + /* Populate the transfer's data buffer */ + if (rw == SPI_READ) + resp_xfer.rx_buf = resp; + else + resp_xfer.tx_buf = resp; + resp_xfer.len = resp_len; + + resp_xfer.ptp_sts_word_pre = resp_xfer.len - 1; + resp_xfer.ptp_sts_word_post = resp_xfer.len - 1; + resp_xfer.ptp_sts = ptp_sts; + + rc = spi_sync_transfer(spi, &resp_xfer, 1); + if (rc < 0) { + dev_err(&spi->dev, "netc cmd %d SPI transfer failed: %d\n", + cmd, rc); + return rc; + } + + usleep_range(NETC_SPI_MSG_RESPONSE_TIME, + NETC_SPI_MSG_RESPONSE_TIME * 10); + + return 0; +} + +int netc_xfer_set_cmd(const struct netc_private *priv, + enum netc_cmd cmd, + void *param, size_t param_len) +{ + return netc_xfer_cmd(priv, SPI_WRITE, cmd, + param, param_len, + NULL, 0, NULL); +} + +int netc_xfer_get_cmd(const struct netc_private *priv, + enum netc_cmd cmd, uint32_t id, + void *resp, size_t resp_len) +{ + struct netc_cmd_read_param param; + + param.id = id; + + return netc_xfer_cmd(priv, SPI_READ, cmd, + ¶m, sizeof(param), + resp, resp_len, NULL); +} + +int netc_xfer_write_reg(const struct netc_private *priv, + uint32_t reg, uint32_t value) +{ + struct netc_cmd_reg_cmd reg_cmd; + + reg_cmd.reg = reg; + reg_cmd.value = value; + + return netc_xfer_set_cmd(priv, NETC_CMD_REG_SET, + ®_cmd, sizeof(reg_cmd)); +} + +int netc_xfer_read_reg(const struct netc_private *priv, + uint32_t reg, uint32_t *value) +{ + return netc_xfer_get_cmd(priv, NETC_CMD_REG_GET, reg, + value, sizeof(*value)); +} + +int netc_xfer_write_u64(const struct netc_private *priv, + enum netc_cmd cmd, uint64_t value, + struct ptp_system_timestamp *ptp_sts) +{ + return netc_xfer_cmd(priv, SPI_WRITE, cmd, + &value, sizeof(value), + NULL, 0, + ptp_sts); +} + +int netc_xfer_read_u64(const struct netc_private *priv, + enum netc_cmd cmd, uint64_t *value, + struct ptp_system_timestamp *ptp_sts) +{ + return netc_xfer_cmd(priv, SPI_READ, cmd, + NULL, 0, + value, sizeof(*value), + ptp_sts); +} diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 3e5105267c57..18f6cddf2478 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -1027,17 +1027,50 @@ static int felix_vlan_add(struct dsa_switch *ds, int port, if (err) return err; - return ocelot_vlan_add(ocelot, port, vlan->vid, + err = ocelot_vlan_add(ocelot, port, vlan->vid, flags & BRIDGE_VLAN_INFO_PVID, flags & BRIDGE_VLAN_INFO_UNTAGGED); + if (err) + return err; + + if (vlan->proto == ETH_P_8021AD) { + if (!ocelot->qinq_enable) { + ocelot->qinq_enable = true; + kref_init(&ocelot->qinq_refcount); + } else { + kref_get(&ocelot->qinq_refcount); + } + } + + return 0; +} + +static void felix_vlan_qinq_release(struct kref *ref) +{ + struct ocelot *ocelot; + + ocelot = container_of(ref, struct ocelot, qinq_refcount); + ocelot->qinq_enable = false; } static int felix_vlan_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan) { struct ocelot *ocelot = ds->priv; + int err; + + err = ocelot_vlan_del(ocelot, port, vlan->vid); + if (err) { + dev_err(ds->dev, "Failed to remove VLAN %d from port %d: %d\n", + vlan->vid, port, err); + return err; + } + + if (ocelot->qinq_enable && vlan->proto == ETH_P_8021AD) + kref_put(&ocelot->qinq_refcount, + felix_vlan_qinq_release); - return ocelot_vlan_del(ocelot, port, vlan->vid); + return 0; } static const u32 felix_phy_match_table[PHY_INTERFACE_MODE_MAX] = { @@ -1108,6 +1141,9 @@ static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port, ocelot_phylink_mac_link_down(ocelot, port, link_an_mode, interface, felix->info->quirks); + + if (felix->info->port_preempt_reset) + felix->info->port_preempt_reset(ocelot, port, 0); } static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port, @@ -1126,6 +1162,9 @@ static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port, if (felix->info->port_sched_speed_set) felix->info->port_sched_speed_set(ocelot, port, speed); + + if (felix->info->port_preempt_reset) + felix->info->port_preempt_reset(ocelot, port, 1); } static int felix_port_enable(struct dsa_switch *ds, int port, @@ -1249,6 +1288,44 @@ static int felix_get_ts_info(struct dsa_switch *ds, int port, return ocelot_get_ts_info(ocelot, port, info); } +static int felix_reset_preempt(struct dsa_switch *ds, int port, bool enable) +{ + struct ocelot *ocelot = ds->priv; + struct felix *felix = ocelot_to_felix(ocelot); + + if (felix->info->port_preempt_reset) { + felix->info->port_preempt_reset(ocelot, port, enable); + + return 0; + } + + return -EOPNOTSUPP; +} + +static int felix_set_preempt(struct dsa_switch *ds, int port, + struct ethtool_fp *fpcmd) +{ + struct ocelot *ocelot = ds->priv; + struct felix *felix = ocelot_to_felix(ocelot); + + if (felix->info->port_set_preempt) + return felix->info->port_set_preempt(ocelot, port, fpcmd); + + return -EOPNOTSUPP; +} + +static int felix_get_preempt(struct dsa_switch *ds, int port, + struct ethtool_fp *fpcmd) +{ + struct ocelot *ocelot = ds->priv; + struct felix *felix = ocelot_to_felix(ocelot); + + if (felix->info->port_get_preempt) + return felix->info->port_get_preempt(ocelot, port, fpcmd); + + return -EOPNOTSUPP; +} + static struct regmap *felix_request_regmap_by_name(struct felix *felix, const char *resource_name) { @@ -1476,6 +1553,97 @@ static int felix_connect_tag_protocol(struct dsa_switch *ds, } } +static int felix_qinq_port_bitmap_get(struct dsa_switch *ds, u32 *bitmap) +{ + struct ocelot *ocelot = ds->priv; + struct ocelot_port *ocelot_port; + int port; + + *bitmap = 0; + for (port = 0; port < ds->num_ports; port++) { + ocelot_port = ocelot->ports[port]; + if (ocelot_port->qinq_mode) + *bitmap |= 0x01 << port; + } + + return 0; +} + +static int felix_qinq_port_bitmap_set(struct dsa_switch *ds, u32 bitmap) +{ + struct ocelot *ocelot = ds->priv; + struct ocelot_port *ocelot_port; + int port; + + for (port = 0; port < ds->num_ports; port++) { + ocelot_port = ocelot->ports[port]; + if (bitmap & (0x01 << port)) + ocelot_port->qinq_mode = true; + else + ocelot_port->qinq_mode = false; + } + + return 0; +} + +enum felix_devlink_param_id { + FELIX_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, + FELIX_DEVLINK_PARAM_ID_QINQ_PORT_BITMAP, +}; + +static int felix_devlink_param_get(struct dsa_switch *ds, u32 id, + struct devlink_param_gset_ctx *ctx) +{ + int err; + + switch (id) { + case FELIX_DEVLINK_PARAM_ID_QINQ_PORT_BITMAP: + err = felix_qinq_port_bitmap_get(ds, &ctx->val.vu32); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static int felix_devlink_param_set(struct dsa_switch *ds, u32 id, + struct devlink_param_gset_ctx *ctx) +{ + int err; + + switch (id) { + case FELIX_DEVLINK_PARAM_ID_QINQ_PORT_BITMAP: + err = felix_qinq_port_bitmap_set(ds, ctx->val.vu32); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static const struct devlink_param felix_devlink_params[] = { + DSA_DEVLINK_PARAM_DRIVER(FELIX_DEVLINK_PARAM_ID_QINQ_PORT_BITMAP, + "qinq_port_bitmap", + DEVLINK_PARAM_TYPE_U32, + BIT(DEVLINK_PARAM_CMODE_RUNTIME)), +}; + +static int felix_setup_devlink_params(struct dsa_switch *ds) +{ + return dsa_devlink_params_register(ds, felix_devlink_params, + ARRAY_SIZE(felix_devlink_params)); +} + +static void felix_teardown_devlink_params(struct dsa_switch *ds) +{ + dsa_devlink_params_unregister(ds, felix_devlink_params, + ARRAY_SIZE(felix_devlink_params)); +} + static int felix_setup(struct dsa_switch *ds) { struct ocelot *ocelot = ds->priv; @@ -1526,6 +1694,10 @@ static int felix_setup(struct dsa_switch *ds) ds->fdb_isolation = true; ds->max_num_bridges = ds->num_ports; + err = felix_setup_devlink_params(ds); + if (err < 0) + return err; + return 0; out_deinit_ports: @@ -1553,6 +1725,8 @@ static void felix_teardown(struct dsa_switch *ds) felix->tag_proto_ops->teardown(ds); rtnl_unlock(); + felix_teardown_devlink_params(ds); + dsa_switch_for_each_available_port(dp, ds) ocelot_deinit_port(ocelot, dp->index); @@ -2036,6 +2210,9 @@ const struct dsa_switch_ops felix_switch_ops = { .get_ethtool_stats = felix_get_ethtool_stats, .get_sset_count = felix_get_sset_count, .get_ts_info = felix_get_ts_info, + .reset_preempt = felix_reset_preempt, + .set_preempt = felix_set_preempt, + .get_preempt = felix_get_preempt, .phylink_get_caps = felix_phylink_get_caps, .phylink_mac_config = felix_phylink_mac_config, .phylink_mac_select_pcs = felix_phylink_mac_select_pcs, @@ -2091,6 +2268,8 @@ const struct dsa_switch_ops felix_switch_ops = { .port_mrp_del_ring_role = felix_mrp_del_ring_role, .tag_8021q_vlan_add = felix_tag_8021q_vlan_add, .tag_8021q_vlan_del = felix_tag_8021q_vlan_del, + .devlink_param_get = felix_devlink_param_get, + .devlink_param_set = felix_devlink_param_set, .port_get_default_prio = felix_port_get_default_prio, .port_set_default_prio = felix_port_set_default_prio, .port_get_dscp_prio = felix_port_get_dscp_prio, diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h index 558a60de64b2..ac21a5451232 100644 --- a/drivers/net/dsa/ocelot/felix.h +++ b/drivers/net/dsa/ocelot/felix.h @@ -65,6 +65,11 @@ struct felix_info { const struct phylink_link_state *state); int (*configure_serdes)(struct ocelot *ocelot, int port, struct device_node *portnp); + int (*port_set_preempt)(struct ocelot *ocelot, int port, + struct ethtool_fp *fpcmd); + int (*port_get_preempt)(struct ocelot *ocelot, int port, + struct ethtool_fp *fpcmd); + void (*port_preempt_reset)(struct ocelot *ocelot, int port, bool enable); }; /* Methods for initializing the hardware resources specific to a tagging diff --git a/drivers/net/dsa/ocelot/felix_tsn.c b/drivers/net/dsa/ocelot/felix_tsn.c index be3576445afc..0d0f647915ed 100644 --- a/drivers/net/dsa/ocelot/felix_tsn.c +++ b/drivers/net/dsa/ocelot/felix_tsn.c @@ -415,10 +415,12 @@ static int felix_cb_streamid_set(struct net_device *ndev, u32 index, bool enable if (!stream) return -EINVAL; + dst_idx = stream->dst_idx; ocelot_mact_forget(ocelot, stream->mac, stream->vid); + felix_stream_table_del(index); - felix_streamid_force_forward_clear(ocelot, stream->dst_idx); + felix_streamid_force_forward_clear(ocelot, dst_idx); return 0; } @@ -1456,7 +1458,7 @@ static int felix_pcpmap_set(struct net_device *ndev, ocelot = dp->ds->priv; port = dp->index; - index = (c->pcp & GENMASK(2, 0)) * ((c->dei & BIT(0)) + 1); + index = (c->dei & BIT(0)) * 8 + (c->pcp & GENMASK(2, 0)); ocelot_rmw_ix(ocelot, (ANA_PORT_PCP_DEI_MAP_DP_PCP_DEI_VAL & (c->dpl << 3)) | diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index 79a06db6b458..10cf8db2c8f0 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -1556,15 +1556,15 @@ static void vsc9959_tas_clock_adjust(struct ocelot *ocelot) if (!taprio) continue; + /* Disable time-aware shaper */ + ocelot_rmw_rix(ocelot, 0, QSYS_TAG_CONFIG_ENABLE, + QSYS_TAG_CONFIG, port); + ocelot_rmw(ocelot, QSYS_TAS_PARAM_CFG_CTRL_PORT_NUM(port), QSYS_TAS_PARAM_CFG_CTRL_PORT_NUM_M, QSYS_TAS_PARAM_CFG_CTRL); - /* Disable time-aware shaper */ - ocelot_rmw_rix(ocelot, 0, QSYS_TAG_CONFIG_ENABLE, - QSYS_TAG_CONFIG, port); - vsc9959_new_base_time(ocelot, taprio->base_time, taprio->cycle_time, &base_ts); @@ -1577,6 +1577,9 @@ static void vsc9959_tas_clock_adjust(struct ocelot *ocelot) QSYS_PARAM_CFG_REG_3_BASE_TIME_SEC_MSB_M, QSYS_PARAM_CFG_REG_3); + for (int i = 0; i < taprio->num_entries; i++) + vsc9959_tas_gcl_set(ocelot, i, &taprio->entries[i]); + ocelot_rmw(ocelot, QSYS_TAS_PARAM_CFG_CTRL_CONFIG_CHANGE, QSYS_TAS_PARAM_CFG_CTRL_CONFIG_CHANGE, QSYS_TAS_PARAM_CFG_CTRL); @@ -2629,6 +2632,91 @@ static const struct ocelot_ops vsc9959_ops = { .tas_guard_bands_update = vsc9959_tas_guard_bands_update, }; +static void vsc9959_port_preempt_reset(struct ocelot *ocelot, int port, bool enable) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + + ocelot_port_rmwl(ocelot_port, 0, + DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA | + DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA, + DEV_MM_ENABLE_CONFIG); + + if (enable) { + if (ocelot_port->fp_enabled_admin) { + ocelot_port_rmwl(ocelot_port, + DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA | + DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA, + DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA | + DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA, + DEV_MM_ENABLE_CONFIG); + } + } +} + +static int vsc9959_port_set_preempt(struct ocelot *ocelot, int port, + struct ethtool_fp *fpcmd) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + u8 preemptible_tcs = fpcmd->preemptible_queues_mask; + struct ethtool_mm_cfg cfg; + int ret; + + if (!fpcmd->disabled && + (fpcmd->min_frag_size < 60 || fpcmd->min_frag_size > 252)) + return -EINVAL; + + cfg.tx_min_frag_size = fpcmd->min_frag_size; + + if (!fpcmd->disabled) { + ocelot_port->fp_enabled_admin = 1; + cfg.pmac_enabled = 1; + cfg.tx_enabled = 1; + } else { + ocelot_port->fp_enabled_admin = 0; + cfg.pmac_enabled = 0; + cfg.tx_enabled = 0; + } + + cfg.verify_enabled = fpcmd->fp_enabled; + cfg.verify_time = 0xa; + + ret = ocelot_port_set_mm(ocelot, port, &cfg, NULL); + if (ret) + return ret; + + mutex_lock(&ocelot->fwd_domain_lock); + ret = ocelot_port_change_fp(ocelot, port, preemptible_tcs); + mutex_unlock(&ocelot->fwd_domain_lock); + + return ret; +} + +static int vsc9959_port_get_preempt(struct ocelot *ocelot, int port, + struct ethtool_fp *fpcmd) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + u8 fragsize; + u32 val; + + fpcmd->fp_supported = 1; + fpcmd->supported_queues_mask = GENMASK(7, 0); + + val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS); + val &= DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STATUS; + fpcmd->fp_active = (val ? 1 : 0); + + val = ocelot_port_readl(ocelot_port, DEV_MM_ENABLE_CONFIG); + val &= DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA; + fpcmd->fp_status = val; + + val = ocelot_read_rix(ocelot, QSYS_PREEMPTION_CFG, port); + fpcmd->preemptible_queues_mask = val & QSYS_PREEMPTION_CFG_P_QUEUES_M; + fragsize = QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_X(val); + fpcmd->min_frag_size = (fragsize + 1) * 64 - 4; + + return 0; +} + static const struct felix_info felix_info_vsc9959 = { .resources = vsc9959_resources, .num_resources = ARRAY_SIZE(vsc9959_resources), @@ -2652,6 +2740,9 @@ static const struct felix_info felix_info_vsc9959 = { .port_modes = vsc9959_port_modes, .port_setup_tc = vsc9959_port_setup_tc, .port_sched_speed_set = vsc9959_sched_speed_set, + .port_set_preempt = vsc9959_port_set_preempt, + .port_get_preempt = vsc9959_port_get_preempt, + .port_preempt_reset = vsc9959_port_preempt_reset, }; /* The INTB interrupt is shared between for PTP TX timestamp availability diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h index 8c66d3bf61f0..eabc5b90a552 100644 --- a/drivers/net/dsa/sja1105/sja1105.h +++ b/drivers/net/dsa/sja1105/sja1105.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "sja1105_static_config.h" @@ -255,6 +256,7 @@ struct sja1105_private { unsigned long bcast_egress_floods; unsigned long hwts_tx_en; unsigned long hwts_rx_en; + u32 hostprio; const struct sja1105_info *info; size_t max_xfer_len; struct spi_device *spidev; @@ -298,6 +300,7 @@ enum sja1105_reset_reason { SJA1105_SCHEDULING, SJA1105_BEST_EFFORT_POLICING, SJA1105_VIRTUAL_LINKS, + SJA1105_VLAN_PCP_TO_TXQ_MAPPING, }; int sja1105_static_config_reload(struct sja1105_private *priv, @@ -305,6 +308,8 @@ int sja1105_static_config_reload(struct sja1105_private *priv, int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, struct netlink_ext_ack *extack); void sja1105_frame_memory_partitioning(struct sja1105_private *priv); +int sja1105_setup_tc_mqprio(struct dsa_switch *ds, int port, + struct tc_mqprio_qopt_offload *mqprio); /* From sja1105_mdio.c */ int sja1105_mdiobus_register(struct dsa_switch *ds); diff --git a/drivers/net/dsa/sja1105/sja1105_clocking.c b/drivers/net/dsa/sja1105/sja1105_clocking.c index e3699f76f6d7..08a3e7b96254 100644 --- a/drivers/net/dsa/sja1105/sja1105_clocking.c +++ b/drivers/net/dsa/sja1105/sja1105_clocking.c @@ -153,14 +153,14 @@ static int sja1105_cgu_mii_tx_clk_config(struct sja1105_private *priv, { const struct sja1105_regs *regs = priv->info->regs; struct sja1105_cgu_mii_ctrl mii_tx_clk; - const int mac_clk_sources[] = { + static const int mac_clk_sources[] = { CLKSRC_MII0_TX_CLK, CLKSRC_MII1_TX_CLK, CLKSRC_MII2_TX_CLK, CLKSRC_MII3_TX_CLK, CLKSRC_MII4_TX_CLK, }; - const int phy_clk_sources[] = { + static const int phy_clk_sources[] = { CLKSRC_IDIV0, CLKSRC_IDIV1, CLKSRC_IDIV2, @@ -194,7 +194,7 @@ sja1105_cgu_mii_rx_clk_config(struct sja1105_private *priv, int port) const struct sja1105_regs *regs = priv->info->regs; struct sja1105_cgu_mii_ctrl mii_rx_clk; u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; - const int clk_sources[] = { + static const int clk_sources[] = { CLKSRC_MII0_RX_CLK, CLKSRC_MII1_RX_CLK, CLKSRC_MII2_RX_CLK, @@ -221,7 +221,7 @@ sja1105_cgu_mii_ext_tx_clk_config(struct sja1105_private *priv, int port) const struct sja1105_regs *regs = priv->info->regs; struct sja1105_cgu_mii_ctrl mii_ext_tx_clk; u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; - const int clk_sources[] = { + static const int clk_sources[] = { CLKSRC_IDIV0, CLKSRC_IDIV1, CLKSRC_IDIV2, @@ -248,7 +248,7 @@ sja1105_cgu_mii_ext_rx_clk_config(struct sja1105_private *priv, int port) const struct sja1105_regs *regs = priv->info->regs; struct sja1105_cgu_mii_ctrl mii_ext_rx_clk; u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; - const int clk_sources[] = { + static const int clk_sources[] = { CLKSRC_IDIV0, CLKSRC_IDIV1, CLKSRC_IDIV2, @@ -349,8 +349,13 @@ static int sja1105_cgu_rgmii_tx_clk_config(struct sja1105_private *priv, if (speed == priv->info->port_speed[SJA1105_SPEED_1000MBPS]) { clksrc = CLKSRC_PLL0; } else { - int clk_sources[] = {CLKSRC_IDIV0, CLKSRC_IDIV1, CLKSRC_IDIV2, - CLKSRC_IDIV3, CLKSRC_IDIV4}; + static const int clk_sources[] = { + CLKSRC_IDIV0, + CLKSRC_IDIV1, + CLKSRC_IDIV2, + CLKSRC_IDIV3, + CLKSRC_IDIV4, + }; clksrc = clk_sources[port]; } @@ -638,7 +643,7 @@ static int sja1105_cgu_rmii_ref_clk_config(struct sja1105_private *priv, const struct sja1105_regs *regs = priv->info->regs; struct sja1105_cgu_mii_ctrl ref_clk; u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; - const int clk_sources[] = { + static const int clk_sources[] = { CLKSRC_MII0_TX_CLK, CLKSRC_MII1_TX_CLK, CLKSRC_MII2_TX_CLK, diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 1a367e64bc3b..fd0a3fcdac59 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -862,7 +862,7 @@ static int sja1105_init_general_params(struct sja1105_private *priv) /* Priority queue for link-local management frames * (both ingress to and egress from CPU - PTP, STP etc) */ - .hostprio = 7, + .hostprio = priv->hostprio, .mac_fltres1 = SJA1105_LINKLOCAL_FILTER_A, .mac_flt1 = SJA1105_LINKLOCAL_FILTER_A_MASK, .incl_srcpt1 = true, @@ -1255,6 +1255,15 @@ static int sja1105_parse_dt(struct sja1105_private *priv) return -ENODEV; } + if (of_property_read_u32(switch_node, "hostprio", &priv->hostprio) < 0) { + priv->hostprio = 7; + } else if (priv->hostprio >= SJA1105_NUM_TC) { + dev_err(dev, "Out of range hostprio, must be between 0 and %d\n", (SJA1105_NUM_TC - 1)); + return -ERANGE; + } + + dev_info(dev, "Configured hostprio: using queue %u\n", priv->hostprio); + rc = sja1105_parse_ports_node(priv, ports_node); of_node_put(ports_node); @@ -2229,8 +2238,8 @@ static int sja1105_setup_tc_cbs(struct dsa_switch *ds, int port, * but deduce the port transmit rate from idleslope - sendslope. */ port_transmit_rate_kbps = offload->idleslope - offload->sendslope; - cbs->idle_slope = div_s64(offload->idleslope * BYTES_PER_KBIT, - port_transmit_rate_kbps); + cbs->idle_slope = DIV_ROUND_UP_ULL(offload->idleslope * BYTES_PER_KBIT, + port_transmit_rate_kbps); cbs->send_slope = div_s64(abs(offload->sendslope * BYTES_PER_KBIT), port_transmit_rate_kbps); /* Convert the negative values from 64-bit 2's complement @@ -2275,6 +2284,7 @@ static const char * const sja1105_reset_reasons[] = { [SJA1105_SCHEDULING] = "Time-aware scheduling", [SJA1105_BEST_EFFORT_POLICING] = "Best-effort policing", [SJA1105_VIRTUAL_LINKS] = "Virtual links", + [SJA1105_VLAN_PCP_TO_TXQ_MAPPING] = "VLAN PCP to TX queue mapping", }; /* For situations where we need to change a setting at runtime that is only @@ -2424,6 +2434,56 @@ sja1105_get_tag_protocol(struct dsa_switch *ds, int port, return priv->info->tag_proto; } +int sja1105_setup_tc_mqprio(struct dsa_switch *ds, int port, + struct tc_mqprio_qopt_offload *mqprio) +{ + struct sja1105_l2_forwarding_entry *l2fwd; + struct sja1105_private *priv = ds->priv; + struct sja1105_table *table; + int pcp, tc; + + if (mqprio->qopt.num_tc > SJA1105_MAX_NUM_PCP) { + dev_err(ds->dev, + "Only a maximum of %u traffic classes are supported by hardware\n", + SJA1105_MAX_NUM_PCP); + return -ERANGE; + } + + table = &priv->static_config.tables[BLK_IDX_L2_FORWARDING]; + + l2fwd = table->entries; + + if (!mqprio->qopt.num_tc) { + /* Delete qdisc: reset to default 1:1 mapping. */ + for (pcp = 0; pcp < SJA1105_MAX_NUM_PCP; pcp++) + l2fwd[ds->num_ports + pcp].vlan_pmap[port] = pcp; + } else { + /* We restrict a single TXQ per traffic class + * The SJA1105 doesn't offer round robin among TXQs of the same priority + */ + for (tc = 0; tc < mqprio->qopt.num_tc; tc++) { + if (mqprio->qopt.count[tc] != 1) { + dev_err(ds->dev, + "Only a single TXQ per traffic class is supported\n"); + return -EOPNOTSUPP; + } + } + + /* Use MQPRIO mapping to configure Egress PCP to HW queue mapping. */ + for (pcp = 0; pcp < SJA1105_MAX_NUM_PCP; pcp++) + l2fwd[ds->num_ports + pcp].vlan_pmap[port] = mqprio->qopt.prio_tc_map[pcp]; + } + + /* Although, the Egress PCP to HW queue mapping (the latter 8 entries) + * should be configured dynamically (once max_dynp is properly set: e.g 7), + * that did not work in practice. The switch (SJA1105Q) kept using + * the old mapping until a switch reset appears and force the reload of + * the static configuration. + * So, force a switch reset on mapping offload from here. + */ + return sja1105_static_config_reload(priv, SJA1105_VLAN_PCP_TO_TXQ_MAPPING); +} + /* The TPID setting belongs to the General Parameters table, * which can only be partially reconfigured at runtime (and not the TPID). * So a switch reset is required. @@ -2825,6 +2885,8 @@ static int sja1105_port_setup_tc(struct dsa_switch *ds, int port, return sja1105_setup_tc_taprio(ds, port, type_data); case TC_SETUP_QDISC_CBS: return sja1105_setup_tc_cbs(ds, port, type_data); + case TC_SETUP_QDISC_MQPRIO: + return sja1105_setup_tc_mqprio(ds, port, type_data); default: return -EOPNOTSUPP; } diff --git a/drivers/net/dsa/sja1105/sja1105_tas.c b/drivers/net/dsa/sja1105/sja1105_tas.c index d7818710bc02..71073c3a413b 100644 --- a/drivers/net/dsa/sja1105/sja1105_tas.c +++ b/drivers/net/dsa/sja1105/sja1105_tas.c @@ -528,6 +528,8 @@ int sja1105_setup_tc_taprio(struct dsa_switch *ds, int port, if (rc < 0) return rc; + sja1105_setup_tc_mqprio(ds, port, &admin->mqprio); + return sja1105_static_config_reload(priv, SJA1105_SCHEDULING); } else if (admin->cmd != TAPRIO_CMD_REPLACE) { return -EOPNOTSUPP; @@ -578,6 +580,8 @@ int sja1105_setup_tc_taprio(struct dsa_switch *ds, int port, if (rc < 0) return rc; + sja1105_setup_tc_mqprio(ds, port, &admin->mqprio); + return sja1105_static_config_reload(priv, SJA1105_SCHEDULING); } diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig index 2a8bdf9d1d68..81c4c7d2ca1a 100644 --- a/drivers/net/ethernet/freescale/Kconfig +++ b/drivers/net/ethernet/freescale/Kconfig @@ -35,6 +35,18 @@ config FEC Say Y here if you want to use the built-in 10/100 Fast ethernet controller on some Motorola ColdFire and Freescale i.MX/S32 processors. +config FEC_ECAT + tristate "FEC native dirver for FEC ethernet controller (of ColdFire and some i.MX CPUs)" + depends on (ARCH_MXC) + default ARCH_MXC if ARM + depends on PTP_1588_CLOCK_OPTIONAL + select CRC32 + select PHYLIB + imply NET_SELFTESTS + help + Say Y here if you want to use the built-in 10/100 Fast ethercat + controller on some Motorola ColdFire and Freescale i.MX/S32 processors. + config FEC_UIO tristate "FEC_UIO ethernet controller (i.MX 8M Mini CPU)" default n @@ -43,6 +55,14 @@ config FEC_UIO Say Y here if you want to use the built-in 10/100 Fast ethernet controller on Freescale i.MX 8M Mini processor. +config AVB_SUPPORT + bool "AVB interface support" + depends on FEC && ARCH_MXC + help + Say Y here if you want to enable the AVB interface in the FEC + driver. For the actual AVB functionality an external module is + still required. + config FEC_MPC52xx tristate "FEC MPC52xx driver" depends on PPC_MPC52xx && PPC_BESTCOMM diff --git a/drivers/net/ethernet/freescale/Makefile b/drivers/net/ethernet/freescale/Makefile index 13ae9401e4c2..bd3ee981ce54 100644 --- a/drivers/net/ethernet/freescale/Makefile +++ b/drivers/net/ethernet/freescale/Makefile @@ -7,6 +7,8 @@ obj-$(CONFIG_FEC) += fec.o fec-objs :=fec_main.o fec_ptp.o obj-$(CONFIG_FEC_UIO) += fec_uio.o +obj-$(CONFIG_FEC_ECAT) += fec_ecat.o + obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y) obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx_phy.o diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c index 20d6f2f66c4d..7c65a112b0d2 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.c +++ b/drivers/net/ethernet/freescale/enetc/enetc.c @@ -3735,6 +3735,7 @@ static int enetc_bdr_init(struct enetc_ndev_priv *priv, int i, int v_tx_rings) { struct enetc_int_vector *v __free(kfree); struct enetc_bdr *bdr; + char name[NAPINAMSIZ]; int j, err; v = kzalloc(struct_size(v, tx_ring, v_tx_rings), GFP_KERNEL); @@ -3768,7 +3769,10 @@ static int enetc_bdr_init(struct enetc_ndev_priv *priv, int i, int v_tx_rings) v->rx_dim_en = true; } INIT_WORK(&v->rx_dim.work, enetc_rx_dim_work); - netif_napi_add(priv->ndev, &v->napi, enetc_poll); + + snprintf(name, NAPINAMSIZ, "rxtx-%d", i); + netif_napi_add_named(priv->ndev, &v->napi, enetc_poll, + NAPI_POLL_WEIGHT, name); v->count_tx_rings = v_tx_rings; for (j = 0; j < v_tx_rings; j++) { diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h index e6f29cca3fe0..824cffe4cfc4 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.h +++ b/drivers/net/ethernet/freescale/enetc/enetc.h @@ -533,6 +533,8 @@ struct enetc_ndev_priv { * and link state updates */ struct mutex mm_lock; + + bool fp_enabled_admin; }; #define ENETC_CBD(R, i) (&(((struct enetc_cbd *)((R).bd_base))[i])) @@ -582,6 +584,8 @@ void enetc_refresh_vlan_ht_filter(struct enetc_si *si); void enetc_set_ethtool_ops(struct net_device *ndev); void enetc_mm_link_state_update(struct enetc_ndev_priv *priv, bool link); void enetc_mm_commit_preemptible_tcs(struct enetc_ndev_priv *priv); +int enetc_preempt_reset(struct net_device *ndev, bool enable); +int enetc_pmac_reset(struct net_device *ndev, bool enable); void enetc_eee_mode_set(struct net_device *dev, bool enable); /* control buffer descriptor ring (CBDR) */ @@ -802,6 +806,7 @@ static inline int enetc_set_psfp(struct net_device *ndev, bool en) void enetc_tsn_pf_init(struct net_device *netdev, struct pci_dev *pdev); void enetc_tsn_pf_deinit(struct net_device *netdev); +void enetc_ptp_clock_update(void); #else @@ -813,6 +818,9 @@ static inline void enetc_tsn_pf_deinit(struct net_device *netdev) { } +static inline void enetc_ptp_clock_update(void) +{ +} #endif #if IS_ENABLED(CONFIG_DEBUG_FS) diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c index 8e63dff9d4ec..97a675c6d9ef 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c @@ -8,6 +8,8 @@ #include #include "enetc_pf.h" +static void enetc_configure_port_pmac(struct enetc_hw *hw, bool enable); + static const u32 enetc_si_regs[] = { ENETC_SIMR, ENETC_SIPMAR0, ENETC_SIPMAR1, ENETC_SICBDRMR, ENETC_SICBDRSR, ENETC_SICBDRBAR0, ENETC_SICBDRBAR1, ENETC_SICBDRPIR, @@ -1313,6 +1315,27 @@ static int enetc_set_rxfh(struct net_device *ndev, const u32 *indir, return err; } +static int enetc_reset_preempt(struct net_device *ndev, bool enable) +{ + struct enetc_ndev_priv *priv = netdev_priv(ndev); + u32 temp; + + temp = enetc_rd(&priv->si->hw, ENETC_PTGCR); + if (temp & ENETC_PTGCR_TGE) + enetc_wr(&priv->si->hw, ENETC_PTGCR, + temp & (~ENETC_PTGCR_TGPE)); + + if (enable) { + if (priv->fp_enabled_admin) { + enetc_configure_port_pmac(&priv->si->hw, 1); + } + } else { + enetc_configure_port_pmac(&priv->si->hw, 0); + } + + return 0; +} + static void enetc_get_channels(struct net_device *ndev, struct ethtool_channels *ch) { @@ -1754,6 +1777,34 @@ static int enetc_get_mm(struct net_device *ndev, struct ethtool_mm_state *state) return 0; } +static void enetc_configure_port_pmac(struct enetc_hw *hw, bool enable) +{ + u32 temp; + + /* Set pMAC step lock */ + temp = enetc_port_rd(hw, ENETC_PFPMR); + enetc_port_wr(hw, ENETC_PFPMR, temp | ENETC_PFPMR_PMACE); + + temp = enetc_port_rd(hw, ENETC_MMCSR); + if (enable) + temp |= ENETC_MMCSR_ME; + else + temp &= (~ENETC_MMCSR_ME); + enetc_port_wr(hw, ENETC_MMCSR, temp); +} + +int enetc_pmac_reset(struct net_device *ndev, bool enable) +{ + struct enetc_ndev_priv *priv = netdev_priv(ndev); + u32 temp; + + temp = enetc_port_rd(&priv->si->hw, ENETC_PFPMR); + if (temp & ENETC_PFPMR_PMACE) + enetc_configure_port_pmac(&priv->si->hw, enable); + + return 0; +} + static int enetc_mm_wait_tx_active(struct enetc_hw *hw, int verify_time) { int timeout = verify_time * USEC_PER_MSEC * ENETC_MM_VERIFY_RETRIES; @@ -2051,6 +2102,105 @@ void enetc_mm_link_state_update(struct enetc_ndev_priv *priv, bool link) } EXPORT_SYMBOL_GPL(enetc_mm_link_state_update); +static int enetc_set_preempt(struct net_device *ndev, + struct ethtool_fp *pt) +{ + struct enetc_ndev_priv *priv = netdev_priv(ndev); + u32 preempt, temp; + int rafs; + int i; + + if (!pt) + return -EINVAL; + + if (!pt->disabled && (pt->min_frag_size < 60 || pt->min_frag_size > 252)) + return -EINVAL; + + rafs = DIV_ROUND_UP((pt->min_frag_size + 4), 64) - 1; + + preempt = pt->preemptible_queues_mask; + + temp = enetc_rd(&priv->si->hw, ENETC_PTGCR); + if (temp & ENETC_PTGCR_TGE) + enetc_wr(&priv->si->hw, ENETC_PTGCR, + temp & (~ENETC_PTGCR_TGPE)); + + for (i = 0; i < 8; i++) { + /* 1 Enabled. Traffic is transmitted on the preemptive MAC. */ + temp = enetc_port_rd(&priv->si->hw, ENETC_PTCFPR(i)); + + if ((preempt >> i) & 0x1) + enetc_port_wr(&priv->si->hw, + ENETC_PTCFPR(i), + temp | ENETC_PTCFPR_FPE); + else + enetc_port_wr(&priv->si->hw, + ENETC_PTCFPR(i), + temp & ~ENETC_PTCFPR_FPE); + } + + temp = enetc_port_rd(&priv->si->hw, ENETC_MMCSR); + temp &= ~ENETC_MMCSR_RAFS_MASK; + temp |= ENETC_MMCSR_RAFS(rafs); + if (pt->fp_enabled) + temp &= ~ENETC_MMCSR_VDIS; + else + temp |= ENETC_MMCSR_VDIS; + enetc_port_wr(&priv->si->hw, ENETC_MMCSR, temp); + + if (pt->disabled) { + enetc_configure_port_pmac(&priv->si->hw, 0); + priv->fp_enabled_admin = 0; + } else { + enetc_configure_port_pmac(&priv->si->hw, 1); + priv->fp_enabled_admin = 1; + } + + if (pt->disabled) { + temp = enetc_port_rd(&priv->si->hw, ENETC_PFPMR); + enetc_port_wr(&priv->si->hw, ENETC_PFPMR, + temp & ~ENETC_PFPMR_PMACE); + } + + return 0; +} + +static int enetc_get_preempt(struct net_device *ndev, + struct ethtool_fp *pt) +{ + struct enetc_ndev_priv *priv = netdev_priv(ndev); + u32 temp; + int i; + + if (!pt) + return -EINVAL; + + temp = enetc_port_rd(&priv->si->hw, ENETC_MMCSR); + if (!(temp & ENETC_MMCSR_VDIS) && (ENETC_MMCSR_GET_VSTS(temp) == 3)) + pt->fp_active = true; + else if ((temp & ENETC_MMCSR_VDIS) && (temp & ENETC_MMCSR_ME)) + pt->fp_active = true; + else + pt->fp_active = false; + + if (temp & ENETC_MMCSR_ME) + pt->fp_status = true; + else + pt->fp_status = false; + + pt->preemptible_queues_mask = 0; + for (i = 0; i < 8; i++) + if (enetc_port_rd(&priv->si->hw, ENETC_PTCFPR(i)) & 0x80000000) + pt->preemptible_queues_mask |= 1 << i; + + pt->fp_supported = !!(priv->si->hw_features & ENETC_SI_F_QBU); + pt->supported_queues_mask = 0xff; + temp = enetc_port_rd(&priv->si->hw, ENETC_MMCSR); + pt->min_frag_size = (ENETC_MMCSR_GET_RAFS(temp) + 1) * 64; + + return 0; +} + static const struct ethtool_ops enetc_pf_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_MAX_FRAMES | @@ -2087,6 +2237,9 @@ static const struct ethtool_ops enetc_pf_ethtool_ops = { .get_mm = enetc_get_mm, .set_mm = enetc_set_mm, .get_mm_stats = enetc_get_mm_stats, + .set_preempt = enetc_set_preempt, + .get_preempt = enetc_get_preempt, + .reset_preempt = enetc_reset_preempt, }; static const struct ethtool_ops enetc_vf_ethtool_ops = { diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c index f9f37e7ee258..d528efd900c5 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c @@ -634,6 +634,7 @@ static void enetc_pl_mac_link_down(struct phylink_config *config, enetc_mm_link_state_update(priv, false); enetc_mac_enable(si, false); + enetc_pmac_reset(pf->si->ndev, 0); } static const struct phylink_mac_ops enetc_mac_phylink_ops = { diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ptp.c b/drivers/net/ethernet/freescale/enetc/enetc_ptp.c index 5243fc031058..9c671494cd95 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_ptp.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_ptp.c @@ -10,6 +10,19 @@ int enetc_phc_index = -1; EXPORT_SYMBOL_GPL(enetc_phc_index); +int ptp_enetc_settime(struct ptp_clock_info *ptp, const struct timespec64 *ts) +{ + int ret; + + ret = ptp_qoriq_settime(ptp, ts); + if (ret) + return ret; + + enetc_ptp_clock_update(); + + return ret; +} + static struct ptp_clock_info enetc_ptp_caps = { .owner = THIS_MODULE, .name = "ENETC PTP clock", @@ -22,7 +35,7 @@ static struct ptp_clock_info enetc_ptp_caps = { .adjfine = ptp_qoriq_adjfine, .adjtime = ptp_qoriq_adjtime, .gettime64 = ptp_qoriq_gettime, - .settime64 = ptp_qoriq_settime, + .settime64 = ptp_enetc_settime, .enable = ptp_qoriq_enable, }; diff --git a/drivers/net/ethernet/freescale/enetc/enetc_qos.c b/drivers/net/ethernet/freescale/enetc/enetc_qos.c index 582ddbff1c94..441bce3dfef8 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_qos.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_qos.c @@ -2403,10 +2403,9 @@ static int enetc_config_clsflower(struct enetc_ndev_priv *priv, err = enetc_psfp_parse_clsflower(priv, cls_flower); else err = enetc4_psfp_parse_clsflower(priv, cls_flower); - if (err) { - NL_SET_ERR_MSG_MOD(extack, "Invalid PSFP inputs"); + + if (err) return err; - } } else { NL_SET_ERR_MSG_MOD(extack, "Unsupported actions"); return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/freescale/enetc/enetc_tsn.c b/drivers/net/ethernet/freescale/enetc/enetc_tsn.c index 2da3055b2703..e0e7fea5fc36 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_tsn.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_tsn.c @@ -5,6 +5,13 @@ #include +struct netdev_list_entry { + struct list_head list; + struct net_device *dev; +}; + +static struct list_head netdev_list = {0}; + static int alloc_cbdr(struct enetc_si *si, struct enetc_cbd **curr_cbd) { struct enetc_cbdr *ring = &si->cbd_ring; @@ -86,8 +93,8 @@ static int enetc_qbv_get(struct net_device *ndev, { struct tsn_qbv_basic *admin_basic = &admin_conf->admin; struct enetc_ndev_priv *priv = netdev_priv(ndev); + u16 data_size, admin_len, oper_len, maxlen; struct enetc_hw *hw = &priv->si->hw; - u16 data_size, admin_len, maxlen; struct tgs_gcl_query *gcl_query; struct tgs_gcl_resp *gcl_data; struct enetc_cbd *cbdr; @@ -120,6 +127,7 @@ static int enetc_qbv_get(struct net_device *ndev, gce = (struct gce *)(gcl_data + 1); gcl_query->acl_len = cpu_to_le16(maxlen); + gcl_query->ocl_len = cpu_to_le16(maxlen); dma_size = cpu_to_le16(data_size); cbdr->length = dma_size; @@ -142,8 +150,12 @@ static int enetc_qbv_get(struct net_device *ndev, /* since cbdr already passed to free, below could be get wrong */ admin_len = le16_to_cpu(gcl_query->admin_list_len); + oper_len = le16_to_cpu(gcl_query->oper_list_len); - admin_basic->control_list_length = admin_len; + if (!admin_len) + admin_basic->control_list_length = oper_len; + else + admin_basic->control_list_length = admin_len; temp = ((u64)le32_to_cpu(gcl_data->abth)) << 32; admin_basic->base_time = le32_to_cpu(gcl_data->abtl) + temp; @@ -151,7 +163,7 @@ static int enetc_qbv_get(struct net_device *ndev, admin_basic->cycle_time = le32_to_cpu(gcl_data->act); admin_basic->cycle_time_extension = le32_to_cpu(gcl_data->acte); - admin_basic->control_list = kcalloc(admin_len, + admin_basic->control_list = kcalloc(admin_basic->control_list_length, sizeof(*admin_basic->control_list), GFP_KERNEL); if (!admin_basic->control_list) { @@ -160,10 +172,15 @@ static int enetc_qbv_get(struct net_device *ndev, return -ENOMEM; } - for (i = 0; i < admin_len; i++) { - struct gce *temp_gce = gce + i; + for (i = 0; i < admin_basic->control_list_length; i++) { + struct gce *temp_gce; struct tsn_qbv_entry *temp_entry; + if (!admin_len) + temp_gce = gce + maxlen + i; + else + temp_gce = gce + i; + temp_entry = admin_basic->control_list + i; temp_entry->gate_state = temp_gce->gate; @@ -298,6 +315,120 @@ static int enetc_qbv_get_status(struct net_device *ndev, return 0; } +static int enetc_qbv_gcl_reset(struct net_device *ndev, + struct tsn_qbv_conf *admin_conf) +{ + struct tsn_qbv_basic *admin_basic = &admin_conf->admin; + struct enetc_ndev_priv *priv = netdev_priv(ndev); + struct enetc_hw *hw = &priv->si->hw; + struct tgs_gcl_conf *gcl_config; + struct tgs_gcl_data *gcl_data; + struct enetc_cbd *cbdr; + struct gce *gce; + dma_addr_t dma; + u16 data_size; + int curr_cbd; + u16 gcl_len; + u32 temp; + int i; + + gcl_len = admin_basic->control_list_length; + if (gcl_len > enetc_get_max_gcl_len(hw)) + return -EINVAL; + + temp = enetc_rd(hw, ENETC_PTGCR); + if (admin_conf->gate_enabled) { + enetc_wr(hw, ENETC_PTGCR, temp & ~ENETC_PTGCR_TGE); + usleep_range(10, 20); + enetc_wr(hw, ENETC_PTGCR, temp | ENETC_PTGCR_TGE); + } else if (!admin_conf->gate_enabled) { + return 0; + } + + /* Configure the (administrative) gate control list using the + * control BD descriptor. + */ + curr_cbd = alloc_cbdr(priv->si, &cbdr); + + gcl_config = &cbdr->gcl_conf; + + data_size = struct_size(gcl_data, entry, gcl_len); + + gcl_data = kzalloc(data_size, __GFP_DMA | GFP_KERNEL); + if (!gcl_data) + return -ENOMEM; + + gce = &gcl_data->entry[0]; + + gcl_config->atc = admin_basic->gate_states; + gcl_config->acl_len = cpu_to_le16(gcl_len); + + gcl_data->btl = cpu_to_le32(lower_32_bits(admin_basic->base_time)); + gcl_data->bth = cpu_to_le32(upper_32_bits(admin_basic->base_time)); + + gcl_data->ct = cpu_to_le32(admin_basic->cycle_time); + gcl_data->cte = cpu_to_le32(admin_basic->cycle_time_extension); + + for (i = 0; i < gcl_len; i++) { + struct gce *temp_gce = gce + i; + struct tsn_qbv_entry *temp_entry; + + temp_entry = admin_basic->control_list + i; + + temp_gce->gate = temp_entry->gate_state; + temp_gce->period = cpu_to_le32(temp_entry->time_interval); + } + + cbdr->length = cpu_to_le16(data_size); + cbdr->status_flags = 0; + + dma = dma_map_single(&priv->si->pdev->dev, gcl_data, + data_size, DMA_TO_DEVICE); + if (dma_mapping_error(&priv->si->pdev->dev, dma)) { + netdev_err(priv->si->ndev, "DMA mapping failed!\n"); + kfree(gcl_data); + return -ENOMEM; + } + + cbdr->addr[0] = cpu_to_le32(lower_32_bits(dma)); + cbdr->addr[1] = cpu_to_le32(upper_32_bits(dma)); + cbdr->cmd = 0; + cbdr->cls = BDCR_CMD_PORT_GCL; + + /* Updated by ENETC on completion of the configuration + * command. A zero value indicates success. + */ + cbdr->status_flags = 0; + + xmit_cbdr(priv->si, curr_cbd); + + memset(cbdr, 0, sizeof(struct enetc_cbd)); + dma_unmap_single(&priv->si->pdev->dev, dma, data_size, DMA_TO_DEVICE); + kfree(gcl_data); + + return 0; +} + +static int enetc_est_reset(struct net_device *ndev) +{ + struct tsn_qbv_conf admin_conf = {0}; + int ret; + + ret = enetc_qbv_get(ndev, &admin_conf); + if (ret) + return ret; + + return enetc_qbv_gcl_reset(ndev, &admin_conf); +} + +void enetc_ptp_clock_update() +{ + struct netdev_list_entry *entry; + + list_for_each_entry(entry, &netdev_list, list) + enetc_est_reset(entry->dev); +} + /* CBD Class 7: Stream Identity Entry Set Descriptor - Long Format */ static int enetc_cb_streamid_set(struct net_device *ndev, u32 index, bool en, struct tsn_cb_streamid *streamid) @@ -1383,6 +1514,14 @@ static int enetc_qbu_set(struct net_device *ndev, u8 preemptible_tcs) enetc_change_preemptible_tcs(priv, preemptible_tcs); + /* Set pMAC step lock */ + val = enetc_port_rd(&priv->si->hw, ENETC_PFPMR); + enetc_port_wr(&priv->si->hw, ENETC_PFPMR, + val | ENETC_PFPMR_PMACE); + + val = enetc_port_rd(&priv->si->hw, ENETC_MMCSR); + enetc_port_wr(&priv->si->hw, ENETC_MMCSR, val | ENETC_MMCSR_ME); + return 0; } @@ -1734,6 +1873,33 @@ static const struct tsn_ops enetc_tsn_ops_part = { .qci_fmi_get = enetc_qci_fmi_get, }; +static void enetc_tsn_netdev_list_add(struct net_device *ndev) +{ + struct netdev_list_entry *entry; + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return; + + entry->dev = ndev; + + if (!netdev_list.next) + INIT_LIST_HEAD(&netdev_list); + + list_add_tail(&entry->list, &netdev_list); +} + +static void enetc_tsn_netdev_list_del(struct net_device *ndev) +{ + struct netdev_list_entry *tmp, *n; + + list_for_each_entry_safe(tmp, n, &netdev_list, list) + if (tmp->dev == ndev) { + list_del(&tmp->list); + kfree(tmp); + } +} + void enetc_tsn_pf_init(struct net_device *netdev, struct pci_dev *pdev) { int port = pdev->devfn & 0x7; @@ -1744,9 +1910,12 @@ void enetc_tsn_pf_init(struct net_device *netdev, struct pci_dev *pdev) else tsn_port_register(netdev, &enetc_tsn_ops_full, (u16)pdev->bus->number); + + enetc_tsn_netdev_list_add(netdev); } void enetc_tsn_pf_deinit(struct net_device *netdev) { tsn_port_unregister(netdev); + enetc_tsn_netdev_list_del(netdev); } diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index b4e819ff34f7..7faa09477396 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -23,6 +23,7 @@ #include #include #include +#include #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ @@ -334,30 +335,30 @@ struct bufdesc_ex { (IDLE_SLOPE_2 & IDLE_SLOPE_MASK)) #define RCMR_MATCHEN (0x1 << 16) #define RCMR_CMP_CFG(v, n) (((v) & 0x7) << (n << 2)) +#ifdef CONFIG_AVB_SUPPORT +#define SR_CLASS_A_PRIORITY 3 +#define SR_CLASS_B_PRIORITY 2 +#define RCMR_CMP_1 (RCMR_CMP_CFG(SR_CLASS_A_PRIORITY, 0) | \ + RCMR_CMP_CFG(SR_CLASS_A_PRIORITY, 1) | \ + RCMR_CMP_CFG(SR_CLASS_A_PRIORITY, 2) | \ + RCMR_CMP_CFG(SR_CLASS_A_PRIORITY, 3)) +#define RCMR_CMP_2 (RCMR_CMP_CFG(SR_CLASS_B_PRIORITY, 0) | \ + RCMR_CMP_CFG(SR_CLASS_B_PRIORITY, 1) | \ + RCMR_CMP_CFG(SR_CLASS_B_PRIORITY, 2) | \ + RCMR_CMP_CFG(SR_CLASS_B_PRIORITY, 3)) +#else #define RCMR_CMP_1 (RCMR_CMP_CFG(0, 0) | RCMR_CMP_CFG(1, 1) | \ RCMR_CMP_CFG(2, 2) | RCMR_CMP_CFG(3, 3)) #define RCMR_CMP_2 (RCMR_CMP_CFG(4, 0) | RCMR_CMP_CFG(5, 1) | \ RCMR_CMP_CFG(6, 2) | RCMR_CMP_CFG(7, 3)) +#endif #define RCMR_CMP(X) (((X) == 1) ? RCMR_CMP_1 : RCMR_CMP_2) #define FEC_TX_BD_FTYPE(X) (((X) & 0xf) << 20) -/* The number of Tx and Rx buffers. These are allocated from the page - * pool. The code may assume these are power of two, so it it best - * to keep them that size. - * We don't need to allocate pages for the transmitter. We just use - * the skbuffer directly. - */ +#define FEC_RX_FLUSH(X) (1 << ((X) + 3)) -#define FEC_ENET_XDP_HEADROOM (XDP_PACKET_HEADROOM) -#define FEC_ENET_RX_PAGES 256 -#define FEC_ENET_RX_FRSIZE (PAGE_SIZE - FEC_ENET_XDP_HEADROOM \ - - SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) -#define FEC_ENET_RX_FRPPG (PAGE_SIZE / FEC_ENET_RX_FRSIZE) -#define RX_RING_SIZE (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES) -#define FEC_ENET_TX_FRSIZE 2048 -#define FEC_ENET_TX_FRPPG (PAGE_SIZE / FEC_ENET_TX_FRSIZE) -#define TX_RING_SIZE 1024 /* Must be power of two */ -#define TX_RING_MOD_MASK 511 /* for this to work */ +#define FEC_TX_SCHEME_CB 0x0 /* Credit based */ +#define FEC_TX_SCHEME_RR 0x1 /* Round-robin */ #define BD_ENET_RX_INT 0x00800000 #define BD_ENET_RX_PTP ((ushort)0x0400) @@ -402,9 +403,14 @@ struct bufdesc_ex { #define FEC_ITR_EN (0x1 << 31) #define FEC_ITR_ICFT(X) (((X) & 0xff) << 20) #define FEC_ITR_ICTT(X) ((X) & 0xffff) -#define FEC_ITR_ICFT_DEFAULT 200 /* Set 200 frame count threshold */ #define FEC_ITR_ICTT_DEFAULT 1000 /* Set 1000us timer threshold */ +#ifdef CONFIG_AVB_SUPPORT +#define FEC_ITR_ICFT_DEFAULT 50 /* Keep it coherent with FEC_TX_RING_SIZE/FEC_RX_RING_SIZE */ +#else +#define FEC_ITR_ICFT_DEFAULT 200 /* Set 200 frame count threshold */ +#endif + #define FEC_VLAN_TAG_LEN 0x04 #define FEC_ETHTYPE_LEN 0x02 @@ -559,20 +565,31 @@ struct fec_tx_buffer { struct fec_enet_priv_tx_q { struct bufdesc_prop bd; - unsigned char *tx_bounce[TX_RING_SIZE]; - struct fec_tx_buffer tx_buf[TX_RING_SIZE]; + struct bufdesc *dirty_tx; + struct fec_tx_buffer tx_buf[FEC_TX_RING_SIZE]; + + unsigned int tx_bounce_size; +#ifdef CONFIG_AVB_SUPPORT + unsigned int tx_index; + unsigned char *tx_bounce[FEC_TX_RING_SIZE + 32]; +#else + unsigned char *tx_bounce[FEC_TX_RING_SIZE]; +#endif unsigned short tx_stop_threshold; unsigned short tx_wake_threshold; - struct bufdesc *dirty_tx; char *tso_hdrs; dma_addr_t tso_hdrs_dma; + +#ifdef CONFIG_AVB_SUPPORT + unsigned long tx_idle_slope; +#endif }; struct fec_enet_priv_rx_q { struct bufdesc_prop bd; - struct fec_enet_priv_txrx_info rx_skb_info[RX_RING_SIZE]; + struct fec_enet_priv_txrx_info rx_skb_info[FEC_RX_RING_SIZE]; /* page_pool */ struct page_pool *page_pool; @@ -622,6 +639,12 @@ struct fec_enet_private { unsigned int total_tx_ring_size; unsigned int total_rx_ring_size; +#ifdef CONFIG_AVB_SUPPORT + const struct avb_ops *avb; + void *avb_data; + unsigned int avb_enabled; + __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_advertising); +#endif struct platform_device *pdev; int dev_id; @@ -654,12 +677,19 @@ struct fec_enet_private { struct ptp_clock *ptp_clock; struct ptp_clock_info ptp_caps; - spinlock_t tmreg_lock; + raw_spinlock_t tmreg_lock; struct cyclecounter cc; struct timecounter tc; u32 cycle_speed; int hwts_rx_en; int hwts_tx_en; + + /* Transmit and receive latency, depending on link speed, for + * packets timestamps in ns + */ + u32 rx_tstamp_latency; + u32 tx_tstamp_latency; + struct delayed_work time_keep; struct regulator *reg_mdio; struct regulator *reg_phy; @@ -696,9 +726,35 @@ struct fec_enet_private { /* XDP BPF Program */ struct bpf_prog *xdp_prog; + /* Configured rx/tx timestamps delays for different link speeds + * to compensate for FEC-PHY latency in ns + */ + u32 rx_delay_100; + u32 tx_delay_100; + u32 rx_delay_1000; + u32 tx_delay_1000; + +#ifdef CONFIG_AVB_SUPPORT + int rec_channel; + int rec_enable; +#endif + u64 ethtool_stats[]; }; +#ifdef CONFIG_AVB_SUPPORT +#define FEC_MAX_RATE 400 /* Mbps */ +#define FEC_MAX_RATE_HAS_AVB 1000 /* Mbps */ + +static inline int fec_max_rate(struct fec_enet_private *fep) +{ + int max_rate = (fep->quirks & FEC_QUIRK_HAS_AVB) ? FEC_MAX_RATE_HAS_AVB : FEC_MAX_RATE; + return min(max_rate, fep->speed); +} + +#define IDLE_SLOPE_DIVISOR 512 +#endif + void fec_ptp_init(struct platform_device *pdev, int irq_idx); void fec_ptp_stop(struct platform_device *pdev); void fec_ptp_start_cyclecounter(struct net_device *ndev); diff --git a/drivers/net/ethernet/freescale/fec_ecat.c b/drivers/net/ethernet/freescale/fec_ecat.c new file mode 100644 index 000000000000..642cfe340930 --- /dev/null +++ b/drivers/net/ethernet/freescale/fec_ecat.c @@ -0,0 +1,3063 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2023 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fec_ecat.h" + + +#define DRIVER_NAME "fec_ecat" + +/* Pause frame feild and FIFO threshold */ +#define FEC_ENET_FCE (1 << 5) +#define FEC_ENET_RSEM_V 0x84 +#define FEC_ENET_RSFL_V 16 +#define FEC_ENET_RAEM_V 0x8 +#define FEC_ENET_RAFL_V 0x8 +#define FEC_ENET_OPD_V 0xFFF0 +#define FEC_MDIO_PM_TIMEOUT 100 /* ms */ + +struct fec_devinfo { + u32 quirks; +}; + +static const struct fec_devinfo fec_imx25_info = { + .quirks = FEC_QUIRK_USE_GASKET | FEC_QUIRK_MIB_CLEAR | + FEC_QUIRK_HAS_FRREG, +}; + +static const struct fec_devinfo fec_imx27_info = { + .quirks = FEC_QUIRK_MIB_CLEAR | FEC_QUIRK_HAS_FRREG, +}; + +static const struct fec_devinfo fec_imx28_info = { + .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME | + FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC | + FEC_QUIRK_HAS_FRREG | FEC_QUIRK_CLEAR_SETUP_MII | + FEC_QUIRK_NO_HARD_RESET, +}; + +static const struct fec_devinfo fec_imx6q_info = { + .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | + FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | + FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 | + FEC_QUIRK_HAS_RACC | FEC_QUIRK_CLEAR_SETUP_MII | + FEC_QUIRK_HAS_PMQOS, +}; + +static const struct fec_devinfo fec_mvf600_info = { + .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC, +}; + +static const struct fec_devinfo fec_imx6x_info = { + .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | + FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | + FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | + FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | + FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE | + FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES, +}; + +static const struct fec_devinfo fec_imx6ul_info = { + .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | + FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | + FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR007885 | + FEC_QUIRK_BUG_CAPTURE | FEC_QUIRK_HAS_RACC | + FEC_QUIRK_HAS_COALESCE | FEC_QUIRK_CLEAR_SETUP_MII, +}; + +static const struct fec_devinfo fec_imx8mq_info = { + .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | + FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | + FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | + FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | + FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE | + FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES | + FEC_QUIRK_HAS_EEE | FEC_QUIRK_WAKEUP_FROM_INT2, +}; + +static const struct fec_devinfo fec_imx8qm_info = { + .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | + FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | + FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | + FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE | + FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE | + FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_HAS_MULTI_QUEUES | + FEC_QUIRK_DELAYED_CLKS_SUPPORT, +}; + +static const struct fec_devinfo fec_s32v234_info = { + .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | + FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | + FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB | + FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE, +}; + +static struct platform_device_id fec_devtype[] = { + { + /* keep it for coldfire */ + .name = DRIVER_NAME, + .driver_data = 0, + }, { + .name = "imx25-fec", + .driver_data = (kernel_ulong_t)&fec_imx25_info, + }, { + .name = "imx27-fec", + .driver_data = (kernel_ulong_t)&fec_imx27_info, + }, { + .name = "imx28-fec", + .driver_data = (kernel_ulong_t)&fec_imx28_info, + }, { + .name = "imx6q-fec", + .driver_data = (kernel_ulong_t)&fec_imx6q_info, + }, { + .name = "mvf600-fec", + .driver_data = (kernel_ulong_t)&fec_mvf600_info, + }, { + .name = "imx6sx-fec", + .driver_data = (kernel_ulong_t)&fec_imx6x_info, + }, { + .name = "imx6ul-fec", + .driver_data = (kernel_ulong_t)&fec_imx6ul_info, + }, { + .name = "imx8mq-fec", + .driver_data = (kernel_ulong_t)&fec_imx8mq_info, + }, { + .name = "imx8qm-fec", + .driver_data = (kernel_ulong_t)&fec_imx8qm_info, + }, { + .name = "s32v234-fec", + .driver_data = (kernel_ulong_t)&fec_s32v234_info, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, fec_devtype); + +enum imx_fec_type { + IMX25_FEC = 1, /* runs on i.mx25/50/53 */ + IMX27_FEC, /* runs on i.mx27/35/51 */ + IMX28_FEC, + IMX6Q_FEC, + MVF600_FEC, + IMX6SX_FEC, + IMX6UL_FEC, + IMX8MQ_FEC, + IMX8QM_FEC, + S32V234_FEC, +}; + +static const struct of_device_id fec_dt_ids[] = { + { .compatible = "fsl,imx25-fec-ecat", .data = &fec_devtype[IMX25_FEC], }, + { .compatible = "fsl,imx27-fec-ecat", .data = &fec_devtype[IMX27_FEC], }, + { .compatible = "fsl,imx28-fec-ecat", .data = &fec_devtype[IMX28_FEC], }, + { .compatible = "fsl,imx6q-fec-ecat", .data = &fec_devtype[IMX6Q_FEC], }, + { .compatible = "fsl,mvf600-fec-ecat", .data = &fec_devtype[MVF600_FEC], }, + { .compatible = "fsl,imx6sx-fec-ecat", .data = &fec_devtype[IMX6SX_FEC], }, + { .compatible = "fsl,imx6ul-fec-ecat", .data = &fec_devtype[IMX6UL_FEC], }, + { .compatible = "fsl,imx8mq-fec-ecat", .data = &fec_devtype[IMX8MQ_FEC], }, + { .compatible = "fsl,imx8qm-fec-ecat", .data = &fec_devtype[IMX8QM_FEC], }, + { .compatible = "fsl,s32v234-fec-ecat", .data = &fec_devtype[S32V234_FEC], }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, fec_dt_ids); + +static unsigned char macaddr[ETH_ALEN]; +module_param_array(macaddr, byte, NULL, 0); +MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); + +#if defined(CONFIG_M5272) +/* + * Some hardware gets it MAC address out of local flash memory. + * if this is non-zero then assume it is the address to get MAC from. + */ +#if defined(CONFIG_NETtel) +#define FEC_FLASHMAC 0xf0006006 +#elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES) +#define FEC_FLASHMAC 0xf0006000 +#elif defined(CONFIG_CANCam) +#define FEC_FLASHMAC 0xf0020000 +#elif defined (CONFIG_M5272C3) +#define FEC_FLASHMAC (0xffe04000 + 4) +#elif defined(CONFIG_MOD5272) +#define FEC_FLASHMAC 0xffc0406b +#else +#define FEC_FLASHMAC 0 +#endif +#endif /* CONFIG_M5272 */ + +/* The FEC stores dest/src/type/vlan, data, and checksum for receive packets. + * + * 2048 byte skbufs are allocated. However, alignment requirements + * varies between FEC variants. Worst case is 64, so round down by 64. + */ +#define PKT_MAXBUF_SIZE (round_down(2048 - 64, 64)) +#define PKT_MINBUF_SIZE 64 + +/* FEC receive acceleration */ +#define FEC_RACC_IPDIS (1 << 1) +#define FEC_RACC_PRODIS (1 << 2) +#define FEC_RACC_SHIFT16 BIT(7) +#define FEC_RACC_OPTIONS (FEC_RACC_IPDIS | FEC_RACC_PRODIS) + +/* MIB Control Register */ +#define FEC_MIB_CTRLSTAT_DISABLE BIT(31) + +/* + * The 5270/5271/5280/5282/532x RX control register also contains maximum frame + * size bits. Other FEC hardware does not, so we need to take that into + * account when setting it. + */ +#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ + defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ + defined(CONFIG_ARM64) +#define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16) +#else +#define OPT_FRAME_SIZE 0 +#endif + +/* FEC MII MMFR bits definition */ +#define FEC_MMFR_ST (1 << 30) +#define FEC_MMFR_ST_C45 (0) +#define FEC_MMFR_OP_READ (2 << 28) +#define FEC_MMFR_OP_READ_C45 (3 << 28) +#define FEC_MMFR_OP_WRITE (1 << 28) +#define FEC_MMFR_OP_ADDR_WRITE (0) +#define FEC_MMFR_PA(v) ((v & 0x1f) << 23) +#define FEC_MMFR_RA(v) ((v & 0x1f) << 18) +#define FEC_MMFR_TA (2 << 16) +#define FEC_MMFR_DATA(v) (v & 0xffff) +/* FEC ECR bits definition */ +#define FEC_ECR_MAGICEN (1 << 2) +#define FEC_ECR_SLEEP (1 << 3) + +#define FEC_MII_TIMEOUT 30000 /* us */ + +/* Transmitter timeout */ +#define TX_TIMEOUT (2 * HZ) + +#define FEC_PAUSE_FLAG_AUTONEG 0x1 +#define FEC_PAUSE_FLAG_ENABLE 0x2 +#define FEC_WOL_HAS_MAGIC_PACKET (0x1 << 0) +#define FEC_WOL_FLAG_ENABLE (0x1 << 1) +#define FEC_WOL_FLAG_SLEEP_ON (0x1 << 2) + +static void set_multicast_list(struct net_device *ndev); +static int mii_cnt; + +static struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, + struct bufdesc_prop *bd) +{ + return (bdp >= bd->last) ? bd->base + : (struct bufdesc *)(((void *)bdp) + bd->dsize); +} + +static struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, + struct bufdesc_prop *bd) +{ + return (bdp <= bd->base) ? bd->last + : (struct bufdesc *)(((void *)bdp) - bd->dsize); +} + +static int fec_enet_get_bd_index(struct bufdesc *bdp, + struct bufdesc_prop *bd) +{ + return ((const char *)bdp - (const char *)bd->base) >> bd->dsize_log2; +} + +static void swap_buffer(void *bufaddr, int len) +{ + int i; + unsigned int *buf = bufaddr; + + for (i = 0; i < len; i += 4, buf++) + swab32s(buf); +} + +static void swap_buffer2(void *dst_buf, void *src_buf, int len) +{ + int i; + unsigned int *src = src_buf; + unsigned int *dst = dst_buf; + + for (i = 0; i < len; i += 4, src++, dst++) + *dst = swab32p(src); +} + +static void fec_dump(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct bufdesc *bdp; + struct fec_enet_priv_tx_q *txq; + int index = 0; + + netdev_info(ndev, "TX ring dump\n"); + pr_info("Nr SC addr len SKB\n"); + + txq = fep->tx_queue; + bdp = txq->bd.base; + + do { + pr_info("%3u %c%c 0x%04x 0x%08x %4u %p\n", + index, + bdp == txq->bd.cur ? 'S' : ' ', + bdp == txq->dirty_tx ? 'H' : ' ', + fec16_to_cpu(bdp->cbd_sc), + fec32_to_cpu(bdp->cbd_bufaddr), + fec16_to_cpu(bdp->cbd_datlen), + txq->tx_skbuff[index]); + bdp = fec_enet_get_nextdesc(bdp, &txq->bd); + index++; + } while (bdp != txq->bd.base); +} + + +static int fec_ecat_txq_submit_buff(struct fec_enet_priv_tx_q *txq, + void __user *buff, size_t len, struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct bufdesc *bdp, *last_bdp; + void *bufaddr; + unsigned short status; + unsigned short buflen; + unsigned int index; + struct sk_buff *skb; + + bdp = txq->bd.cur; + last_bdp = bdp; + status = fec16_to_cpu(bdp->cbd_sc); + status &= ~BD_ENET_TX_STATS; + + index = fec_enet_get_bd_index(last_bdp, &txq->bd); + skb = txq->tx_skbuff[index]; + bufaddr = skb->data; + buflen = len; + copy_from_user(skb->data, buff, len); + bdp->cbd_datlen = cpu_to_fec16(buflen); + /* Push the data cache so the CPM does not get stale memory data. */ + dma_sync_single_for_device(&fep->pdev->dev, + fec32_to_cpu(bdp->cbd_bufaddr), + buflen, + DMA_TO_DEVICE); + + status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST); + + wmb(); + + /* Send it on its way. Tell FEC it's ready, interrupt when done, + * it's the last BD of the frame, and to put the CRC on the end. + */ + status |= (BD_ENET_TX_READY | BD_ENET_TX_TC); + bdp->cbd_sc = cpu_to_fec16(status); + + /* If this was the last BD in the ring, start at the beginning again. */ + bdp = fec_enet_get_nextdesc(last_bdp, &txq->bd); + + wmb(); + txq->bd.cur = bdp; + + /* Trigger transmission start */ + if (!(fep->quirks & FEC_QUIRK_ERR007885) || + !readl(txq->bd.reg_desc_active) || + !readl(txq->bd.reg_desc_active) || + !readl(txq->bd.reg_desc_active) || + !readl(txq->bd.reg_desc_active)) + writel(0, txq->bd.reg_desc_active); + + return 0; +} + +static void fec_ecat_tx_queue(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct bufdesc *bdp; + unsigned short status; + struct fec_enet_priv_tx_q *txq = fep->tx_queue; + int index = 0; + + /* get next bdp of dirty_tx */ + bdp = txq->dirty_tx; + + /* get next bdp of dirty_tx */ + bdp = fec_enet_get_nextdesc(bdp, &txq->bd); + + while (bdp != READ_ONCE(txq->bd.cur)) { + /* Order the load of bd.cur and cbd_sc */ + rmb(); + status = fec16_to_cpu(READ_ONCE(bdp->cbd_sc)); + if (status & BD_ENET_TX_READY) + break; + + /* Check for errors. */ + if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | + BD_ENET_TX_RL | BD_ENET_TX_UN | + BD_ENET_TX_CSL)) { + ndev->stats.tx_errors++; + } else { + ndev->stats.tx_packets++; + } + + wmb(); + txq->dirty_tx = bdp; + + /* Update pointer to next buffer descriptor to be transmitted */ + bdp = fec_enet_get_nextdesc(bdp, &txq->bd); + } + + /* ERR006358: Keep the transmitter going */ + if (bdp != txq->bd.cur && + readl(txq->bd.reg_desc_active) == 0) + writel(0, txq->bd.reg_desc_active); +} + +static int fec_ecat_fast_xmit(struct net_device *ndev, void __user *buff, size_t len) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + int ret; + + if (!mutex_trylock(&fep->fast_ndev_lock)) { + return -EBUSY; + } + + ret = fec_ecat_txq_submit_buff(fep->tx_queue, buff, len, ndev); + fec_ecat_tx_queue(ndev); + + mutex_unlock(&fep->fast_ndev_lock); + + return NETDEV_TX_OK; +} + +/* Init RX n TX buffer descriptors + */ +static void fec_enet_bd_init(struct net_device *dev) +{ + struct fec_enet_private *fep = netdev_priv(dev); + struct fec_enet_priv_tx_q *txq = fep->tx_queue; + struct fec_enet_priv_rx_q *rxq = fep->rx_queue; + struct bufdesc *bdp; + int i; + struct sk_buff *skb; + + /* Initialize the receive buffer descriptors. */ + bdp = rxq->bd.base; + + for (i = 0; i < rxq->bd.ring_size; i++) { + /* Initialize the BD for every fragment in the page. */ + if (bdp->cbd_bufaddr) + bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); + else + bdp->cbd_sc = cpu_to_fec16(0); + bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); + } + + /* Set the last buffer to wrap */ + bdp = fec_enet_get_prevdesc(bdp, &rxq->bd); + bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); + rxq->bd.cur = rxq->bd.base; + + /* ...and the same for transmit */ + bdp = txq->bd.base; + txq->bd.cur = bdp; + + for (i = 0; i < txq->bd.ring_size; i++) { + bdp->cbd_sc = cpu_to_fec16(0); + bdp = fec_enet_get_nextdesc(bdp, &txq->bd); + } + + /* Set the last buffer to wrap */ + bdp = fec_enet_get_prevdesc(bdp, &txq->bd); + bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); + txq->dirty_tx = bdp; +} + +/* + * This function is called to start or restart the FEC during a link + * change, transmit timeout, or to reconfigure the FEC. The network + * packet processing for this device must be stopped before this call. + */ +static void +fec_restart(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + u32 temp_mac[2]; + u32 rcntl = OPT_FRAME_SIZE | 0x04; + u32 ecntl = FEC_ENET_ETHEREN; /* ETHEREN */ + + /* Always use disable MAC instead of MAC reset to: + * - Keep the ENET counter running + * - Avoid dead system bus for SoCs using the ENET-AXI bus + * and not the AHB bus, like the i.MX6SX + */ + writel(0, fep->hwp + FEC_ECNTRL); + + /* + * enet-mac reset will reset mac address registers too, + * so need to reconfigure it. + */ + memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN); + writel((__force u32)cpu_to_be32(temp_mac[0]), + fep->hwp + FEC_ADDR_LOW); + writel((__force u32)cpu_to_be32(temp_mac[1]), + fep->hwp + FEC_ADDR_HIGH); + + /* Clear any outstanding interrupt, except MDIO. */ + writel((0xffffffff & ~FEC_ENET_MII), fep->hwp + FEC_IEVENT); + + fec_enet_bd_init(ndev); + + writel(fep->rx_queue->bd.dma, fep->hwp + FEC_R_DES_START(0)); + writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_R_BUFF_SIZE(0)); + + writel(fep->tx_queue->bd.dma, fep->hwp + FEC_X_DES_START(0)); + + if (fep->quirks & FEC_QUIRK_HAS_AVB) + writel(FEC_RX_FLUSH(0) | FEC_TX_SCHEME_CB, + fep->hwp + FEC_QOS_SCHEME); + + /* Enable MII mode */ + if (fep->full_duplex == DUPLEX_FULL) { + /* FD enable */ + writel(0x04, fep->hwp + FEC_X_CNTRL); + } else { + /* No Rcv on Xmit */ + rcntl |= 0x02; + writel(0x0, fep->hwp + FEC_X_CNTRL); + } + + /* Set MII speed */ + writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); + +#if !defined(CONFIG_M5272) + if (fep->quirks & FEC_QUIRK_HAS_RACC) { + u32 val = readl(fep->hwp + FEC_RACC); + + /* align IP header */ + val |= FEC_RACC_SHIFT16; + if (fep->csum_flags & FLAG_RX_CSUM_ENABLED) + /* set RX checksum */ + val |= FEC_RACC_OPTIONS; + else + val &= ~FEC_RACC_OPTIONS; + writel(val, fep->hwp + FEC_RACC); + writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_FTRL); + } +#endif + + /* + * The phy interface and speed need to get configured + * differently on enet-mac. + */ + if (fep->quirks & FEC_QUIRK_ENET_MAC) { + /* Enable flow control and length check */ + rcntl |= 0x40000000 | 0x00000020; + + /* RGMII, RMII or MII */ + if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII || + fep->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || + fep->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID || + fep->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) + rcntl |= (1 << 6); + else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) + rcntl |= (1 << 8); + else + rcntl &= ~(1 << 8); + + /* 1G, 100M or 10M */ + if (ndev->phydev) { + if (ndev->phydev->speed == SPEED_1000) + ecntl |= (1 << 5); + else if (ndev->phydev->speed == SPEED_100) + rcntl &= ~(1 << 9); + else + rcntl |= (1 << 9); + } + } else { +#ifdef FEC_MIIGSK_ENR + if (fep->quirks & FEC_QUIRK_USE_GASKET) { + u32 cfgr; + /* disable the gasket and wait */ + writel(0, fep->hwp + FEC_MIIGSK_ENR); + while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4) + udelay(1); + + /* + * configure the gasket: + * RMII, 50 MHz, no loopback, no echo + * MII, 25 MHz, no loopback, no echo + */ + cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII) + ? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII; + if (ndev->phydev && ndev->phydev->speed == SPEED_10) + cfgr |= BM_MIIGSK_CFGR_FRCONT_10M; + writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR); + + /* re-enable the gasket */ + writel(2, fep->hwp + FEC_MIIGSK_ENR); + } +#endif + } + +#if !defined(CONFIG_M5272) + /* enable pause frame*/ + if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) || + ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) && + ndev->phydev && ndev->phydev->pause)) { + rcntl |= FEC_ENET_FCE; + + /* set FIFO threshold parameter to reduce overrun */ + writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM); + writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL); + writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM); + writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL); + + /* OPD */ + writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD); + } else { + rcntl &= ~FEC_ENET_FCE; + } +#endif /* !defined(CONFIG_M5272) */ + + writel(rcntl, fep->hwp + FEC_R_CNTRL); + + /* Setup multicast filter. */ + set_multicast_list(ndev); +#ifndef CONFIG_M5272 + writel(0, fep->hwp + FEC_HASH_TABLE_HIGH); + writel(0, fep->hwp + FEC_HASH_TABLE_LOW); +#endif + + if (fep->quirks & FEC_QUIRK_ENET_MAC) { + /* enable ENET endian swap */ + ecntl |= (1 << 8); + /* enable ENET store and forward mode */ + writel(1 << 8, fep->hwp + FEC_X_WMRK); + } + + if (fep->quirks & FEC_QUIRK_DELAYED_CLKS_SUPPORT && + fep->rgmii_txc_dly) + ecntl |= FEC_ENET_TXC_DLY; + if (fep->quirks & FEC_QUIRK_DELAYED_CLKS_SUPPORT && + fep->rgmii_rxc_dly) + ecntl |= FEC_ENET_RXC_DLY; + +#ifndef CONFIG_M5272 + /* Enable the MIB statistic event counters */ + writel(0 << 31, fep->hwp + FEC_MIB_CTRLSTAT); +#endif + + /* And last, enable the transmit and receive processing */ + writel(ecntl, fep->hwp + FEC_ECNTRL); + writel(0, fep->rx_queue->bd.reg_desc_active); + + writel(0, fep->hwp + FEC_IMASK); +} + +static int fec_enet_ipc_handle_init(struct fec_enet_private *fep) +{ + if (!(of_machine_is_compatible("fsl,imx8qm") || + of_machine_is_compatible("fsl,imx8qxp") || + of_machine_is_compatible("fsl,imx8dxl"))) + return 0; + + return imx_scu_get_handle(&fep->ipc_handle); +} + +static void fec_enet_ipg_stop_set(struct fec_enet_private *fep, bool enabled) +{ + struct device_node *np = fep->pdev->dev.of_node; + u32 rsrc_id, val; + int idx; + + if (!np || !fep->ipc_handle) + return; + + idx = of_alias_get_id(np, "ethernet"); + if (idx < 0) + idx = 0; + rsrc_id = idx ? IMX_SC_R_ENET_1 : IMX_SC_R_ENET_0; + + val = enabled ? 1 : 0; + imx_sc_misc_set_control(fep->ipc_handle, rsrc_id, IMX_SC_C_IPG_STOP, val); +} + +static void fec_enet_stop_mode(struct fec_enet_private *fep, bool enabled) +{ + struct fec_platform_data *pdata = fep->pdev->dev.platform_data; + struct fec_stop_mode_gpr *stop_gpr = &fep->stop_gpr; + + if (stop_gpr->gpr) { + if (enabled) + regmap_update_bits(stop_gpr->gpr, stop_gpr->reg, + BIT(stop_gpr->bit), + BIT(stop_gpr->bit)); + else + regmap_update_bits(stop_gpr->gpr, stop_gpr->reg, + BIT(stop_gpr->bit), 0); + } else if (pdata && pdata->sleep_mode_enable) { + pdata->sleep_mode_enable(enabled); + } else { + fec_enet_ipg_stop_set(fep, enabled); + } +} + +static inline void fec_irqs_disable(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + + writel(0, fep->hwp + FEC_IMASK); +} + +static void +fec_stop(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); + + /* We cannot expect a graceful transmit stop without link !!! */ + if (fep->link) { + writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */ + udelay(10); + if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA)) + netdev_err(ndev, "Graceful transmit stop did not complete!\n"); + } + + writel(0, fep->hwp + FEC_ECNTRL); + + writel(0, fep->hwp + FEC_IMASK); + writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); +} + + +static void +fec_timeout(struct net_device *ndev, unsigned int txqueue) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + + fec_dump(ndev); + + ndev->stats.tx_errors++; + + schedule_work(&fep->tx_timeout_work); +} + +static void fec_enet_timeout_work(struct work_struct *work) +{ + struct fec_enet_private *fep = + container_of(work, struct fec_enet_private, tx_timeout_work); + struct net_device *ndev = fep->netdev; + + if (netif_device_present(ndev) || netif_running(ndev)) { + mutex_lock(&fep->fast_ndev_lock); + + fec_restart(ndev); + mutex_unlock(&fep->fast_ndev_lock); + } +} + +// must be powers of 2 +#define MAX_TX_BUF (64) +#define MAX_RX_BUF (64) + +static int fec_ecat_recv_from_queue(struct net_device *ndev, void __user *buff, size_t len, struct sockaddr __user *addr, int *addr_len) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct fec_enet_priv_rx_q *rxq = fep->rx_queue; + struct bufdesc *bdp; + unsigned short status; + struct sk_buff *skb = NULL; + ushort pkt_len; + __u8 *data; + int recv_len = 0; + int index = 0; + bool need_swap = fep->quirks & FEC_QUIRK_SWAP_FRAME; + int ret = 0; + +#ifdef CONFIG_M532x + flush_cache_all(); +#endif + + /* First, grab all of the stats for the incoming packet. + * These get messed up if we get called due to a busy condition. + */ + bdp = rxq->bd.cur; + + while (!((status = fec16_to_cpu(bdp->cbd_sc)) & BD_ENET_RX_EMPTY)) { + + writel(FEC_ENET_RXF_GET(0), fep->hwp + FEC_IEVENT); + + /* Check for errors. */ + status ^= BD_ENET_RX_LAST; + if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | + BD_ENET_RX_CR | BD_ENET_RX_OV | BD_ENET_RX_LAST | + BD_ENET_RX_CL)) { + ndev->stats.rx_errors++; + if (status & BD_ENET_RX_OV) { + /* FIFO overrun */ + ndev->stats.rx_fifo_errors++; + goto rx_processing_done; + } + if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH + | BD_ENET_RX_LAST)) { + /* Frame too long or too short. */ + ndev->stats.rx_length_errors++; + if (status & BD_ENET_RX_LAST) + netdev_err(ndev, "rcv is not +last\n"); + } + if (status & BD_ENET_RX_CR) /* CRC Error */ + ndev->stats.rx_crc_errors++; + /* Report late collisions as a frame error. */ + if (status & (BD_ENET_RX_NO | BD_ENET_RX_CL)) + ndev->stats.rx_frame_errors++; + goto rx_processing_done; + } + + /* Process the incoming frame. */ + ndev->stats.rx_packets++; + pkt_len = fec16_to_cpu(bdp->cbd_datlen); + ndev->stats.rx_bytes += pkt_len; + + index = fec_enet_get_bd_index(bdp, &rxq->bd); + skb = rxq->rx_skbuff[index]; + + prefetch(skb->data - NET_IP_ALIGN); + dma_sync_single_for_cpu(&fep->pdev->dev, + fec32_to_cpu(bdp->cbd_bufaddr), + FEC_ENET_RX_FRSIZE - fep->rx_align, + DMA_FROM_DEVICE); + + pkt_len -= 6; + data = skb->data; +#if !defined(CONFIG_M5272) + if (fep->quirks & FEC_QUIRK_HAS_RACC) + data += 2; +#endif + if (data[12] ==0x88 && data[13] ==0xa4) { + len = len < pkt_len? len : pkt_len; + if (!need_swap) { + copy_to_user(buff, data, len); + } + else { + swap_buffer2(buff, data, len); + } + if (addr != NULL) { + struct sockaddr_ll *sll = (struct sockaddr_ll *)addr; + sll->sll_hatype = ndev->type; + sll->sll_ifindex = ndev->ifindex; + } + recv_len = len; + } + dma_sync_single_for_device(&fep->pdev->dev, + fec32_to_cpu(bdp->cbd_bufaddr), + FEC_ENET_RX_FRSIZE - fep->rx_align, + DMA_FROM_DEVICE); +rx_processing_done: + /* Clear the status flags for this buffer */ + status &= ~BD_ENET_RX_STATS; + + /* Mark the buffer empty */ + status |= BD_ENET_RX_EMPTY; + + /* Make sure the updates to rest of the descriptor are + * performed before transferring ownership. + */ + wmb(); + bdp->cbd_sc = cpu_to_fec16(status); + + /* Update BD pointer to next entry */ + bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); + + /* Doing this here will keep the FEC running while we process + * incoming frames. On a heavily loaded network, we should be + * able to keep up at the expense of system resources. + */ + writel(0, rxq->bd.reg_desc_active); + if (recv_len) { + break; + } + } + rxq->bd.cur = bdp; + return recv_len; +} + +static int fec_ecat_fast_recv(struct net_device *ndev, void __user *buff, size_t len, struct sockaddr __user *addr, int *addr_len) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + int ret; + + if (!mutex_trylock(&fep->fast_ndev_lock)) { + return -EBUSY; + } + + ret = fec_ecat_recv_from_queue(ndev, buff, len, addr, addr_len); + + mutex_unlock(&fep->fast_ndev_lock); + return ret; +} + +/* ------------------------------------------------------------------------- */ +static int fec_get_mac(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + unsigned char *iap, tmpaddr[ETH_ALEN]; + int ret; + + /* + * try to get mac address in following order: + * + * 1) module parameter via kernel command line in form + * fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0 + */ + iap = macaddr; + + /* + * 2) from device tree data + */ + if (!is_valid_ether_addr(iap)) { + struct device_node *np = fep->pdev->dev.of_node; + if (np) { + ret = of_get_mac_address(np, tmpaddr); + if (!ret) + iap = tmpaddr; + else if (ret == -EPROBE_DEFER) + return ret; + } + } + + /* + * 3) from flash or fuse (via platform data) + */ + if (!is_valid_ether_addr(iap)) { +#ifdef CONFIG_M5272 + if (FEC_FLASHMAC) + iap = (unsigned char *)FEC_FLASHMAC; +#else + struct fec_platform_data *pdata = dev_get_platdata(&fep->pdev->dev); + + if (pdata) + iap = (unsigned char *)&pdata->mac; +#endif + } + + /* + * 4) FEC mac registers set by bootloader + */ + if (!is_valid_ether_addr(iap)) { + *((__be32 *) &tmpaddr[0]) = + cpu_to_be32(readl(fep->hwp + FEC_ADDR_LOW)); + *((__be16 *) &tmpaddr[4]) = + cpu_to_be16(readl(fep->hwp + FEC_ADDR_HIGH) >> 16); + iap = &tmpaddr[0]; + } + + /* + * 5) random mac address + */ + if (!is_valid_ether_addr(iap)) { + /* Report it and use a random ethernet address instead */ + dev_err(&fep->pdev->dev, "Invalid MAC address: %pM\n", iap); + eth_hw_addr_random(ndev); + dev_info(&fep->pdev->dev, "Using random MAC address: %pM\n", + ndev->dev_addr); + return 0; + } + + /* Adjust MAC if using macaddr */ + eth_hw_addr_gen(ndev, iap, iap == macaddr ? fep->dev_id : 0); + + return 0; +} + +/* ------------------------------------------------------------------------- */ + +/* + * Phy section + */ +static void fec_enet_adjust_link(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct phy_device *phy_dev = ndev->phydev; + int status_change = 0; + + /* + * If the netdev is down, or is going down, we're not interested + * in link state events, so just mark our idea of the link as down + * and ignore the event. + */ + if (!netif_running(ndev) || !netif_device_present(ndev)) { + fep->link = 0; + } else if (phy_dev->link) { + if (!fep->link) { + fep->link = phy_dev->link; + status_change = 1; + } + + if (fep->full_duplex != phy_dev->duplex) { + fep->full_duplex = phy_dev->duplex; + status_change = 1; + } + + if (phy_dev->speed != fep->speed) { + fep->speed = phy_dev->speed; + status_change = 1; + } + + switch (fep->speed) { + case SPEED_100: + fep->rx_tstamp_latency = fep->rx_delay_100; + fep->tx_tstamp_latency = fep->tx_delay_100; + break; + case SPEED_1000: + fep->rx_tstamp_latency = fep->rx_delay_1000; + fep->tx_tstamp_latency = fep->tx_delay_1000; + break; + default: + fep->rx_tstamp_latency = 0; + fep->tx_tstamp_latency = 0; + } + + /* if any of the above changed restart the FEC */ + if (status_change) { + mutex_lock(&fep->fast_ndev_lock); + fec_restart(ndev); + mutex_unlock(&fep->fast_ndev_lock); + } + } else { + if (fep->link) { + mutex_lock(&fep->fast_ndev_lock); + fec_stop(ndev); + mutex_unlock(&fep->fast_ndev_lock); + fep->link = phy_dev->link; + status_change = 1; + } + } + + if (status_change) + phy_print_status(phy_dev); +} + +static int fec_enet_mdio_wait(struct fec_enet_private *fep) +{ + uint ievent; + int ret; + + ret = readl_poll_timeout_atomic(fep->hwp + FEC_IEVENT, ievent, + ievent & FEC_ENET_MII, 2, 30000); + + if (!ret) + writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT); + + return ret; +} + +static int fec_enet_mdio_read_c22(struct mii_bus *bus, int mii_id, int regnum) +{ + struct fec_enet_private *fep = bus->priv; + struct device *dev = &fep->pdev->dev; + int ret = 0, frame_start, frame_addr, frame_op; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + /* C22 read */ + frame_op = FEC_MMFR_OP_READ; + frame_start = FEC_MMFR_ST; + frame_addr = regnum; + + /* start a read op */ + writel(frame_start | frame_op | + FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | + FEC_MMFR_TA, fep->hwp + FEC_MII_DATA); + + /* wait for end of transfer */ + ret = fec_enet_mdio_wait(fep); + if (ret) { + netdev_err(fep->netdev, "MDIO read timeout\n"); + goto out; + } + + ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); + +out: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static int fec_enet_mdio_read_c45(struct mii_bus *bus, int mii_id, + int devad, int regnum) +{ + struct fec_enet_private *fep = bus->priv; + struct device *dev = &fep->pdev->dev; + int ret = 0, frame_start, frame_op; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + frame_start = FEC_MMFR_ST_C45; + + /* write address */ + writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | + FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) | + FEC_MMFR_TA | (regnum & 0xFFFF), + fep->hwp + FEC_MII_DATA); + + /* wait for end of transfer */ + ret = fec_enet_mdio_wait(fep); + if (ret) { + netdev_err(fep->netdev, "MDIO address write timeout\n"); + goto out; + } + + frame_op = FEC_MMFR_OP_READ_C45; + + /* start a read op */ + writel(frame_start | frame_op | + FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) | + FEC_MMFR_TA, fep->hwp + FEC_MII_DATA); + + /* wait for end of transfer */ + ret = fec_enet_mdio_wait(fep); + if (ret) { + netdev_err(fep->netdev, "MDIO read timeout\n"); + goto out; + } + + ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA)); + +out: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static int fec_enet_mdio_write_c22(struct mii_bus *bus, int mii_id, int regnum, + u16 value) +{ + struct fec_enet_private *fep = bus->priv; + struct device *dev = &fep->pdev->dev; + int ret, frame_start, frame_addr; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + /* C22 write */ + frame_start = FEC_MMFR_ST; + frame_addr = regnum; + + /* start a write op */ + writel(frame_start | FEC_MMFR_OP_WRITE | + FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(frame_addr) | + FEC_MMFR_TA | FEC_MMFR_DATA(value), + fep->hwp + FEC_MII_DATA); + + /* wait for end of transfer */ + ret = fec_enet_mdio_wait(fep); + if (ret) + netdev_err(fep->netdev, "MDIO write timeout\n"); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static int fec_enet_mdio_write_c45(struct mii_bus *bus, int mii_id, + int devad, int regnum, u16 value) +{ + struct fec_enet_private *fep = bus->priv; + struct device *dev = &fep->pdev->dev; + int ret, frame_start; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + frame_start = FEC_MMFR_ST_C45; + + /* write address */ + writel(frame_start | FEC_MMFR_OP_ADDR_WRITE | + FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) | + FEC_MMFR_TA | (regnum & 0xFFFF), + fep->hwp + FEC_MII_DATA); + + /* wait for end of transfer */ + ret = fec_enet_mdio_wait(fep); + if (ret) { + netdev_err(fep->netdev, "MDIO address write timeout\n"); + goto out; + } + + /* start a write op */ + writel(frame_start | FEC_MMFR_OP_WRITE | + FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(devad) | + FEC_MMFR_TA | FEC_MMFR_DATA(value), + fep->hwp + FEC_MII_DATA); + + /* wait for end of transfer */ + ret = fec_enet_mdio_wait(fep); + if (ret) + netdev_err(fep->netdev, "MDIO write timeout\n"); + +out: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static void fec_enet_phy_reset_after_clk_enable(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct phy_device *phy_dev = ndev->phydev; + + if (phy_dev) { + phy_reset_after_clk_enable(phy_dev); + } else if (fep->phy_node) { + /* + * If the PHY still is not bound to the MAC, but there is + * OF PHY node and a matching PHY device instance already, + * use the OF PHY node to obtain the PHY device instance, + * and then use that PHY device instance when triggering + * the PHY reset. + */ + phy_dev = of_phy_find_device(fep->phy_node); + phy_reset_after_clk_enable(phy_dev); + put_device(&phy_dev->mdio.dev); + } +} + +static int fec_enet_clk_enable(struct net_device *ndev, bool enable) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + int ret; + + if (enable) { + ret = clk_prepare_enable(fep->clk_enet_out); + if (ret) + return ret; + + if (fep->clk_ptp) { + mutex_lock(&fep->ptp_clk_mutex); + ret = clk_prepare_enable(fep->clk_ptp); + if (ret) { + mutex_unlock(&fep->ptp_clk_mutex); + goto failed_clk_ptp; + } else { + fep->ptp_clk_on = true; + } + mutex_unlock(&fep->ptp_clk_mutex); + } + + ret = clk_prepare_enable(fep->clk_ref); + if (ret) + goto failed_clk_ref; + + ret = clk_prepare_enable(fep->clk_2x_txclk); + if (ret) + goto failed_clk_2x_txclk; + + fec_enet_phy_reset_after_clk_enable(ndev); + } else { + clk_disable_unprepare(fep->clk_enet_out); + if (fep->clk_ptp) { + mutex_lock(&fep->ptp_clk_mutex); + clk_disable_unprepare(fep->clk_ptp); + fep->ptp_clk_on = false; + mutex_unlock(&fep->ptp_clk_mutex); + } + clk_disable_unprepare(fep->clk_ref); + clk_disable_unprepare(fep->clk_2x_txclk); + } + + return 0; + +failed_clk_2x_txclk: + if (fep->clk_ref) + clk_disable_unprepare(fep->clk_ref); +failed_clk_ref: + if (fep->clk_ptp) { + mutex_lock(&fep->ptp_clk_mutex); + clk_disable_unprepare(fep->clk_ptp); + fep->ptp_clk_on = false; + mutex_unlock(&fep->ptp_clk_mutex); + } +failed_clk_ptp: + clk_disable_unprepare(fep->clk_enet_out); + + return ret; +} + +static int fec_enet_parse_rgmii_delay(struct fec_enet_private *fep, + struct device_node *np) +{ + u32 rgmii_tx_delay, rgmii_rx_delay; + + /* For rgmii tx internal delay, valid values are 0ps and 2000ps */ + if (!of_property_read_u32(np, "tx-internal-delay-ps", &rgmii_tx_delay)) { + if (rgmii_tx_delay != 0 && rgmii_tx_delay != 2000) { + dev_err(&fep->pdev->dev, "The only allowed RGMII TX delay values are: 0ps, 2000ps"); + return -EINVAL; + } else if (rgmii_tx_delay == 2000) { + fep->rgmii_txc_dly = true; + } + } + + /* For rgmii rx internal delay, valid values are 0ps and 2000ps */ + if (!of_property_read_u32(np, "rx-internal-delay-ps", &rgmii_rx_delay)) { + if (rgmii_rx_delay != 0 && rgmii_rx_delay != 2000) { + dev_err(&fep->pdev->dev, "The only allowed RGMII RX delay values are: 0ps, 2000ps"); + return -EINVAL; + } else if (rgmii_rx_delay == 2000) { + fep->rgmii_rxc_dly = true; + } + } + + return 0; +} + +static int fec_restore_mii_bus(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + int ret; + + ret = pm_runtime_get_sync(&fep->pdev->dev); + if (ret < 0) + return ret; + + writel(0xffc00000, fep->hwp + FEC_IEVENT); + writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); + writel(FEC_ENET_MII, fep->hwp + FEC_IMASK); + writel(FEC_ENET_ETHEREN, fep->hwp + FEC_ECNTRL); + + pm_runtime_mark_last_busy(&fep->pdev->dev); + pm_runtime_put_autosuspend(&fep->pdev->dev); + + return 0; +} + +static int fec_enet_mii_probe(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct phy_device *phy_dev = NULL; + char mdio_bus_id[MII_BUS_ID_SIZE]; + char phy_name[MII_BUS_ID_SIZE + 3]; + int phy_id; + int dev_id = fep->dev_id; + + if (fep->phy_node) { + phy_dev = of_phy_connect(ndev, fep->phy_node, + &fec_enet_adjust_link, 0, + fep->phy_interface); + if (!phy_dev) { + netdev_err(ndev, "Unable to connect to phy\n"); + return -ENODEV; + } + } else { + /* check for attached phy */ + for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) { + if (!mdiobus_is_registered_device(fep->mii_bus, phy_id)) + continue; + if (dev_id--) + continue; + strlcpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE); + break; + } + + if (phy_id >= PHY_MAX_ADDR) { + netdev_info(ndev, "no PHY, assuming direct connection to switch\n"); + strlcpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); + phy_id = 0; + } + + snprintf(phy_name, sizeof(phy_name), + PHY_ID_FMT, mdio_bus_id, phy_id); + phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, + fep->phy_interface); + } + + if (IS_ERR(phy_dev)) { + netdev_err(ndev, "could not attach to PHY\n"); + return PTR_ERR(phy_dev); + } + + /* mask with MAC supported features */ + if (fep->quirks & FEC_QUIRK_HAS_GBIT) { + phy_set_max_speed(phy_dev, 1000); + phy_remove_link_mode(phy_dev, + ETHTOOL_LINK_MODE_1000baseT_Half_BIT); +#if !defined(CONFIG_M5272) + phy_support_sym_pause(phy_dev); +#endif + } + else + phy_set_max_speed(phy_dev, 100); + + fep->link = 0; + fep->full_duplex = 0; + + phy_dev->mac_managed_pm = 1; + + phy_attached_info(phy_dev); + + return 0; +} + +static int fec_enet_mii_init(struct platform_device *pdev) +{ + static struct mii_bus *fec0_mii_bus; + static bool *fec_mii_bus_share; + struct net_device *ndev = platform_get_drvdata(pdev); + struct fec_enet_private *fep = netdev_priv(ndev); + bool suppress_preamble = false; + struct device_node *node; + int err = -ENXIO; + u32 mii_speed, holdtime; + u32 bus_freq; + + /* + * The i.MX28 dual fec interfaces are not equal. + * Here are the differences: + * + * - fec0 supports MII & RMII modes while fec1 only supports RMII + * - fec0 acts as the 1588 time master while fec1 is slave + * - external phys can only be configured by fec0 + * + * That is to say fec1 can not work independently. It only works + * when fec0 is working. The reason behind this design is that the + * second interface is added primarily for Switch mode. + * + * Because of the last point above, both phys are attached on fec0 + * mdio interface in board design, and need to be configured by + * fec0 mii_bus. + */ + if ((fep->quirks & FEC_QUIRK_SINGLE_MDIO) && fep->dev_id > 0) { + /* fec1 uses fec0 mii_bus */ + if (mii_cnt && fec0_mii_bus) { + fep->mii_bus = fec0_mii_bus; + *fec_mii_bus_share = true; + mii_cnt++; + return 0; + } + return -ENOENT; + } + + bus_freq = 2500000; /* 2.5MHz by default */ + node = of_get_child_by_name(pdev->dev.of_node, "mdio"); + if (node) { + of_property_read_u32(node, "clock-frequency", &bus_freq); + suppress_preamble = of_property_read_bool(node, + "suppress-preamble"); + } + + /* + * Set MII speed (= clk_get_rate() / 2 * phy_speed) + * + * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while + * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28 + * Reference Manual has an error on this, and gets fixed on i.MX6Q + * document. + */ + mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), bus_freq * 2); + if (fep->quirks & FEC_QUIRK_ENET_MAC) + mii_speed--; + if (mii_speed > 63) { + dev_err(&pdev->dev, + "fec clock (%lu) too fast to get right mii speed\n", + clk_get_rate(fep->clk_ipg)); + err = -EINVAL; + goto err_out; + } + + /* + * The i.MX28 and i.MX6 types have another filed in the MSCR (aka + * MII_SPEED) register that defines the MDIO output hold time. Earlier + * versions are RAZ there, so just ignore the difference and write the + * register always. + * The minimal hold time according to IEE802.3 (clause 22) is 10 ns. + * HOLDTIME + 1 is the number of clk cycles the fec is holding the + * output. + * The HOLDTIME bitfield takes values between 0 and 7 (inclusive). + * Given that ceil(clkrate / 5000000) <= 64, the calculation for + * holdtime cannot result in a value greater than 3. + */ + holdtime = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 100000000) - 1; + + fep->phy_speed = mii_speed << 1 | holdtime << 8; + + if (suppress_preamble) + fep->phy_speed |= BIT(7); + + if (fep->quirks & FEC_QUIRK_CLEAR_SETUP_MII) { + /* Clear MMFR to avoid to generate MII event by writing MSCR. + * MII event generation condition: + * - writing MSCR: + * - mmfr[31:0]_not_zero & mscr[7:0]_is_zero & + * mscr_reg_data_in[7:0] != 0 + * - writing MMFR: + * - mscr[7:0]_not_zero + */ + writel(0, fep->hwp + FEC_MII_DATA); + } + + writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); + + /* Clear any pending transaction complete indication */ + writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT); + + fep->mii_bus = mdiobus_alloc(); + if (fep->mii_bus == NULL) { + err = -ENOMEM; + goto err_out; + } + + fep->mii_bus->name = "fec_enet_mii_bus"; + fep->mii_bus->read = fec_enet_mdio_read_c22; + fep->mii_bus->write = fec_enet_mdio_write_c22; + if (fep->quirks & FEC_QUIRK_HAS_MDIO_C45) { + fep->mii_bus->read_c45 = fec_enet_mdio_read_c45; + fep->mii_bus->write_c45 = fec_enet_mdio_write_c45; + } + snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", + pdev->name, fep->dev_id + 1); + fep->mii_bus->priv = fep; + fep->mii_bus->parent = &pdev->dev; + + err = of_mdiobus_register(fep->mii_bus, node); + if (err) + goto err_out_free_mdiobus; + of_node_put(node); + + mii_cnt++; + + /* save fec0 mii_bus */ + if (fep->quirks & FEC_QUIRK_SINGLE_MDIO) { + fec0_mii_bus = fep->mii_bus; + fec_mii_bus_share = &fep->mii_bus_share; + } + + return 0; + +err_out_free_mdiobus: + mdiobus_free(fep->mii_bus); +err_out: + of_node_put(node); + return err; +} + +static void fec_enet_mii_remove(struct fec_enet_private *fep) +{ + if (--mii_cnt == 0) { + mdiobus_unregister(fep->mii_bus); + mdiobus_free(fep->mii_bus); + } +} + +static void fec_enet_get_drvinfo(struct net_device *ndev, + struct ethtool_drvinfo *info) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + + strlcpy(info->driver, fep->pdev->dev.driver->name, + sizeof(info->driver)); + strlcpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info)); +} + +static int fec_enet_get_regs_len(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct resource *r; + int s = 0; + + r = platform_get_resource(fep->pdev, IORESOURCE_MEM, 0); + if (r) + s = resource_size(r); + + return s; +} + +/* List of registers that can be safety be read to dump them with ethtool */ +#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ + defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ + defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST) +static __u32 fec_enet_register_version = 2; +static u32 fec_enet_register_offset[] = { + FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0, + FEC_ECNTRL, FEC_MII_DATA, FEC_MII_SPEED, FEC_MIB_CTRLSTAT, FEC_R_CNTRL, + FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, FEC_OPD, FEC_TXIC0, FEC_TXIC1, + FEC_TXIC2, FEC_RXIC0, FEC_RXIC1, FEC_RXIC2, FEC_HASH_TABLE_HIGH, + FEC_HASH_TABLE_LOW, FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, + FEC_X_WMRK, FEC_R_BOUND, FEC_R_FSTART, FEC_R_DES_START_1, + FEC_X_DES_START_1, FEC_R_BUFF_SIZE_1, FEC_R_DES_START_2, + FEC_X_DES_START_2, FEC_R_BUFF_SIZE_2, FEC_R_DES_START_0, + FEC_X_DES_START_0, FEC_R_BUFF_SIZE_0, FEC_R_FIFO_RSFL, FEC_R_FIFO_RSEM, + FEC_R_FIFO_RAEM, FEC_R_FIFO_RAFL, FEC_RACC, FEC_RCMR_1, FEC_RCMR_2, + FEC_DMA_CFG_1, FEC_DMA_CFG_2, FEC_R_DES_ACTIVE_1, FEC_X_DES_ACTIVE_1, + FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_2, FEC_QOS_SCHEME, + RMON_T_DROP, RMON_T_PACKETS, RMON_T_BC_PKT, RMON_T_MC_PKT, + RMON_T_CRC_ALIGN, RMON_T_UNDERSIZE, RMON_T_OVERSIZE, RMON_T_FRAG, + RMON_T_JAB, RMON_T_COL, RMON_T_P64, RMON_T_P65TO127, RMON_T_P128TO255, + RMON_T_P256TO511, RMON_T_P512TO1023, RMON_T_P1024TO2047, + RMON_T_P_GTE2048, RMON_T_OCTETS, + IEEE_T_DROP, IEEE_T_FRAME_OK, IEEE_T_1COL, IEEE_T_MCOL, IEEE_T_DEF, + IEEE_T_LCOL, IEEE_T_EXCOL, IEEE_T_MACERR, IEEE_T_CSERR, IEEE_T_SQE, + IEEE_T_FDXFC, IEEE_T_OCTETS_OK, + RMON_R_PACKETS, RMON_R_BC_PKT, RMON_R_MC_PKT, RMON_R_CRC_ALIGN, + RMON_R_UNDERSIZE, RMON_R_OVERSIZE, RMON_R_FRAG, RMON_R_JAB, + RMON_R_RESVD_O, RMON_R_P64, RMON_R_P65TO127, RMON_R_P128TO255, + RMON_R_P256TO511, RMON_R_P512TO1023, RMON_R_P1024TO2047, + RMON_R_P_GTE2048, RMON_R_OCTETS, + IEEE_R_DROP, IEEE_R_FRAME_OK, IEEE_R_CRC, IEEE_R_ALIGN, IEEE_R_MACERR, + IEEE_R_FDXFC, IEEE_R_OCTETS_OK +}; +#else +static __u32 fec_enet_register_version = 1; +static u32 fec_enet_register_offset[] = { + FEC_ECNTRL, FEC_IEVENT, FEC_IMASK, FEC_IVEC, FEC_R_DES_ACTIVE_0, + FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2, FEC_X_DES_ACTIVE_0, + FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2, FEC_MII_DATA, FEC_MII_SPEED, + FEC_R_BOUND, FEC_R_FSTART, FEC_X_WMRK, FEC_X_FSTART, FEC_R_CNTRL, + FEC_MAX_FRM_LEN, FEC_X_CNTRL, FEC_ADDR_LOW, FEC_ADDR_HIGH, + FEC_GRP_HASH_TABLE_HIGH, FEC_GRP_HASH_TABLE_LOW, FEC_R_DES_START_0, + FEC_R_DES_START_1, FEC_R_DES_START_2, FEC_X_DES_START_0, + FEC_X_DES_START_1, FEC_X_DES_START_2, FEC_R_BUFF_SIZE_0, + FEC_R_BUFF_SIZE_1, FEC_R_BUFF_SIZE_2 +}; +#endif + +static void fec_enet_get_regs(struct net_device *ndev, + struct ethtool_regs *regs, void *regbuf) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + u32 __iomem *theregs = (u32 __iomem *)fep->hwp; + struct device *dev = &fep->pdev->dev; + u32 *buf = (u32 *)regbuf; + u32 i, off; + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return; + + regs->version = fec_enet_register_version; + + memset(buf, 0, regs->len); + + for (i = 0; i < ARRAY_SIZE(fec_enet_register_offset); i++) { + off = fec_enet_register_offset[i]; + + if ((off == FEC_R_BOUND || off == FEC_R_FSTART) && + !(fep->quirks & FEC_QUIRK_HAS_FRREG)) + continue; + + off >>= 2; + buf[off] = readl(&theregs[off]); + } + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +} + + +#if !defined(CONFIG_M5272) + +static void fec_enet_get_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *pause) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + + pause->autoneg = (fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) != 0; + pause->tx_pause = (fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) != 0; + pause->rx_pause = pause->tx_pause; +} + +static int fec_enet_set_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *pause) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + + if (!ndev->phydev) + return -ENODEV; + + if (pause->tx_pause != pause->rx_pause) { + netdev_info(ndev, + "hardware only support enable/disable both tx and rx"); + return -EINVAL; + } + + fep->pause_flag = 0; + + /* tx pause must be same as rx pause */ + fep->pause_flag |= pause->rx_pause ? FEC_PAUSE_FLAG_ENABLE : 0; + fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0; + + phy_set_sym_pause(ndev->phydev, pause->rx_pause, pause->tx_pause, + pause->autoneg); + + if (pause->autoneg) { + if (netif_running(ndev)) + fec_stop(ndev); + phy_start_aneg(ndev->phydev); + } + if (netif_running(ndev)) { + mutex_lock(&fep->fast_ndev_lock); + + netif_tx_lock_bh(ndev); + //netif_tx_wake_all_queues(ndev); + + mutex_unlock(&fep->fast_ndev_lock); + } + + return 0; +} + +static const struct fec_stat { + char name[ETH_GSTRING_LEN]; + u16 offset; +} fec_stats[] = { + /* RMON TX */ + { "tx_dropped", RMON_T_DROP }, + { "tx_packets", RMON_T_PACKETS }, + { "tx_broadcast", RMON_T_BC_PKT }, + { "tx_multicast", RMON_T_MC_PKT }, + { "tx_crc_errors", RMON_T_CRC_ALIGN }, + { "tx_undersize", RMON_T_UNDERSIZE }, + { "tx_oversize", RMON_T_OVERSIZE }, + { "tx_fragment", RMON_T_FRAG }, + { "tx_jabber", RMON_T_JAB }, + { "tx_collision", RMON_T_COL }, + { "tx_64byte", RMON_T_P64 }, + { "tx_65to127byte", RMON_T_P65TO127 }, + { "tx_128to255byte", RMON_T_P128TO255 }, + { "tx_256to511byte", RMON_T_P256TO511 }, + { "tx_512to1023byte", RMON_T_P512TO1023 }, + { "tx_1024to2047byte", RMON_T_P1024TO2047 }, + { "tx_GTE2048byte", RMON_T_P_GTE2048 }, + { "tx_octets", RMON_T_OCTETS }, + + /* IEEE TX */ + { "IEEE_tx_drop", IEEE_T_DROP }, + { "IEEE_tx_frame_ok", IEEE_T_FRAME_OK }, + { "IEEE_tx_1col", IEEE_T_1COL }, + { "IEEE_tx_mcol", IEEE_T_MCOL }, + { "IEEE_tx_def", IEEE_T_DEF }, + { "IEEE_tx_lcol", IEEE_T_LCOL }, + { "IEEE_tx_excol", IEEE_T_EXCOL }, + { "IEEE_tx_macerr", IEEE_T_MACERR }, + { "IEEE_tx_cserr", IEEE_T_CSERR }, + { "IEEE_tx_sqe", IEEE_T_SQE }, + { "IEEE_tx_fdxfc", IEEE_T_FDXFC }, + { "IEEE_tx_octets_ok", IEEE_T_OCTETS_OK }, + + /* RMON RX */ + { "rx_packets", RMON_R_PACKETS }, + { "rx_broadcast", RMON_R_BC_PKT }, + { "rx_multicast", RMON_R_MC_PKT }, + { "rx_crc_errors", RMON_R_CRC_ALIGN }, + { "rx_undersize", RMON_R_UNDERSIZE }, + { "rx_oversize", RMON_R_OVERSIZE }, + { "rx_fragment", RMON_R_FRAG }, + { "rx_jabber", RMON_R_JAB }, + { "rx_64byte", RMON_R_P64 }, + { "rx_65to127byte", RMON_R_P65TO127 }, + { "rx_128to255byte", RMON_R_P128TO255 }, + { "rx_256to511byte", RMON_R_P256TO511 }, + { "rx_512to1023byte", RMON_R_P512TO1023 }, + { "rx_1024to2047byte", RMON_R_P1024TO2047 }, + { "rx_GTE2048byte", RMON_R_P_GTE2048 }, + { "rx_octets", RMON_R_OCTETS }, + + /* IEEE RX */ + { "IEEE_rx_drop", IEEE_R_DROP }, + { "IEEE_rx_frame_ok", IEEE_R_FRAME_OK }, + { "IEEE_rx_crc", IEEE_R_CRC }, + { "IEEE_rx_align", IEEE_R_ALIGN }, + { "IEEE_rx_macerr", IEEE_R_MACERR }, + { "IEEE_rx_fdxfc", IEEE_R_FDXFC }, + { "IEEE_rx_octets_ok", IEEE_R_OCTETS_OK }, +}; + +#define FEC_STATS_SIZE (ARRAY_SIZE(fec_stats) * sizeof(u64)) + +static void fec_enet_update_ethtool_stats(struct net_device *dev) +{ + struct fec_enet_private *fep = netdev_priv(dev); + int i; + + for (i = 0; i < ARRAY_SIZE(fec_stats); i++) + fep->ethtool_stats[i] = readl(fep->hwp + fec_stats[i].offset); +} + +static void fec_enet_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct fec_enet_private *fep = netdev_priv(dev); + + if (netif_running(dev)) + fec_enet_update_ethtool_stats(dev); + + memcpy(data, fep->ethtool_stats, FEC_STATS_SIZE); +} + +static void fec_enet_get_strings(struct net_device *netdev, + u32 stringset, u8 *data) +{ + int i; + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < ARRAY_SIZE(fec_stats); i++) + memcpy(data + i * ETH_GSTRING_LEN, + fec_stats[i].name, ETH_GSTRING_LEN); + break; + case ETH_SS_TEST: + net_selftest_get_strings(data); + break; + } +} + +static int fec_enet_get_sset_count(struct net_device *dev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return ARRAY_SIZE(fec_stats); + case ETH_SS_TEST: + return net_selftest_get_count(); + default: + return -EOPNOTSUPP; + } +} + +static void fec_enet_clear_ethtool_stats(struct net_device *dev) +{ + struct fec_enet_private *fep = netdev_priv(dev); + int i; + + /* Disable MIB statistics counters */ + writel(FEC_MIB_CTRLSTAT_DISABLE, fep->hwp + FEC_MIB_CTRLSTAT); + + for (i = 0; i < ARRAY_SIZE(fec_stats); i++) + writel(0, fep->hwp + fec_stats[i].offset); + + /* Don't disable MIB statistics counters */ + writel(0, fep->hwp + FEC_MIB_CTRLSTAT); +} + +#else /* !defined(CONFIG_M5272) */ +#define FEC_STATS_SIZE 0 +static inline void fec_enet_update_ethtool_stats(struct net_device *dev) +{ +} + +static inline void fec_enet_clear_ethtool_stats(struct net_device *dev) +{ +} +#endif /* !defined(CONFIG_M5272) */ + +/* ITR clock source is enet system clock (clk_ahb). + * TCTT unit is cycle_ns * 64 cycle + * So, the ICTT value = X us / (cycle_ns * 64) + */ +static int fec_enet_us_to_itr_clock(struct net_device *ndev, int us) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + + return us * (fep->itr_clk_rate / 64000) / 1000; +} + + +/* LPI Sleep Ts count base on tx clk (clk_ref). + * The lpi sleep cnt value = X us / (cycle_ns). + */ +static int fec_enet_us_to_tx_cycle(struct net_device *ndev, int us) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + + return us * (fep->clk_ref_rate / 1000) / 1000; +} + +static int fec_enet_eee_mode_set(struct net_device *ndev, bool enable) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct ethtool_eee *p = &fep->eee; + unsigned int sleep_cycle, wake_cycle; + int ret = 0; + + if (enable) { + ret = phy_init_eee(ndev->phydev, 0); + if (ret) + return ret; + + sleep_cycle = fec_enet_us_to_tx_cycle(ndev, p->tx_lpi_timer); + wake_cycle = sleep_cycle; + } else { + sleep_cycle = 0; + wake_cycle = 0; + } + + p->tx_lpi_enabled = enable; + p->eee_enabled = enable; + p->eee_active = enable; + + writel(sleep_cycle, fep->hwp + FEC_LPI_SLEEP); + writel(wake_cycle, fep->hwp + FEC_LPI_WAKE); + + return 0; +} + +static int +fec_enet_get_eee(struct net_device *ndev, struct ethtool_eee *edata) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct ethtool_eee *p = &fep->eee; + + if (!(fep->quirks & FEC_QUIRK_HAS_EEE)) + return -EOPNOTSUPP; + + if (!netif_running(ndev)) + return -ENETDOWN; + + edata->eee_enabled = p->eee_enabled; + edata->eee_active = p->eee_active; + edata->tx_lpi_timer = p->tx_lpi_timer; + edata->tx_lpi_enabled = p->tx_lpi_enabled; + + return phy_ethtool_get_eee(ndev->phydev, edata); +} + +static int +fec_enet_set_eee(struct net_device *ndev, struct ethtool_eee *edata) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct ethtool_eee *p = &fep->eee; + int ret = 0; + + if (!(fep->quirks & FEC_QUIRK_HAS_EEE)) + return -EOPNOTSUPP; + + if (!netif_running(ndev)) + return -ENETDOWN; + + p->tx_lpi_timer = edata->tx_lpi_timer; + + if (!edata->eee_enabled || !edata->tx_lpi_enabled || + !edata->tx_lpi_timer) + ret = fec_enet_eee_mode_set(ndev, false); + else + ret = fec_enet_eee_mode_set(ndev, true); + + if (ret) + return ret; + + return phy_ethtool_set_eee(ndev->phydev, edata); +} + +static const struct ethtool_ops fec_enet_ethtool_ops = { + .supported_coalesce_params = ETHTOOL_COALESCE_USECS | + ETHTOOL_COALESCE_MAX_FRAMES, + .get_drvinfo = fec_enet_get_drvinfo, + .get_regs_len = fec_enet_get_regs_len, + .get_regs = fec_enet_get_regs, + .nway_reset = phy_ethtool_nway_reset, + .get_link = ethtool_op_get_link, +#ifndef CONFIG_M5272 + .get_strings = fec_enet_get_strings, + .get_ethtool_stats = fec_enet_get_ethtool_stats, + .get_sset_count = fec_enet_get_sset_count, +#endif + .get_eee = fec_enet_get_eee, + .set_eee = fec_enet_set_eee, + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, + .self_test = net_selftest, +}; + +static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct phy_device *phydev = ndev->phydev; + + if (!netif_running(ndev)) + return -EINVAL; + + if (!phydev) + return -ENODEV; + + return phy_mii_ioctl(phydev, rq, cmd); +} + +static void fec_enet_free_buffers(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + unsigned int i; + struct sk_buff *skb; + struct bufdesc *bdp; + struct fec_enet_priv_tx_q *txq = fep->tx_queue; + struct fec_enet_priv_rx_q *rxq = fep->rx_queue; + unsigned int q; + + bdp = rxq->bd.base; + for (i = 0; i < rxq->bd.ring_size; i++) { + skb = rxq->rx_skbuff[i]; + rxq->rx_skbuff[i] = NULL; + if (skb) { + dma_unmap_single(&fep->pdev->dev, + fec32_to_cpu(bdp->cbd_bufaddr), + FEC_ENET_RX_FRSIZE - fep->rx_align, + DMA_FROM_DEVICE); + dev_kfree_skb(skb); + } + } + + for (i = 0; i < txq->bd.ring_size; i++) { + skb = txq->tx_skbuff[i]; + txq->tx_skbuff[i] = NULL; + if (skb) { + dma_unmap_single(&fep->pdev->dev, + fec32_to_cpu(bdp->cbd_bufaddr), + FEC_ENET_RX_FRSIZE - fep->rx_align, + DMA_FROM_DEVICE); + dev_kfree_skb(skb); + } + } +} + +static void fec_enet_free_queue(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + + if (fep->rx_queue) + kfree(fep->rx_queue); + if (fep->tx_queue) + kfree(fep->tx_queue); +} + +static int fec_enet_alloc_queue(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + int ret = 0; + + fep->tx_queue = kzalloc(sizeof(*fep->tx_queue), GFP_KERNEL); + if (!fep->tx_queue) { + ret = -ENOMEM; + goto alloc_failed; + } + + fep->tx_queue->bd.ring_size = FEC_TX_RING_SIZE; + fep->total_tx_ring_size += fep->tx_queue->bd.ring_size; + + + fep->rx_queue = kzalloc(sizeof(*fep->rx_queue), GFP_KERNEL); + if (!fep->rx_queue) { + ret = -ENOMEM; + goto alloc_failed; + } + + fep->rx_queue->bd.ring_size = FEC_RX_RING_SIZE; + fep->total_rx_ring_size += fep->rx_queue->bd.ring_size; + return ret; + +alloc_failed: + fec_enet_free_queue(ndev); + return ret; +} + +static int fec_enet_alloc_buffers(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + unsigned int i; + struct sk_buff *skb; + struct bufdesc *bdp; + int off; + struct fec_enet_priv_rx_q *rxq; + struct fec_enet_priv_tx_q *txq; + + rxq = fep->rx_queue; + txq = fep->tx_queue; + bdp = rxq->bd.base; + for (i = 0; i < rxq->bd.ring_size; i++) { + skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE); + if (!skb) + goto err_alloc; + + if ((off = ((unsigned long)skb->data) & fep->rx_align) > 0) + skb_reserve(skb, fep->rx_align + 1 - off); + + bdp->cbd_bufaddr = cpu_to_fec32(dma_map_single(&fep->pdev->dev, skb->data, FEC_ENET_RX_FRSIZE - fep->rx_align, DMA_FROM_DEVICE)); + if (dma_mapping_error(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr))) { + dev_kfree_skb(skb); + goto err_alloc; + } + + rxq->rx_skbuff[i] = skb; + bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); + + bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); + } + + /* Set the last buffer to wrap. */ + bdp = fec_enet_get_prevdesc(bdp, &rxq->bd); + bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); + + bdp = txq->bd.base; + txq->bd.cur = bdp; + + for (i = 0; i < txq->bd.ring_size; i++) { + bdp->cbd_sc = cpu_to_fec16(0); + skb = netdev_alloc_skb(ndev, FEC_ENET_TX_FRSIZE); + if (!skb) + goto err_alloc; + + if ((off = ((unsigned long)skb->data) & fep->tx_align) > 0) + skb_reserve(skb, fep->tx_align + 1 - off); + + bdp->cbd_bufaddr = cpu_to_fec32(dma_map_single(&fep->pdev->dev, skb->data, FEC_ENET_TX_FRSIZE - fep->tx_align, DMA_TO_DEVICE)); + if (dma_mapping_error(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr))) { + goto err_alloc; + } + txq->tx_skbuff[i] = skb; + bdp = fec_enet_get_nextdesc(bdp, &txq->bd); + } + + /* Set the last buffer to wrap. */ + bdp = fec_enet_get_prevdesc(bdp, &txq->bd); + bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); + txq->dirty_tx = bdp; + return 0; + + err_alloc: + fec_enet_free_buffers(ndev); + return -ENOMEM; +} + +static int +fec_enet_open(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + int ret; + bool reset_again; + int i; + + ret = pm_runtime_resume_and_get(&fep->pdev->dev); + if (ret < 0) + return ret; + + pinctrl_pm_select_default_state(&fep->pdev->dev); + + /* During the first fec_enet_open call the PHY isn't probed at this + * point. Therefore the phy_reset_after_clk_enable() call within + * fec_enet_clk_enable() fails. As we need this reset in order to be + * sure the PHY is working correctly we check if we need to reset again + * later when the PHY is probed + */ + if (ndev->phydev && ndev->phydev->drv) + reset_again = false; + else + reset_again = true; + + /* I should reset the ring buffers here, but I don't yet know + * a simple way to do that. + */ + + ret = fec_enet_alloc_buffers(ndev); + if (ret) + goto err_enet_alloc; + + /* Init MAC prior to mii bus probe */ + fec_restart(ndev); + + /* Call phy_reset_after_clk_enable() again if it failed during + * phy_reset_after_clk_enable() before because the PHY wasn't probed. + */ + if (reset_again) + fec_enet_phy_reset_after_clk_enable(ndev); + + /* Probe and connect to PHY when open the interface */ + ret = fec_enet_mii_probe(ndev); + if (ret) + goto err_enet_mii_probe; + + if (fep->quirks & FEC_QUIRK_ERR006687) + imx6q_cpuidle_fec_irqs_used(); + if (fep->quirks & FEC_QUIRK_HAS_PMQOS) + cpu_latency_qos_add_request(&fep->pm_qos_req, 0); + + phy_start(ndev->phydev); + mutex_unlock(&fep->fast_ndev_lock); + return 0; + +err_enet_mii_probe: + fec_enet_free_buffers(ndev); +err_enet_alloc: + pm_runtime_mark_last_busy(&fep->pdev->dev); + pm_runtime_put_autosuspend(&fep->pdev->dev); + if (!fep->mii_bus_share) + pinctrl_pm_select_sleep_state(&fep->pdev->dev); + + return ret; +} + +static int +fec_enet_close(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + + phy_stop(ndev->phydev); + + if (netif_device_present(ndev)) { + netif_tx_disable(ndev); + fec_stop(ndev); + } + + phy_disconnect(ndev->phydev); + ndev->phydev = NULL; + + if (fep->quirks & FEC_QUIRK_ERR006687) + imx6q_cpuidle_fec_irqs_unused(); + + fec_enet_update_ethtool_stats(ndev); + + if (!fep->mii_bus_share) + pinctrl_pm_select_sleep_state(&fep->pdev->dev); + pm_runtime_mark_last_busy(&fep->pdev->dev); + pm_runtime_put_autosuspend(&fep->pdev->dev); + + fec_enet_free_buffers(ndev); + mutex_lock(&fep->fast_ndev_lock); + return 0; +} + +/* Set or clear the multicast filter for this adaptor. + * Skeleton taken from sunlance driver. + * The CPM Ethernet implementation allows Multicast as well as individual + * MAC address filtering. Some of the drivers check to make sure it is + * a group multicast address, and discard those that are not. I guess I + * will do the same for now, but just remove the test if you want + * individual filtering as well (do the upper net layers want or support + * this kind of feature?). + */ + +#define FEC_HASH_BITS 6 /* #bits in hash */ + +static void set_multicast_list(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct netdev_hw_addr *ha; + unsigned int crc, tmp; + unsigned char hash; + unsigned int hash_high = 0, hash_low = 0; + + if (ndev->flags & IFF_PROMISC) { + tmp = readl(fep->hwp + FEC_R_CNTRL); + tmp |= 0x8; + writel(tmp, fep->hwp + FEC_R_CNTRL); + return; + } + + tmp = readl(fep->hwp + FEC_R_CNTRL); + tmp &= ~0x8; + writel(tmp, fep->hwp + FEC_R_CNTRL); + + if (ndev->flags & IFF_ALLMULTI) { + /* Catch all multicast addresses, so set the + * filter to all 1's + */ + writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); + writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW); + + return; + } + + /* Add the addresses in hash register */ + netdev_for_each_mc_addr(ha, ndev) { + /* calculate crc32 value of mac address */ + crc = ether_crc_le(ndev->addr_len, ha->addr); + + /* only upper 6 bits (FEC_HASH_BITS) are used + * which point to specific bit in the hash registers + */ + hash = (crc >> (32 - FEC_HASH_BITS)) & 0x3f; + + if (hash > 31) + hash_high |= 1 << (hash - 32); + else + hash_low |= 1 << hash; + } + + writel(hash_high, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); + writel(hash_low, fep->hwp + FEC_GRP_HASH_TABLE_LOW); +} + +/* Set a MAC change in hardware. */ +static int +fec_set_mac_address(struct net_device *ndev, void *p) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct sockaddr *addr = p; + + if (addr) { + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len); + } + + /* Add netif status check here to avoid system hang in below case: + * ifconfig ethx down; ifconfig ethx hw ether xx:xx:xx:xx:xx:xx; + * After ethx down, fec all clocks are gated off and then register + * access causes system hang. + */ + if (!netif_running(ndev)) + return 0; + + writel(ndev->dev_addr[3] | (ndev->dev_addr[2] << 8) | + (ndev->dev_addr[1] << 16) | (ndev->dev_addr[0] << 24), + fep->hwp + FEC_ADDR_LOW); + writel((ndev->dev_addr[5] << 16) | (ndev->dev_addr[4] << 24), + fep->hwp + FEC_ADDR_HIGH); + return 0; +} + +static inline void fec_enet_set_netdev_features(struct net_device *netdev, + netdev_features_t features) +{ + struct fec_enet_private *fep = netdev_priv(netdev); + netdev_features_t changed = features ^ netdev->features; + + netdev->features = features; + + /* Receive checksum has been changed */ + if (changed & NETIF_F_RXCSUM) { + if (features & NETIF_F_RXCSUM) + fep->csum_flags |= FLAG_RX_CSUM_ENABLED; + else + fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED; + } +} + +static int fec_set_features(struct net_device *netdev, + netdev_features_t features) +{ + struct fec_enet_private *fep = netdev_priv(netdev); + netdev_features_t changed = features ^ netdev->features; + mutex_lock(&fep->fast_ndev_lock); + + if (netif_running(netdev) && changed & NETIF_F_RXCSUM) { + fec_stop(netdev); + fec_enet_set_netdev_features(netdev, features); + fec_restart(netdev); + } else { + fec_enet_set_netdev_features(netdev, features); + } + + mutex_unlock(&fep->fast_ndev_lock); + return 0; +} + +static netdev_tx_t +fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + + return NETDEV_TX_OK; +} + +static u16 fec_enet_select_queue(struct net_device *ndev, struct sk_buff *skb, + struct net_device *sb_dev) +{ + return 0; +} + +static const struct net_device_ops fec_netdev_ops = { + .ndo_open = fec_enet_open, + .ndo_stop = fec_enet_close, + .ndo_start_xmit = fec_enet_start_xmit, + .ndo_select_queue = fec_enet_select_queue, + .ndo_fast_xmit = fec_ecat_fast_xmit, + .ndo_fast_recv = fec_ecat_fast_recv, + .ndo_set_rx_mode = set_multicast_list, + .ndo_validate_addr = eth_validate_addr, + .ndo_tx_timeout = fec_timeout, + .ndo_set_mac_address = fec_set_mac_address, + .ndo_eth_ioctl = fec_enet_ioctl, + .ndo_set_features = fec_set_features, +}; + +static int fec_enet_init(struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct bufdesc *cbd_base; + dma_addr_t bd_dma; + int bd_size; + unsigned int i; + unsigned dsize = sizeof(struct bufdesc); + unsigned dsize_log2 = __fls(dsize); + int ret; + unsigned char *pm = NULL; + + WARN_ON(dsize != (1 << dsize_log2)); +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) + fep->rx_align = 0xf; + fep->tx_align = 0xf; +#else + fep->rx_align = 0x3; + fep->tx_align = 0x3; +#endif + + /* Check mask of the streaming and coherent API */ + ret = dma_set_mask_and_coherent(&fep->pdev->dev, DMA_BIT_MASK(32)); + if (ret < 0) { + dev_warn(&fep->pdev->dev, "No suitable DMA available\n"); + return ret; + } + + ret = fec_enet_alloc_queue(ndev); + if (ret) + return ret; + + bd_size = (fep->total_tx_ring_size + fep->total_rx_ring_size) * dsize; + + /* Allocate memory for buffer descriptors. */ + cbd_base = dmam_alloc_coherent(&fep->pdev->dev, bd_size, &bd_dma, + GFP_KERNEL); + if (!cbd_base) { + ret = -ENOMEM; + goto free_queue_mem; + } + + /* Get the Ethernet address */ + ret = fec_get_mac(ndev); + if (ret) + goto free_queue_mem; + + /* make sure MAC we just acquired is programmed into the hw */ + fec_set_mac_address(ndev, NULL); + + /* Set receive and transmit descriptor base. */ + struct fec_enet_priv_rx_q *rxq = fep->rx_queue; + unsigned size; + + size = dsize * rxq->bd.ring_size; + rxq->bd.qid = i; + rxq->bd.base = cbd_base; + rxq->bd.cur = cbd_base; + rxq->bd.dma = bd_dma; + rxq->bd.dsize = dsize; + rxq->bd.dsize_log2 = dsize_log2; + rxq->bd.reg_desc_active = fep->hwp + FEC_R_DES_ACTIVE_0; + bd_dma += size; + cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); + rxq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); + + struct fec_enet_priv_tx_q *txq = fep->tx_queue; + size = dsize * txq->bd.ring_size; + txq->bd.qid = i; + txq->bd.base = cbd_base; + txq->bd.cur = cbd_base; + txq->bd.dma = bd_dma; + txq->bd.dsize = dsize; + txq->bd.dsize_log2 = dsize_log2; + txq->bd.reg_desc_active = fep->hwp + FEC_X_DES_ACTIVE_0; + bd_dma += size; + cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); + txq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); + + fep->netdev = ndev; + + /* The FEC Ethernet specific entries in the device structure */ + ndev->watchdog_timeo = TX_TIMEOUT; + ndev->netdev_ops = &fec_netdev_ops; + ndev->ethtool_ops = &fec_enet_ethtool_ops; + + pm = ndev->dev_addr; + writel(0, fep->hwp + FEC_IMASK); + + if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) { + fep->tx_align = 0; + fep->rx_align = 0x3f; + } + + ndev->hw_features = ndev->features; + + fec_restart(ndev); + + if (fep->quirks & FEC_QUIRK_MIB_CLEAR) + fec_enet_clear_ethtool_stats(ndev); + else + fec_enet_update_ethtool_stats(ndev); + + return 0; + +free_queue_mem: + fec_enet_free_queue(ndev); + return ret; +} + +#ifdef CONFIG_OF +static int fec_reset_phy(struct platform_device *pdev) +{ + int err, phy_reset; + bool active_high = false; + int msec = 1, phy_post_delay = 0; + struct device_node *np = pdev->dev.of_node; + + if (!np) + return 0; + + err = of_property_read_u32(np, "phy-reset-duration", &msec); + /* A sane reset duration should not be longer than 1s */ + if (!err && msec > 1000) + msec = 1; + + phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0); + if (phy_reset == -EPROBE_DEFER) + return phy_reset; + else if (!gpio_is_valid(phy_reset)) + return 0; + + err = of_property_read_u32(np, "phy-reset-post-delay", &phy_post_delay); + /* valid reset duration should be less than 1s */ + if (!err && phy_post_delay > 1000) + return -EINVAL; + + active_high = of_property_read_bool(np, "phy-reset-active-high"); + + err = devm_gpio_request_one(&pdev->dev, phy_reset, + active_high ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, + "phy-reset"); + if (err) { + dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err); + return err; + } + + if (msec > 20) + msleep(msec); + else + usleep_range(msec * 1000, msec * 1000 + 1000); + + gpio_set_value_cansleep(phy_reset, !active_high); + + if (!phy_post_delay) + return 0; + + if (phy_post_delay > 20) + msleep(phy_post_delay); + else + usleep_range(phy_post_delay * 1000, + phy_post_delay * 1000 + 1000); + + return 0; +} +#else /* CONFIG_OF */ +static int fec_reset_phy(struct platform_device *pdev) +{ + /* + * In case of platform probe, the reset has been done + * by machine code. + */ + return 0; +} +#endif /* CONFIG_OF */ + +static int fec_enet_init_stop_mode(struct fec_enet_private *fep, + struct device_node *np) +{ + struct device_node *gpr_np; + u32 out_val[3]; + int ret = 0; + + gpr_np = of_parse_phandle(np, "fsl,stop-mode", 0); + if (!gpr_np) + return 0; + + ret = of_property_read_u32_array(np, "fsl,stop-mode", out_val, + ARRAY_SIZE(out_val)); + if (ret) { + dev_dbg(&fep->pdev->dev, "no stop mode property\n"); + goto out; + } + + fep->stop_gpr.gpr = syscon_node_to_regmap(gpr_np); + if (IS_ERR(fep->stop_gpr.gpr)) { + dev_err(&fep->pdev->dev, "could not find gpr regmap\n"); + ret = PTR_ERR(fep->stop_gpr.gpr); + fep->stop_gpr.gpr = NULL; + goto out; + } + + fep->stop_gpr.reg = out_val[1]; + fep->stop_gpr.bit = out_val[2]; + +out: + of_node_put(gpr_np); + + return ret; +} + +static int +fec_probe(struct platform_device *pdev) +{ + struct fec_enet_private *fep; + struct fec_platform_data *pdata; + phy_interface_t interface; + struct net_device *ndev; + int i, irq, ret = 0; + const struct of_device_id *of_id; + static int dev_id; + struct device_node *np = pdev->dev.of_node, *phy_node; + char irq_name[8]; + struct fec_devinfo *dev_info; + + /* Init network device */ + ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private) + + FEC_STATS_SIZE, 1, 1); + if (!ndev) + return -ENOMEM; + ndev->fast_raw_device = 1; + SET_NETDEV_DEV(ndev, &pdev->dev); + + /* setup board info structure */ + fep = netdev_priv(ndev); + + of_id = of_match_device(fec_dt_ids, &pdev->dev); + if (of_id) + pdev->id_entry = of_id->data; + dev_info = (struct fec_devinfo *)pdev->id_entry->driver_data; + if (dev_info) + fep->quirks = dev_info->quirks; + + fep->netdev = ndev; + mutex_init(&fep->fast_ndev_lock); +#if !defined(CONFIG_M5272) + /* default enable pause frame auto negotiation */ + if (fep->quirks & FEC_QUIRK_HAS_GBIT) + fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG; +#endif + + /* Select default pin state */ + pinctrl_pm_select_default_state(&pdev->dev); + + fep->hwp = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(fep->hwp)) { + ret = PTR_ERR(fep->hwp); + goto failed_ioremap; + } + + fep->pdev = pdev; + fep->dev_id = dev_id++; + + platform_set_drvdata(pdev, ndev); + + if ((of_machine_is_compatible("fsl,imx6q") || + of_machine_is_compatible("fsl,imx6dl")) && + !of_property_read_bool(np, "fsl,err006687-workaround-present")) + fep->quirks |= FEC_QUIRK_ERR006687; + + ret = fec_enet_ipc_handle_init(fep); + if (ret) + goto failed_ipc_init; + + ret = fec_enet_init_stop_mode(fep, np); + if (ret) + goto failed_stop_mode; + + phy_node = of_parse_phandle(np, "phy-handle", 0); + if (!phy_node && of_phy_is_fixed_link(np)) { + ret = of_phy_register_fixed_link(np); + if (ret < 0) { + dev_err(&pdev->dev, + "broken fixed-link specification\n"); + goto failed_phy; + } + phy_node = of_node_get(np); + } + fep->phy_node = phy_node; + + ret = of_get_phy_mode(pdev->dev.of_node, &interface); + if (ret) { + pdata = dev_get_platdata(&pdev->dev); + if (pdata) + fep->phy_interface = pdata->phy; + else + fep->phy_interface = PHY_INTERFACE_MODE_MII; + } else { + fep->phy_interface = interface; + } + + ret = fec_enet_parse_rgmii_delay(fep, np); + if (ret) + goto failed_rgmii_delay; + + request_bus_freq(BUS_FREQ_HIGH); + + fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(fep->clk_ipg)) { + ret = PTR_ERR(fep->clk_ipg); + goto failed_clk; + } + + fep->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(fep->clk_ahb)) { + ret = PTR_ERR(fep->clk_ahb); + goto failed_clk; + } + + fep->itr_clk_rate = clk_get_rate(fep->clk_ahb); + + /* enet_out is optional, depends on board */ + fep->clk_enet_out = devm_clk_get(&pdev->dev, "enet_out"); + if (IS_ERR(fep->clk_enet_out)) + fep->clk_enet_out = NULL; + + fep->ptp_clk_on = false; + mutex_init(&fep->ptp_clk_mutex); + + /* clk_ref is optional, depends on board */ + fep->clk_ref = devm_clk_get(&pdev->dev, "enet_clk_ref"); + if (IS_ERR(fep->clk_ref)) + fep->clk_ref = NULL; + fep->clk_ref_rate = clk_get_rate(fep->clk_ref); + + /* clk_2x_txclk is optional, depends on board */ + if (fep->rgmii_txc_dly || fep->rgmii_rxc_dly) { + fep->clk_2x_txclk = devm_clk_get(&pdev->dev, "enet_2x_txclk"); + if (IS_ERR(fep->clk_2x_txclk)) + fep->clk_2x_txclk = NULL; + } + + ret = fec_enet_clk_enable(ndev, true); + if (ret) + goto failed_clk; + + ret = clk_prepare_enable(fep->clk_ipg); + if (ret) + goto failed_clk_ipg; + ret = clk_prepare_enable(fep->clk_ahb); + if (ret) + goto failed_clk_ahb; + + fep->reg_phy = devm_regulator_get_optional(&pdev->dev, "phy"); + if (!IS_ERR(fep->reg_phy)) { + ret = regulator_enable(fep->reg_phy); + if (ret) { + dev_err(&pdev->dev, + "Failed to enable phy regulator: %d\n", ret); + goto failed_regulator; + } + } else { + if (PTR_ERR(fep->reg_phy) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto failed_regulator; + } + fep->reg_phy = NULL; + } + + if (of_property_read_u32(np, "fsl,rx-phy-delay-100-ns", &fep->rx_delay_100)) + fep->rx_delay_100 = 600; + + if (of_property_read_u32(np, "fsl,tx-phy-delay-100-ns", &fep->tx_delay_100)) + fep->tx_delay_100 = 0; + + if (of_property_read_u32(np, "fsl,rx-phy-delay-1000-ns", &fep->rx_delay_1000)) + fep->rx_delay_1000 = 0; + + if (of_property_read_u32(np, "fsl,tx-phy-delay-1000-ns", &fep->tx_delay_1000)) + fep->tx_delay_1000 = 0; + + pm_runtime_set_autosuspend_delay(&pdev->dev, FEC_MDIO_PM_TIMEOUT); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + ret = fec_reset_phy(pdev); + if (ret) + goto failed_reset; + + //irq_cnt = fec_enet_get_irq_cnt(pdev); + + ret = fec_enet_init(ndev); + if (ret) + goto failed_init; + + /* board only enable one mii bus in default */ + if (!of_get_property(np, "fsl,mii-exclusive", NULL)) + fep->quirks |= FEC_QUIRK_SINGLE_MDIO; + ret = fec_enet_mii_init(pdev); + if (ret) + goto failed_mii_init; + + /* Carrier starts down, phylib will bring it up */ + netif_carrier_off(ndev); + pinctrl_pm_select_sleep_state(&pdev->dev); + + ndev->max_mtu = PKT_MAXBUF_SIZE - ETH_HLEN - ETH_FCS_LEN; + + ret = register_netdev(ndev); + if (ret) + goto failed_register; + + device_init_wakeup(&ndev->dev, fep->wol_flag & + FEC_WOL_HAS_MAGIC_PACKET); + + + INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work); + + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + mutex_lock(&fep->fast_ndev_lock); + return 0; + +failed_register: + fec_enet_mii_remove(fep); +failed_mii_init: +failed_irq: +failed_init: + //fec_ptp_stop(pdev); +failed_reset: + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_disable(&pdev->dev); + if (fep->reg_phy) + regulator_disable(fep->reg_phy); +failed_regulator: + clk_disable_unprepare(fep->clk_ahb); +failed_clk_ahb: + clk_disable_unprepare(fep->clk_ipg); +failed_clk_ipg: + fec_enet_clk_enable(ndev, false); +failed_clk: + release_bus_freq(BUS_FREQ_HIGH); +failed_rgmii_delay: + if (of_phy_is_fixed_link(np)) + of_phy_deregister_fixed_link(np); + of_node_put(phy_node); +failed_stop_mode: +failed_ipc_init: +failed_phy: + dev_id--; +failed_ioremap: + free_netdev(ndev); + + return ret; +} + +static int +fec_drv_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct fec_enet_private *fep = netdev_priv(ndev); + struct device_node *np = pdev->dev.of_node; + int ret; + + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret < 0) + return ret; + + cancel_work_sync(&fep->tx_timeout_work); + //fec_ptp_stop(pdev); + unregister_netdev(ndev); + fec_enet_mii_remove(fep); + if (fep->reg_phy) + regulator_disable(fep->reg_phy); + + if (of_phy_is_fixed_link(np)) + of_phy_deregister_fixed_link(np); + of_node_put(fep->phy_node); + + clk_disable_unprepare(fep->clk_ahb); + clk_disable_unprepare(fep->clk_ipg); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + free_netdev(ndev); + mutex_unlock(&fep->fast_ndev_lock); + return 0; +} + +static int __maybe_unused fec_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct fec_enet_private *fep = netdev_priv(ndev); + + if (netif_running(ndev)) { + int ret; + + phy_stop(ndev->phydev); + + netif_device_detach(ndev); + fec_stop(ndev); + fec_enet_clk_enable(ndev, false); + + fep->rpm_active = !pm_runtime_status_suspended(dev); + if (fep->rpm_active) { + ret = pm_runtime_force_suspend(dev); + if (ret < 0) + return ret; + } + } else if (fep->mii_bus_share && !ndev->phydev) { + pinctrl_pm_select_sleep_state(&fep->pdev->dev); + } + + if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) + regulator_disable(fep->reg_phy); + + /* SOC supply clock to phy, when clock is disabled, phy link down + * SOC control phy regulator, when regulator is disabled, phy link down + */ + if (fep->clk_enet_out || fep->reg_phy) + fep->link = 0; + + return 0; +} + +static int __maybe_unused fec_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct fec_enet_private *fep = netdev_priv(ndev); + int ret = 0; + int val; + + if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { + ret = regulator_enable(fep->reg_phy); + if (ret) + return ret; + } + + if (netif_running(ndev)) { + if (fep->rpm_active) + pm_runtime_force_resume(dev); + + ret = fec_enet_clk_enable(ndev, true); + if (ret) { + rtnl_unlock(); + goto failed_clk; + } + pinctrl_pm_select_default_state(&fep->pdev->dev); + fec_restart(ndev); + netif_device_attach(ndev); + + //napi_enable(&fep->napi); + + phy_init_hw(ndev->phydev); + phy_start(ndev->phydev); + } else if (fep->mii_bus_share && !ndev->phydev) { + pinctrl_pm_select_default_state(&fep->pdev->dev); + /* And then recovery mii bus */ + ret = fec_restore_mii_bus(ndev); + } + + return ret; + +failed_clk: + if (fep->reg_phy) + regulator_disable(fep->reg_phy); + return ret; +} + +static int __maybe_unused fec_runtime_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct fec_enet_private *fep = netdev_priv(ndev); + + clk_disable_unprepare(fep->clk_ahb); + clk_disable_unprepare(fep->clk_ipg); + release_bus_freq(BUS_FREQ_HIGH); + + return 0; +} + +static int __maybe_unused fec_runtime_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct fec_enet_private *fep = netdev_priv(ndev); + int ret; + + request_bus_freq(BUS_FREQ_HIGH); + + ret = clk_prepare_enable(fep->clk_ahb); + if (ret) + return ret; + ret = clk_prepare_enable(fep->clk_ipg); + if (ret) + goto failed_clk_ipg; + + return 0; + +failed_clk_ipg: + clk_disable_unprepare(fep->clk_ahb); + release_bus_freq(BUS_FREQ_HIGH); + return ret; +} + +static const struct dev_pm_ops fec_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fec_suspend, fec_resume) + SET_RUNTIME_PM_OPS(fec_runtime_suspend, fec_runtime_resume, NULL) +}; + +static struct platform_driver fec_ecat_driver = { + .driver = { + .name = DRIVER_NAME, + .pm = &fec_pm_ops, + .of_match_table = fec_dt_ids, + .suppress_bind_attrs = true, + }, + .id_table = fec_devtype, + .probe = fec_probe, + .remove = fec_drv_remove, +}; + +module_platform_driver(fec_ecat_driver); + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/freescale/fec_ecat.h b/drivers/net/ethernet/freescale/fec_ecat.h new file mode 100644 index 000000000000..e8235a0978a8 --- /dev/null +++ b/drivers/net/ethernet/freescale/fec_ecat.h @@ -0,0 +1,718 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2023 NXP + */ + +/****************************************************************************/ +#ifndef FEC_ECAT_H +#define FEC_ECAT_H +/****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ + defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \ + defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST) +/* + * Just figures, Motorola would have to change the offsets for + * registers in the same peripheral device on different models + * of the ColdFire! + */ +#define FEC_IEVENT 0x004 /* Interrupt event reg */ +#define FEC_IMASK 0x008 /* Interrupt mask reg */ +#define FEC_R_DES_ACTIVE_0 0x010 /* Receive descriptor reg */ +#define FEC_X_DES_ACTIVE_0 0x014 /* Transmit descriptor reg */ +#define FEC_ECNTRL 0x024 /* Ethernet control reg */ +#define FEC_MII_DATA 0x040 /* MII manage frame reg */ +#define FEC_MII_SPEED 0x044 /* MII speed control reg */ +#define FEC_MIB_CTRLSTAT 0x064 /* MIB control/status reg */ +#define FEC_R_CNTRL 0x084 /* Receive control reg */ +#define FEC_X_CNTRL 0x0c4 /* Transmit Control reg */ +#define FEC_ADDR_LOW 0x0e4 /* Low 32bits MAC address */ +#define FEC_ADDR_HIGH 0x0e8 /* High 16bits MAC address */ +#define FEC_OPD 0x0ec /* Opcode + Pause duration */ +#define FEC_TXIC0 0x0f0 /* Tx Interrupt Coalescing for ring 0 */ +#define FEC_TXIC1 0x0f4 /* Tx Interrupt Coalescing for ring 1 */ +#define FEC_TXIC2 0x0f8 /* Tx Interrupt Coalescing for ring 2 */ +#define FEC_RXIC0 0x100 /* Rx Interrupt Coalescing for ring 0 */ +#define FEC_RXIC1 0x104 /* Rx Interrupt Coalescing for ring 1 */ +#define FEC_RXIC2 0x108 /* Rx Interrupt Coalescing for ring 2 */ +#define FEC_HASH_TABLE_HIGH 0x118 /* High 32bits hash table */ +#define FEC_HASH_TABLE_LOW 0x11c /* Low 32bits hash table */ +#define FEC_GRP_HASH_TABLE_HIGH 0x120 /* High 32bits hash table */ +#define FEC_GRP_HASH_TABLE_LOW 0x124 /* Low 32bits hash table */ +#define FEC_X_WMRK 0x144 /* FIFO transmit water mark */ +#define FEC_R_BOUND 0x14c /* FIFO receive bound reg */ +#define FEC_R_FSTART 0x150 /* FIFO receive start reg */ +#define FEC_R_DES_START_1 0x160 /* Receive descriptor ring 1 */ +#define FEC_X_DES_START_1 0x164 /* Transmit descriptor ring 1 */ +#define FEC_R_BUFF_SIZE_1 0x168 /* Maximum receive buff ring1 size */ +#define FEC_R_DES_START_2 0x16c /* Receive descriptor ring 2 */ +#define FEC_X_DES_START_2 0x170 /* Transmit descriptor ring 2 */ +#define FEC_R_BUFF_SIZE_2 0x174 /* Maximum receive buff ring2 size */ +#define FEC_R_DES_START_0 0x180 /* Receive descriptor ring */ +#define FEC_X_DES_START_0 0x184 /* Transmit descriptor ring */ +#define FEC_R_BUFF_SIZE_0 0x188 /* Maximum receive buff size */ +#define FEC_R_FIFO_RSFL 0x190 /* Receive FIFO section full threshold */ +#define FEC_R_FIFO_RSEM 0x194 /* Receive FIFO section empty threshold */ +#define FEC_R_FIFO_RAEM 0x198 /* Receive FIFO almost empty threshold */ +#define FEC_R_FIFO_RAFL 0x19c /* Receive FIFO almost full threshold */ +#define FEC_FTRL 0x1b0 /* Frame truncation receive length*/ +#define FEC_RACC 0x1c4 /* Receive Accelerator function */ +#define FEC_RCMR_1 0x1c8 /* Receive classification match ring 1 */ +#define FEC_RCMR_2 0x1cc /* Receive classification match ring 2 */ +#define FEC_DMA_CFG_1 0x1d8 /* DMA class configuration for ring 1 */ +#define FEC_DMA_CFG_2 0x1dc /* DMA class Configuration for ring 2 */ +#define FEC_R_DES_ACTIVE_1 0x1e0 /* Rx descriptor active for ring 1 */ +#define FEC_X_DES_ACTIVE_1 0x1e4 /* Tx descriptor active for ring 1 */ +#define FEC_R_DES_ACTIVE_2 0x1e8 /* Rx descriptor active for ring 2 */ +#define FEC_X_DES_ACTIVE_2 0x1ec /* Tx descriptor active for ring 2 */ +#define FEC_QOS_SCHEME 0x1f0 /* Set multi queues Qos scheme */ +#define FEC_LPI_SLEEP 0x1f4 /* Set IEEE802.3az LPI Sleep Ts time */ +#define FEC_LPI_WAKE 0x1f8 /* Set IEEE802.3az LPI Wake Tw time */ +#define FEC_MIIGSK_CFGR 0x300 /* MIIGSK Configuration reg */ +#define FEC_MIIGSK_ENR 0x308 /* MIIGSK Enable reg */ + +#define BM_MIIGSK_CFGR_MII 0x00 +#define BM_MIIGSK_CFGR_RMII 0x01 +#define BM_MIIGSK_CFGR_FRCONT_10M 0x40 + +#define RMON_T_DROP 0x200 /* Count of frames not cntd correctly */ +#define RMON_T_PACKETS 0x204 /* RMON TX packet count */ +#define RMON_T_BC_PKT 0x208 /* RMON TX broadcast pkts */ +#define RMON_T_MC_PKT 0x20c /* RMON TX multicast pkts */ +#define RMON_T_CRC_ALIGN 0x210 /* RMON TX pkts with CRC align err */ +#define RMON_T_UNDERSIZE 0x214 /* RMON TX pkts < 64 bytes, good CRC */ +#define RMON_T_OVERSIZE 0x218 /* RMON TX pkts > MAX_FL bytes good CRC */ +#define RMON_T_FRAG 0x21c /* RMON TX pkts < 64 bytes, bad CRC */ +#define RMON_T_JAB 0x220 /* RMON TX pkts > MAX_FL bytes, bad CRC */ +#define RMON_T_COL 0x224 /* RMON TX collision count */ +#define RMON_T_P64 0x228 /* RMON TX 64 byte pkts */ +#define RMON_T_P65TO127 0x22c /* RMON TX 65 to 127 byte pkts */ +#define RMON_T_P128TO255 0x230 /* RMON TX 128 to 255 byte pkts */ +#define RMON_T_P256TO511 0x234 /* RMON TX 256 to 511 byte pkts */ +#define RMON_T_P512TO1023 0x238 /* RMON TX 512 to 1023 byte pkts */ +#define RMON_T_P1024TO2047 0x23c /* RMON TX 1024 to 2047 byte pkts */ +#define RMON_T_P_GTE2048 0x240 /* RMON TX pkts > 2048 bytes */ +#define RMON_T_OCTETS 0x244 /* RMON TX octets */ +#define IEEE_T_DROP 0x248 /* Count of frames not counted crtly */ +#define IEEE_T_FRAME_OK 0x24c /* Frames tx'd OK */ +#define IEEE_T_1COL 0x250 /* Frames tx'd with single collision */ +#define IEEE_T_MCOL 0x254 /* Frames tx'd with multiple collision */ +#define IEEE_T_DEF 0x258 /* Frames tx'd after deferral delay */ +#define IEEE_T_LCOL 0x25c /* Frames tx'd with late collision */ +#define IEEE_T_EXCOL 0x260 /* Frames tx'd with excesv collisions */ +#define IEEE_T_MACERR 0x264 /* Frames tx'd with TX FIFO underrun */ +#define IEEE_T_CSERR 0x268 /* Frames tx'd with carrier sense err */ +#define IEEE_T_SQE 0x26c /* Frames tx'd with SQE err */ +#define IEEE_T_FDXFC 0x270 /* Flow control pause frames tx'd */ +#define IEEE_T_OCTETS_OK 0x274 /* Octet count for frames tx'd w/o err */ +#define RMON_R_PACKETS 0x284 /* RMON RX packet count */ +#define RMON_R_BC_PKT 0x288 /* RMON RX broadcast pkts */ +#define RMON_R_MC_PKT 0x28c /* RMON RX multicast pkts */ +#define RMON_R_CRC_ALIGN 0x290 /* RMON RX pkts with CRC alignment err */ +#define RMON_R_UNDERSIZE 0x294 /* RMON RX pkts < 64 bytes, good CRC */ +#define RMON_R_OVERSIZE 0x298 /* RMON RX pkts > MAX_FL bytes good CRC */ +#define RMON_R_FRAG 0x29c /* RMON RX pkts < 64 bytes, bad CRC */ +#define RMON_R_JAB 0x2a0 /* RMON RX pkts > MAX_FL bytes, bad CRC */ +#define RMON_R_RESVD_O 0x2a4 /* Reserved */ +#define RMON_R_P64 0x2a8 /* RMON RX 64 byte pkts */ +#define RMON_R_P65TO127 0x2ac /* RMON RX 65 to 127 byte pkts */ +#define RMON_R_P128TO255 0x2b0 /* RMON RX 128 to 255 byte pkts */ +#define RMON_R_P256TO511 0x2b4 /* RMON RX 256 to 511 byte pkts */ +#define RMON_R_P512TO1023 0x2b8 /* RMON RX 512 to 1023 byte pkts */ +#define RMON_R_P1024TO2047 0x2bc /* RMON RX 1024 to 2047 byte pkts */ +#define RMON_R_P_GTE2048 0x2c0 /* RMON RX pkts > 2048 bytes */ +#define RMON_R_OCTETS 0x2c4 /* RMON RX octets */ +#define IEEE_R_DROP 0x2c8 /* Count frames not counted correctly */ +#define IEEE_R_FRAME_OK 0x2cc /* Frames rx'd OK */ +#define IEEE_R_CRC 0x2d0 /* Frames rx'd with CRC err */ +#define IEEE_R_ALIGN 0x2d4 /* Frames rx'd with alignment err */ +#define IEEE_R_MACERR 0x2d8 /* Receive FIFO overflow count */ +#define IEEE_R_FDXFC 0x2dc /* Flow control pause frames rx'd */ +#define IEEE_R_OCTETS_OK 0x2e0 /* Octet cnt for frames rx'd w/o err */ + +#else + +#define FEC_ECNTRL 0x000 /* Ethernet control reg */ +#define FEC_IEVENT 0x004 /* Interrupt even reg */ +#define FEC_IMASK 0x008 /* Interrupt mask reg */ +#define FEC_IVEC 0x00c /* Interrupt vec status reg */ +#define FEC_R_DES_ACTIVE_0 0x010 /* Receive descriptor reg */ +#define FEC_R_DES_ACTIVE_1 FEC_R_DES_ACTIVE_0 +#define FEC_R_DES_ACTIVE_2 FEC_R_DES_ACTIVE_0 +#define FEC_X_DES_ACTIVE_0 0x014 /* Transmit descriptor reg */ +#define FEC_X_DES_ACTIVE_1 FEC_X_DES_ACTIVE_0 +#define FEC_X_DES_ACTIVE_2 FEC_X_DES_ACTIVE_0 +#define FEC_MII_DATA 0x040 /* MII manage frame reg */ +#define FEC_MII_SPEED 0x044 /* MII speed control reg */ +#define FEC_R_BOUND 0x08c /* FIFO receive bound reg */ +#define FEC_R_FSTART 0x090 /* FIFO receive start reg */ +#define FEC_X_WMRK 0x0a4 /* FIFO transmit water mark */ +#define FEC_X_FSTART 0x0ac /* FIFO transmit start reg */ +#define FEC_R_CNTRL 0x104 /* Receive control reg */ +#define FEC_MAX_FRM_LEN 0x108 /* Maximum frame length reg */ +#define FEC_X_CNTRL 0x144 /* Transmit Control reg */ +#define FEC_ADDR_LOW 0x3c0 /* Low 32bits MAC address */ +#define FEC_ADDR_HIGH 0x3c4 /* High 16bits MAC address */ +#define FEC_GRP_HASH_TABLE_HIGH 0x3c8 /* High 32bits hash table */ +#define FEC_GRP_HASH_TABLE_LOW 0x3cc /* Low 32bits hash table */ +#define FEC_R_DES_START_0 0x3d0 /* Receive descriptor ring */ +#define FEC_R_DES_START_1 FEC_R_DES_START_0 +#define FEC_R_DES_START_2 FEC_R_DES_START_0 +#define FEC_X_DES_START_0 0x3d4 /* Transmit descriptor ring */ +#define FEC_X_DES_START_1 FEC_X_DES_START_0 +#define FEC_X_DES_START_2 FEC_X_DES_START_0 +#define FEC_R_BUFF_SIZE_0 0x3d8 /* Maximum receive buff size */ +#define FEC_R_BUFF_SIZE_1 FEC_R_BUFF_SIZE_0 +#define FEC_R_BUFF_SIZE_2 FEC_R_BUFF_SIZE_0 +#define FEC_FIFO_RAM 0x400 /* FIFO RAM buffer */ +/* Not existed in real chip + * Just for pass build. + */ +#define FEC_RCMR_1 0xfff +#define FEC_RCMR_2 0xfff +#define FEC_DMA_CFG_1 0xfff +#define FEC_DMA_CFG_2 0xfff +#define FEC_TXIC0 0xfff +#define FEC_TXIC1 0xfff +#define FEC_TXIC2 0xfff +#define FEC_RXIC0 0xfff +#define FEC_RXIC1 0xfff +#define FEC_RXIC2 0xfff +#define FEC_LPI_SLEEP 0xfff +#define FEC_LPI_WAKE 0xfff +#endif /* CONFIG_M5272 */ + + +/* + * Define the buffer descriptor structure. + * + * Evidently, ARM SoCs have the FEC block generated in a + * little endian mode so adjust endianness accordingly. + */ +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) +#define fec32_to_cpu le32_to_cpu +#define fec16_to_cpu le16_to_cpu +#define cpu_to_fec32 cpu_to_le32 +#define cpu_to_fec16 cpu_to_le16 +#define __fec32 __le32 +#define __fec16 __le16 + +struct bufdesc { + __fec16 cbd_datlen; /* Data length */ + __fec16 cbd_sc; /* Control and status info */ + __fec32 cbd_bufaddr; /* Buffer address */ +}; +#else +#define fec32_to_cpu be32_to_cpu +#define fec16_to_cpu be16_to_cpu +#define cpu_to_fec32 cpu_to_be32 +#define cpu_to_fec16 cpu_to_be16 +#define __fec32 __be32 +#define __fec16 __be16 + +struct bufdesc { + __fec16 cbd_sc; /* Control and status info */ + __fec16 cbd_datlen; /* Data length */ + __fec32 cbd_bufaddr; /* Buffer address */ +}; +#endif + +struct bufdesc_ex { + struct bufdesc desc; + __fec32 cbd_esc; + __fec32 cbd_prot; + __fec32 cbd_bdu; + __fec32 ts; + __fec16 res0[4]; +}; + +/* + * The following definitions courtesy of commproc.h, which where + * Copyright (c) 1997 Dan Malek (dmalek@jlc.net). + */ +#define BD_SC_EMPTY ((ushort)0x8000) /* Receive is empty */ +#define BD_SC_READY ((ushort)0x8000) /* Transmit is ready */ +#define BD_SC_WRAP ((ushort)0x2000) /* Last buffer descriptor */ +#define BD_SC_INTRPT ((ushort)0x1000) /* Interrupt on change */ +#define BD_SC_CM ((ushort)0x0200) /* Continuous mode */ +#define BD_SC_ID ((ushort)0x0100) /* Rec'd too many idles */ +#define BD_SC_P ((ushort)0x0100) /* xmt preamble */ +#define BD_SC_BR ((ushort)0x0020) /* Break received */ +#define BD_SC_FR ((ushort)0x0010) /* Framing error */ +#define BD_SC_PR ((ushort)0x0008) /* Parity error */ +#define BD_SC_OV ((ushort)0x0002) /* Overrun */ +#define BD_SC_CD ((ushort)0x0001) /* ?? */ + +/* Buffer descriptor control/status used by Ethernet receive. + */ +#define BD_ENET_RX_EMPTY ((ushort)0x8000) +#define BD_ENET_RX_WRAP ((ushort)0x2000) +#define BD_ENET_RX_INTR ((ushort)0x1000) +#define BD_ENET_RX_LAST ((ushort)0x0800) +#define BD_ENET_RX_FIRST ((ushort)0x0400) +#define BD_ENET_RX_MISS ((ushort)0x0100) +#define BD_ENET_RX_LG ((ushort)0x0020) +#define BD_ENET_RX_NO ((ushort)0x0010) +#define BD_ENET_RX_SH ((ushort)0x0008) +#define BD_ENET_RX_CR ((ushort)0x0004) +#define BD_ENET_RX_OV ((ushort)0x0002) +#define BD_ENET_RX_CL ((ushort)0x0001) +#define BD_ENET_RX_STATS ((ushort)0x013f) /* All status bits */ + +/* Enhanced buffer descriptor control/status used by Ethernet receive */ +#define BD_ENET_RX_VLAN 0x00000004 + +/* Buffer descriptor control/status used by Ethernet transmit. + */ +#define BD_ENET_TX_READY ((ushort)0x8000) +#define BD_ENET_TX_PAD ((ushort)0x4000) +#define BD_ENET_TX_WRAP ((ushort)0x2000) +#define BD_ENET_TX_INTR ((ushort)0x1000) +#define BD_ENET_TX_LAST ((ushort)0x0800) +#define BD_ENET_TX_TC ((ushort)0x0400) +#define BD_ENET_TX_DEF ((ushort)0x0200) +#define BD_ENET_TX_HB ((ushort)0x0100) +#define BD_ENET_TX_LC ((ushort)0x0080) +#define BD_ENET_TX_RL ((ushort)0x0040) +#define BD_ENET_TX_RCMASK ((ushort)0x003c) +#define BD_ENET_TX_UN ((ushort)0x0002) +#define BD_ENET_TX_CSL ((ushort)0x0001) +#define BD_ENET_TX_STATS ((ushort)0x0fff) /* All status bits */ + +/* enhanced buffer descriptor control/status used by Ethernet transmit */ +#define BD_ENET_TX_INT 0x40000000 +#define BD_ENET_TX_TS 0x20000000 +#define BD_ENET_TX_PINS 0x10000000 +#define BD_ENET_TX_IINS 0x08000000 + + +/* This device has up to three irqs on some platforms */ +#define FEC_IRQ_NUM 3 + +/* Maximum number of queues supported + * ENET with AVB IP can support up to 3 independent tx queues and rx queues. + * User can point the queue number that is less than or equal to 3. + */ +#define FEC_ENET_MAX_TX_QS 3 +#define FEC_ENET_MAX_RX_QS 3 + +#define FEC_R_DES_START(X) (((X) == 1) ? FEC_R_DES_START_1 : \ + (((X) == 2) ? \ + FEC_R_DES_START_2 : FEC_R_DES_START_0)) +#define FEC_X_DES_START(X) (((X) == 1) ? FEC_X_DES_START_1 : \ + (((X) == 2) ? \ + FEC_X_DES_START_2 : FEC_X_DES_START_0)) +#define FEC_R_BUFF_SIZE(X) (((X) == 1) ? FEC_R_BUFF_SIZE_1 : \ + (((X) == 2) ? \ + FEC_R_BUFF_SIZE_2 : FEC_R_BUFF_SIZE_0)) + +#define FEC_DMA_CFG(X) (((X) == 2) ? FEC_DMA_CFG_2 : FEC_DMA_CFG_1) + +#define DMA_CLASS_EN (1 << 16) +#define FEC_RCMR(X) (((X) == 2) ? FEC_RCMR_2 : FEC_RCMR_1) +#define IDLE_SLOPE_MASK 0xffff +#define IDLE_SLOPE_1 0x200 /* BW fraction: 0.5 */ +#define IDLE_SLOPE_2 0x200 /* BW fraction: 0.5 */ +#define IDLE_SLOPE(X) (((X) == 1) ? \ + (IDLE_SLOPE_1 & IDLE_SLOPE_MASK) : \ + (IDLE_SLOPE_2 & IDLE_SLOPE_MASK)) +#define RCMR_MATCHEN (0x1 << 16) +#define RCMR_CMP_CFG(v, n) (((v) & 0x7) << (n << 2)) +#ifdef CONFIG_AVB_SUPPORT +#define SR_CLASS_A_PRIORITY 3 +#define SR_CLASS_B_PRIORITY 2 +#define RCMR_CMP_1 (RCMR_CMP_CFG(SR_CLASS_A_PRIORITY, 0) | \ + RCMR_CMP_CFG(SR_CLASS_A_PRIORITY, 1) | \ + RCMR_CMP_CFG(SR_CLASS_A_PRIORITY, 2) | \ + RCMR_CMP_CFG(SR_CLASS_A_PRIORITY, 3)) +#define RCMR_CMP_2 (RCMR_CMP_CFG(SR_CLASS_B_PRIORITY, 0) | \ + RCMR_CMP_CFG(SR_CLASS_B_PRIORITY, 1) | \ + RCMR_CMP_CFG(SR_CLASS_B_PRIORITY, 2) | \ + RCMR_CMP_CFG(SR_CLASS_B_PRIORITY, 3)) +#else +#define RCMR_CMP_1 (RCMR_CMP_CFG(0, 0) | RCMR_CMP_CFG(1, 1) | \ + RCMR_CMP_CFG(2, 2) | RCMR_CMP_CFG(3, 3)) +#define RCMR_CMP_2 (RCMR_CMP_CFG(4, 0) | RCMR_CMP_CFG(5, 1) | \ + RCMR_CMP_CFG(6, 2) | RCMR_CMP_CFG(7, 3)) +#endif +#define RCMR_CMP(X) (((X) == 1) ? RCMR_CMP_1 : RCMR_CMP_2) +#define FEC_TX_BD_FTYPE(X) (((X) & 0xf) << 20) + +#define FEC_RX_FLUSH(X) (1 << ((X) + 3)) + +#define FEC_TX_SCHEME_CB 0x0 /* Credit based */ +#define FEC_TX_SCHEME_RR 0x1 /* Round-robin */ + +#define BD_ENET_RX_INT 0x00800000 +#define BD_ENET_RX_PTP ((ushort)0x0400) +#define BD_ENET_RX_ICE 0x00000020 +#define BD_ENET_RX_PCR 0x00000010 +#define FLAG_RX_CSUM_ENABLED (BD_ENET_RX_ICE | BD_ENET_RX_PCR) +#define FLAG_RX_CSUM_ERROR (BD_ENET_RX_ICE | BD_ENET_RX_PCR) + +/* Interrupt events/masks. */ +#define FEC_ENET_HBERR ((uint)0x80000000) /* Heartbeat error */ +#define FEC_ENET_BABR ((uint)0x40000000) /* Babbling receiver */ +#define FEC_ENET_BABT ((uint)0x20000000) /* Babbling transmitter */ +#define FEC_ENET_GRA ((uint)0x10000000) /* Graceful stop complete */ +#define FEC_ENET_TXF_0 ((uint)0x08000000) /* Full frame transmitted */ +#define FEC_ENET_TXF_1 ((uint)0x00000008) /* Full frame transmitted */ +#define FEC_ENET_TXF_2 ((uint)0x00000080) /* Full frame transmitted */ +#define FEC_ENET_TXB ((uint)0x04000000) /* A buffer was transmitted */ +#define FEC_ENET_RXF_0 ((uint)0x02000000) /* Full frame received */ +#define FEC_ENET_RXF_1 ((uint)0x00000002) /* Full frame received */ +#define FEC_ENET_RXF_2 ((uint)0x00000020) /* Full frame received */ +#define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */ +#define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */ +#define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */ +#define FEC_ENET_WAKEUP ((uint)0x00020000) /* Wakeup request */ +#define FEC_ENET_TXF (FEC_ENET_TXF_0 | FEC_ENET_TXF_1 | FEC_ENET_TXF_2) +#define FEC_ENET_RXF (FEC_ENET_RXF_0 | FEC_ENET_RXF_1 | FEC_ENET_RXF_2) +#define FEC_ENET_RXF_GET(X) (((X) == 0) ? FEC_ENET_RXF_0 : \ + (((X) == 1) ? FEC_ENET_RXF_1 : \ + FEC_ENET_RXF_2)) +#define FEC_ENET_TS_AVAIL ((uint)0x00010000) +#define FEC_ENET_TS_TIMER ((uint)0x00008000) + +#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF) +#define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF)) + +#define FEC_ENET_ETHEREN ((uint)0x00000002) +#define FEC_ENET_TXC_DLY ((uint)0x00010000) +#define FEC_ENET_RXC_DLY ((uint)0x00020000) + +/* ENET interrupt coalescing macro define */ +#define FEC_ITR_CLK_SEL (0x1 << 30) +#define FEC_ITR_EN (0x1 << 31) +#define FEC_ITR_ICFT(X) (((X) & 0xff) << 20) +#define FEC_ITR_ICTT(X) ((X) & 0xffff) +#define FEC_ITR_ICFT_DEFAULT 200 /* Set 200 frame count threshold */ +#define FEC_ITR_ICTT_DEFAULT 1000 /* Set 1000us timer threshold */ + +#define FEC_VLAN_TAG_LEN 0x04 +#define FEC_ETHTYPE_LEN 0x02 + +/* Controller is ENET-MAC */ +#define FEC_QUIRK_ENET_MAC (1 << 0) +/* Controller needs driver to swap frame */ +#define FEC_QUIRK_SWAP_FRAME (1 << 1) +/* Controller uses gasket */ +#define FEC_QUIRK_USE_GASKET (1 << 2) +/* Controller has GBIT support */ +#define FEC_QUIRK_HAS_GBIT (1 << 3) +/* Controller has extend desc buffer */ +#define FEC_QUIRK_HAS_BUFDESC_EX (1 << 4) +/* Controller has hardware checksum support */ +#define FEC_QUIRK_HAS_CSUM (1 << 5) +/* Controller has hardware vlan support */ +#define FEC_QUIRK_HAS_VLAN (1 << 6) +/* ENET IP errata ERR006358 + * + * If the ready bit in the transmit buffer descriptor (TxBD[R]) is previously + * detected as not set during a prior frame transmission, then the + * ENET_TDAR[TDAR] bit is cleared at a later time, even if additional TxBDs + * were added to the ring and the ENET_TDAR[TDAR] bit is set. This results in + * frames not being transmitted until there is a 0-to-1 transition on + * ENET_TDAR[TDAR]. + */ +#define FEC_QUIRK_ERR006358 (1 << 7) +/* ENET IP hw AVB + * + * i.MX6SX ENET IP add Audio Video Bridging (AVB) feature support. + * - Two class indicators on receive with configurable priority + * - Two class indicators and line speed timer on transmit allowing + * implementation class credit based shapers externally + * - Additional DMA registers provisioned to allow managing up to 3 + * independent rings + */ +#define FEC_QUIRK_HAS_AVB (1 << 8) +/* There is a TDAR race condition for mutliQ when the software sets TDAR + * and the UDMA clears TDAR simultaneously or in a small window (2-4 cycles). + * This will cause the udma_tx and udma_tx_arbiter state machines to hang. + * The issue exist at i.MX6SX enet IP. + */ +#define FEC_QUIRK_ERR007885 (1 << 9) +/* ENET Block Guide/ Chapter for the iMX6SX (PELE) address one issue: + * After set ENET_ATCR[Capture], there need some time cycles before the counter + * value is capture in the register clock domain. + * The wait-time-cycles is at least 6 clock cycles of the slower clock between + * the register clock and the 1588 clock. The 1588 ts_clk is fixed to 25Mhz, + * register clock is 66Mhz, so the wait-time-cycles must be greater than 240ns + * (40ns * 6). + */ +#define FEC_QUIRK_BUG_CAPTURE (1 << 10) +/* Controller has only one MDIO bus */ +#define FEC_QUIRK_SINGLE_MDIO (1 << 11) +/* Controller supports RACC register */ +#define FEC_QUIRK_HAS_RACC (1 << 12) +/* Controller supports interrupt coalesc */ +#define FEC_QUIRK_HAS_COALESCE (1 << 13) +/* Interrupt doesn't wake CPU from deep idle */ +#define FEC_QUIRK_ERR006687 (1 << 14) +/* The MIB counters should be cleared and enabled during + * initialisation. + */ +#define FEC_QUIRK_MIB_CLEAR (1 << 15) +/* Only i.MX25/i.MX27/i.MX28 controller supports FRBR,FRSR registers, + * those FIFO receive registers are resolved in other platforms. + */ +#define FEC_QUIRK_HAS_FRREG (1 << 16) + +/* Some FEC hardware blocks need the MMFR cleared at setup time to avoid + * the generation of an MII event. This must be avoided in the older + * FEC blocks where it will stop MII events being generated. + */ +#define FEC_QUIRK_CLEAR_SETUP_MII (1 << 17) + +/* Some link partners do not tolerate the momentary reset of the REF_CLK + * frequency when the RNCTL register is cleared by hardware reset. + */ +#define FEC_QUIRK_NO_HARD_RESET (1 << 18) + +/* i.MX6SX ENET IP supports multiple queues (3 queues), use this quirk to + * represents this ENET IP. + */ +#define FEC_QUIRK_HAS_MULTI_QUEUES (1 << 19) + +/* i.MX8MQ ENET IP version add new feature to support IEEE 802.3az EEE + * standard. For the transmission, MAC supply two user registers to set + * Sleep (TS) and Wake (TW) time. + */ +#define FEC_QUIRK_HAS_EEE (1 << 20) + +/* i.MX8QM ENET IP version add new feture to generate delayed TXC/RXC + * as an alternative option to make sure it works well with various PHYs. + * For the implementation of delayed clock, ENET takes synchronized 250MHz + * clocks to generate 2ns delay. + */ +#define FEC_QUIRK_DELAYED_CLKS_SUPPORT (1 << 21) + + +/* i.MX8MQ SoC integration mix wakeup interrupt signal into "int2" interrupt line. */ +#define FEC_QUIRK_WAKEUP_FROM_INT2 (1 << 22) + +/* request pmqos during low power */ +#define FEC_QUIRK_HAS_PMQOS BIT(23) + +/* Not all FEC hardware block MDIOs support accesses in C45 mode. + * Older blocks in the ColdFire parts do not support it. + */ +#define FEC_QUIRK_HAS_MDIO_C45 BIT(24) + +struct bufdesc_prop { + int qid; + /* Address of Rx and Tx buffers */ + struct bufdesc *base; + struct bufdesc *last; + struct bufdesc *cur; + void __iomem *reg_desc_active; + dma_addr_t dma; + unsigned short ring_size; + unsigned char dsize; + unsigned char dsize_log2; +}; + +struct fec_enet_priv_tx_q { + struct bufdesc_prop bd; + struct bufdesc *dirty_tx; + struct sk_buff *tx_skbuff[FEC_TX_RING_SIZE]; + + unsigned int tx_bounce_size; + +#ifdef CONFIG_AVB_SUPPORT + unsigned int tx_index; + unsigned char *tx_bounce[FEC_TX_RING_SIZE + 32]; +#else + unsigned char *tx_bounce[FEC_TX_RING_SIZE]; +#endif + unsigned short tx_stop_threshold; + unsigned short tx_wake_threshold; + + char *tso_hdrs; + dma_addr_t tso_hdrs_dma; + +#ifdef CONFIG_AVB_SUPPORT + unsigned long tx_idle_slope; +#endif +}; + +struct fec_enet_priv_rx_q { + struct bufdesc_prop bd; + struct sk_buff *rx_skbuff[FEC_RX_RING_SIZE]; +}; + +struct fec_stop_mode_gpr { + struct regmap *gpr; + u8 reg; + u8 bit; +}; + +#ifdef CONFIG_AVB_SUPPORT +#define AVB_DMA_MAPPING 1 +#endif + +/* The FEC buffer descriptors track the ring buffers. The rx_bd_base and + * tx_bd_base always point to the base of the buffer descriptors. The + * cur_rx and cur_tx point to the currently available buffer. + * The dirty_tx tracks the current buffer that is being sent by the + * controller. The cur_tx and dirty_tx are equal under both completely + * empty and completely full conditions. The empty/ready indicator in + * the buffer descriptor determines the actual condition. + */ +struct fec_enet_private { + /* Hardware registers of the FEC device */ + void __iomem *hwp; + + struct net_device *netdev; + + struct clk *clk_ipg; + struct clk *clk_ahb; + struct clk *clk_ref; + struct clk *clk_enet_out; + struct clk *clk_ptp; + struct clk *clk_2x_txclk; + + bool ptp_clk_on; + struct mutex ptp_clk_mutex; + unsigned int num_tx_queues; + unsigned int num_rx_queues; + + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct fec_enet_priv_tx_q *tx_queue; + struct fec_enet_priv_rx_q *rx_queue; + + unsigned int total_tx_ring_size; + unsigned int total_rx_ring_size; + +#ifdef CONFIG_AVB_SUPPORT + const struct avb_ops *avb; + void *avb_data; + unsigned int avb_enabled; + __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_advertising); +#endif + struct platform_device *pdev; + + int dev_id; + + /* Phylib and MDIO interface */ + struct mii_bus *mii_bus; + uint phy_speed; + phy_interface_t phy_interface; + struct device_node *phy_node; + bool rgmii_txc_dly; + bool rgmii_rxc_dly; + bool mii_bus_share; + bool rpm_active; + int link; + int full_duplex; + int speed; + int irq[FEC_IRQ_NUM]; + bool bufdesc_ex; + int pause_flag; + int wol_flag; + int wake_irq; + u32 quirks; + + struct napi_struct napi; + int csum_flags; + + struct work_struct tx_timeout_work; + + struct ptp_clock *ptp_clock; + struct ptp_clock_info ptp_caps; + unsigned long last_overflow_check; + raw_spinlock_t tmreg_lock; + struct cyclecounter cc; + struct timecounter tc; + int rx_hwtstamp_filter; + u32 base_incval; + u32 cycle_speed; + int hwts_rx_en; + int hwts_tx_en; + + /* Transmit and receive latency, depending on link speed, for + * packets timestamps in ns + */ + u32 rx_tstamp_latency; + u32 tx_tstamp_latency; + + struct delayed_work time_keep; + struct regulator *reg_phy; + struct fec_stop_mode_gpr stop_gpr; + struct pm_qos_request pm_qos_req; + + unsigned int tx_align; + unsigned int rx_align; + + /* hw interrupt coalesce */ + unsigned int rx_pkts_itr; + unsigned int rx_time_itr; + unsigned int tx_pkts_itr; + unsigned int tx_time_itr; + unsigned int itr_clk_rate; + + /* tx lpi eee mode */ + struct ethtool_eee eee; + unsigned int clk_ref_rate; + + u32 rx_copybreak; + + /* ptp clock period in ns*/ + unsigned int ptp_inc; + + /* pps */ + int pps_channel; + unsigned int reload_period; + int pps_enable; + unsigned int next_counter; + + struct mutex fast_ndev_lock; + struct imx_sc_ipc *ipc_handle; + + /* Configured rx/tx timestamps delays for different link speeds + * to compensate for FEC-PHY latency in ns + */ + u32 rx_delay_100; + u32 tx_delay_100; + u32 rx_delay_1000; + u32 tx_delay_1000; + +#ifdef CONFIG_AVB_SUPPORT + int rec_channel; + int rec_enable; +#endif + + u64 ethtool_stats[]; +}; + +#ifdef CONFIG_AVB_SUPPORT +#define FEC_MAX_RATE 400 /* Mbps */ +#define FEC_MAX_RATE_HAS_AVB 1000 /* Mbps */ + +static inline int fec_max_rate(struct fec_enet_private *fep) +{ + int max_rate = (fep->quirks & FEC_QUIRK_HAS_AVB) ? FEC_MAX_RATE_HAS_AVB : FEC_MAX_RATE; + return min(max_rate, fep->speed); +} + +#define IDLE_SLOPE_DIVISOR 512 +#endif + +void fec_ptp_init(struct platform_device *pdev, int irq_idx); +void fec_ptp_stop(struct platform_device *pdev); +void fec_ptp_start_cyclecounter(struct net_device *ndev); +void fec_ptp_disable_hwts(struct net_device *ndev); +int fec_ptp_set(struct net_device *ndev, struct ifreq *ifr); +int fec_ptp_get(struct net_device *ndev, struct ifreq *ifr); + +/****************************************************************************/ +#endif /* FEC_H */ diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index fb0497a4cd79..be3f9740a0b6 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -79,14 +79,22 @@ static void set_multicast_list(struct net_device *ndev); static void fec_enet_itr_coal_set(struct net_device *ndev); +#ifndef CONFIG_AVB_SUPPORT static int fec_enet_xdp_tx_xmit(struct fec_enet_private *fep, int cpu, struct xdp_buff *xdp, u32 dma_sync_len); +#endif #define DRIVER_NAME "fec" static const u16 fec_enet_vlan_pri_to_queue[8] = {0, 0, 1, 1, 1, 2, 2, 2}; +#ifdef CONFIG_AVB_SUPPORT +/* Idle Slope values specific to AVB capable boards */ +static const unsigned short idle_slope_values[] = {1, 2, 4, 8, 16, 32, 64, 128, + 256, 384, 512, 640, 768, 896, 1024, 1152, 1280, 1408, 1536}; +#endif + /* Pause frame feild and FIFO threshold */ #define FEC_ENET_FCE (1 << 5) #define FEC_ENET_RSEM_V 0x84 @@ -357,6 +365,24 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); static int mii_cnt; +#ifdef CONFIG_AVB_SUPPORT +static inline void read16(void *dst, void *src) +{ +#ifdef CONFIG_ARM64 + asm volatile ( "ldp x10, x11, [%1]\n\t" + "stp x10, x11, [%0]\n\t" + : : "r" (dst), "r" (src) : "x10", "x11", "memory"); +#elif CONFIG_ARM + asm volatile ( "ldmia %1, {r5-r8}\n\t" + "stmia %0, {r5-r8}\n\t" + : : "r" (dst), "r" (src) : "r5", "r6", "r7", "r8", "memory"); +#else + ((u64 *)dst)[0] = ((u64 *)src)[0]; + ((u64 *)dst)[1] = ((u64 *)src)[1]; +#endif +} +#endif /*CONFIG_AVB_SUPPORT*/ + static struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, struct bufdesc_prop *bd) { @@ -488,6 +514,253 @@ fec_enet_create_page_pool(struct fec_enet_private *fep, return err; } +#ifdef CONFIG_AVB_SUPPORT +/* + * Sends an AVB buffer on the network. + */ +int fec_enet_start_xmit_avb(void *data, struct avb_tx_desc *desc) +{ + struct fec_enet_private *fep = data; + struct bufdesc *bdp; + unsigned short status; + unsigned int index; + struct bufdesc_ex *ebdp; + unsigned long cbd_esc; + unsigned short queue_id = desc->queue_id; + struct fec_enet_priv_tx_q *txq = fep->tx_queue[queue_id]; + + /* ring buffer base address */ + /* registers base address */ + /* current descriptor pointer */ + bdp = txq->bd.cur; + + if (bdp == txq->dirty_tx) + return -2; + + status = fec16_to_cpu(bdp->cbd_sc); + + if (status & BD_ENET_TX_READY) + return -2; + + /* Clear all of the status flags */ + status &= ~BD_ENET_TX_STATS; + + index = fec_enet_get_bd_index(bdp, &txq->bd); + + /* Save desc pointer */ + txq->tx_buf[index].buf_p = (void *)desc; + + bdp->cbd_datlen = cpu_to_fec16(desc->common.len); + bdp->cbd_bufaddr = cpu_to_fec32(desc->dma_addr); + + ebdp = (struct bufdesc_ex *)bdp; + + ebdp->cbd_bdu = cpu_to_fec32(0); + + if (desc->common.flags & AVB_TX_FLAG_HW_TS) + cbd_esc = BD_ENET_TX_TS | desc->esc; + else + cbd_esc = desc->esc; + + if (desc->common.flags & AVB_TX_FLAG_HW_CSUM) + cbd_esc |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; + + if (fep->quirks & FEC_QUIRK_HAS_AVB) + cbd_esc |= FEC_TX_BD_FTYPE(txq->bd.qid); + + ebdp->cbd_esc = cpu_to_fec32(cbd_esc); + + wmb(); + + bdp->cbd_sc = cpu_to_fec16(status | (BD_ENET_TX_READY | BD_ENET_TX_LAST | BD_ENET_TX_TC)); + + /* If this was the last BD in the ring, start at the beginning again. */ + bdp = fec_enet_get_nextdesc(bdp, &txq->bd); + + txq->bd.cur = bdp; + + /* Trigger transmission start */ + if (!(fep->quirks & FEC_QUIRK_ERR006358)) + if (!(fep->quirks & FEC_QUIRK_ERR007885) || + !readl(txq->bd.reg_desc_active) || + !readl(txq->bd.reg_desc_active) || + !readl(txq->bd.reg_desc_active) || + !readl(txq->bd.reg_desc_active)) + writel(0, txq->bd.reg_desc_active); + + if (bdp == txq->dirty_tx) + return -1; + + return 0; +} +EXPORT_SYMBOL(fec_enet_start_xmit_avb); + +void fec_enet_finish_xmit_avb(void *data, unsigned int queue_id) +{ + struct fec_enet_private *fep = data; + + /* Trigger transmission start */ + if (fep->quirks & FEC_QUIRK_ERR006358) + if (!(fep->quirks & FEC_QUIRK_ERR007885) || + !readl(fep->tx_queue[queue_id]->bd.reg_desc_active) || + !readl(fep->tx_queue[queue_id]->bd.reg_desc_active) || + !readl(fep->tx_queue[queue_id]->bd.reg_desc_active) || + !readl(fep->tx_queue[queue_id]->bd.reg_desc_active)) + writel(0, fep->tx_queue[queue_id]->bd.reg_desc_active); +} +EXPORT_SYMBOL(fec_enet_finish_xmit_avb); + +/* + * When AVB is enabled, it is the transmit function for the regular + * network traffic. It does not support any SG/TSO skb. + * Frames are posted to the AVB module for further scheduling. + */ +static int fec_enet_start_xmit_best_effort(struct fec_enet_priv_tx_q *txq, + struct netdev_queue *nq, struct sk_buff *skb, struct net_device *ndev) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + void *bufaddr; + struct avb_tx_desc *desc; + int rc; + + if (fep->avb->tx_full(fep->avb_data)) { + netdev_err(ndev, "tx queue full!\n"); + return NETDEV_TX_BUSY; + } + + /* Protocol checksum off-load for TCP and UDP. */ + if (fec_enet_clear_csum(skb, ndev)) { + dev_kfree_skb_any(skb); + ndev->stats.tx_dropped++; + return NETDEV_TX_OK; + } + + if (skb_headroom(skb) < sizeof(struct avb_tx_desc)) { + if (pskb_expand_head(skb, sizeof(struct avb_tx_desc), 0, GFP_ATOMIC)) { + dev_kfree_skb_any(skb); + ndev->stats.tx_dropped++; + return NETDEV_TX_OK; + } + } + + bufaddr = skb->data; + desc = (struct avb_tx_desc *)(skb->data - sizeof(struct avb_tx_desc)); + + /* Set buffer length and buffer pointer */ + desc->common.offset = sizeof(struct avb_tx_desc); + desc->common.len = skb->len; + desc->queue_id = txq->bd.qid; + + if ((((unsigned long) bufaddr) & fep->tx_align) || + (fep->quirks & FEC_QUIRK_SWAP_FRAME)) { + memcpy(txq->tx_bounce[txq->tx_index], skb->data, skb->len); + bufaddr = txq->tx_bounce[txq->tx_index]; + + txq->tx_index++; + if (txq->tx_index >= txq->tx_bounce_size) + txq->tx_index = 0; + + if (fep->quirks & FEC_QUIRK_SWAP_FRAME) + swap_buffer(bufaddr, skb->len); + } + + /* Save skb pointer */ + desc->data = skb; + desc->common.flags = AVB_TX_FLAG_SKB; + + /* Push the data cache so the CPM does not get stale memory data. */ + desc->dma_addr = dma_map_single(&fep->pdev->dev, bufaddr, skb->len, DMA_TO_DEVICE); + if (dma_mapping_error(&fep->pdev->dev, desc->dma_addr)) { + dev_kfree_skb_any(skb); + if (net_ratelimit()) + netdev_err(ndev, "Tx DMA memory map failed\n"); + return NETDEV_TX_OK; + } + + desc->esc = 0; + + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && + fep->hwts_tx_en)) { + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + desc->esc |= BD_ENET_TX_TS; + } + + if (skb->ip_summed == CHECKSUM_PARTIAL) + desc->esc |= BD_ENET_TX_PINS | BD_ENET_TX_IINS; + + skb_tx_timestamp(skb); + + if ((rc = fep->avb->tx(fep->avb_data, desc)) < 0) { + if (rc < -1) { + kfree_skb(skb); + ndev->stats.tx_dropped++; + } + + netif_tx_stop_queue(nq); + return NETDEV_TX_OK; + } + + return NETDEV_TX_OK; +} + +int fec_enet_set_idle_slope(void *data, unsigned int queue_id, + u32 desired_rate) +{ + struct fec_enet_private *fep = data; + struct fec_enet_priv_tx_q *txq; + u64 idle_slope; + u32 line_rate; + int i; + + if (!fep) + return -EINVAL; + + /* Nothing to be done for non-AVB boards */ + if (!(fep->quirks & FEC_QUIRK_HAS_AVB)) + return 0; + + if ((queue_id == 0) || (queue_id >= fep->num_tx_queues)) + return -EINVAL; + + if ((fep->speed != SPEED_100) && (fep->speed != SPEED_1000)) + return -EOPNOTSUPP; + + line_rate = fep->speed * 1000000ULL; + if (desired_rate > line_rate) + return -EINVAL; + + txq = fep->tx_queue[queue_id]; + + /* + * Compute the desired Idle-Slope based on the desired rate and use + * the round up to the next integer. + */ + idle_slope = (u64)desired_rate * IDLE_SLOPE_DIVISOR + line_rate - desired_rate - 1; + idle_slope = div_u64(idle_slope, line_rate - desired_rate); + + for (i = 0; i < ARRAY_SIZE(idle_slope_values); i++) + if (idle_slope <= idle_slope_values[i]) + break; + + if (i >= ARRAY_SIZE(idle_slope_values)) + return -EINVAL; + /* + * If the desired rate is higher than the last available bandwidth + * threshold then we should not configure the Credit-Based shaper + * at all. + */ + if (idle_slope > idle_slope_values[i]) + return -EINVAL; + + txq->tx_idle_slope = idle_slope_values[i]; + writel(DMA_CLASS_EN | txq->tx_idle_slope, + fep->hwp + FEC_DMA_CFG(queue_id)); + + return 0; +} +EXPORT_SYMBOL(fec_enet_set_idle_slope); +#endif + static struct bufdesc * fec_enet_txq_submit_frag_skb(struct fec_enet_priv_tx_q *txq, struct sk_buff *skb, @@ -697,7 +970,12 @@ static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq, txq->bd.cur = bdp; /* Trigger transmission start */ - writel(0, txq->bd.reg_desc_active); + if (!(fep->quirks & FEC_QUIRK_ERR007885) || + !readl(txq->bd.reg_desc_active) || + !readl(txq->bd.reg_desc_active) || + !readl(txq->bd.reg_desc_active) || + !readl(txq->bd.reg_desc_active)) + writel(0, txq->bd.reg_desc_active); return 0; } @@ -914,6 +1192,11 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) txq = fep->tx_queue[queue]; nq = netdev_get_tx_queue(ndev, queue); +#ifdef CONFIG_AVB_SUPPORT + if (fep->avb_enabled) + return fec_enet_start_xmit_best_effort(txq, nq, skb, ndev); +#endif + if (skb_is_gso(skb)) ret = fec_enet_txq_submit_tso(txq, skb, ndev); else @@ -938,6 +1221,9 @@ static void fec_enet_bd_init(struct net_device *dev) struct bufdesc *bdp; unsigned int i; unsigned int q; +#ifdef CONFIG_AVB_SUPPORT + struct sk_buff *skb; +#endif for (q = 0; q < fep->num_rx_queues; q++) { /* Initialize the receive buffer descriptors. */ @@ -961,6 +1247,23 @@ static void fec_enet_bd_init(struct net_device *dev) rxq->bd.cur = rxq->bd.base; } +#ifdef CONFIG_AVB_SUPPORT + if (fep->avb_enabled) { + struct avb_tx_desc *desc; + + while ((desc = fep->avb->tx_cleanup_dequeue(fep->avb_data)) != (void *) -1) { + + skb = desc->data; + + dma_unmap_single(&fep->pdev->dev, desc->bufaddr, + skb->len, DMA_TO_DEVICE); + + /* Free the sk buffer associated with this last transmit */ + dev_kfree_skb_any(skb); + } + } +#endif + for (q = 0; q < fep->num_tx_queues; q++) { /* ...and the same for transmit */ txq = fep->tx_queue[q]; @@ -970,6 +1273,42 @@ static void fec_enet_bd_init(struct net_device *dev) for (i = 0; i < txq->bd.ring_size; i++) { /* Initialize the BD for every fragment in the page. */ bdp->cbd_sc = cpu_to_fec16(0); +#ifdef CONFIG_AVB_SUPPORT + /* AVB not compatible with XDP: all buffers are either avb descriptors or skbs. */ + if (txq->tx_buf[i].buf_p) { + skb = NULL; + if (fep->avb_enabled) { + struct avb_tx_desc *desc = (struct avb_tx_desc *)txq->tx_buf[i].buf_p; + + if (!(desc->common.flags & AVB_TX_FLAG_SKB)) { + fep->avb->free(fep->avb_data, &desc->common); + /* Avoid unmapping AVB buffers below */ + bdp->cbd_bufaddr = cpu_to_fec32(0); + } else { + skb = desc->data; + } + } else + skb = txq->tx_buf[i].buf_p; + + if (skb) { + dev_kfree_skb_any(skb); + } + + txq->tx_buf[i].buf_p = NULL; + } + + if (bdp->cbd_bufaddr && + !IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) + dma_unmap_single(&fep->pdev->dev, + fec32_to_cpu(bdp->cbd_bufaddr), + fec16_to_cpu(bdp->cbd_datlen), + DMA_TO_DEVICE); + if (txq->tx_buf[i].buf_p) { + dev_kfree_skb_any(txq->tx_buf[i].buf_p); + txq->tx_buf[i].buf_p = NULL; + } + +#else if (txq->tx_buf[i].type == FEC_TXBUF_T_SKB) { if (bdp->cbd_bufaddr && !IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) @@ -998,6 +1337,7 @@ static void fec_enet_bd_init(struct net_device *dev) txq->tx_buf[i].buf_p = NULL; /* restore default tx buffer type: FEC_TXBUF_T_SKB */ txq->tx_buf[i].type = FEC_TXBUF_T_SKB; +#endif bdp->cbd_bufaddr = cpu_to_fec32(0); bdp = fec_enet_get_nextdesc(bdp, &txq->bd); } @@ -1023,6 +1363,7 @@ static void fec_enet_enable_ring(struct net_device *ndev) struct fec_enet_private *fep = netdev_priv(ndev); struct fec_enet_priv_tx_q *txq; struct fec_enet_priv_rx_q *rxq; + unsigned long idle_slope; int i; for (i = 0; i < fep->num_rx_queues; i++) { @@ -1041,10 +1382,25 @@ static void fec_enet_enable_ring(struct net_device *ndev) writel(txq->bd.dma, fep->hwp + FEC_X_DES_START(i)); /* enable DMA1/2 */ - if (i) - writel(DMA_CLASS_EN | IDLE_SLOPE(i), + if (i) { +#ifdef CONFIG_AVB_SUPPORT + idle_slope = txq->tx_idle_slope; +#else + idle_slope = IDLE_SLOPE(i); +#endif + + writel(DMA_CLASS_EN | idle_slope, fep->hwp + FEC_DMA_CFG(i)); + } } + + /* + * For AVB capable devices we should enable RX flushing for the + * best effort queue (ring 0) and also the TX credit based shaper. + */ + if (fep->quirks & FEC_QUIRK_HAS_AVB) + writel(FEC_RX_FLUSH(0) | FEC_TX_SCHEME_CB, + fep->hwp + FEC_QOS_SCHEME); } /* @@ -1064,13 +1420,7 @@ fec_restart(struct net_device *ndev) * For i.MX6SX SOC, enet use AXI bus, we use disable MAC * instead of reset MAC itself. */ - if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES || - ((fep->quirks & FEC_QUIRK_NO_HARD_RESET) && fep->link)) { - writel(0, fep->hwp + FEC_ECNTRL); - } else { - writel(1, fep->hwp + FEC_ECNTRL); - udelay(10); - } + writel(0, fep->hwp + FEC_ECNTRL); /* * enet-mac reset will reset mac address registers too, @@ -1227,9 +1577,6 @@ fec_restart(struct net_device *ndev) writel(ecntl, fep->hwp + FEC_ECNTRL); fec_enet_active_rxring(ndev); - if (fep->bufdesc_ex) - fec_ptp_start_cyclecounter(ndev); - /* Enable interrupts we wish to service */ if (fep->link) writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); @@ -1319,17 +1666,14 @@ fec_stop(struct net_device *ndev) netdev_err(ndev, "Graceful transmit stop did not complete!\n"); } - /* Whack a reset. We should wait for this. - * For i.MX6SX SOC, enet use AXI bus, we use disable MAC - * instead of reset MAC itself. - */ if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { - if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) { - writel(0, fep->hwp + FEC_ECNTRL); - } else { - writel(FEC_ECR_RESET, fep->hwp + FEC_ECNTRL); - udelay(10); - } + /* Always use disable MAC instead of MAC reset to: + * - Keep the ENET counter running + * - Avoid dead system bus for SoCs using the ENET-AXI bus + * and not the AHB bus, like the i.MX6SX + */ + writel(0, fep->hwp + FEC_ECNTRL); + } else { val = readl(fep->hwp + FEC_ECNTRL); val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP); @@ -1374,7 +1718,15 @@ static void fec_enet_timeout_work(struct work_struct *work) if (netif_device_present(ndev) || netif_running(ndev)) { napi_disable(&fep->napi); netif_tx_lock_bh(ndev); +#ifdef CONFIG_AVB_SUPPORT + if (fep->avb_enabled) + fep->avb->close(fep->avb_data); +#endif fec_restart(ndev); +#ifdef CONFIG_AVB_SUPPORT + if (fep->avb_enabled) + fep->avb->open(fep->avb_data, fep, fec_max_rate(fep)); +#endif netif_tx_wake_all_queues(ndev); netif_tx_unlock_bh(ndev); napi_enable(&fep->napi); @@ -1389,14 +1741,194 @@ fec_enet_hwtstamp(struct fec_enet_private *fep, unsigned ts, unsigned long flags; u64 ns; - spin_lock_irqsave(&fep->tmreg_lock, flags); + raw_spin_lock_irqsave(&fep->tmreg_lock, flags); ns = timecounter_cyc2time(&fep->tc, ts); - spin_unlock_irqrestore(&fep->tmreg_lock, flags); + raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); memset(hwtstamps, 0, sizeof(*hwtstamps)); hwtstamps->hwtstamp = ns_to_ktime(ns); } +#ifdef CONFIG_AVB_SUPPORT +static int +fec_enet_tx_queue_avb(struct net_device *ndev, u16 queue_id) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct bufdesc *bdp; + struct bufdesc_ex local_ebdp; + struct avb_tx_desc *desc; + unsigned short status; + struct fec_enet_priv_tx_q *txq; + struct netdev_queue *nq; + int index = 0; + int rc = 0; + unsigned int total_tx_packets = 0; + unsigned int total_tx_bytes = 0; + u16 tx_tstamp_latency = fep->tx_tstamp_latency; + + txq = fep->tx_queue[queue_id]; + nq = netdev_get_tx_queue(ndev, queue_id); + + bdp = txq->dirty_tx; + + /* get next bdp of dirty_tx */ + bdp = fec_enet_get_nextdesc(bdp, &txq->bd); + + while (bdp != READ_ONCE(txq->bd.cur)) { + /* Order the load of cur_tx and cbd_sc */ + rmb(); + + /* Read the first 16 bytes of the descriptor at once to avoid + * multiple reads of non cacheable memory from RAM */ + read16(&local_ebdp, bdp); + + status = fec16_to_cpu(local_ebdp.desc.cbd_sc); + if (status & BD_ENET_TX_READY) + break; + + index = fec_enet_get_bd_index(bdp, &txq->bd); + desc = (struct avb_tx_desc *)txq->tx_buf[index].buf_p; + + if (!(desc->common.flags & AVB_TX_FLAG_SKB)) { + if ((desc->common.flags & AVB_TX_FLAG_HW_TS)) { + struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; + + desc->common.ts = ebdp->ts + tx_tstamp_latency; + + /* upper layer will retrieve the timestamp and free the descriptor */ + rc |= fep->avb->tx_ts(fep->avb_data, &desc->common); + } + else + fep->avb->free(fep->avb_data, &desc->common); + + total_tx_packets++; + total_tx_bytes += desc->datlen; + } else { + /* Backup hardware descriptor fields in software descriptor */ + desc->sc = status; + desc->datlen = fec16_to_cpu(local_ebdp.desc.cbd_datlen); + desc->bufaddr = fec32_to_cpu(local_ebdp.desc.cbd_bufaddr); + desc->common.ts = fec32_to_cpu(((struct bufdesc_ex *)bdp)->ts); + + if (fep->avb->tx_cleanup(fep->avb_data, desc) < 0) + BUG(); + } + + txq->tx_buf[index].buf_p = NULL; + bdp->cbd_bufaddr = cpu_to_fec32(0); + txq->dirty_tx = bdp; + + /* Update pointer to next buffer descriptor to be transmitted */ + bdp = fec_enet_get_nextdesc(bdp, &txq->bd); + } + + /* schedule tx napi, based on level of tx cleanup queue or time passed */ + if (fep->avb->tx_cleanup_ready(fep->avb_data) || netif_tx_queue_stopped(nq)) { + if (napi_schedule_prep(&fep->napi)) { + __napi_schedule(&fep->napi); + } + } + + /* Update stats*/ + ndev->stats.tx_packets += total_tx_packets; + ndev->stats.tx_bytes += total_tx_bytes; + + return rc; +} + +int +fec_enet_tx_avb(void *data) +{ + struct fec_enet_private *fep = data; + u16 queue_id; + int rc = 0; + + for (queue_id = 0; queue_id < fep->num_tx_queues; queue_id++) + rc |= fec_enet_tx_queue_avb(fep->netdev, queue_id); + + return rc; +} +EXPORT_SYMBOL(fec_enet_tx_avb); + +static void +fec_enet_tx_best_effort(struct net_device *ndev) +{ + struct fec_enet_private *fep; + unsigned short status; + struct avb_tx_desc *desc; + struct sk_buff *skb; + struct fec_enet_priv_tx_q *txq; + struct netdev_queue *nq; + + fep = netdev_priv(ndev); + + while ((desc = fep->avb->tx_cleanup_dequeue(fep->avb_data)) != (void *) -1) { + + txq = fep->tx_queue[desc->queue_id]; + nq = netdev_get_tx_queue(ndev, desc->queue_id); + + status = desc->sc; + + /* Check for errors. */ + if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | + BD_ENET_TX_RL | BD_ENET_TX_UN | + BD_ENET_TX_CSL)) { + ndev->stats.tx_errors++; + if (status & BD_ENET_TX_HB) /* No heartbeat */ + ndev->stats.tx_heartbeat_errors++; + if (status & BD_ENET_TX_LC) /* Late collision */ + ndev->stats.tx_window_errors++; + if (status & BD_ENET_TX_RL) /* Retrans limit */ + ndev->stats.tx_aborted_errors++; + if (status & BD_ENET_TX_UN) /* Underrun */ + ndev->stats.tx_fifo_errors++; + if (status & BD_ENET_TX_CSL) /* Carrier lost */ + ndev->stats.tx_carrier_errors++; + } else { + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += desc->datlen; + } + + skb = desc->data; + + if (!IS_TSO_HEADER(txq, desc->bufaddr)) + dma_unmap_single(&fep->pdev->dev, desc->bufaddr, + desc->datlen, DMA_TO_DEVICE); + + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) { + struct skb_shared_hwtstamps shhwtstamps; + + desc->common.ts += fep->tx_tstamp_latency; + fec_enet_hwtstamp(fep, desc->common.ts, + &shhwtstamps); + skb_tstamp_tx(skb, &shhwtstamps); + } + + /* Deferred means some collisions occurred during transmit, + * but we eventually sent the packet OK. + */ + if (status & BD_ENET_TX_DEF) + ndev->stats.collisions++; + + /* Free the sk buffer associated with this last transmit */ + dev_kfree_skb_any(skb); + + /* Make sure the update to bdp and tx_buf are performed + * before dirty_tx + */ + wmb(); + + //FIXME add treshold + if (!fep->avb->tx_full(fep->avb_data)) { + if (netif_tx_queue_stopped(nq)) { + //` netdev_info(ndev, "wake queue\n"); + netif_tx_wake_queue(nq); + } + } + } +} +#endif + static void fec_enet_tx_queue(struct net_device *ndev, u16 queue_id, int budget) { @@ -1512,6 +2044,11 @@ fec_enet_tx_queue(struct net_device *ndev, u16 queue_id, int budget) struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), &shhwtstamps); + /* Adjust for TX MAC-PHY latency + */ + shhwtstamps.hwtstamp = + ktime_add_ns(shhwtstamps.hwtstamp, fep->tx_tstamp_latency); + skb_tstamp_tx(skb, &shhwtstamps); } @@ -1578,6 +2115,7 @@ static void fec_enet_update_cbd(struct fec_enet_priv_rx_q *rxq, bdp->cbd_bufaddr = cpu_to_fec32(phys_addr); } +#ifndef CONFIG_AVB_SUPPORT static u32 fec_enet_run_xdp(struct fec_enet_private *fep, struct bpf_prog *prog, struct xdp_buff *xdp, struct fec_enet_priv_rx_q *rxq, int cpu) @@ -1619,29 +2157,299 @@ fec_enet_run_xdp(struct fec_enet_private *fep, struct bpf_prog *prog, goto xdp_err; } - ret = FEC_ENET_XDP_TX; - break; + ret = FEC_ENET_XDP_TX; + break; + + default: + bpf_warn_invalid_xdp_action(fep->netdev, prog, act); + fallthrough; + + case XDP_ABORTED: + fallthrough; /* handle aborts by dropping packet */ + + case XDP_DROP: + rxq->stats[RX_XDP_DROP]++; +xdp_err: + ret = FEC_ENET_XDP_CONSUMED; + page = virt_to_head_page(xdp->data); + page_pool_put_page(rxq->page_pool, page, sync, true); + if (act != XDP_DROP) + trace_xdp_exception(fep->netdev, prog, act); + break; + } + + return ret; +} +#else /* CONFIG_AVB_SUPPORT */ + +static int +fec_enet_rx_best_effort(struct net_device *ndev, int budget) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct avb_rx_desc *desc; + struct sk_buff *skb; + ushort pkt_len; + __u8 *data; + bool vlan_packet_rcvd = false; + u16 vlan_tag; + int pkt_received = 0; + + do { + desc = fep->avb->dequeue(fep->avb_data); + if (desc == (void *)-1) + break; + + /* Process the incoming frame. */ + pkt_len = desc->common.len; + data = (u8 *)desc + desc->common.offset; + + skb = netdev_alloc_skb(ndev, pkt_len - 4); + if (unlikely(!skb)) { + ndev->stats.rx_dropped++; + goto rx_processing_done; + } + else { + /* Make some room minus FCS */ + skb_put(skb, pkt_len - 4); + + /* Copy AVB buffer to skb */ + skb_copy_to_linear_data(skb, data, pkt_len - 4); + data = skb->data; + + /* Get receive timestamp from the skb */ + if (fep->hwts_rx_en) { + skb_reset_mac_header(skb); + fec_enet_hwtstamp(fep, desc->common.ts, + skb_hwtstamps(skb)); + } + + /* If this is a VLAN packet remove the VLAN Tag */ + vlan_packet_rcvd = false; + if (desc->common.private & BD_ENET_RX_VLAN) { + /* Push and remove the vlan tag */ + struct vlan_hdr *vlan_header = + (struct vlan_hdr *) (data + ETH_HLEN); + vlan_tag = ntohs(vlan_header->h_vlan_TCI); + + vlan_packet_rcvd = true; + + memmove(skb->data + VLAN_HLEN, data, ETH_ALEN * 2); + + skb_pull(skb, VLAN_HLEN); + } + + skb->protocol = eth_type_trans(skb, ndev); + + if (fep->csum_flags & FLAG_RX_CSUM_ENABLED) { + if (!(desc->common.private & FLAG_RX_CSUM_ERROR)) { + /* don't check it */ + skb->ip_summed = CHECKSUM_UNNECESSARY; + } else { + skb_checksum_none_assert(skb); + } + } + + /* Handle received VLAN packets */ + if (vlan_packet_rcvd) + __vlan_hwaccel_put_tag(skb, + htons(ETH_P_8021Q), + vlan_tag); + + napi_gro_receive(&fep->napi, skb); + } +rx_processing_done: + fep->avb->free(fep->avb_data, &desc->common); + + } while (++pkt_received < budget); + + return pkt_received; +} + +static unsigned int +fec_enet_rx_queue_avb(struct net_device *ndev, u16 queue_id) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct fec_enet_priv_rx_q *rxq; + struct bufdesc *bdp; + unsigned short status; + ushort pkt_len; + __u8 *data, *new_data; + int index = 0; + struct bufdesc_ex *ebdp = NULL; + struct bufdesc_ex local_ebdp; + struct avb_rx_desc *desc; + unsigned int rc = 0; + unsigned int net_data_offset; + unsigned int count = 0; + unsigned int total_rx_packets = 0; + unsigned int total_rx_bytes = 0; + u16 rx_tstamp_latency = fep->rx_tstamp_latency; + bool need_swap = fep->quirks & FEC_QUIRK_SWAP_FRAME; + bool has_racc = fep->quirks & FEC_QUIRK_HAS_RACC; + + rxq = fep->rx_queue[queue_id]; + + /* First, grab all of the stats for the incoming packet. + * These get messed up if we get called due to a busy condition. + */ + bdp = rxq->bd.cur; + + /* 20 packets per 125us > 64 bytes packets @ 100Mbps */ + while (!((status = fec16_to_cpu(bdp->cbd_sc)) & BD_ENET_RX_EMPTY) && (count++ < 20)) { + + writel(FEC_ENET_RXF, fep->hwp + FEC_IEVENT); + + /* Read the first 16 bytes of the descriptor at once to avoid + * multiple reads of non cacheable memory from RAM */ + read16(&local_ebdp, bdp); + + /* Check for errors. */ + status ^= BD_ENET_RX_LAST; + if (unlikely(status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | + BD_ENET_RX_CR | BD_ENET_RX_OV | BD_ENET_RX_LAST | + BD_ENET_RX_CL))) { + ndev->stats.rx_errors++; + if (status & BD_ENET_RX_OV) { + /* FIFO overrun */ + ndev->stats.rx_fifo_errors++; + goto rx_processing_done; + } + if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH + | BD_ENET_RX_LAST)) { + /* Frame too long or too short. */ + ndev->stats.rx_length_errors++; + if (status & BD_ENET_RX_LAST) + netdev_err(ndev, "rcv is not +last\n"); + } + if (status & BD_ENET_RX_CR) /* CRC Error */ + ndev->stats.rx_crc_errors++; + /* Report late collisions as a frame error. */ + if (status & (BD_ENET_RX_NO | BD_ENET_RX_CL)) + ndev->stats.rx_frame_errors++; + goto rx_processing_done; + } + + new_data = fep->avb->alloc(fep->avb_data); + if (unlikely(!new_data)) { + ndev->stats.rx_dropped++; + goto rx_processing_done; + } + + /* Process the incoming frame. */ + total_rx_packets++; + pkt_len = fec16_to_cpu(local_ebdp.desc.cbd_datlen); + total_rx_bytes += pkt_len; + index = fec_enet_get_bd_index(bdp, &rxq->bd); + data = (__u8 *)rxq->rx_skb_info[index].skb; + + /* FIXME, skip unmap of audio data */ + dma_sync_single_for_cpu(&fep->pdev->dev, fec32_to_cpu(local_ebdp.desc.cbd_bufaddr), + L1_CACHE_ALIGN(pkt_len), DMA_FROM_DEVICE); + + desc = (struct avb_rx_desc *)data; + + net_data_offset = desc->common.offset; + +#if !defined(CONFIG_M5272) + if (has_racc) + net_data_offset -= 2; +#endif + prefetch(data + net_data_offset); + + if (need_swap) + swap_buffer(data, pkt_len); + + desc->common.len = pkt_len; + desc->sc = fec16_to_cpu(local_ebdp.desc.cbd_sc); + + /* Extract the enhanced buffer descriptor */ + ebdp = (struct bufdesc_ex *)bdp; + + desc->common.ts = ebdp->ts - rx_tstamp_latency; + desc->common.private = fec32_to_cpu(local_ebdp.cbd_esc); + + rc |= fep->avb->rx(fep->avb_data, desc); + + data = new_data; + + desc = (struct avb_rx_desc *)data; + + desc->common.len = 0; + desc->queue_id = queue_id; + + bdp->cbd_bufaddr = cpu_to_fec32((dma_addr_t)(desc->dma_addr)); + +#if !defined(CONFIG_M5272) + if (has_racc) + desc->common.offset += 2; +#endif + rxq->rx_skb_info[index].skb = (void *)data; +rx_processing_done: + /* Clear the status flags for this buffer */ + status &= ~BD_ENET_RX_STATS; + + /* Mark the buffer empty */ + status |= BD_ENET_RX_EMPTY; + bdp->cbd_sc = cpu_to_fec16(status); + + ebdp = (struct bufdesc_ex *)bdp; + + ebdp->cbd_esc = cpu_to_fec32(0); + ebdp->cbd_prot = cpu_to_fec32(0); + ebdp->cbd_bdu = cpu_to_fec32(0); + + /* Update BD pointer to next entry */ + + bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); + } + + /* If the receive ring buffer can hold at least double the maximum + number of packets per polling period (18.2 packets @ 100Mbps), it's + ok to only re-enable receive after processing all current packets */ + + writel(0, rxq->bd.reg_desc_active); + + rxq->bd.cur = bdp; + + /*Update stats*/ + ndev->stats.rx_packets += total_rx_packets; + ndev->stats.rx_bytes += total_rx_bytes; + + return rc; +} - default: - bpf_warn_invalid_xdp_action(fep->netdev, prog, act); - fallthrough; +static unsigned int +fec_enet_rx_avb(struct net_device *ndev) +{ + u16 queue_id; + struct fec_enet_private *fep = netdev_priv(ndev); + unsigned int rc = 0; - case XDP_ABORTED: - fallthrough; /* handle aborts by dropping packet */ + for (queue_id = 0; queue_id < fep->num_rx_queues; queue_id++) + rc |= fec_enet_rx_queue_avb(ndev, queue_id); - case XDP_DROP: - rxq->stats[RX_XDP_DROP]++; -xdp_err: - ret = FEC_ENET_XDP_CONSUMED; - page = virt_to_head_page(xdp->data); - page_pool_put_page(rxq->page_pool, page, sync, true); - if (act != XDP_DROP) - trace_xdp_exception(fep->netdev, prog, act); - break; + return rc; +} + +int fec_enet_rx_poll_avb(void *data) +{ + struct fec_enet_private *fep = data; + struct net_device *ndev = fep->netdev; + unsigned int rc; + + rc = fec_enet_rx_avb(ndev); + + if (rc & AVB_WAKE_NAPI) { + /* Best effort packets were posted, schedule napi if not scheduled yet. */ + if (napi_schedule_prep(&fep->napi)) + __napi_schedule(&fep->napi); } - return ret; + return rc; } +EXPORT_SYMBOL(fec_enet_rx_poll_avb); + +#endif /* CONFIG_AVB_SUPPORT */ /* During a receive, the bd_rx.cur points to the current incoming buffer. * When we update through the ring, if the next incoming buffer has @@ -1664,11 +2472,13 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) u16 vlan_tag; int index = 0; bool need_swap = fep->quirks & FEC_QUIRK_SWAP_FRAME; +#ifndef CONFIG_AVB_SUPPORT struct bpf_prog *xdp_prog = READ_ONCE(fep->xdp_prog); u32 ret, xdp_result = FEC_ENET_XDP_PASS; - u32 data_start = FEC_ENET_XDP_HEADROOM; int cpu = smp_processor_id(); struct xdp_buff xdp; +#endif + u32 data_start = FEC_ENET_XDP_HEADROOM; struct page *page; u32 sub_len = 4; @@ -1691,7 +2501,9 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) * These get messed up if we get called due to a busy condition. */ bdp = rxq->bd.cur; +#ifndef CONFIG_AVB_SUPPORT xdp_init_buff(&xdp, PAGE_SIZE, &rxq->xdp_rxq); +#endif while (!((status = fec16_to_cpu(bdp->cbd_sc)) & BD_ENET_RX_EMPTY)) { @@ -1741,6 +2553,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) prefetch(page_address(page)); fec_enet_update_cbd(rxq, bdp, index); +#ifndef CONFIG_AVB_SUPPORT if (xdp_prog) { xdp_buff_clear_frags_flag(&xdp); /* subtract 16bit shift and FCS */ @@ -1751,6 +2564,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) if (ret != FEC_ENET_XDP_PASS) goto rx_processing_done; } +#endif /* The packet length includes FCS, but we don't want to * include that when passing upstream as it messes up @@ -1799,10 +2613,16 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) skb->protocol = eth_type_trans(skb, ndev); /* Get receive timestamp from the skb */ - if (fep->hwts_rx_en && fep->bufdesc_ex) + if (fep->hwts_rx_en && fep->bufdesc_ex) { fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), skb_hwtstamps(skb)); + /* Adjust for RX MAC-PHY latency + */ + skb_hwtstamps(skb)->hwtstamp = + ktime_sub_ns(skb_hwtstamps(skb)->hwtstamp, fep->rx_tstamp_latency); + } + if (fep->bufdesc_ex && (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { if (!(ebdp->cbd_esc & cpu_to_fec32(FLAG_RX_CSUM_ERROR))) { @@ -1853,8 +2673,10 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) } rxq->bd.cur = bdp; +#ifndef CONFIG_AVB_SUPPORT if (xdp_result & FEC_ENET_XDP_REDIR) xdp_do_flush_map(); +#endif return pkt_received; } @@ -1895,6 +2717,10 @@ fec_enet_interrupt(int irq, void *dev_id) if (fec_enet_collect_events(fep) && fep->link) { ret = IRQ_HANDLED; +#ifdef CONFIG_AVB_SUPPORT + if (fep->avb_enabled) + dev_err(&fep->pdev->dev, "Rx/Tx IRQ with AVB enabled, should not happen\n"); +#endif if (napi_schedule_prep(&fep->napi)) { /* Disable interrupts */ writel(0, fep->hwp + FEC_IMASK); @@ -1911,10 +2737,18 @@ static int fec_enet_rx_napi(struct napi_struct *napi, int budget) struct fec_enet_private *fep = netdev_priv(ndev); int done = 0; - do { - done += fec_enet_rx(ndev, budget - done); - fec_enet_tx(ndev, budget); - } while ((done < budget) && fec_enet_collect_events(fep)); +#ifdef CONFIG_AVB_SUPPORT + if (fep->avb_enabled) { + done = fec_enet_rx_best_effort(ndev, budget); + fec_enet_tx_best_effort(ndev); + } else +#endif + { + do { + done += fec_enet_rx(ndev, budget - done); + fec_enet_tx(ndev, budget); + } while ((done < budget) && fec_enet_collect_events(fep)); + } if (done < budget) { napi_complete_done(napi, done); @@ -2031,12 +2865,34 @@ static void fec_enet_adjust_link(struct net_device *ndev) status_change = 1; } + switch (fep->speed) { + case SPEED_100: + fep->rx_tstamp_latency = fep->rx_delay_100; + fep->tx_tstamp_latency = fep->tx_delay_100; + break; + case SPEED_1000: + fep->rx_tstamp_latency = fep->rx_delay_1000; + fep->tx_tstamp_latency = fep->tx_delay_1000; + break; + default: + fep->rx_tstamp_latency = 0; + fep->tx_tstamp_latency = 0; + } + /* if any of the above changed restart the FEC */ if (status_change) { netif_stop_queue(ndev); napi_disable(&fep->napi); netif_tx_lock_bh(ndev); +#ifdef CONFIG_AVB_SUPPORT + if (fep->avb_enabled) + fep->avb->close(fep->avb_data); +#endif fec_restart(ndev); +#ifdef CONFIG_AVB_SUPPORT + if (fep->avb_enabled) + fep->avb->open(fep->avb_data, fep, fec_max_rate(fep)); +#endif netif_tx_wake_all_queues(ndev); netif_tx_unlock_bh(ndev); napi_enable(&fep->napi); @@ -2046,7 +2902,15 @@ static void fec_enet_adjust_link(struct net_device *ndev) netif_stop_queue(ndev); napi_disable(&fep->napi); netif_tx_lock_bh(ndev); +#ifdef CONFIG_AVB_SUPPORT + if (fep->avb_enabled) + fep->avb->close(fep->avb_data); +#endif fec_stop(ndev); +#ifdef CONFIG_AVB_SUPPORT + if (fep->avb_enabled) + fep->avb->open(fep->avb_data, fep, fec_max_rate(fep)); +#endif netif_tx_unlock_bh(ndev); napi_enable(&fep->napi); fep->link = phy_dev->link; @@ -2420,6 +3284,12 @@ static int fec_enet_mii_probe(struct net_device *ndev) else phy_set_max_speed(phy_dev, 100); +#ifdef CONFIG_AVB_SUPPORT + /* Restore advertising settings saved last interface close */ + if (!linkmode_empty(fep->phy_advertising)) + linkmode_copy(phy_dev->advertising, fep->phy_advertising); +#endif + fep->link = 0; fep->full_duplex = 0; @@ -3291,24 +4161,93 @@ static void fec_enet_free_buffers(struct net_device *ndev) unsigned int i; struct fec_enet_priv_tx_q *txq; struct fec_enet_priv_rx_q *rxq; +#ifdef CONFIG_AVB_SUPPORT + struct sk_buff *skb; +#endif unsigned int q; for (q = 0; q < fep->num_rx_queues; q++) { rxq = fep->rx_queue[q]; - for (i = 0; i < rxq->bd.ring_size; i++) - page_pool_put_full_page(rxq->page_pool, rxq->rx_skb_info[i].page, false); +#ifdef CONFIG_AVB_SUPPORT + if (fep->avb_enabled) { + struct bufdesc *bdp = rxq->bd.base; + for (i = 0; i < rxq->bd.ring_size; i++) { + if (bdp->cbd_bufaddr) { + struct avb_rx_desc *desc; + + desc = (struct avb_rx_desc *)rxq->rx_skb_info[i].skb; + fep->avb->free(fep->avb_data, &desc->common); + bdp->cbd_bufaddr = cpu_to_fec32(0); + } + + bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); + } + } else +#endif + { + for (i = 0; i < rxq->bd.ring_size; i++) + page_pool_put_full_page(rxq->page_pool, rxq->rx_skb_info[i].page, false); - for (i = 0; i < XDP_STATS_TOTAL; i++) - rxq->stats[i] = 0; + for (i = 0; i < XDP_STATS_TOTAL; i++) + rxq->stats[i] = 0; - if (xdp_rxq_info_is_reg(&rxq->xdp_rxq)) - xdp_rxq_info_unreg(&rxq->xdp_rxq); - page_pool_destroy(rxq->page_pool); - rxq->page_pool = NULL; + if (xdp_rxq_info_is_reg(&rxq->xdp_rxq)) + xdp_rxq_info_unreg(&rxq->xdp_rxq); + page_pool_destroy(rxq->page_pool); + rxq->page_pool = NULL; + } + } + +#ifdef CONFIG_AVB_SUPPORT + if (fep->avb_enabled) { + struct avb_rx_desc *rx_desc; + struct avb_tx_desc *tx_desc; + + while ((rx_desc = fep->avb->dequeue(fep->avb_data)) != (void *) -1) + fep->avb->free(fep->avb_data, &rx_desc->common); + + while ((tx_desc = fep->avb->tx_cleanup_dequeue(fep->avb_data)) != (void *) -1) { + + skb = tx_desc->data; + + dma_unmap_single(&fep->pdev->dev, tx_desc->bufaddr, + skb->len, DMA_TO_DEVICE); + + /* Free the sk buffer associated with this last transmit */ + dev_kfree_skb_any(skb); + } } +#endif for (q = 0; q < fep->num_tx_queues; q++) { txq = fep->tx_queue[q]; +#ifdef CONFIG_AVB_SUPPORT + for (i = 0; i < txq->bd.ring_size; i++) { + if (txq->tx_buf[i].buf_p) { + skb = NULL; + if (fep->avb_enabled) { + struct avb_tx_desc *desc = (struct avb_tx_desc *)txq->tx_buf[i].buf_p; + + if (!(desc->common.flags & AVB_TX_FLAG_SKB)) + fep->avb->free(fep->avb_data, &desc->common); + else + skb = desc->data; + } else { + skb = txq->tx_buf[i].buf_p; + } + + if (skb) + dev_kfree_skb(skb); + + txq->tx_buf[i].buf_p = NULL; + } + } + + for (i = 0; i < txq->tx_bounce_size; i++) { + kfree(txq->tx_bounce[i]); + txq->tx_bounce[i] = NULL; + } +#else for (i = 0; i < txq->bd.ring_size; i++) { kfree(txq->tx_bounce[i]); txq->tx_bounce[i] = NULL; @@ -3331,6 +4270,7 @@ static void fec_enet_free_buffers(struct net_device *ndev) txq->tx_buf[i].buf_p = NULL; txq->tx_buf[i].type = FEC_TXBUF_T_SKB; } +#endif } } @@ -3370,11 +4310,18 @@ static int fec_enet_alloc_queue(struct net_device *ndev) } fep->tx_queue[i] = txq; - txq->bd.ring_size = TX_RING_SIZE; + txq->bd.ring_size = FEC_TX_RING_SIZE; fep->total_tx_ring_size += fep->tx_queue[i]->bd.ring_size; +#ifdef CONFIG_AVB_SUPPORT + txq->tx_stop_threshold = FEC_TX_RING_SIZE * 3/4; + + txq->tx_wake_threshold = + (txq->bd.ring_size - txq->tx_stop_threshold) / 2; +#else txq->tx_stop_threshold = FEC_MAX_SKB_DESCS; txq->tx_wake_threshold = FEC_MAX_SKB_DESCS + 2 * MAX_SKB_FRAGS; +#endif txq->tso_hdrs = dma_alloc_coherent(&fep->pdev->dev, txq->bd.ring_size * TSO_HEADER_SIZE, @@ -3394,7 +4341,7 @@ static int fec_enet_alloc_queue(struct net_device *ndev) goto alloc_failed; } - fep->rx_queue[i]->bd.ring_size = RX_RING_SIZE; + fep->rx_queue[i]->bd.ring_size = FEC_RX_RING_SIZE; fep->total_rx_ring_size += fep->rx_queue[i]->bd.ring_size; } return ret; @@ -3415,32 +4362,77 @@ fec_enet_alloc_rxq_buffers(struct net_device *ndev, unsigned int queue) int i, err; rxq = fep->rx_queue[queue]; + +#ifdef CONFIG_AVB_SUPPORT bdp = rxq->bd.base; - err = fec_enet_create_page_pool(fep, rxq, rxq->bd.ring_size); - if (err < 0) { - netdev_err(ndev, "%s failed queue %d (%d)\n", __func__, queue, err); - return err; + for (i = 0; i < rxq->bd.ring_size; i++) { + bdp->cbd_bufaddr = cpu_to_fec32(0); + bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); } +#endif - for (i = 0; i < rxq->bd.ring_size; i++) { - page = page_pool_dev_alloc_pages(rxq->page_pool); - if (!page) - goto err_alloc; + bdp = rxq->bd.base; +#ifdef CONFIG_AVB_SUPPORT + if (fep->avb_enabled) { + for (i = 0; i < rxq->bd.ring_size; i++) { + struct avb_rx_desc *desc; + void *buffer; - phys_addr = page_pool_get_dma_addr(page) + FEC_ENET_XDP_HEADROOM; - bdp->cbd_bufaddr = cpu_to_fec32(phys_addr); + buffer = fep->avb->alloc(fep->avb_data); + if (!buffer) + goto err_alloc; - rxq->rx_skb_info[i].page = page; - rxq->rx_skb_info[i].offset = FEC_ENET_XDP_HEADROOM; - bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); + desc = buffer; - if (fep->bufdesc_ex) { - struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; - ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT); + desc->common.len = 0; + desc->queue_id = queue; + + rxq->rx_skb_info[i].skb = buffer; + bdp->cbd_bufaddr = cpu_to_fec32((dma_addr_t)(desc->dma_addr)); + +#if !defined(CONFIG_M5272) + if (fep->quirks & FEC_QUIRK_HAS_RACC) + desc->common.offset += 2; +#endif + + bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); + + if (fep->bufdesc_ex) { + struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; + ebdp->cbd_esc = cpu_to_fec32(0); + } + + bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); + } + } else +#endif + { + err = fec_enet_create_page_pool(fep, rxq, rxq->bd.ring_size); + if (err < 0) { + netdev_err(ndev, "%s failed queue %d (%d)\n", __func__, queue, err); + return err; } - bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); + for (i = 0; i < rxq->bd.ring_size; i++) { + page = page_pool_dev_alloc_pages(rxq->page_pool); + if (!page) + goto err_alloc; + + phys_addr = page_pool_get_dma_addr(page) + FEC_ENET_XDP_HEADROOM; + bdp->cbd_bufaddr = cpu_to_fec32(phys_addr); + + rxq->rx_skb_info[i].page = page; + rxq->rx_skb_info[i].offset = FEC_ENET_XDP_HEADROOM; + bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); + + if (fep->bufdesc_ex) { + struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; + ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT); + } + + bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); + } } /* Set the last buffer to wrap. */ @@ -3463,17 +4455,28 @@ fec_enet_alloc_txq_buffers(struct net_device *ndev, unsigned int queue) txq = fep->tx_queue[queue]; bdp = txq->bd.base; - for (i = 0; i < txq->bd.ring_size; i++) { + for (i = 0; i < txq->tx_bounce_size; i++) { txq->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL); if (!txq->tx_bounce[i]) goto err_alloc; + } + bdp = txq->bd.base; + txq->bd.cur = bdp; + + for (i = 0; i < txq->bd.ring_size; i++) { bdp->cbd_sc = cpu_to_fec16(0); bdp->cbd_bufaddr = cpu_to_fec32(0); if (fep->bufdesc_ex) { struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; - ebdp->cbd_esc = cpu_to_fec32(BD_ENET_TX_INT); + +#ifdef CONFIG_AVB_SUPPORT + if (fep->avb_enabled) + ebdp->cbd_esc = cpu_to_fec32(0); + else +#endif + ebdp->cbd_esc = cpu_to_fec32(BD_ENET_TX_INT); } bdp = fec_enet_get_nextdesc(bdp, &txq->bd); @@ -3482,6 +4485,7 @@ fec_enet_alloc_txq_buffers(struct net_device *ndev, unsigned int queue) /* Set the last buffer to wrap. */ bdp = fec_enet_get_prevdesc(bdp, &txq->bd); bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); + txq->dirty_tx = bdp; return 0; @@ -3511,15 +4515,31 @@ fec_enet_open(struct net_device *ndev) struct fec_enet_private *fep = netdev_priv(ndev); int ret; bool reset_again; + int i; + +#ifdef CONFIG_AVB_SUPPORT + if (fep->avb_enabled) { + if (!try_module_get(fep->avb->owner)) { + ret = -EIO; + goto err_module_get; + } + for (i = 0; i < fep->num_tx_queues; i++) + fep->tx_queue[i]->tx_bounce_size = FEC_TX_RING_SIZE + 32; + } else +#endif + for (i = 0; i < fep->num_tx_queues; i++) + fep->tx_queue[i]->tx_bounce_size = FEC_TX_RING_SIZE; ret = pm_runtime_resume_and_get(&fep->pdev->dev); if (ret < 0) return ret; pinctrl_pm_select_default_state(&fep->pdev->dev); +#ifndef CONFIG_AVB_SUPPORT ret = fec_enet_clk_enable(ndev, true); if (ret) goto clk_enable; +#endif /* During the first fec_enet_open call the PHY isn't probed at this * point. Therefore the phy_reset_after_clk_enable() call within @@ -3562,6 +4582,12 @@ fec_enet_open(struct net_device *ndev) napi_enable(&fep->napi); phy_start(ndev->phydev); + +#ifdef CONFIG_AVB_SUPPORT + if (fep->avb_enabled) + fep->avb->open(fep->avb_data, fep, fec_max_rate(fep)); +#endif + netif_tx_start_all_queues(ndev); device_set_wakeup_enable(&ndev->dev, fep->wol_flag & @@ -3572,12 +4598,23 @@ fec_enet_open(struct net_device *ndev) err_enet_mii_probe: fec_enet_free_buffers(ndev); err_enet_alloc: +#ifndef CONFIG_AVB_SUPPORT fec_enet_clk_enable(ndev, false); clk_enable: +#endif pm_runtime_mark_last_busy(&fep->pdev->dev); pm_runtime_put_autosuspend(&fep->pdev->dev); if (!fep->mii_bus_share) pinctrl_pm_select_sleep_state(&fep->pdev->dev); + +#ifdef CONFIG_AVB_SUPPORT + if (fep->avb_enabled) + module_put(fep->avb->owner); +#endif + +#ifdef CONFIG_AVB_SUPPORT +err_module_get: +#endif return ret; } @@ -3591,9 +4628,20 @@ fec_enet_close(struct net_device *ndev) if (netif_device_present(ndev)) { napi_disable(&fep->napi); netif_tx_disable(ndev); +#ifdef CONFIG_AVB_SUPPORT + if (fep->avb_enabled) + fep->avb->close(fep->avb_data); +#endif fec_stop(ndev); } +#ifdef CONFIG_AVB_SUPPORT + /* + * Save advertising settings so there are not lost + * when opening the interface again. + */ + linkmode_copy(fep->phy_advertising, ndev->phydev->advertising); +#endif phy_disconnect(ndev->phydev); ndev->phydev = NULL; @@ -3602,7 +4650,9 @@ fec_enet_close(struct net_device *ndev) fec_enet_update_ethtool_stats(ndev); +#ifndef CONFIG_AVB_SUPPORT fec_enet_clk_enable(ndev, false); +#endif if (fep->quirks & FEC_QUIRK_HAS_PMQOS) cpu_latency_qos_remove_request(&fep->pm_qos_req); @@ -3613,6 +4663,11 @@ fec_enet_close(struct net_device *ndev) fec_enet_free_buffers(ndev); +#ifdef CONFIG_AVB_SUPPORT + if (fep->avb_enabled) + module_put(fep->avb->owner); +#endif + return 0; } @@ -3769,6 +4824,7 @@ static u16 fec_enet_select_queue(struct net_device *ndev, struct sk_buff *skb, return fec_enet_vlan_pri_to_queue[vlan_tag >> 13]; } +#ifndef CONFIG_AVB_SUPPORT static int fec_enet_bpf(struct net_device *dev, struct netdev_bpf *bpf) { struct fec_enet_private *fep = netdev_priv(dev); @@ -3970,6 +5026,7 @@ static int fec_enet_xdp_xmit(struct net_device *dev, return sent_frames; } +#endif /* !CONFIG_AVB_SUPPORT */ static int fec_hwtstamp_get(struct net_device *ndev, struct kernel_hwtstamp_config *config) @@ -4013,8 +5070,11 @@ static const struct net_device_ops fec_netdev_ops = { .ndo_set_mac_address = fec_set_mac_address, .ndo_eth_ioctl = phy_do_ioctl_running, .ndo_set_features = fec_set_features, +#ifndef CONFIG_AVB_SUPPORT + /* AVB support not compatible with XDP */ .ndo_bpf = fec_enet_bpf, .ndo_xdp_xmit = fec_enet_xdp_xmit, +#endif .ndo_hwtstamp_get = fec_hwtstamp_get, .ndo_hwtstamp_set = fec_hwtstamp_set, }; @@ -4027,6 +5087,196 @@ static const unsigned short offset_des_active_txq[] = { FEC_X_DES_ACTIVE_0, FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2 }; +#ifdef CONFIG_AVB_SUPPORT + +static struct platform_driver fec_driver; + +/* Checks if the net_device is registered by the fec */ +static bool __is_fec_net_device(struct net_device *ndev) +{ + if (!ndev) + return false; + + if (ndev->dev.parent->driver == &fec_driver.driver) + return true; + else + return false; +} + +struct device *fec_enet_avb_get_device(const char *ifname) +{ + struct net_device *ndev; + struct fec_enet_private *fep; + + ndev = dev_get_by_name(&init_net, ifname); + if (!ndev) + goto err_dev_get; + + if (!__is_fec_net_device(ndev)) + goto err_ndev; + + fep = netdev_priv(ndev); + + dev_put(ndev); + + return &fep->pdev->dev; + +err_ndev: + dev_put(ndev); + +err_dev_get: + return NULL; +} +EXPORT_SYMBOL(fec_enet_avb_get_device); + +int fec_enet_avb_register(const char *ifname, const struct avb_ops *avb, void *data) +{ + struct net_device *ndev; + struct fec_enet_private *fep; + unsigned int up; + int ifindex; + + ndev = dev_get_by_name(&init_net, ifname); + if (!ndev) + goto err_dev_get; + + if (!__is_fec_net_device(ndev)) + goto err_ndev; + + fep = netdev_priv(ndev); + + if (fep->avb) + goto err_avb; + + rtnl_lock(); + up = ndev->flags & IFF_UP; + if (up) + dev_close(ndev); + + fep->avb = avb; + fep->avb_data = data; + fep->avb_enabled = 1; + ifindex = ndev->ifindex; + + if (up) { + /* In case of error, device is closed but avb interface is registered */ + dev_open(ndev, NULL); + } + + rtnl_unlock(); + + dev_put(ndev); + + return ifindex; + +err_avb: +err_ndev: + dev_put(ndev); + +err_dev_get: + return -1; +} +EXPORT_SYMBOL(fec_enet_avb_register); + +int fec_enet_avb_unregister(int ifindex, const struct avb_ops *avb) +{ + struct net_device *ndev; + struct fec_enet_private *fep; + unsigned int up; + + ndev = dev_get_by_index(&init_net, ifindex); + if (!ndev) + goto err_dev_get; + + if (!__is_fec_net_device(ndev)) + goto err_ndev; + + fep = netdev_priv(ndev); + if (fep->avb != avb) + goto err_avb; + + rtnl_lock(); + up = ndev->flags & IFF_UP; + if (up) + dev_close(ndev); + + fep->avb = NULL; + fep->avb_data = NULL; + fep->avb_enabled = 0; + + if (up) + /* In case of error, device is closed but avb interface is unregistered */ + dev_open(ndev, NULL); + + rtnl_unlock(); + + dev_put(ndev); + + return 0; + +err_avb: +err_ndev: + dev_put(ndev); + +err_dev_get: + return -1; +} +EXPORT_SYMBOL(fec_enet_avb_unregister); + +int fec_enet_get_tx_queue_properties(int ifindex, struct tx_queue_properties *prop) +{ + struct net_device *ndev; + struct fec_enet_private *fep; + + ndev = dev_get_by_index(&init_net, ifindex); + if (!ndev) + goto err_dev_get; + + if (!__is_fec_net_device(ndev)) + goto err_ndev; + + fep = netdev_priv(ndev); + + if (fep->num_tx_queues >= TX_QUEUE_PROP_MAX) + goto err_queues; + + if (fep->quirks & FEC_QUIRK_HAS_AVB) { + prop->num_queues = fep->num_tx_queues; + prop->queue[0].priority = 0; + prop->queue[0].flags = TX_QUEUE_FLAGS_STRICT_PRIORITY; + prop->queue[1].priority = 2; + prop->queue[1].flags = TX_QUEUE_FLAGS_CREDIT_SHAPER; + prop->queue[2].priority = 1; + prop->queue[2].flags = TX_QUEUE_FLAGS_CREDIT_SHAPER; + } else { + /* + * For now, there is no MAC non-AVB capable + * with more than 1 queue. + */ + if (fep->num_tx_queues == 1) { + prop->num_queues = fep->num_tx_queues; + prop->queue[0].priority = 0; + prop->queue[0].flags = TX_QUEUE_FLAGS_STRICT_PRIORITY; + } else { + netdev_err(ndev, "%s invalid/unknown TX queues configuration\n", __func__); + goto err_queues; + } + } + + dev_put(ndev); + + return 0; + +err_queues: +err_ndev: + dev_put(ndev); + +err_dev_get: + return -1; +} +EXPORT_SYMBOL(fec_enet_get_tx_queue_properties); +#endif + /* * XXX: We need to clean up on failure exits here. * @@ -4111,10 +5361,14 @@ static int fec_enet_init(struct net_device *ndev) txq->bd.dsize_log2 = dsize_log2; txq->bd.reg_desc_active = fep->hwp + offset_des_active_txq[i]; bd_dma += size; +#ifdef CONFIG_AVB_SUPPORT + txq->tx_idle_slope = IDLE_SLOPE(i); +#endif cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); txq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); } + fep->netdev = ndev; /* The FEC Ethernet specific entries in the device structure */ ndev->watchdog_timeo = TX_TIMEOUT; @@ -4132,8 +5386,13 @@ static int fec_enet_init(struct net_device *ndev) netif_set_tso_max_segs(ndev, FEC_MAX_TSO_SEGS); /* enable hw accelerator */ +#ifdef CONFIG_AVB_SUPPORT + /* AVB support not compatible with SG or TSO */ + ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM); +#else ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO); +#endif fep->csum_flags |= FLAG_RX_CSUM_ENABLED; } @@ -4144,9 +5403,11 @@ static int fec_enet_init(struct net_device *ndev) ndev->hw_features = ndev->features; +#ifndef CONFIG_AVB_SUPPORT if (!(fep->quirks & FEC_QUIRK_SWAP_FRAME)) ndev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT; +#endif fec_restart(ndev); @@ -4469,6 +5730,13 @@ fec_probe(struct platform_device *pdev) } fep->bufdesc_ex = fep->quirks & FEC_QUIRK_HAS_BUFDESC_EX; +#ifdef CONFIG_AVB_SUPPORT + if (!fep->bufdesc_ex) { + dev_err(&pdev->dev, + "Error: AVB Support requires extended buffer descriptor\n"); + goto failed_clk; + } +#endif fep->clk_ptp = devm_clk_get(&pdev->dev, "ptp"); if (IS_ERR(fep->clk_ptp)) { fep->clk_ptp = NULL; @@ -4518,12 +5786,32 @@ fec_probe(struct platform_device *pdev) fep->reg_phy = NULL; } + if (of_property_read_u32(np, "fsl,rx-phy-delay-100-ns", &fep->rx_delay_100)) + fep->rx_delay_100 = 0; + + if (of_property_read_u32(np, "fsl,tx-phy-delay-100-ns", &fep->tx_delay_100)) + fep->tx_delay_100 = 0; + + if (of_property_read_u32(np, "fsl,rx-phy-delay-1000-ns", &fep->rx_delay_1000)) + fep->rx_delay_1000 = 0; + + if (of_property_read_u32(np, "fsl,tx-phy-delay-1000-ns", &fep->tx_delay_1000)) + fep->tx_delay_1000 = 0; + pm_runtime_set_autosuspend_delay(&pdev->dev, FEC_MDIO_PM_TIMEOUT); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_get_noresume(&pdev->dev); pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); +#ifdef CONFIG_AVB_SUPPORT + /* + * Prevent runtime pm with avb module to keep all clocks + * running even on link down. + */ + pm_runtime_forbid(&pdev->dev); +#endif + ret = fec_reset_phy(pdev); if (ret) goto failed_reset; @@ -4566,7 +5854,9 @@ fec_probe(struct platform_device *pdev) /* Carrier starts down, phylib will bring it up */ netif_carrier_off(ndev); +#ifndef CONFIG_AVB_SUPPORT fec_enet_clk_enable(ndev, false); +#endif pinctrl_pm_select_sleep_state(&pdev->dev); ndev->max_mtu = PKT_MAXBUF_SIZE - ETH_HLEN - ETH_FCS_LEN; @@ -4679,6 +5969,10 @@ static int __maybe_unused fec_suspend(struct device *dev) netif_tx_lock_bh(ndev); netif_device_detach(ndev); netif_tx_unlock_bh(ndev); +#ifdef CONFIG_AVB_SUPPORT + if (fep->avb_enabled) + fep->avb->close(fep->avb_data); +#endif fec_stop(ndev); if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { fec_irqs_disable(ndev); @@ -4777,6 +6071,10 @@ static int __maybe_unused fec_resume(struct device *dev) napi_enable(&fep->napi); phy_init_hw(ndev->phydev); phy_start(ndev->phydev); +#ifdef CONFIG_AVB_SUPPORT + if (fep->avb_enabled) + fep->avb->open(fep->avb_data, fep, fec_max_rate(fep)); +#endif } else if (fep->mii_bus_share && !ndev->phydev) { pinctrl_pm_select_default_state(&fep->pdev->dev); if (fep->phy_reset_in_suspend) diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c index 92fc03f29ca1..88215a644d82 100644 --- a/drivers/net/ethernet/freescale/fec_ptp.c +++ b/drivers/net/ethernet/freescale/fec_ptp.c @@ -104,16 +104,22 @@ static int fec_ptp_enable_pps(struct fec_enet_private *fep, uint enable) struct timespec64 ts; u64 ns; - spin_lock_irqsave(&fep->tmreg_lock, flags); + raw_spin_lock_irqsave(&fep->tmreg_lock, flags); if (fep->pps_enable == enable) { - spin_unlock_irqrestore(&fep->tmreg_lock, flags); + raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); return 0; } fep->pps_channel = DEFAULT_PPS_CHANNEL; fep->reload_period = PPS_OUPUT_RELOAD_PERIOD; +#ifdef CONFIG_AVB_SUPPORT + if (fep->rec_enable && (fep->pps_channel == fep->rec_channel)) { + raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); + return -EBUSY; + } +#endif if (enable) { /* clear capture or output compare interrupt status if have. */ @@ -197,7 +203,7 @@ static int fec_ptp_enable_pps(struct fec_enet_private *fep, uint enable) } fep->pps_enable = enable; - spin_unlock_irqrestore(&fep->tmreg_lock, flags); + raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); return 0; } @@ -208,7 +214,7 @@ static int fec_ptp_pps_perout(struct fec_enet_private *fep) u64 curr_time; unsigned long flags; - spin_lock_irqsave(&fep->tmreg_lock, flags); + raw_spin_lock_irqsave(&fep->tmreg_lock, flags); /* Update time counter */ timecounter_read(&fep->tc); @@ -231,7 +237,7 @@ static int fec_ptp_pps_perout(struct fec_enet_private *fep) */ if (fep->perout_stime < curr_time + 100 * NSEC_PER_MSEC) { dev_err(&fep->pdev->dev, "Current time is too close to the start time!\n"); - spin_unlock_irqrestore(&fep->tmreg_lock, flags); + raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); return -1; } @@ -259,7 +265,7 @@ static int fec_ptp_pps_perout(struct fec_enet_private *fep) */ writel(fep->next_counter, fep->hwp + FEC_TCCR(fep->pps_channel)); fep->next_counter = (fep->next_counter + fep->reload_period) & fep->cc.mask; - spin_unlock_irqrestore(&fep->tmreg_lock, flags); + raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); return 0; } @@ -274,6 +280,26 @@ static enum hrtimer_restart fec_ptp_pps_perout_handler(struct hrtimer *timer) return HRTIMER_NORESTART; } +#ifdef CONFIG_AVB_SUPPORT +/** + * fec_timecounter_set + * @start_tstamp: new time in ns + * + * update the FEC timecounter structure to a new time, and make sure + * the HW counter matches that value. + */ +static inline void fec_timecounter_set(struct fec_enet_private *fep, + u64 start_tstamp) +{ + u32 tempval; + + tempval = start_tstamp & fep->cc.mask; + writel(tempval, fep->hwp + FEC_ATIME); + fep->tc.cycle_last = tempval; + fep->tc.nsec = start_tstamp; +} +#endif + /** * fec_ptp_read - read raw cycle counter (to be used by time counter) * @cc: the cyclecounter structure @@ -288,14 +314,14 @@ static u64 fec_ptp_read(const struct cyclecounter *cc) container_of(cc, struct fec_enet_private, cc); u32 tempval; - tempval = readl(fep->hwp + FEC_ATIME_CTRL); + tempval = readl_relaxed(fep->hwp + FEC_ATIME_CTRL); tempval |= FEC_T_CTRL_CAPTURE; writel(tempval, fep->hwp + FEC_ATIME_CTRL); if (fep->quirks & FEC_QUIRK_BUG_CAPTURE) udelay(1); - return readl(fep->hwp + FEC_ATIME); + return readl_relaxed(fep->hwp + FEC_ATIME); } /** @@ -315,29 +341,315 @@ void fec_ptp_start_cyclecounter(struct net_device *ndev) inc = 1000000000 / fep->cycle_speed; /* grab the ptp lock */ - spin_lock_irqsave(&fep->tmreg_lock, flags); + raw_spin_lock_irqsave(&fep->tmreg_lock, flags); - /* 1ns counter */ - writel(inc << FEC_T_INC_OFFSET, fep->hwp + FEC_ATIME_INC); + /* 1ns counter, disable correction period */ + writel_relaxed(0, fep->hwp + FEC_ATIME_CORR); + writel_relaxed(inc << FEC_T_INC_OFFSET, fep->hwp + FEC_ATIME_INC); - /* use 31-bit timer counter */ - writel(FEC_COUNTER_PERIOD, fep->hwp + FEC_ATIME_EVT_PERIOD); - writel(FEC_T_CTRL_ENABLE | FEC_T_CTRL_PERIOD_RST, +#ifdef CONFIG_AVB_SUPPORT + /* use 32-bits timer counter */ + writel_relaxed(0, fep->hwp + FEC_ATIME_EVT_PERIOD); + writel_relaxed(FEC_T_CTRL_ENABLE, fep->hwp + FEC_ATIME_CTRL); +#else + /* use 31-bit timer counter */ + writel_relaxed(FEC_COUNTER_PERIOD, fep->hwp + FEC_ATIME_EVT_PERIOD); + writel_relaxed(FEC_T_CTRL_ENABLE | FEC_T_CTRL_PERIOD_RST, fep->hwp + FEC_ATIME_CTRL); +#endif memset(&fep->cc, 0, sizeof(fep->cc)); fep->cc.read = fec_ptp_read; +#ifdef CONFIG_AVB_SUPPORT + fep->cc.mask = CLOCKSOURCE_MASK(32); +#else fep->cc.mask = CLOCKSOURCE_MASK(31); +#endif fep->cc.shift = 31; fep->cc.mult = FEC_CC_MULT; /* reset the ns time counter */ +#ifdef CONFIG_AVB_SUPPORT + fep->tc.cc = &fep->cc; + fec_timecounter_set(fep, 0); +#else timecounter_init(&fep->tc, &fep->cc, 0); +#endif + + raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); +} + +#ifdef CONFIG_AVB_SUPPORT +/** + * fec_ptp_adjfreq - adjust ptp cycle frequency + * @ptp: the ptp clock structure + * @scaled_ppm: scaled parts per million adjustment from base + * + * Adjust the frequency of the ptp cycle counter by the + * indicated amount from the base frequency. + * + * Scaled parts per million is ppm with a 16-bit binary fractional field. + * + * This version adjusts the HW counter. The HW implementation results in the following equation: + * fe * diff = ppb * (pc + 1) + * with: + * . fe : ENET ref clock frequency in Hz + * . diff = inc_corr - inc : difference between default increment and correction increment + * . ppb : parts per billion adjustment from base + * . pc : correction period (in number of fe clock cycles) + * + * Limitations: + * . only add or remove 1ns per correction period. This will limit jitter and improve short term + * accuracy (in particular for trigger events during clock recovery) but increase the max possible + * error, since the correction period will be the shortest possible and thus may not be optimal. + * In the case of an adjustment of about 100ppm (which should be the max if the ref clock + * is within the 802.1AS spec), the max error will be about 0.2ppm with a 50MHz ref clock, and + * 0.08ppm with a 125MHz ref clock. + * Long term accuracy will also be lower (20ns per 100ms @50MHz, 8ns per 100ms @125MHz), but this + * can be fixed by phase adjustments. + * . It seems not all period/correction values are valid. With a 1ns correction, all even + * period values return wrong timings (half the requested correction), but on the other hand odd + * values are not taken into account systematically by the hardware, so we choose the closest even + * value that matches the following equation: + * fe * diff = 2 * ppb * (pc + 1) + * . given we force abs(diff) = 1, limit max adjustment to prevent pc < 1. + * + */ +static int fec_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + struct fec_enet_private *fep = + container_of(ptp, struct fec_enet_private, ptp_caps); + s32 ppb = scaled_ppm_to_ppb(scaled_ppm); + unsigned long flags; + u32 inc, cor, pc; + + inc = fep->ptp_inc; + if (ppb == 0) { + cor = 0; + pc = 0; + } else { + if (ppb < 0) { + ppb = -ppb; + cor = inc - 1; + } + else + cor = inc + 1; + + if (ppb > fep->ptp_caps.max_adj) { + dev_err(&fep->pdev->dev, "ppb value %d outside accepted range (max_adj = %d)", (cor > inc) ? ppb : -ppb, ptp->max_adj); + return -1; + } + + pc = (((fep->cycle_speed / (2*ppb)) - 1) + 1) & ~ 0x1; // + 1) & ~ 0x1 returns the closest even value + } - spin_unlock_irqrestore(&fep->tmreg_lock, flags); + raw_spin_lock_irqsave(&fep->tmreg_lock, flags); + + writel_relaxed((cor << FEC_T_INC_CORR_OFFSET) | (inc << FEC_T_INC_OFFSET), fep->hwp + FEC_ATIME_INC); + writel_relaxed(pc, fep->hwp + FEC_ATIME_CORR); + + raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); + + return 0; } +/** + * fec_ptp_adjtime + * @ptp: the ptp clock structure + * @delta: offset to adjust the cycle counter by + * + * adjust the timer by updating the HW counter AND the timecounter structure + * as well. Since updating the HW register requires a read and a write, make + * a crude estimate of the read time and subtract it from the desired delta. + */ +static int fec_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct fec_enet_private *fep = + container_of(ptp, struct fec_enet_private, ptp_caps); + unsigned long flags; + u64 now, then; + s64 real_delta; + + raw_spin_lock_irqsave(&fep->tmreg_lock, flags); + + now = timecounter_read(&fep->tc); + then = timecounter_read(&fep->tc); + + real_delta = delta + (then - now); + + now = timecounter_read(&fep->tc); + now += real_delta; + + fec_timecounter_set(fep, now); + + raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); + + return 0; +} + +/** + * fec_ptp_settime + * @ptp: the ptp clock structure + * @ts: the timespec containing the new time for the cycle counter + * + * Update the timecounter to use a new base value instead of the kernel + * wall timer value, and update the HW counter as well. + */ +static int fec_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + struct fec_enet_private *fep = + container_of(ptp, struct fec_enet_private, ptp_caps); + u64 ns; + unsigned long flags; + + ns = ts->tv_sec * 1000000000ULL; + ns += ts->tv_nsec; + + raw_spin_lock_irqsave(&fep->tmreg_lock, flags); + fec_timecounter_set(fep, ns); + raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); + return 0; +} + +/** + * fec_ptp_read + * @data: fec private context ptr + * @cnt: data pointer for counter value + * + * Returns status + */ +int fec_ptp_read_cnt(void *data, u32 *cnt) +{ + struct fec_enet_private *fep = data; + unsigned long flags; + + /* Note : it might be possible to avoid taking the lock */ + raw_spin_lock_irqsave(&fep->tmreg_lock, flags); + + *cnt = (u32) fec_ptp_read(&fep->cc); + + raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); + + return 0; +} +EXPORT_SYMBOL(fec_ptp_read_cnt); + +/** + * fec_ptp_tc_start + * @data: fec private context ptr + * @id: TC register ID + * @ts_0: First timestamp + * @ts_1: Second timestamp + * @tcsr_val: TCSR register value + * + * Returns 0 on success, -1 if PTP counter is not + * enabled. + */ +int fec_ptp_tc_start(void *data, u8 id, u32 ts_0, u32 ts_1, u32 tcsr_val) +{ + struct fec_enet_private *fep = data; + unsigned long flags; + u32 ctrl_val; + int rc = 0; + + if (id > MAX_TIMER_CHANNEL) { + rc = -1; + goto exit; + } + + raw_spin_lock_irqsave(&fep->tmreg_lock, flags); + + /* Simple resource sharing handling with pps */ + if (fep->pps_enable && (fep->pps_channel == id)) { + rc = -1; + goto exit_unlock; + } + + ctrl_val = readl_relaxed(fep->hwp + FEC_ATIME_CTRL); + if (!(ctrl_val & FEC_T_CTRL_ENABLE)) { + rc = -1; + goto exit_unlock; + } + + writel_relaxed(ts_0, fep->hwp + FEC_TCCR(id)); + writel_relaxed(tcsr_val, fep->hwp + FEC_TCSR(id)); + writel_relaxed(ts_1, fep->hwp + FEC_TCCR(id)); + + fep->rec_enable = 1; + fep->rec_channel = id; + +exit_unlock: + raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); +exit: + return rc; +} +EXPORT_SYMBOL(fec_ptp_tc_start); + +/** + * fec_ptp_tc_stop + * @data: fec private context ptr + * @id: TC register ID + * + * Returns none + */ +void fec_ptp_tc_stop(void *data, u8 id) +{ + struct fec_enet_private *fep = data; + unsigned long flags; + + raw_spin_lock_irqsave(&fep->tmreg_lock, flags); + + writel_relaxed(0, fep->hwp + FEC_TCCR(id)); + writel_relaxed(FEC_T_TF_MASK, fep->hwp + FEC_TCSR(id)); + + fep->rec_enable = 0; + + raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); +} +EXPORT_SYMBOL(fec_ptp_tc_stop); + +/** + * fec_ptp_tc_reload + * @data: fec private context ptr + * @id: TC register ID + * @ts: New timestamp to load + * + * Returns 0 if success, -1 if compare has not occured + * or if PTP counter is not enabled. + */ +int fec_ptp_tc_reload(void *data, u8 id, u32 ts) +{ + struct fec_enet_private *fep = data; + unsigned long flags; + u32 tcsr_val; + u32 ctrl_val; + int rc = 0; + + raw_spin_lock_irqsave(&fep->tmreg_lock, flags); + + ctrl_val = readl_relaxed(fep->hwp + FEC_ATIME_CTRL); + if (!(ctrl_val & FEC_T_CTRL_ENABLE)) { + rc = -1; + goto exit; + } + + tcsr_val = readl_relaxed(fep->hwp + FEC_TCSR(id)); + if (tcsr_val & FEC_T_TF_MASK) { + writel_relaxed(ts, fep->hwp + FEC_TCCR(id)); + writel_relaxed(tcsr_val, fep->hwp + FEC_TCSR(id)); + } + else + rc = -1; + +exit: + raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); + return rc; +} +EXPORT_SYMBOL(fec_ptp_tc_reload); +#else /* CONFIG_AVB_SUPPORT */ + /** * fec_ptp_adjfine - adjust ptp cycle frequency * @ptp: the ptp clock structure @@ -399,7 +711,7 @@ static int fec_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) else corr_ns = fep->ptp_inc + corr_inc; - spin_lock_irqsave(&fep->tmreg_lock, flags); + raw_spin_lock_irqsave(&fep->tmreg_lock, flags); tmp = readl(fep->hwp + FEC_ATIME_INC) & FEC_T_INC_MASK; tmp |= corr_ns << FEC_T_INC_CORR_OFFSET; @@ -409,7 +721,7 @@ static int fec_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) /* dummy read to update the timer. */ timecounter_read(&fep->tc); - spin_unlock_irqrestore(&fep->tmreg_lock, flags); + raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); return 0; } @@ -427,27 +739,30 @@ static int fec_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) container_of(ptp, struct fec_enet_private, ptp_caps); unsigned long flags; - spin_lock_irqsave(&fep->tmreg_lock, flags); + raw_spin_lock_irqsave(&fep->tmreg_lock, flags); timecounter_adjtime(&fep->tc, delta); - spin_unlock_irqrestore(&fep->tmreg_lock, flags); + raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); return 0; } /** - * fec_ptp_gettime + * fec_ptp_settime * @ptp: the ptp clock structure - * @ts: timespec structure to hold the current time value + * @ts: the timespec containing the new time for the cycle counter * - * read the timecounter and return the correct value on ns, - * after converting it into a struct timespec. + * reset the timecounter to use a new base value instead of the kernel + * wall timer value. */ -static int fec_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) +static int fec_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) { struct fec_enet_private *fep = container_of(ptp, struct fec_enet_private, ptp_caps); + u64 ns; unsigned long flags; + u32 counter; mutex_lock(&fep->ptp_clk_mutex); /* Check the ptp clock */ @@ -455,33 +770,37 @@ static int fec_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) mutex_unlock(&fep->ptp_clk_mutex); return -EINVAL; } - spin_lock_irqsave(&fep->tmreg_lock, flags); - ns = timecounter_read(&fep->tc); - spin_unlock_irqrestore(&fep->tmreg_lock, flags); - mutex_unlock(&fep->ptp_clk_mutex); - *ts = ns_to_timespec64(ns); + ns = timespec64_to_ns(ts); + /* Get the timer value based on timestamp. + * Update the counter with the masked value. + */ + counter = ns & fep->cc.mask; + raw_spin_lock_irqsave(&fep->tmreg_lock, flags); + writel(counter, fep->hwp + FEC_ATIME); + timecounter_init(&fep->tc, &fep->cc, ns); + raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); + mutex_unlock(&fep->ptp_clk_mutex); return 0; } +#endif /* CONFIG_AVB_SUPPORT */ /** - * fec_ptp_settime + * fec_ptp_gettime * @ptp: the ptp clock structure - * @ts: the timespec containing the new time for the cycle counter + * @ts: timespec structure to hold the current time value * - * reset the timecounter to use a new base value instead of the kernel - * wall timer value. + * read the timecounter and return the correct value on ns, + * after converting it into a struct timespec. */ -static int fec_ptp_settime(struct ptp_clock_info *ptp, - const struct timespec64 *ts) +static int fec_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) { struct fec_enet_private *fep = container_of(ptp, struct fec_enet_private, ptp_caps); - u64 ns; + u32 remainder; unsigned long flags; - u32 counter; mutex_lock(&fep->ptp_clk_mutex); /* Check the ptp clock */ @@ -489,18 +808,14 @@ static int fec_ptp_settime(struct ptp_clock_info *ptp, mutex_unlock(&fep->ptp_clk_mutex); return -EINVAL; } + raw_spin_lock_irqsave(&fep->tmreg_lock, flags); + ns = timecounter_read(&fep->tc); + raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); + mutex_unlock(&fep->ptp_clk_mutex); - ns = timespec64_to_ns(ts); - /* Get the timer value based on timestamp. - * Update the counter with the masked value. - */ - counter = ns & fep->cc.mask; + ts->tv_sec = div_u64_rem(ns, 1000000000ULL, &remainder); + ts->tv_nsec = remainder; - spin_lock_irqsave(&fep->tmreg_lock, flags); - writel(counter, fep->hwp + FEC_ATIME); - timecounter_init(&fep->tc, &fep->cc, ns); - spin_unlock_irqrestore(&fep->tmreg_lock, flags); - mutex_unlock(&fep->ptp_clk_mutex); return 0; } @@ -508,9 +823,9 @@ static int fec_ptp_pps_disable(struct fec_enet_private *fep, uint channel) { unsigned long flags; - spin_lock_irqsave(&fep->tmreg_lock, flags); + raw_spin_lock_irqsave(&fep->tmreg_lock, flags); writel(0, fep->hwp + FEC_TCSR(channel)); - spin_unlock_irqrestore(&fep->tmreg_lock, flags); + raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); return 0; } @@ -574,10 +889,10 @@ static int fec_ptp_enable(struct ptp_clock_info *ptp, mutex_unlock(&fep->ptp_clk_mutex); return -EOPNOTSUPP; } - spin_lock_irqsave(&fep->tmreg_lock, flags); + raw_spin_lock_irqsave(&fep->tmreg_lock, flags); /* Read current timestamp */ curr_time = timecounter_read(&fep->tc); - spin_unlock_irqrestore(&fep->tmreg_lock, flags); + raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); mutex_unlock(&fep->ptp_clk_mutex); /* Calculate time difference */ @@ -662,9 +977,9 @@ static void fec_time_keep(struct work_struct *work) mutex_lock(&fep->ptp_clk_mutex); if (fep->ptp_clk_on) { - spin_lock_irqsave(&fep->tmreg_lock, flags); + raw_spin_lock_irqsave(&fep->tmreg_lock, flags); timecounter_read(&fep->tc); - spin_unlock_irqrestore(&fep->tmreg_lock, flags); + raw_spin_unlock_irqrestore(&fep->tmreg_lock, flags); } mutex_unlock(&fep->ptp_clk_mutex); @@ -680,6 +995,9 @@ static irqreturn_t fec_pps_interrupt(int irq, void *dev_id) u8 channel = fep->pps_channel; struct ptp_clock_event event; + if (fep->pps_enable) + goto exit; + val = readl(fep->hwp + FEC_TCSR(channel)); if (val & FEC_T_TF_MASK) { /* Write the next next compare(not the next according the spec) @@ -699,6 +1017,7 @@ static irqreturn_t fec_pps_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +exit: return IRQ_NONE; } @@ -722,7 +1041,18 @@ void fec_ptp_init(struct platform_device *pdev, int irq_idx) fep->ptp_caps.owner = THIS_MODULE; strscpy(fep->ptp_caps.name, "fec ptp", sizeof(fep->ptp_caps.name)); + fep->cycle_speed = clk_get_rate(fep->clk_ptp); + if (!fep->cycle_speed) { + fep->cycle_speed = NSEC_PER_SEC; + dev_err(&fep->pdev->dev, "clk_ptp clock rate is zero\n"); + } + fep->ptp_inc = NSEC_PER_SEC / fep->cycle_speed; + +#ifdef CONFIG_AVB_SUPPORT + fep->ptp_caps.max_adj = fep->cycle_speed / 2; +#else fep->ptp_caps.max_adj = 250000000; +#endif fep->ptp_caps.n_alarm = 0; fep->ptp_caps.n_ext_ts = 0; fep->ptp_caps.n_per_out = 1; @@ -734,14 +1064,7 @@ void fec_ptp_init(struct platform_device *pdev, int irq_idx) fep->ptp_caps.settime64 = fec_ptp_settime; fep->ptp_caps.enable = fec_ptp_enable; - fep->cycle_speed = clk_get_rate(fep->clk_ptp); - if (!fep->cycle_speed) { - fep->cycle_speed = NSEC_PER_SEC; - dev_err(&fep->pdev->dev, "clk_ptp clock rate is zero\n"); - } - fep->ptp_inc = NSEC_PER_SEC / fep->cycle_speed; - - spin_lock_init(&fep->tmreg_lock); + raw_spin_lock_init(&fep->tmreg_lock); fec_ptp_start_cyclecounter(ndev); diff --git a/drivers/net/ethernet/freescale/fec_uio.c b/drivers/net/ethernet/freescale/fec_uio.c index 17049d99c3e7..3503c3142dec 100644 --- a/drivers/net/ethernet/freescale/fec_uio.c +++ b/drivers/net/ethernet/freescale/fec_uio.c @@ -1249,8 +1249,8 @@ static int fec_enet_uio_init(struct net_device *ndev) return ret; } - tx_ring_size = RING_SIZE_TX; - rx_ring_size = RING_SIZE_RX; + tx_ring_size = FEC_TX_RING_SIZE; + rx_ring_size = FEC_RX_RING_SIZE; for (i = 0; i < FEC_MAX_Q; i++) { total_tx_ring_size += tx_ring_size; diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c index 7a15b9245698..4d2fba3b2dd2 100644 --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c @@ -11,6 +11,7 @@ * Modifier: Sandeep Gopalpet * * Copyright 2003-2006, 2008-2009, 2011 Freescale Semiconductor, Inc. + * Copyright 2017-2023 NXP */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -168,6 +169,32 @@ static void gfar_gdrvinfo(struct net_device *dev, strscpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); } +static int gfar_get_eee(struct net_device *dev, struct ethtool_eee *et_eee) +{ + struct phy_device *phydev = dev->phydev; + + if (!phydev) + return -ENODEV; + + return phy_ethtool_get_eee(phydev, et_eee); +} + +static int gfar_set_eee(struct net_device *dev, struct ethtool_eee *et_eee) +{ + struct phy_device *phydev = dev->phydev; + + if (!phydev) + return -ENODEV; + + if (et_eee->eee_enabled || + et_eee->tx_lpi_enabled || + et_eee->tx_lpi_timer) { + return -EOPNOTSUPP; + } + + return phy_ethtool_set_eee(phydev, et_eee); +} + /* Return the length of the register structure */ static int gfar_reglen(struct net_device *dev) { @@ -1491,6 +1518,8 @@ static int gfar_get_ts_info(struct net_device *dev, const struct ethtool_ops gfar_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_MAX_FRAMES, + .get_eee = gfar_get_eee, + .set_eee = gfar_set_eee, .get_drvinfo = gfar_gdrvinfo, .get_regs_len = gfar_reglen, .get_regs = gfar_get_regs, diff --git a/drivers/net/ethernet/freescale/sdk_dpaa/Kconfig b/drivers/net/ethernet/freescale/sdk_dpaa/Kconfig index bb6693e2d702..9e219bbfd978 100644 --- a/drivers/net/ethernet/freescale/sdk_dpaa/Kconfig +++ b/drivers/net/ethernet/freescale/sdk_dpaa/Kconfig @@ -84,7 +84,7 @@ config FSL_DPAA_ETH_JUMBO_FRAME config FSL_DPAA_TS bool "Linux compliant timestamping" depends on FSL_SDK_DPAA_ETH - default n + default y help Enable Linux API compliant timestamping support. @@ -96,6 +96,15 @@ config FSL_DPAA_1588 help Enable IEEE1588 support code. +config FSL_DPAA_ETHERCAT + bool "Enable DPAA Ethercat support" + depends on FSL_SDK_DPAA_ETH + default n + help + Enable DPAA Ethercat support code, if enabling this feature, will create a new + QMan Portal for Ethercat port, and isolate the last cpu core for Ethercat traffic. + Regular ethernet traffic will NOT be in the isolated core. + config FSL_DPAA_ETH_MAX_BUF_COUNT int "Maximum number of buffers in the private bpool" depends on FSL_SDK_DPAA_ETH diff --git a/drivers/net/ethernet/freescale/sdk_dpaa/Makefile b/drivers/net/ethernet/freescale/sdk_dpaa/Makefile index a0623b24fa1c..9f9980099aad 100644 --- a/drivers/net/ethernet/freescale/sdk_dpaa/Makefile +++ b/drivers/net/ethernet/freescale/sdk_dpaa/Makefile @@ -11,6 +11,9 @@ ccflags-y += -I$(NET_DPA) obj-$(CONFIG_FSL_SDK_DPAA_ETH) += fsl_mac.o fsl_dpa.o fsl_dpa-objs += dpaa_ethtool.o dpaa_eth_sysfs.o dpaa_eth.o dpaa_eth_sg.o dpaa_eth_common.o +ifeq ($(CONFIG_FSL_DPAA_ETHERCAT),y) +fsl_dpa-objs += dpaa_ethercat.o +endif ifeq ($(CONFIG_FSL_DPAA_DBG_LOOP),y) fsl_dpa-objs += dpaa_debugfs.o endif diff --git a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.h b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.h index a8f59cd1b4f6..857705fb85f3 100644 --- a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.h +++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.h @@ -1,5 +1,5 @@ /* Copyright 2008-2012 Freescale Semiconductor Inc. - * Copyright 2019 NXP + * Copyright 2019-2023 NXP * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -415,6 +415,12 @@ struct dpa_priv_s { #ifdef CONFIG_FSL_DPAA_CEETM bool ceetm_en; /* CEETM QoS enabled */ #endif + +#ifdef CONFIG_FSL_DPAA_ETHERCAT + uint16_t ethercat_channel; /* "fsl,qman-channel-id" */ + struct qman_portal *p; + void *ecdev; +#endif }; struct fm_port_fqs { @@ -461,6 +467,11 @@ int __hot skb_to_sg_fd(struct dpa_priv_s *priv, int __cold __attribute__((nonnull)) _dpa_fq_free(struct device *dev, struct qman_fq *fq); +#ifdef CONFIG_FSL_DPAA_ETHERCAT +int dpa_unregister_ethercat(struct net_device *net_dev); +int ec_dpaa_receive_data(void *pecdev, const void *data, size_t size); +#endif + /* Turn on HW checksum computation for this outgoing frame. * If the current protocol is not something we support in this regard * (or if the stack has already computed the SW checksum), we do nothing. diff --git a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_common.c b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_common.c index b31bd1b0461b..2967d09905e6 100644 --- a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_common.c +++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_common.c @@ -1,4 +1,5 @@ /* Copyright 2008-2013 Freescale Semiconductor, Inc. + * Copyright 2019-2023 NXP * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -471,7 +472,15 @@ int __cold dpa_remove(struct platform_device *of_dev) dpaa_eth_sysfs_remove(dev); dev_set_drvdata(dev, NULL); + +#ifdef CONFIG_FSL_DPAA_ETHERCAT + if (priv->ecdev) + dpa_unregister_ethercat(net_dev); + else + unregister_netdev(net_dev); +#else unregister_netdev(net_dev); +#endif err = dpa_fq_free(dev, &priv->dpa_fq_list); @@ -985,6 +994,49 @@ void dpa_release_channel(void) } EXPORT_SYMBOL(dpa_release_channel); +#ifdef CONFIG_FSL_DPAA_ETHERCAT +static int ec_cpu_isolated[NR_CPUS]; +static int __init ethercat_cpus_setup(char *str) +{ + int last_idx = -1; + int dash_flag = 0; + int idx = 0; + int i = 0; + int j = 0; + + if (!str) + return 0; + + for (i = 0; i < strlen(str); i++) { + if (str[i] == ',') { + last_idx = -1; + dash_flag = 0; + continue; + } else if ((str[i] == '-') && (last_idx >= 0)) { + dash_flag = 1; + continue; + } + + if ((str[i] >= '0') && (str[i] <= '9')) { + idx = str[i] - '0'; + ec_cpu_isolated[idx] = 1; + + if (dash_flag) { + for (j = last_idx; j < idx; j++) + ec_cpu_isolated[j] = 1; + last_idx = -1; + dash_flag = 0; + } else { + last_idx = idx; + } + } + } + + return 1; +} +__setup("ethercat_cpus=", ethercat_cpus_setup); +#endif + void dpaa_eth_add_channel(u16 channel) { const cpumask_t *cpus = qman_affine_cpus(); @@ -993,6 +1045,10 @@ void dpaa_eth_add_channel(u16 channel) struct qman_portal *portal; for_each_cpu(cpu, cpus) { +#ifdef CONFIG_FSL_DPAA_ETHERCAT + if (ec_cpu_isolated[cpu]) + continue; +#endif portal = (struct qman_portal *)qman_get_affine_portal(cpu); qman_p_static_dequeue_add(portal, pool); } diff --git a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_sg.c b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_sg.c index 14015cf6a9a6..0834a5d8265f 100644 --- a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_sg.c +++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_sg.c @@ -1,5 +1,5 @@ /* Copyright 2012 Freescale Semiconductor Inc. - * Copyright 2019 NXP + * Copyright 2019-2023 NXP * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -610,10 +610,31 @@ void __hot _dpa_rx(struct net_device *net_dev, * which case we were in) having been removed from the pool. */ (*count_ptr)--; +#ifndef CONFIG_FSL_DPAA_ETHERCAT skb->protocol = eth_type_trans(skb, net_dev); - +#endif skb_len = skb->len; +#ifdef CONFIG_FSL_DPAA_ETHERCAT + if (priv->ecdev) { + u16 rawcpuid = 0; + u16 prot = 0; + + rawcpuid = (u16)raw_smp_processor_id(); + skb_record_rx_queue(skb, rawcpuid); + + prot = ntohs(*(u16 *)(skb->data + 12)); + if (prot == ETH_P_ETHERCAT) + ec_dpaa_receive_data(priv->ecdev, skb->data, skb->len); + else + pr_warn("invalid ethercat protocol 0x%x\n", prot); + + dev_kfree_skb(skb); + goto ethercat_tag; + } + skb->protocol = eth_type_trans(skb, net_dev); +#endif + #ifdef CONFIG_FSL_DPAA_DBG_LOOP if (dpa_skb_loop(priv, skb)) { percpu_stats->rx_packets++; @@ -637,6 +658,9 @@ void __hot _dpa_rx(struct net_device *net_dev, } else if (unlikely(netif_receive_skb(skb) == NET_RX_DROP)) return; +#ifdef CONFIG_FSL_DPAA_ETHERCAT +ethercat_tag: +#endif percpu_stats->rx_packets++; percpu_stats->rx_bytes += skb_len; diff --git a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_ethercat.c b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_ethercat.c new file mode 100644 index 000000000000..74a77460fecf --- /dev/null +++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_ethercat.c @@ -0,0 +1,1216 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2021-2023 NXP + */ + +#include +#include +#include +#include +#include +#include +#include /* arp_hdr_len() */ +#include /* VLAN_HLEN */ +#include /* struct icmphdr */ +#include /* struct iphdr */ +#include /* struct ipv6hdr */ +#include /* struct udphdr */ +#include /* struct tcphdr */ +#include /* net_ratelimit() */ +#include /* ETH_P_IP and ETH_P_IPV6 */ +#include +#include +#include +#include +#ifdef CONFIG_SOC_BUS +#include /* soc_device_match */ +#endif + +#include "fsl_fman.h" +#include "fm_ext.h" +#include "fm_port_ext.h" + +#include "mac.h" +#include "dpaa_eth.h" +#include "dpaa_eth_common.h" +#ifdef CONFIG_FSL_DPAA_DBG_LOOP +#include "dpaa_debugfs.h" +#endif /* CONFIG_FSL_DPAA_DBG_LOOP */ + +#define DPA_NAPI_WEIGHT 64 + +/* Valid checksum indication */ +#define DPA_CSUM_VALID 0xFFFF + +#define DPA_DESCRIPTION "FSL/NXP DPAA Ethercat driver" + +MODULE_LICENSE("Dual BSD/GPL"); + +MODULE_DESCRIPTION(DPA_DESCRIPTION); + +static u8 debug = -1; + +/* This has to work in tandem with the DPA_CS_THRESHOLD_xxx values. */ +static u16 tx_timeout = 1000; + +static const char rtx[][3] = { + [RX] = "RX", + [TX] = "TX" +}; + +/* BM */ + +#define DPAA_ETH_MAX_PAD (L1_CACHE_BYTES * 8) + +static u32 dpa_priv_bpid; +static u32 dpa_priv_cpuid; + +#ifdef CONFIG_FSL_DPAA_DBG_LOOP +struct net_device *dpa_loop_netdevs[20]; +#endif + +#ifdef CONFIG_PM + +static int dpaa_suspend(struct device *dev) +{ + struct net_device *net_dev; + struct dpa_priv_s *priv; + struct mac_device *mac_dev; + int err = 0; + + net_dev = dev_get_drvdata(dev); + + if (net_dev->flags & IFF_UP) { + priv = netdev_priv(net_dev); + mac_dev = priv->mac_dev; + + if (priv->wol & DPAA_WOL_MAGIC) { + err = priv->mac_dev->set_wol(mac_dev->port_dev[RX], + priv->mac_dev->get_mac_handle(mac_dev), true); + if (err) { + netdev_err(net_dev, "set_wol() = %d\n", err); + goto set_wol_failed; + } + } + + err = fm_port_suspend(mac_dev->port_dev[RX]); + if (err) { + netdev_err(net_dev, "fm_port_suspend(RX) = %d\n", err); + goto rx_port_suspend_failed; + } + + err = fm_port_suspend(mac_dev->port_dev[TX]); + if (err) { + netdev_err(net_dev, "fm_port_suspend(TX) = %d\n", err); + goto tx_port_suspend_failed; + } + } + + return 0; + +tx_port_suspend_failed: + fm_port_resume(mac_dev->port_dev[RX]); +rx_port_suspend_failed: + if (priv->wol & DPAA_WOL_MAGIC) { + priv->mac_dev->set_wol(mac_dev->port_dev[RX], + priv->mac_dev->get_mac_handle(mac_dev), false); + } +set_wol_failed: + return err; +} + +static int dpaa_resume(struct device *dev) +{ + struct net_device *net_dev; + struct dpa_priv_s *priv; + struct mac_device *mac_dev; + int err = 0; + + net_dev = dev_get_drvdata(dev); + + if (net_dev->flags & IFF_UP) { + priv = netdev_priv(net_dev); + mac_dev = priv->mac_dev; + + err = fm_mac_resume(mac_dev->get_mac_handle(mac_dev)); + if (err) { + netdev_err(net_dev, "fm_mac_resume = %d\n", err); + goto resume_failed; + } + + err = fm_port_resume(mac_dev->port_dev[TX]); + if (err) { + netdev_err(net_dev, "fm_port_resume(TX) = %d\n", err); + goto resume_failed; + } + + err = fm_port_resume(mac_dev->port_dev[RX]); + if (err) { + netdev_err(net_dev, "fm_port_resume(RX) = %d\n", err); + goto resume_failed; + } + + if (priv->wol & DPAA_WOL_MAGIC) { + err = priv->mac_dev->set_wol(mac_dev->port_dev[RX], + priv->mac_dev->get_mac_handle(mac_dev), false); + if (err) { + netdev_err(net_dev, "set_wol() = %d\n", err); + goto resume_failed; + } + } + } + + return 0; + +resume_failed: + return err; +} + +static const struct dev_pm_ops dpaa_pm_ops = { + .suspend = dpaa_suspend, + .resume = dpaa_resume, +}; + +#define DPAA_PM_OPS (&dpaa_pm_ops) + +#else /* CONFIG_PM */ + +#define DPAA_PM_OPS NULL + +#endif /* CONFIG_PM */ + +/* Checks whether the checksum field in Parse Results array is valid + * (equals 0xFFFF) and increments the .cse counter otherwise + */ +static inline void +dpa_csum_validation(const struct dpa_priv_s *priv, + struct dpa_percpu_priv_s *percpu_priv, + const struct qm_fd *fd) +{ + dma_addr_t addr = qm_fd_addr(fd); + struct dpa_bp *dpa_bp = priv->dpa_bp; + void *frm = phys_to_virt(addr); + fm_prs_result_t *parse_result; + + if (unlikely(!frm)) + return; + + dma_sync_single_for_cpu(dpa_bp->dev, addr, DPA_RX_PRIV_DATA_SIZE + + DPA_PARSE_RESULTS_SIZE, DMA_BIDIRECTIONAL); + + parse_result = (fm_prs_result_t *)(frm + DPA_RX_PRIV_DATA_SIZE); + + if (parse_result->cksum != DPA_CSUM_VALID) + percpu_priv->rx_errors.cse++; +} + +static void _dpa_rx_error(struct net_device *net_dev, + const struct dpa_priv_s *priv, + struct dpa_percpu_priv_s *percpu_priv, + const struct qm_fd *fd, + u32 fqid) +{ + /* limit common, possibly innocuous Rx FIFO Overflow errors' + * interference with zero-loss convergence benchmark results. + */ + if (likely(fd->status & FM_FD_STAT_ERR_PHYSICAL)) + pr_warn_once("fsl-dpa: non-zero error counters in fman statistics (sysfs)\n"); + else + if (netif_msg_hw(priv) && net_ratelimit()) + netdev_dbg(net_dev, "Err FD status = 0x%08x\n", + fd->status & FM_FD_STAT_RX_ERRORS); +#ifdef CONFIG_FSL_DPAA_HOOKS + if (dpaa_eth_hooks.rx_error && + dpaa_eth_hooks.rx_error(net_dev, fd, fqid) == DPAA_ETH_STOLEN) + /* it's up to the hook to perform resource cleanup */ + return; +#endif + percpu_priv->stats.rx_errors++; + + if (fd->status & FM_PORT_FRM_ERR_DMA) + percpu_priv->rx_errors.dme++; + if (fd->status & FM_PORT_FRM_ERR_PHYSICAL) + percpu_priv->rx_errors.fpe++; + if (fd->status & FM_PORT_FRM_ERR_SIZE) + percpu_priv->rx_errors.fse++; + if (fd->status & FM_PORT_FRM_ERR_PRS_HDR_ERR) + percpu_priv->rx_errors.phe++; + if (fd->status & FM_FD_STAT_L4CV) + dpa_csum_validation(priv, percpu_priv, fd); + + dpa_fd_release(net_dev, fd); +} + +static void _dpa_tx_error(struct net_device *net_dev, + const struct dpa_priv_s *priv, + struct dpa_percpu_priv_s *percpu_priv, + const struct qm_fd *fd, + u32 fqid) +{ + struct sk_buff *skb; + + if (netif_msg_hw(priv) && net_ratelimit()) + netdev_warn(net_dev, "FD status = 0x%08x\n", + fd->status & FM_FD_STAT_TX_ERRORS); +#ifdef CONFIG_FSL_DPAA_HOOKS + if (dpaa_eth_hooks.tx_error && + dpaa_eth_hooks.tx_error(net_dev, fd, fqid) == DPAA_ETH_STOLEN) + /* now the hook must ensure proper cleanup */ + return; +#endif + percpu_priv->stats.tx_errors++; + + /* If we intended the buffers from this frame to go into the bpools + * when the FMan transmit was done, we need to put it in manually. + */ + if (fd->bpid != 0xff) { + dpa_fd_release(net_dev, fd); + return; + } + + skb = _dpa_cleanup_tx_fd(priv, fd); + if (!priv->ecdev) + dev_kfree_skb(skb); +} + +static void __hot _dpa_tx_conf(struct net_device *net_dev, + const struct dpa_priv_s *priv, + struct dpa_percpu_priv_s *percpu_priv, + const struct qm_fd *fd, + u32 fqid) +{ + struct sk_buff *skb; + + /* do we need the timestamp for the error frames? */ + + if (unlikely(fd->status & FM_FD_STAT_TX_ERRORS) != 0) { + if (netif_msg_hw(priv) && net_ratelimit()) + netdev_warn(net_dev, "FD status = 0x%08x\n", + fd->status & FM_FD_STAT_TX_ERRORS); + + percpu_priv->stats.tx_errors++; + } + + /* hopefully we need not get the timestamp before the hook */ +#ifdef CONFIG_FSL_DPAA_HOOKS + if (dpaa_eth_hooks.tx_confirm && + dpaa_eth_hooks.tx_confirm(net_dev, fd, fqid) == DPAA_ETH_STOLEN) + /* it's the hook that must now perform cleanup */ + return; +#endif + /* This might not perfectly reflect the reality, if the core dequeuing + * the Tx confirmation is different from the one that did the enqueue, + * but at least it'll show up in the total count. + */ + percpu_priv->tx_confirm++; + + skb = _dpa_cleanup_tx_fd(priv, fd); + if (!priv->ecdev) + dev_kfree_skb(skb); +} + +static enum qman_cb_dqrr_result +priv_rx_error_dqrr(struct qman_portal *portal, + struct qman_fq *fq, + const struct qm_dqrr_entry *dq) +{ + struct net_device *net_dev; + struct dpa_priv_s *priv; + struct dpa_percpu_priv_s *percpu_priv; + int *count_ptr; + + net_dev = ((struct dpa_fq *)fq)->net_dev; + priv = netdev_priv(net_dev); + + percpu_priv = raw_cpu_ptr(priv->percpu_priv); + count_ptr = raw_cpu_ptr(priv->percpu_count); + + if (unlikely(dpaa_eth_refill_bpools(priv->dpa_bp, count_ptr))) + /* Unable to refill the buffer pool due to insufficient + * system memory. Just release the frame back into the pool, + * otherwise we'll soon end up with an empty buffer pool. + */ + dpa_fd_release(net_dev, &dq->fd); + else + _dpa_rx_error(net_dev, priv, percpu_priv, &dq->fd, fq->fqid); + + return qman_cb_dqrr_consume; +} + +static enum qman_cb_dqrr_result __hot +priv_rx_default_dqrr(struct qman_portal *portal, + struct qman_fq *fq, + const struct qm_dqrr_entry *dq) +{ + struct net_device *net_dev; + struct dpa_priv_s *priv; + struct dpa_percpu_priv_s *percpu_priv; + int *count_ptr; + struct dpa_bp *dpa_bp; + + net_dev = ((struct dpa_fq *)fq)->net_dev; + priv = netdev_priv(net_dev); + dpa_bp = priv->dpa_bp; + + /* IRQ handler, non-migratable; safe to use raw_cpu_ptr here */ + percpu_priv = raw_cpu_ptr(priv->percpu_priv); + count_ptr = raw_cpu_ptr(priv->percpu_count); + + /* Vale of plenty: make sure we didn't run out of buffers */ + + if (unlikely(dpaa_eth_refill_bpools(dpa_bp, count_ptr))) + /* Unable to refill the buffer pool due to insufficient + * system memory. Just release the frame back into the pool, + * otherwise we'll soon end up with an empty buffer pool. + */ + dpa_fd_release(net_dev, &dq->fd); + else + _dpa_rx(net_dev, portal, priv, percpu_priv, &dq->fd, fq->fqid, + count_ptr); + + return qman_cb_dqrr_consume; +} + +static enum qman_cb_dqrr_result +priv_tx_conf_error_dqrr(struct qman_portal *portal, + struct qman_fq *fq, + const struct qm_dqrr_entry *dq) +{ + struct net_device *net_dev; + struct dpa_priv_s *priv; + struct dpa_percpu_priv_s *percpu_priv; + + net_dev = ((struct dpa_fq *)fq)->net_dev; + priv = netdev_priv(net_dev); + + percpu_priv = raw_cpu_ptr(priv->percpu_priv); + + _dpa_tx_error(net_dev, priv, percpu_priv, &dq->fd, fq->fqid); + + return qman_cb_dqrr_consume; +} + +static enum qman_cb_dqrr_result __hot +priv_tx_conf_default_dqrr(struct qman_portal *portal, + struct qman_fq *fq, + const struct qm_dqrr_entry *dq) +{ + struct net_device *net_dev; + struct dpa_priv_s *priv; + struct dpa_percpu_priv_s *percpu_priv; + + net_dev = ((struct dpa_fq *)fq)->net_dev; + priv = netdev_priv(net_dev); + + /* Non-migratable context, safe to use raw_cpu_ptr */ + percpu_priv = raw_cpu_ptr(priv->percpu_priv); + + _dpa_tx_conf(net_dev, priv, percpu_priv, &dq->fd, fq->fqid); + + return qman_cb_dqrr_consume; +} + +static void priv_ern(struct qman_portal *portal, + struct qman_fq *fq, + const struct qm_mr_entry *msg) +{ + struct net_device *net_dev; + const struct dpa_priv_s *priv; + struct sk_buff *skb; + struct dpa_percpu_priv_s *percpu_priv; + struct qm_fd fd = msg->ern.fd; + + net_dev = ((struct dpa_fq *)fq)->net_dev; + priv = netdev_priv(net_dev); + /* Non-migratable context, safe to use raw_cpu_ptr */ + percpu_priv = raw_cpu_ptr(priv->percpu_priv); + + percpu_priv->stats.tx_dropped++; + percpu_priv->stats.tx_fifo_errors++; + count_ern(percpu_priv, msg); + + /* If we intended this buffer to go into the pool + * when the FM was done, we need to put it in + * manually. + */ + if (msg->ern.fd.bpid != 0xff) { + dpa_fd_release(net_dev, &fd); + return; + } + + skb = _dpa_cleanup_tx_fd(priv, &fd); + dev_kfree_skb_any(skb); +} + +static const struct dpa_fq_cbs_t private_fq_cbs = { + .rx_defq = { .cb = { .dqrr = priv_rx_default_dqrr } }, + .tx_defq = { .cb = { .dqrr = priv_tx_conf_default_dqrr } }, + .rx_errq = { .cb = { .dqrr = priv_rx_error_dqrr } }, + .tx_errq = { .cb = { .dqrr = priv_tx_conf_error_dqrr } }, + .egress_ern = { .cb = { .ern = priv_ern } } +}; + +static int __cold dpa_eth_priv_start(struct net_device *net_dev) +{ + int err; + struct dpa_priv_s *priv; + + priv = netdev_priv(net_dev); + + err = dpa_start(net_dev); + + return err; +} + +static int __cold dpa_eth_priv_stop(struct net_device *net_dev) +{ + int _errno; + struct dpa_priv_s *priv; + + _errno = dpa_stop(net_dev); + /* Allow NAPI to consume any frame still in the Rx/TxConfirm + * ingress queues. This is to avoid a race between the current + * context and ksoftirqd which could leave NAPI disabled while + * in fact there's still Rx traffic to be processed. + */ + usleep_range(5000, 10000); + + priv = netdev_priv(net_dev); + + return _errno; +} + +static const struct net_device_ops dpa_private_ops = { + .ndo_open = dpa_eth_priv_start, + .ndo_start_xmit = dpa_tx, + .ndo_stop = dpa_eth_priv_stop, + .ndo_tx_timeout = dpa_timeout, + .ndo_get_stats64 = dpa_get_stats64, + .ndo_set_mac_address = dpa_set_mac_address, + .ndo_validate_addr = eth_validate_addr, +#ifdef CONFIG_FMAN_PFC + .ndo_select_queue = dpa_select_queue, +#endif + .ndo_set_rx_mode = dpa_set_rx_mode, + .ndo_init = dpa_ndo_init, + .ndo_set_features = dpa_set_features, + .ndo_do_ioctl = dpa_ioctl, +}; + +typedef int (*ec_dpaa_receive_cb)(void *pecdev, const void *data, size_t size); +typedef int (*ec_dpaa_link_cb)(void *pecdev, uint8_t link); +typedef int (*ec_dpaa_close_cb)(void *pecdev); + +static ec_dpaa_receive_cb ec_dpaa_recv_func; +static ec_dpaa_close_cb ec_dpaa_close_func; +static ec_dpaa_link_cb ec_dpaa_link_func; + +int ec_dpaa_receive_data(void *pecdev, const void *data, size_t size) +{ + int ret = 0; + + if (ec_dpaa_recv_func) + ret = ec_dpaa_recv_func(pecdev, data, size); + + return ret; +} + +int ec_dpaa_set_func_cb(ec_dpaa_receive_cb recv, ec_dpaa_link_cb link, ec_dpaa_close_cb close) +{ + ec_dpaa_recv_func = recv; + ec_dpaa_link_func = link; + ec_dpaa_close_func = close; + + return 0; +} +EXPORT_SYMBOL(ec_dpaa_set_func_cb); + +struct module *ec_dpaa_get_module(void) +{ + struct module *m = THIS_MODULE; + return m; +} +EXPORT_SYMBOL(ec_dpaa_get_module); + +#define MAX_EC_DPAA_NETDEV_CNT (16) +static int ec_dpaa_netdev_cnt; +static struct net_device *ec_dpaa_netdev[MAX_EC_DPAA_NETDEV_CNT]; + +struct net_device *ec_dpaa_get_netdev(int idx) +{ + if (idx >= MAX_EC_DPAA_NETDEV_CNT) + return NULL; + + if (idx >= ec_dpaa_netdev_cnt) + return NULL; + + return ec_dpaa_netdev[idx]; +} +EXPORT_SYMBOL(ec_dpaa_get_netdev); + +int ec_dpaa_set_ecdev(int idx, void *ecdev) +{ + struct net_device *net_dev = NULL; + struct dpa_priv_s *priv = NULL; + + net_dev = ec_dpaa_get_netdev(idx); + if (net_dev) { + priv = netdev_priv(net_dev); + priv->ecdev = ecdev; + } + + return 0; +} +EXPORT_SYMBOL(ec_dpaa_set_ecdev); + +void *ec_dpaa_get_ecdev(int idx) +{ + struct net_device *net_dev = NULL; + struct dpa_priv_s *priv = NULL; + void *ecdev = NULL; + + net_dev = ec_dpaa_get_netdev(idx); + if (net_dev) { + priv = netdev_priv(net_dev); + ecdev = priv->ecdev; + } + + return ecdev; +} +EXPORT_SYMBOL(ec_dpaa_get_ecdev); + +void ec_dpaa_poll(struct net_device *net_dev) +{ + struct dpa_priv_s *priv = netdev_priv(net_dev); + u8 link = net_dev->phydev->state; + + qman_p_poll_dqrr(priv->p, DPA_NAPI_WEIGHT); + + if (ec_dpaa_link_func) + ec_dpaa_link_func(priv->ecdev, link); +} +EXPORT_SYMBOL(ec_dpaa_poll); + +int dpa_unregister_ethercat(struct net_device *net_dev) +{ + struct dpa_priv_s *priv = netdev_priv(net_dev); + + if (priv->ecdev) { + if (ec_dpaa_close_func) + ec_dpaa_close_func(priv->ecdev); + return 0; + } + + return -1; +} +EXPORT_SYMBOL(dpa_unregister_ethercat); + +static int dpa_ethercat_netdev_init(struct net_device *net_dev, + const u8 *mac_addr, + uint16_t tx_timeout) +{ + struct dpa_priv_s *priv = netdev_priv(net_dev); + + net_dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; + + net_dev->features |= net_dev->hw_features; + net_dev->vlan_features = net_dev->features; + + memcpy(net_dev->perm_addr, mac_addr, net_dev->addr_len); + memcpy(net_dev->dev_addr, mac_addr, net_dev->addr_len); + + net_dev->ethtool_ops = &dpa_ethtool_ops; + + net_dev->needed_headroom = priv->tx_headroom; + net_dev->watchdog_timeo = msecs_to_jiffies(tx_timeout); + + if (ec_dpaa_netdev_cnt < MAX_EC_DPAA_NETDEV_CNT) + ec_dpaa_netdev[ec_dpaa_netdev_cnt++] = net_dev; + + return 0; +} + +static int dpa_private_netdev_init(struct net_device *net_dev) +{ + int i; + struct dpa_priv_s *priv = netdev_priv(net_dev); + struct dpa_percpu_priv_s *percpu_priv; + const u8 *mac_addr; + + /* Although we access another CPU's private data here + * we do it at initialization so it is safe + */ + for_each_possible_cpu(i) { + percpu_priv = per_cpu_ptr(priv->percpu_priv, i); + percpu_priv->net_dev = net_dev; + } + + net_dev->netdev_ops = &dpa_private_ops; + mac_addr = priv->mac_dev->addr; + + net_dev->mem_start = priv->mac_dev->res->start; + net_dev->mem_end = priv->mac_dev->res->end; + + /* Configure the maximum MTU according to the FMan's MAXFRM */ + net_dev->min_mtu = ETH_MIN_MTU; + net_dev->max_mtu = dpa_get_max_mtu(); + + net_dev->hw_features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_LLTX); + + /* Advertise S/G and HIGHDMA support for private interfaces */ + net_dev->hw_features |= NETIF_F_SG | NETIF_F_HIGHDMA; + /* Recent kernels enable GSO automatically, if + * we declare NETIF_F_SG. For conformity, we'll + * still declare GSO explicitly. + */ + net_dev->features |= NETIF_F_GSO; + + /* Advertise GRO support */ + net_dev->features |= NETIF_F_GRO; + + return dpa_ethercat_netdev_init(net_dev, mac_addr, tx_timeout); +} + +static struct dpa_bp * __cold +dpa_priv_bp_probe(struct device *dev) +{ + struct dpa_bp *dpa_bp; + + dpa_bp = devm_kzalloc(dev, sizeof(*dpa_bp), GFP_KERNEL); + if (unlikely(!dpa_bp)) { + dev_err(dev, "devm_kzalloc() failed\n"); + return ERR_PTR(-ENOMEM); + } + + dpa_bp->target_count = CONFIG_FSL_DPAA_ETH_MAX_BUF_COUNT; + + dpa_bp->free_buf_cb = _dpa_bp_free_pf; + + return dpa_bp; +} + +/* Place all ingress FQs (Rx Default, Rx Error, PCD FQs) in a dedicated CGR. + * We won't be sending congestion notifications to FMan; for now, we just use + * this CGR to generate enqueue rejections to FMan in order to drop the frames + * before they reach our ingress queues and eat up memory. + */ +static int dpaa_eth_priv_ingress_cgr_init(struct dpa_priv_s *priv) +{ + struct qm_mcc_initcgr initcgr; + u32 cs_th; + int err; + + err = qman_alloc_cgrid(&priv->ingress_cgr.cgrid); + if (err < 0) { + pr_err("Error %d allocating CGR ID\n", err); + goto out_error; + } + + /* Enable CS TD, but disable Congestion State Change Notifications. */ + memset(&initcgr, 0, sizeof(initcgr)); + initcgr.we_mask = QM_CGR_WE_CS_THRES; + initcgr.cgr.cscn_en = QM_CGR_EN; + cs_th = CONFIG_FSL_DPAA_INGRESS_CS_THRESHOLD; + qm_cgr_cs_thres_set64(&initcgr.cgr.cs_thres, cs_th, 1); + + initcgr.we_mask |= QM_CGR_WE_CSTD_EN; + initcgr.cgr.cstd_en = QM_CGR_EN; + + /* This is actually a hack, because this CGR will be associated with + * our affine SWP. However, we'll place our ingress FQs in it. + */ + err = qman_create_cgr(&priv->ingress_cgr, QMAN_CGR_FLAG_USE_INIT, + &initcgr); + if (err < 0) { + pr_err("Error %d creating ingress CGR with ID %d\n", err, + priv->ingress_cgr.cgrid); + qman_release_cgrid(priv->ingress_cgr.cgrid); + goto out_error; + } + pr_debug("Created ingress CGR %d for netdev with hwaddr %pM\n", + priv->ingress_cgr.cgrid, priv->mac_dev->addr); + + /* struct qman_cgr allows special cgrid values (i.e. outside the 0..255 + * range), but we have no common initialization path between the + * different variants of the DPAA Eth driver, so we do it here rather + * than modifying every other variant than "private Eth". + */ + priv->use_ingress_cgr = true; + +out_error: + return err; +} + +static void dpa_priv_bp_seed(struct net_device *net_dev) +{ + struct dpa_priv_s *priv = netdev_priv(net_dev); + struct dpa_bp *dpa_bp = priv->dpa_bp; + int i; + + /* Give each CPU an allotment of buffers */ + for_each_possible_cpu(i) { + /* Although we access another CPU's counters here + * we do it at boot time so it is safe + */ + int *count_ptr = per_cpu_ptr(priv->percpu_count, i); + + dpaa_eth_refill_bpools(dpa_bp, count_ptr); + } +} + +static const struct of_device_id dpa_match[]; + +#ifdef CONFIG_FSL_DPAA_DBG_LOOP +static int dpa_new_loop_id(void) +{ + static int if_id; + + return if_id++; +} +#endif + +extern int dpa_bp_create(struct net_device *net_dev, struct dpa_bp *dpa_bp, + size_t count); + +static inline void dpa_setup_ingress(const struct dpa_priv_s *priv, + struct dpa_fq *fq, + const struct qman_fq *template) +{ + fq->fq_base = *template; + fq->net_dev = priv->net_dev; + + fq->flags = QMAN_FQ_FLAG_NO_ENQUEUE; + fq->channel = priv->channel; +} + +static inline void dpa_setup_egress(const struct dpa_priv_s *priv, + struct dpa_fq *fq, + struct fm_port *port, + const struct qman_fq *template) +{ + fq->fq_base = *template; + fq->net_dev = priv->net_dev; + + if (port) { + fq->flags = QMAN_FQ_FLAG_TO_DCPORTAL; + fq->channel = (u16)fm_get_tx_port_channel(port); + } else { + fq->flags = QMAN_FQ_FLAG_NO_MODIFY; + } +} + +static void dpa_fq_setup_ethercat(struct dpa_priv_s *priv, const struct dpa_fq_cbs_t *fq_cbs, + struct fm_port *tx_port) +{ + struct dpa_fq *fq; + u16 portals[NR_CPUS]; + int cpu, portal_cnt = 0, num_portals = 0; + u32 pcd_fqid, pcd_fqid_hi_prio; + const cpumask_t *affine_cpus = qman_affine_cpus(); + int egress_cnt = 0, conf_cnt = 0; + + /* Prepare for PCD FQs init */ + for_each_cpu(cpu, affine_cpus) + portals[num_portals++] = priv->ethercat_channel; + + if (num_portals == 0) + dev_err(priv->net_dev->dev.parent, + "No Qman software (affine) channels found"); + + pcd_fqid = (priv->mac_dev) ? + DPAA_ETH_PCD_FQ_BASE(priv->mac_dev->res->start) : 0; + pcd_fqid_hi_prio = (priv->mac_dev) ? + DPAA_ETH_PCD_FQ_HI_PRIO_BASE(priv->mac_dev->res->start) : 0; + + /* Initialize each FQ in the list */ + list_for_each_entry(fq, &priv->dpa_fq_list, list) { + switch (fq->fq_type) { + case FQ_TYPE_RX_DEFAULT: + WARN_ON(!priv->mac_dev); + dpa_setup_ingress(priv, fq, &fq_cbs->rx_defq); + break; + case FQ_TYPE_RX_ERROR: + WARN_ON(!priv->mac_dev); + dpa_setup_ingress(priv, fq, &fq_cbs->rx_errq); + break; + case FQ_TYPE_RX_PCD: + /* For MACless we can't have dynamic Rx queues */ + WARN_ON(!priv->mac_dev && !fq->fqid); + dpa_setup_ingress(priv, fq, &fq_cbs->rx_defq); + if (!fq->fqid) + fq->fqid = pcd_fqid++; + fq->channel = portals[portal_cnt]; + portal_cnt = (portal_cnt + 1) % num_portals; + break; + case FQ_TYPE_RX_PCD_HI_PRIO: + /* For MACless we can't have dynamic Hi Pri Rx queues */ + WARN_ON(!priv->mac_dev && !fq->fqid); + dpa_setup_ingress(priv, fq, &fq_cbs->rx_defq); + if (!fq->fqid) + fq->fqid = pcd_fqid_hi_prio++; + fq->channel = portals[portal_cnt]; + portal_cnt = (portal_cnt + 1) % num_portals; + break; + case FQ_TYPE_TX: + dpa_setup_egress(priv, fq, tx_port, + &fq_cbs->egress_ern); + /* If we have more Tx queues than the number of cores, + * just ignore the extra ones. + */ + if (egress_cnt < DPAA_ETH_TX_QUEUES) + priv->egress_fqs[egress_cnt++] = &fq->fq_base; + break; + case FQ_TYPE_TX_CONFIRM: + WARN_ON(!priv->mac_dev); + dpa_setup_ingress(priv, fq, &fq_cbs->tx_defq); + break; + case FQ_TYPE_TX_CONF_MQ: + WARN_ON(!priv->mac_dev); + dpa_setup_ingress(priv, fq, &fq_cbs->tx_defq); + priv->conf_fqs[conf_cnt++] = &fq->fq_base; + break; + case FQ_TYPE_TX_ERROR: + WARN_ON(!priv->mac_dev); + dpa_setup_ingress(priv, fq, &fq_cbs->tx_errq); + break; + default: + dev_warn(priv->net_dev->dev.parent, + "Unknown FQ type detected!\n"); + break; + } + } + + /* The number of Tx queues may be smaller than the number of cores, if + * the Tx queue range is specified in the device tree instead of being + * dynamically allocated. + * Make sure all CPUs receive a corresponding Tx queue. + */ + while (egress_cnt < DPAA_ETH_TX_QUEUES) { + list_for_each_entry(fq, &priv->dpa_fq_list, list) { + if (fq->fq_type != FQ_TYPE_TX) + continue; + priv->egress_fqs[egress_cnt++] = &fq->fq_base; + if (egress_cnt == DPAA_ETH_TX_QUEUES) + break; + } + } +} + +static int dpaa_ethercat_probe(struct platform_device *_of_dev) +{ + int err = 0, i; + struct device *dev; + struct device_node *dpa_node; + struct dpa_bp *dpa_bp; + size_t count = 1; + struct net_device *net_dev = NULL; + struct dpa_priv_s *priv = NULL; + struct dpa_percpu_priv_s *percpu_priv; + struct fm_port_fqs port_fqs; + struct dpa_buffer_layout_s *buf_layout = NULL; + struct mac_device *mac_dev; + u32 last_cpu = 0; + + dev = &_of_dev->dev; + + dpa_node = dev->of_node; + + if (!of_device_is_available(dpa_node)) + return -ENODEV; + + /* Get the buffer pools assigned to this interface; + * run only once the default pool probing code + */ + + err = of_property_read_u32(dpa_node, "fsl,bpid", &dpa_priv_bpid); + if (err) { + dev_err(dev, "Cannot find buffer pool ID in the device tree\n"); + return -EINVAL; + } + + last_cpu = qman_get_affine_last_cpu(); + err = of_property_read_u32(dpa_node, "fsl,cpuid", &dpa_priv_cpuid); + if (err) { + dev_warn(dev, "Cannot find cpuid in the device tree, using the last core\n"); + dpa_priv_cpuid = last_cpu; + } else if (dpa_priv_cpuid > last_cpu) { + dev_warn(dev, "Invalid cpuid in the device tree, using the last core\n"); + dpa_priv_cpuid = last_cpu; + } + + dpa_bp = (dpa_bpid2pool(dpa_priv_bpid)) ? : + dpa_priv_bp_probe(dev); + if (IS_ERR(dpa_bp)) + return PTR_ERR(dpa_bp); + if (dpa_bp->bpid == 0) + dpa_bp->bpid = dpa_priv_bpid; + + /* Allocate this early, so we can store relevant information in + * the private area (needed by 1588 code in dpa_mac_probe) + */ + net_dev = alloc_etherdev_mq(sizeof(*priv), DPAA_ETH_TX_QUEUES); + if (!net_dev) { + dev_err(dev, "alloc_etherdev_mq() failed\n"); + goto alloc_etherdev_mq_failed; + } + + /* Do this here, so we can be verbose early */ + SET_NETDEV_DEV(net_dev, dev); + dev_set_drvdata(dev, net_dev); + + priv = netdev_priv(net_dev); + priv->net_dev = net_dev; + strcpy(priv->if_type, "private"); + + priv->msg_enable = netif_msg_init(debug, -1); + +#ifdef CONFIG_FSL_DPAA_DBG_LOOP + priv->loop_id = dpa_new_loop_id(); + priv->loop_to = -1; /* disabled by default */ + dpa_loop_netdevs[priv->loop_id] = net_dev; +#endif + + mac_dev = dpa_mac_probe(_of_dev); + if (IS_ERR(mac_dev) || !mac_dev) { + err = PTR_ERR(mac_dev); + goto mac_probe_failed; + } + + /* We have physical ports, so we need to establish + * the buffer layout. + */ + buf_layout = devm_kzalloc(dev, 2 * sizeof(*buf_layout), + GFP_KERNEL); + if (!buf_layout) { + dev_err(dev, "devm_kzalloc() failed\n"); + goto alloc_failed; + } + dpa_set_buffers_layout(mac_dev, buf_layout); + + /* For private ports, need to compute the size of the default + * buffer pool, based on FMan port buffer layout;also update + * the maximum buffer size for private ports if necessary + */ + dpa_bp->size = dpa_bp_size(&buf_layout[RX]); + +#ifdef CONFIG_FSL_DPAA_ETH_JUMBO_FRAME + /* We only want to use jumbo frame optimization if we actually have + * L2 MAX FRM set for jumbo frames as well. + */ + if (fm_get_max_frm() < 9600) + dev_warn(dev, + "Invalid configuration: if jumbo frames support is on, FSL_FM_MAX_FRAME_SIZE should be set to 9600\n"); +#endif + + INIT_LIST_HEAD(&priv->dpa_fq_list); + + memset(&port_fqs, 0, sizeof(port_fqs)); + + err = dpa_fq_probe_mac(dev, &priv->dpa_fq_list, &port_fqs, true, RX); + if (!err) + err = dpa_fq_probe_mac(dev, &priv->dpa_fq_list, + &port_fqs, true, TX); + + if (err < 0) + goto fq_probe_failed; + + /* bp init */ + + err = dpa_bp_create(net_dev, dpa_bp, count); + + if (err < 0) + goto bp_create_failed; + + priv->mac_dev = mac_dev; + priv->ethercat_channel = (u16)qman_affine_channel_ethercat(dpa_priv_cpuid); + priv->channel = priv->ethercat_channel; + priv->p = qman_get_affine_portal_ethercat(dpa_priv_cpuid); + + dpa_fq_setup_ethercat(priv, &private_fq_cbs, priv->mac_dev->port_dev[TX]); + + /* Create a congestion group for this netdev, with + * dynamically-allocated CGR ID. + * Must be executed after probing the MAC, but before + * assigning the egress FQs to the CGRs. + */ + err = dpaa_eth_cgr_init(priv); + if (err < 0) { + dev_err(dev, "Error initializing CGR\n"); + goto tx_cgr_init_failed; + } + err = dpaa_eth_priv_ingress_cgr_init(priv); + if (err < 0) { + dev_err(dev, "Error initializing ingress CGR\n"); + goto rx_cgr_init_failed; + } + + /* Add the FQs to the interface, and make them active */ + err = dpa_fqs_init(dev, &priv->dpa_fq_list, false); + if (err < 0) + goto fq_alloc_failed; + + priv->buf_layout = buf_layout; + priv->tx_headroom = dpa_get_headroom(&priv->buf_layout[TX]); + priv->rx_headroom = dpa_get_headroom(&priv->buf_layout[RX]); + + /* All real interfaces need their ports initialized */ + dpaa_eth_init_ports(mac_dev, dpa_bp, count, &port_fqs, + buf_layout, dev); + +#ifdef CONFIG_FMAN_PFC + for (i = 0; i < CONFIG_FMAN_PFC_COS_COUNT; i++) { + err = fm_port_set_pfc_priorities_mapping_to_qman_wq(mac_dev->port_dev[TX], i, i); + if (unlikely(err != 0)) { + dev_err(dev, "Error maping PFC %u to WQ %u\n", i, i); + goto pfc_mapping_failed; + } + } +#endif + + priv->percpu_priv = devm_alloc_percpu(dev, *priv->percpu_priv); + + if (!priv->percpu_priv) { + dev_err(dev, "devm_alloc_percpu() failed\n"); + err = -ENOMEM; + goto alloc_percpu_failed; + } + + for_each_possible_cpu(i) { + percpu_priv = per_cpu_ptr(priv->percpu_priv, i); + memset(percpu_priv, 0, sizeof(*percpu_priv)); + } + + priv->percpu_count = devm_alloc_percpu(dev, *priv->percpu_count); + if (!priv->percpu_count) { + dev_err(dev, "devm_alloc_percpu() failed\n"); + err = -ENOMEM; + goto alloc_percpu_failed; + } + + for_each_possible_cpu(i) { + int *percpu_count = per_cpu_ptr(priv->percpu_count, i); + *percpu_count = 0; + } + + err = dpa_private_netdev_init(net_dev); + + dpa_priv_bp_seed(net_dev); + + if (err < 0) + goto netdev_init_failed; + + pr_info("Ethercat port:%02hx:%02hx:%02hx:%02hx:%02hx:%02hx bpid:%d cpu:%d\n", + mac_dev->addr[0], mac_dev->addr[1], mac_dev->addr[2], + mac_dev->addr[3], mac_dev->addr[4], mac_dev->addr[5], + dpa_priv_bpid, dpa_priv_cpuid); + +#ifdef CONFIG_PM + device_set_wakeup_capable(dev, true); +#endif + + pr_info("fsl_dpa: Probed interface %s\n", net_dev->name); + + return 0; + +netdev_init_failed: + +alloc_percpu_failed: +#ifdef CONFIG_FMAN_PFC +pfc_mapping_failed: +#endif + dpa_fq_free(dev, &priv->dpa_fq_list); +fq_alloc_failed: + qman_delete_cgr_safe(&priv->ingress_cgr); + qman_release_cgrid(priv->ingress_cgr.cgrid); +rx_cgr_init_failed: + qman_delete_cgr_safe(&priv->cgr_data.cgr); + qman_release_cgrid(priv->cgr_data.cgr.cgrid); +tx_cgr_init_failed: + + dpa_bp_free(priv); +bp_create_failed: +fq_probe_failed: +alloc_failed: +mac_probe_failed: + dev_set_drvdata(dev, NULL); + free_netdev(net_dev); +alloc_etherdev_mq_failed: + if (atomic_read(&dpa_bp->refs) == 0) + devm_kfree(dev, dpa_bp); + + return err; +} + +static const struct of_device_id dpa_match[] = { + { + .compatible = "fsl,dpa-ethercat" + }, + {} +}; +MODULE_DEVICE_TABLE(of, dpa_match); + +static struct platform_driver dpa_driver = { + .driver = { + .name = KBUILD_MODNAME "-ethercat", + .of_match_table = dpa_match, + .owner = THIS_MODULE, + .pm = DPAA_PM_OPS, + }, + .probe = dpaa_ethercat_probe, + .remove = dpa_remove +}; + +static int __init __cold dpa_ethercat_load(void) +{ + int _errno; + + pr_info(DPA_DESCRIPTION "\n"); + +#ifdef CONFIG_FSL_DPAA_DBG_LOOP + dpa_debugfs_module_init(); +#endif /* CONFIG_FSL_DPAA_DBG_LOOP */ + + /* initialise dpaa_eth mirror values */ + dpa_rx_extra_headroom = fm_get_rx_extra_headroom(); + dpa_max_frm = fm_get_max_frm(); + dpa_num_cpus = num_possible_cpus(); + +#ifdef CONFIG_FSL_DPAA_DBG_LOOP + memset(dpa_loop_netdevs, 0, sizeof(dpa_loop_netdevs)); +#endif + + _errno = platform_driver_register(&dpa_driver); + if (unlikely(_errno < 0)) { + pr_err(KBUILD_MODNAME + ": %s:%hu:%s(): platform_driver_register() = %d\n", + KBUILD_BASENAME ".c", __LINE__, __func__, _errno); + } + + pr_debug(KBUILD_MODNAME ": %s:%s() ->\n", + KBUILD_BASENAME ".c", __func__); + + return _errno; +} +module_init(dpa_ethercat_load); + +static void __exit __cold dpa_ethercat_unload(void) +{ + pr_debug(KBUILD_MODNAME ": -> %s:%s()\n", + KBUILD_BASENAME ".c", __func__); + + platform_driver_unregister(&dpa_driver); + +#ifdef CONFIG_FSL_DPAA_DBG_LOOP + dpa_debugfs_module_exit(); +#endif /* CONFIG_FSL_DPAA_DBG_LOOP */ + + /* Only one channel is used and needs to be relased after all + * interfaces are removed + */ + dpa_release_channel(); + + pr_debug(KBUILD_MODNAME ": %s:%s() ->\n", + KBUILD_BASENAME ".c", __func__); +} +module_exit(dpa_ethercat_unload); diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 62733d7e139f..f26026b5a511 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -247,6 +247,12 @@ static void ocelot_vcap_enable(struct ocelot *ocelot, int port) ocelot_write_gix(ocelot, ANA_PORT_VCAP_CFG_S1_ENA, ANA_PORT_VCAP_CFG, port); + /* Use key S1_5TUPLE_IP4 in second lookup. */ + ocelot_write_ix(ocelot, + ANA_PORT_VCAP_S1_KEY_CFG_S1_KEY_IP6_CFG(2) | + ANA_PORT_VCAP_S1_KEY_CFG_S1_KEY_IP4_CFG(2), + ANA_PORT_VCAP_S1_KEY_CFG, port, 1); + ocelot_rmw_gix(ocelot, REW_PORT_CFG_ES0_EN, REW_PORT_CFG_ES0_EN, REW_PORT_CFG, port); @@ -380,6 +386,8 @@ static void ocelot_port_manage_port_tag(struct ocelot *ocelot, int port) struct ocelot_port *ocelot_port = ocelot->ports[port]; enum ocelot_port_tag_config tag_cfg; bool uses_native_vlan = false; + u32 port_tpid = 0; + u32 tag_tpid = 0; if (ocelot_port->vlan_aware) { uses_native_vlan = ocelot_port_uses_native_vlan(ocelot, port); @@ -390,12 +398,17 @@ static void ocelot_port_manage_port_tag(struct ocelot *ocelot, int port) tag_cfg = OCELOT_PORT_TAG_DISABLED; else tag_cfg = OCELOT_PORT_TAG_TRUNK; + + if (ocelot->qinq_enable && ocelot_port->qinq_mode) + tag_tpid = REW_TAG_CFG_TAG_TPID_CFG(1); + else + tag_tpid = REW_TAG_CFG_TAG_TPID_CFG(0); } else { tag_cfg = OCELOT_PORT_TAG_DISABLED; } - ocelot_rmw_gix(ocelot, REW_TAG_CFG_TAG_CFG(tag_cfg), - REW_TAG_CFG_TAG_CFG_M, + ocelot_rmw_gix(ocelot, REW_TAG_CFG_TAG_CFG(tag_cfg) | tag_tpid, + REW_TAG_CFG_TAG_CFG_M | REW_TAG_CFG_TAG_TPID_CFG_M, REW_TAG_CFG, port); if (uses_native_vlan) { @@ -407,9 +420,16 @@ static void ocelot_port_manage_port_tag(struct ocelot *ocelot, int port) */ native_vlan = ocelot_port_find_native_vlan(ocelot, port); + if (ocelot->qinq_enable && ocelot_port->qinq_mode) + port_tpid = REW_PORT_VLAN_CFG_PORT_TPID(ETH_P_8021AD); + else + port_tpid = REW_PORT_VLAN_CFG_PORT_TPID(ETH_P_8021Q); + ocelot_rmw_gix(ocelot, - REW_PORT_VLAN_CFG_PORT_VID(native_vlan->vid), - REW_PORT_VLAN_CFG_PORT_VID_M, + REW_PORT_VLAN_CFG_PORT_VID(native_vlan->vid) | + port_tpid, + REW_PORT_VLAN_CFG_PORT_VID_M | + REW_PORT_VLAN_CFG_PORT_TPID_M, REW_PORT_VLAN_CFG, port); } } @@ -454,6 +474,10 @@ static void ocelot_port_set_pvid(struct ocelot *ocelot, int port, struct ocelot_port *ocelot_port = ocelot->ports[port]; u16 pvid = ocelot_vlan_unaware_pvid(ocelot, ocelot_port->bridge); u32 val = 0; + u32 tag_type = 0; + + if (ocelot->qinq_enable && ocelot_port->qinq_mode) + tag_type = ANA_PORT_VLAN_CFG_VLAN_TAG_TYPE; ocelot_port->pvid_vlan = pvid_vlan; @@ -461,8 +485,8 @@ static void ocelot_port_set_pvid(struct ocelot *ocelot, int port, pvid = pvid_vlan->vid; ocelot_rmw_gix(ocelot, - ANA_PORT_VLAN_CFG_VLAN_VID(pvid), - ANA_PORT_VLAN_CFG_VLAN_VID_M, + ANA_PORT_VLAN_CFG_VLAN_VID(pvid) | tag_type, + ANA_PORT_VLAN_CFG_VLAN_VID_M | tag_type, ANA_PORT_VLAN_CFG, port); /* If there's no pvid, we should drop not only untagged traffic (which @@ -620,6 +644,15 @@ int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1); else val = 0; + + /* if switch is enabled for QinQ, the port for LAN should set + * VLAN_CFG.VLAN_POP_CNT=0 && VLAN_CFG.VLAN_AWARE_ENA=0. + * the port for MAN should set VLAN_CFG.VLAN_POP_CNT=1 && + * VLAN_CFG.VLAN_AWARE_ENA=1. referring to 4.3.3 in VSC9959_1_00_TS.pdf + */ + if (ocelot->qinq_enable && !ocelot_port->qinq_mode) + val = 0; + ocelot_rmw_gix(ocelot, val, ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA | ANA_PORT_VLAN_CFG_VLAN_POP_CNT_M, diff --git a/drivers/net/ethernet/mscc/ocelot_flower.c b/drivers/net/ethernet/mscc/ocelot_flower.c index 33b438c6aec5..0fdcefeed2ef 100644 --- a/drivers/net/ethernet/mscc/ocelot_flower.c +++ b/drivers/net/ethernet/mscc/ocelot_flower.c @@ -468,20 +468,27 @@ static int ocelot_flower_parse_action(struct ocelot *ocelot, int port, switch (ntohs(a->vlan.proto)) { case ETH_P_8021Q: tpid = OCELOT_TAG_TPID_SEL_8021Q; + filter->action.tag_b_tpid_sel = tpid; + filter->action.push_inner_tag = OCELOT_ES0_TAG; + filter->action.tag_b_vid_sel = OCELOT_ES0_VID; + filter->action.tag_b_pcp_sel = OCELOT_ES0_PCP; + filter->action.vid_b_val = a->vlan.vid; + filter->action.pcp_b_val = a->vlan.prio; break; case ETH_P_8021AD: tpid = OCELOT_TAG_TPID_SEL_8021AD; + filter->action.tag_a_tpid_sel = tpid; + filter->action.push_outer_tag = OCELOT_ES0_TAG; + filter->action.tag_a_vid_sel = OCELOT_ES0_VID; + filter->action.tag_a_pcp_sel = OCELOT_ES0_PCP; + filter->action.vid_a_val = a->vlan.vid; + filter->action.pcp_a_val = a->vlan.prio; break; default: NL_SET_ERR_MSG_MOD(extack, "Cannot push custom TPID"); return -EOPNOTSUPP; } - filter->action.tag_a_tpid_sel = tpid; - filter->action.push_outer_tag = OCELOT_ES0_TAG; - filter->action.tag_a_vid_sel = OCELOT_ES0_VID; - filter->action.vid_a_val = a->vlan.vid; - filter->action.pcp_a_val = a->vlan.prio; filter->type = OCELOT_VCAP_FILTER_OFFLOAD; break; case FLOW_ACTION_GATE: @@ -586,6 +593,7 @@ ocelot_flower_parse_key(struct ocelot *ocelot, int port, bool ingress, BIT_ULL(FLOW_DISSECTOR_KEY_META) | BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) | BIT_ULL(FLOW_DISSECTOR_KEY_VLAN) | + BIT_ULL(FLOW_DISSECTOR_KEY_CVLAN) | BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS))) { @@ -627,6 +635,18 @@ ocelot_flower_parse_key(struct ocelot *ocelot, int port, bool ingress, match_protocol = false; } + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CVLAN)) { + struct flow_match_vlan match; + + flow_rule_match_cvlan(rule, &match); + filter->key_type = OCELOT_VCAP_KEY_ANY; + filter->cvlan.vid.value = match.key->vlan_id; + filter->cvlan.vid.mask = match.mask->vlan_id; + filter->cvlan.pcp.value[0] = match.key->vlan_priority; + filter->cvlan.pcp.mask[0] = match.mask->vlan_priority; + match_protocol = false; + } + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { struct flow_match_eth_addrs match; diff --git a/drivers/net/ethernet/mscc/ocelot_ptp.c b/drivers/net/ethernet/mscc/ocelot_ptp.c index cb32234a5bf1..e1ca9d1081e6 100644 --- a/drivers/net/ethernet/mscc/ocelot_ptp.c +++ b/drivers/net/ethernet/mscc/ocelot_ptp.c @@ -14,6 +14,8 @@ #include #include "ocelot.h" +#define OCELOT_PTP_TX_TSTAMP_TIMEOUT (5 * HZ) + int ocelot_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts) { struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info); @@ -325,6 +327,7 @@ static void ocelot_populate_ipv4_ptp_event_trap_key(struct ocelot_vcap_filter *trap) { trap->key_type = OCELOT_VCAP_KEY_IPV4; + trap->key.ipv4.fragment = OCELOT_VCAP_BIT_0; trap->key.ipv4.proto.value[0] = IPPROTO_UDP; trap->key.ipv4.proto.mask[0] = 0xff; trap->key.ipv4.dport.value = PTP_EV_PORT; @@ -345,6 +348,7 @@ static void ocelot_populate_ipv4_ptp_general_trap_key(struct ocelot_vcap_filter *trap) { trap->key_type = OCELOT_VCAP_KEY_IPV4; + trap->key.ipv4.fragment = OCELOT_VCAP_BIT_0; trap->key.ipv4.proto.value[0] = IPPROTO_UDP; trap->key.ipv4.proto.mask[0] = 0xff; trap->key.ipv4.dport.value = PTP_GEN_PORT; @@ -532,6 +536,7 @@ int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr) switch (cfg.tx_type) { case HWTSTAMP_TX_ON: ocelot_port->ptp_cmd = IFH_REW_OP_TWO_STEP_PTP; + skb_queue_purge(&ocelot_port->tx_skbs); break; case HWTSTAMP_TX_ONESTEP_SYNC: /* IFH_REW_OP_ONE_STEP_PTP updates the correctional field, we @@ -607,34 +612,91 @@ int ocelot_get_ts_info(struct ocelot *ocelot, int port, } EXPORT_SYMBOL(ocelot_get_ts_info); -static int ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port, - struct sk_buff *clone) +static struct sk_buff * +ocelot_port_remove_ptp_tx_skb(struct ocelot_port *ocelot_port, u8 ts_id) { - struct ocelot_port *ocelot_port = ocelot->ports[port]; + struct sk_buff *skb, *skb_tmp, *skb_match = NULL; unsigned long flags; - spin_lock_irqsave(&ocelot->ts_id_lock, flags); + spin_lock_irqsave(&ocelot_port->tx_skbs.lock, flags); - if (ocelot_port->ptp_skbs_in_flight == OCELOT_MAX_PTP_ID || - ocelot->ptp_skbs_in_flight == OCELOT_PTP_FIFO_SIZE) { - spin_unlock_irqrestore(&ocelot->ts_id_lock, flags); - return -EBUSY; + skb_queue_walk_safe(&ocelot_port->tx_skbs, skb, skb_tmp) { + if (OCELOT_SKB_CB(skb)->ts_id != ts_id) + continue; + __skb_unlink(skb, &ocelot_port->tx_skbs); + skb_match = skb; + break; } - skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS; - /* Store timestamp ID in OCELOT_SKB_CB(clone)->ts_id */ - OCELOT_SKB_CB(clone)->ts_id = ocelot_port->ts_id; + spin_unlock_irqrestore(&ocelot_port->tx_skbs.lock, flags); - ocelot_port->ts_id++; - if (ocelot_port->ts_id == OCELOT_MAX_PTP_ID) - ocelot_port->ts_id = 0; + return skb_match; +} - ocelot_port->ptp_skbs_in_flight++; +static int ocelot_port_ts_id_get(struct ocelot *ocelot, int port, u8 *ts_id) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + unsigned long n; + + /* To get a better chance of acquiring a timestamp ID, first flush the + * stale packets still waiting in the TX timestamping queue. They are + * probably lost. + */ + for_each_set_bit(n, ocelot_port->ts_id_in_flight, OCELOT_MAX_PTP_ID) { + if (time_before(ocelot_port->ptp_tx_time[n] + + OCELOT_PTP_TX_TSTAMP_TIMEOUT, jiffies)) { + dev_warn_ratelimited(ocelot->dev, + "port %d invalidating stale timestamp ID %lu which seems lost\n", + port, n); + __clear_bit(n, ocelot_port->ts_id_in_flight); + ocelot->ptp_skbs_in_flight--; + ocelot_port_remove_ptp_tx_skb(ocelot_port, n); + } + } + + if (ocelot->ptp_skbs_in_flight == OCELOT_PTP_FIFO_SIZE) + return -EBUSY; + + n = find_first_zero_bit(ocelot_port->ts_id_in_flight, + OCELOT_MAX_PTP_ID); + if (n == OCELOT_MAX_PTP_ID) + return -EBUSY; + + /* Found an available timestamp ID, use it */ + __set_bit(n, ocelot_port->ts_id_in_flight); + *ts_id = n; + ocelot_port->ptp_tx_time[n] = jiffies; ocelot->ptp_skbs_in_flight++; + dev_dbg_ratelimited(ocelot->dev, "port %d timestamp id %lu\n", port, n); - skb_queue_tail(&ocelot_port->tx_skbs, clone); + return 0; +} + +static void ocelot_port_ts_id_put(struct ocelot *ocelot, int port, int ts_id) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + + __clear_bit(ts_id, ocelot_port->ts_id_in_flight); + ocelot->ptp_skbs_in_flight--; +} +static int ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port, + struct sk_buff *clone) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + unsigned long flags; + int err; + + /* Store timestamp ID in OCELOT_SKB_CB(clone)->ts_id */ + spin_lock_irqsave(&ocelot->ts_id_lock, flags); + err = ocelot_port_ts_id_get(ocelot, port, &OCELOT_SKB_CB(clone)->ts_id); spin_unlock_irqrestore(&ocelot->ts_id_lock, flags); + if (err) + return err; + + skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS; + + skb_queue_tail(&ocelot_port->tx_skbs, clone); return 0; } @@ -746,12 +808,11 @@ void ocelot_get_txtstamp(struct ocelot *ocelot) int budget = OCELOT_PTP_QUEUE_SZ; while (budget--) { - struct sk_buff *skb, *skb_tmp, *skb_match = NULL; struct skb_shared_hwtstamps shhwtstamps; u32 val, id, seqid, txport; + struct sk_buff *skb_match; struct ocelot_port *port; struct timespec64 ts; - unsigned long flags; val = ocelot_read(ocelot, SYS_PTP_STATUS); @@ -769,32 +830,24 @@ void ocelot_get_txtstamp(struct ocelot *ocelot) port = ocelot->ports[txport]; spin_lock(&ocelot->ts_id_lock); - port->ptp_skbs_in_flight--; - ocelot->ptp_skbs_in_flight--; + ocelot_port_ts_id_put(ocelot, txport, id); spin_unlock(&ocelot->ts_id_lock); /* Retrieve its associated skb */ try_again: - spin_lock_irqsave(&port->tx_skbs.lock, flags); - - skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) { - if (OCELOT_SKB_CB(skb)->ts_id != id) - continue; - __skb_unlink(skb, &port->tx_skbs); - skb_match = skb; - break; - } - - spin_unlock_irqrestore(&port->tx_skbs.lock, flags); - - if (WARN_ON(!skb_match)) + skb_match = ocelot_port_remove_ptp_tx_skb(port, id); + if (!skb_match) { + dev_warn_ratelimited(ocelot->dev, + "port %d received TX timestamp (seqid %d, ts id %u) for packet previously declared stale\n", + txport, seqid, id); continue; + } if (!ocelot_validate_ptp_skb(skb_match, seqid)) { dev_err_ratelimited(ocelot->dev, - "port %d received stale TX timestamp for seqid %d, discarding\n", - txport, seqid); - dev_kfree_skb_any(skb); + "port %d received stale TX timestamp (seqid %d, ts id %u), discarding\n", + txport, seqid, id); + dev_kfree_skb_any(skb_match); goto try_again; } diff --git a/drivers/net/ethernet/mscc/ocelot_vcap.c b/drivers/net/ethernet/mscc/ocelot_vcap.c index 73cdec5ca6a3..9947b634194a 100644 --- a/drivers/net/ethernet/mscc/ocelot_vcap.c +++ b/drivers/net/ethernet/mscc/ocelot_vcap.c @@ -666,10 +666,11 @@ static void is1_action_set(struct ocelot *ocelot, struct vcap_data *data, vcap_action_set(vcap, data, VCAP_IS1_ACT_PAG_VAL, a->pag_val); } -static void is1_entry_set(struct ocelot *ocelot, int ix, +static int is1_entry_set(struct ocelot *ocelot, int ix, struct ocelot_vcap_filter *filter) { const struct vcap_props *vcap = &ocelot->vcap[VCAP_IS1]; + struct ocelot_vcap_key_vlan *ctag = &filter->cvlan; struct ocelot_vcap_key_vlan *tag = &filter->vlan; struct vcap_data data; int row = ix / 2; @@ -689,6 +690,11 @@ static void is1_entry_set(struct ocelot *ocelot, int ix, if (filter->prio != 0) data.tg |= data.tg_value; + if (filter->lookup == 1) + type = IS1_TYPE_S1_5TUPLE_IP4; + else + type = IS1_TYPE_S1_NORMAL; + vcap_key_set(vcap, &data, VCAP_IS1_HK_LOOKUP, filter->lookup, 0x3); vcap_key_set(vcap, &data, VCAP_IS1_HK_IGR_PORT_MASK, 0, ~filter->ingress_port_mask); @@ -699,12 +705,31 @@ static void is1_entry_set(struct ocelot *ocelot, int ix, tag->vid.value, tag->vid.mask); vcap_key_set(vcap, &data, VCAP_IS1_HK_PCP, tag->pcp.value[0], tag->pcp.mask[0]); - type = IS1_TYPE_S1_NORMAL; + + if (ctag->vid.value != 0) { + if (type != IS1_TYPE_S1_5TUPLE_IP4) + return -EOPNOTSUPP; + + vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_TPID, + OCELOT_VCAP_BIT_1); + vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_VLAN_DBL_TAGGED, + OCELOT_VCAP_BIT_1); + vcap_key_set(vcap, &data, VCAP_IS1_HK_IP4_INNER_VID, + ctag->vid.value, ctag->vid.mask); + vcap_key_set(vcap, &data, VCAP_IS1_HK_IP4_INNER_PCP, + ctag->pcp.value[0], ctag->pcp.mask[0]); + } switch (filter->key_type) { case OCELOT_VCAP_KEY_ETYPE: { struct ocelot_vcap_key_etype *etype = &filter->key.etype; + if (type != IS1_TYPE_S1_NORMAL) + return -EOPNOTSUPP; + + if (etype->dmac.mask[0]) + return -EOPNOTSUPP; + vcap_key_bytes_set(vcap, &data, VCAP_IS1_HK_L2_SMAC, etype->smac.value, etype->smac.mask); vcap_key_bytes_set(vcap, &data, VCAP_IS1_HK_ETYPE, @@ -716,48 +741,103 @@ static void is1_entry_set(struct ocelot *ocelot, int ix, struct ocelot_vcap_udp_tcp *sport = &ipv4->sport; struct ocelot_vcap_udp_tcp *dport = &ipv4->dport; enum ocelot_vcap_bit tcp_udp = OCELOT_VCAP_BIT_0; + enum ocelot_vcap_bit fragment = ipv4->fragment; + enum ocelot_vcap_bit options = ipv4->options; + enum ocelot_vcap_bit tcp = OCELOT_VCAP_BIT_0; struct ocelot_vcap_u8 proto = ipv4->proto; struct ocelot_vcap_ipv4 sip = ipv4->sip; + struct ocelot_vcap_ipv4 dip = ipv4->dip; + struct ocelot_vcap_u32 port; u32 val, msk; - vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_IP_SNAP, - OCELOT_VCAP_BIT_1); - vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_IP4, - OCELOT_VCAP_BIT_1); - vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_ETYPE_LEN, - OCELOT_VCAP_BIT_1); - vcap_key_bytes_set(vcap, &data, VCAP_IS1_HK_L3_IP4_SIP, - sip.value.addr, sip.mask.addr); - - val = proto.value[0]; - msk = proto.mask[0]; - - if ((val == NEXTHDR_TCP || val == NEXTHDR_UDP) && msk == 0xff) - tcp_udp = OCELOT_VCAP_BIT_1; - vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_TCP_UDP, tcp_udp); - - if (tcp_udp) { - enum ocelot_vcap_bit tcp = OCELOT_VCAP_BIT_0; - - if (val == NEXTHDR_TCP) - tcp = OCELOT_VCAP_BIT_1; - - vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_TCP, tcp); - vcap_key_l4_port_set(vcap, &data, VCAP_IS1_HK_L4_SPORT, - sport); - /* Overloaded field */ - vcap_key_l4_port_set(vcap, &data, VCAP_IS1_HK_ETYPE, - dport); - } else { - /* IPv4 "other" frame */ - struct ocelot_vcap_u16 etype = {0}; - - /* Overloaded field */ - etype.value[0] = proto.value[0]; - etype.mask[0] = proto.mask[0]; + if (type == IS1_TYPE_S1_5TUPLE_IP4) { + vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_IP4_IP4, + OCELOT_VCAP_BIT_1); + vcap_key_bit_set(vcap, &data, + VCAP_IS1_HK_IP4_L3_FRAGMENT, + fragment); + vcap_key_bit_set(vcap, &data, + VCAP_IS1_HK_IP4_L3_OPTIONS, + options); + vcap_key_bytes_set(vcap, &data, + VCAP_IS1_HK_IP4_L3_IP4_DIP, + dip.value.addr, dip.mask.addr); + vcap_key_bytes_set(vcap, &data, + VCAP_IS1_HK_IP4_L3_IP4_SIP, + sip.value.addr, sip.mask.addr); + + val = proto.value[0]; + msk = proto.mask[0]; + if ((val == NEXTHDR_TCP || val == NEXTHDR_UDP) && + msk == 0xff) { + tcp_udp = OCELOT_VCAP_BIT_1; + tcp = (val == NEXTHDR_TCP ? + OCELOT_VCAP_BIT_1 : OCELOT_VCAP_BIT_0); + + vcap_key_bit_set(vcap, &data, + VCAP_IS1_HK_IP4_TCP_UDP, + tcp_udp); + vcap_key_bit_set(vcap, &data, + VCAP_IS1_HK_IP4_TCP, tcp); + + port.value[0] = sport->value & 0xFF; + port.value[1] = sport->value >> 8; + port.value[2] = dport->value & 0xFF; + port.value[3] = dport->value >> 8; + port.mask[0] = sport->mask & 0xFF; + port.mask[1] = sport->mask >> 8; + port.mask[2] = dport->mask & 0xFF; + port.mask[3] = dport->mask >> 8; + vcap_key_bytes_set(vcap, &data, + VCAP_IS1_HK_IP4_IP_PAYLOAD_S1_5TUPLE, + port.value, port.mask); + } - vcap_key_bytes_set(vcap, &data, VCAP_IS1_HK_ETYPE, - etype.value, etype.mask); + } else if (type == IS1_TYPE_S1_NORMAL) { + vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_IP_SNAP, + OCELOT_VCAP_BIT_1); + vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_IP4, + OCELOT_VCAP_BIT_1); + vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_ETYPE_LEN, + OCELOT_VCAP_BIT_1); + vcap_key_bytes_set(vcap, &data, VCAP_IS1_HK_L3_IP4_SIP, + sip.value.addr, sip.mask.addr); + + if (dip.mask.addr[0]) + return -EOPNOTSUPP; + + val = proto.value[0]; + msk = proto.mask[0]; + + if ((val == NEXTHDR_TCP || val == NEXTHDR_UDP) && + msk == 0xff) { + tcp_udp = OCELOT_VCAP_BIT_1; + tcp = (val == NEXTHDR_TCP ? + OCELOT_VCAP_BIT_1 : OCELOT_VCAP_BIT_0); + + vcap_key_bit_set(vcap, &data, + VCAP_IS1_HK_TCP_UDP, tcp_udp); + vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_TCP, + tcp); + vcap_key_l4_port_set(vcap, &data, + VCAP_IS1_HK_L4_SPORT, + sport); + /* Overloaded field */ + vcap_key_l4_port_set(vcap, &data, + VCAP_IS1_HK_ETYPE, + dport); + } else if (msk == 0xff) { + /* IPv4 "other" frame */ + struct ocelot_vcap_u16 etype = {0}; + + /* Overloaded field */ + etype.value[0] = proto.value[0]; + etype.mask[0] = proto.mask[0]; + + vcap_key_bytes_set(vcap, &data, + VCAP_IS1_HK_ETYPE, + etype.value, etype.mask); + } } break; } @@ -775,6 +855,8 @@ static void is1_entry_set(struct ocelot *ocelot, int ix, vcap_entry2cache(ocelot, vcap, &data); vcap_action2cache(ocelot, vcap, &data); vcap_row_cmd(ocelot, vcap, row, VCAP_CMD_WRITE, VCAP_SEL_ALL); + + return 0; } static void es0_action_set(struct ocelot *ocelot, struct vcap_data *data, @@ -872,15 +954,17 @@ static void vcap_entry_get(struct ocelot *ocelot, int ix, filter->stats.pkts = cnt; } -static void vcap_entry_set(struct ocelot *ocelot, int ix, - struct ocelot_vcap_filter *filter) +static int vcap_entry_set(struct ocelot *ocelot, int ix, + struct ocelot_vcap_filter *filter) { if (filter->block_id == VCAP_IS1) return is1_entry_set(ocelot, ix, filter); if (filter->block_id == VCAP_IS2) - return is2_entry_set(ocelot, ix, filter); + is2_entry_set(ocelot, ix, filter); if (filter->block_id == VCAP_ES0) - return es0_entry_set(ocelot, ix, filter); + es0_entry_set(ocelot, ix, filter); + + return 0; } struct vcap_policer_entry { @@ -1214,8 +1298,7 @@ int ocelot_vcap_filter_add(struct ocelot *ocelot, } /* Now insert the new filter */ - vcap_entry_set(ocelot, index, filter); - return 0; + return vcap_entry_set(ocelot, index, filter); } EXPORT_SYMBOL(ocelot_vcap_filter_add); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index a9837985a483..419912680e56 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -1244,6 +1244,7 @@ const struct stmmac_ops dwmac410_ops = { .est_configure = dwmac5_est_configure, .est_irq_status = dwmac5_est_irq_status, .fpe_configure = dwmac5_fpe_configure, + .fpe_configure_get = dwmac5_fpe_configure_get, .fpe_send_mpacket = dwmac5_fpe_send_mpacket, .fpe_irq_status = dwmac5_fpe_irq_status, .add_hw_vlan_rx_fltr = dwmac4_add_hw_vlan_rx_fltr, @@ -1297,6 +1298,7 @@ const struct stmmac_ops dwmac510_ops = { .est_configure = dwmac5_est_configure, .est_irq_status = dwmac5_est_irq_status, .fpe_configure = dwmac5_fpe_configure, + .fpe_configure_get = dwmac5_fpe_configure_get, .fpe_send_mpacket = dwmac5_fpe_send_mpacket, .fpe_irq_status = dwmac5_fpe_irq_status, .add_hw_vlan_rx_fltr = dwmac4_add_hw_vlan_rx_fltr, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c index 8fd167501fa0..74825f602482 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c @@ -712,10 +712,15 @@ void dwmac5_est_irq_status(void __iomem *ioaddr, struct net_device *dev, void dwmac5_fpe_configure(void __iomem *ioaddr, struct stmmac_fpe_cfg *cfg, u32 num_txq, u32 num_rxq, - bool enable) + bool enable, struct stmmac_fpe *fpe) { u32 value; + if (fpe) { + value = fpe->p_queues << MTL_FPECTRL_PEC_SHIFT | fpe->fragsize; + writel(value, ioaddr + MTL_FPE_CTRL_STS); + } + if (enable) { cfg->fpe_csr = EFPE; value = readl(ioaddr + GMAC_RXQ_CTRL1); @@ -728,6 +733,18 @@ void dwmac5_fpe_configure(void __iomem *ioaddr, struct stmmac_fpe_cfg *cfg, writel(cfg->fpe_csr, ioaddr + MAC_FPE_CTRL_STS); } +void dwmac5_fpe_configure_get(void __iomem *ioaddr, struct stmmac_fpe *fpe) +{ + u32 value; + + value = readl(ioaddr + MAC_FPE_CTRL_STS); + fpe->enable = value & EFPE; + + value = readl(ioaddr + MTL_FPE_CTRL_STS); + fpe->p_queues = (value >> MTL_FPECTRL_PEC_SHIFT) & GENMASK(7, 0); + fpe->fragsize = value & GENMASK(1, 0); +} + int dwmac5_fpe_irq_status(void __iomem *ioaddr, struct net_device *dev) { u32 value; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5.h b/drivers/net/ethernet/stmicro/stmmac/dwmac5.h index 34e620790eb3..344aad5f3ee1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac5.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.h @@ -20,6 +20,9 @@ #define SVER BIT(1) #define EFPE BIT(0) +#define MTL_FPE_CTRL_STS 0x00000c90 +#define MTL_FPECTRL_PEC_SHIFT 8 + #define MAC_PPS_CONTROL 0x00000b70 #define PPS_MAXIDX(x) ((((x) + 1) * 8) - 1) #define PPS_MINIDX(x) ((x) * 8) @@ -155,7 +158,8 @@ void dwmac5_est_irq_status(void __iomem *ioaddr, struct net_device *dev, struct stmmac_extra_stats *x, u32 txqcnt); void dwmac5_fpe_configure(void __iomem *ioaddr, struct stmmac_fpe_cfg *cfg, u32 num_txq, u32 num_rxq, - bool enable); + bool enable, struct stmmac_fpe *fpe); +void dwmac5_fpe_configure_get(void __iomem *ioaddr, struct stmmac_fpe *fpe); void dwmac5_fpe_send_mpacket(void __iomem *ioaddr, struct stmmac_fpe_cfg *cfg, enum stmmac_mpacket_type type); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c index 052566f5b7f3..4dd0de102ced 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c @@ -1558,7 +1558,8 @@ static int dwxgmac3_est_configure(void __iomem *ioaddr, struct stmmac_est *cfg, static void dwxgmac3_fpe_configure(void __iomem *ioaddr, struct stmmac_fpe_cfg *cfg, u32 num_txq, - u32 num_rxq, bool enable) + u32 num_rxq, bool enable, + struct stmmac_fpe *fpe) { u32 value; diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index 47fb8e1646c2..610b90a31be2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -296,6 +296,12 @@ struct stmmac_pps_cfg; struct stmmac_rss; struct stmmac_est; +struct stmmac_fpe { + u8 enable; + u8 p_queues; + u8 fragsize; +}; + /* Helpers to program the MAC core */ struct stmmac_ops { /* MAC core initialization */ @@ -414,7 +420,8 @@ struct stmmac_ops { struct stmmac_extra_stats *x, u32 txqcnt); void (*fpe_configure)(void __iomem *ioaddr, struct stmmac_fpe_cfg *cfg, u32 num_txq, u32 num_rxq, - bool enable); + bool enable, struct stmmac_fpe *fpe); + void (*fpe_configure_get)(void __iomem *ioaddr, struct stmmac_fpe *fpe); void (*fpe_send_mpacket)(void __iomem *ioaddr, struct stmmac_fpe_cfg *cfg, enum stmmac_mpacket_type type); @@ -521,6 +528,8 @@ struct stmmac_ops { stmmac_do_void_callback(__priv, mac, est_irq_status, __args) #define stmmac_fpe_configure(__priv, __args...) \ stmmac_do_void_callback(__priv, mac, fpe_configure, __args) +#define stmmac_fpe_configure_get(__priv, __args...) \ + stmmac_do_void_callback(__priv, mac, fpe_configure_get, __args) #define stmmac_fpe_send_mpacket(__priv, __args...) \ stmmac_do_void_callback(__priv, mac, fpe_send_mpacket, __args) #define stmmac_fpe_irq_status(__priv, __args...) \ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 04ac04d805fd..ecc164b5d9b9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -335,6 +335,7 @@ struct stmmac_priv { /* XDP BPF Program */ unsigned long *af_xdp_zc_qps; struct bpf_prog *xdp_prog; + bool fp_enabled_admin; }; enum stmmac_state { diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 521b1b5ffebb..892113ef5a06 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -19,6 +19,7 @@ #include "stmmac.h" #include "dwmac_dma.h" #include "dwxgmac2.h" +#include "common.h" #define REG_SPACE_SIZE 0x1060 #define GMAC4_REG_SPACE_SIZE 0x116C @@ -1261,6 +1262,108 @@ static int stmmac_set_tunable(struct net_device *dev, return ret; } +static int stmmac_reset_preempt(struct net_device *dev, bool enable) +{ + struct stmmac_priv *priv = netdev_priv(dev); + struct stmmac_fpe_cfg *fpe_cfg = priv->plat->fpe_cfg; + bool *hs_enable = &fpe_cfg->hs_enable; + + if (enable) { + if (priv->fp_enabled_admin) { + priv->plat->fpe_cfg->enable = 1; + if (!*hs_enable) + stmmac_fpe_configure(priv, priv->ioaddr, fpe_cfg, + priv->plat->tx_queues_to_use, + priv->plat->rx_queues_to_use, true, NULL); + } + } else { + priv->plat->fpe_cfg->enable = 0; + stmmac_fpe_configure(priv, priv->ioaddr, fpe_cfg, + priv->plat->tx_queues_to_use, + priv->plat->rx_queues_to_use, false, NULL); + } + + return 0; +} + +static int stmmac_set_preempt(struct net_device *dev, struct ethtool_fp *fpcmd) +{ + struct stmmac_priv *priv = netdev_priv(dev); + struct stmmac_fpe fpe; + + if (!priv->dma_cap.fpesel) + return -EOPNOTSUPP; + + if (!fpcmd->disabled && + (fpcmd->min_frag_size < 60 || fpcmd->min_frag_size > 252)) + return -EINVAL; + + fpe.p_queues = fpcmd->preemptible_queues_mask; + fpe.fragsize = DIV_ROUND_UP((fpcmd->min_frag_size + 4), 64) - 1; + + if (priv->plat->fpe_cfg->lo_fpe_state == FPE_STATE_ON) + fpe.enable = 1; + else + fpe.enable = fpcmd->fp_enabled ? 0 : 1; + + /* To support preemption MAC should have more than 1 TX queue with at + * least 1 Queue designated as Express Queue. Queue 0 is always used as + * preemption queue when preemption MAC is enabled. + */ + if (fpe.p_queues >= (GENMASK(priv->plat->tx_queues_to_use - 1, 0) - 1)) { + netdev_err(priv->dev, + "Preemptable queue mask 0x%x not supported.", + fpe.p_queues); + return -EINVAL; + } + + if (fpcmd->disabled) { + fpe.enable = 0; + priv->plat->fpe_cfg->enable = 0; + priv->fp_enabled_admin = 0; + } else { + priv->plat->fpe_cfg->enable = 1; + priv->fp_enabled_admin = 1; + } + stmmac_fpe_configure(priv, priv->ioaddr, priv->plat->fpe_cfg, + priv->plat->tx_queues_to_use, + priv->plat->rx_queues_to_use, fpe.enable, &fpe); + + stmmac_fpe_handshake(priv, fpcmd->fp_enabled); + + return 0; +} + +static int stmmac_get_preempt(struct net_device *dev, struct ethtool_fp *fpcmd) +{ + struct stmmac_priv *priv = netdev_priv(dev); + struct stmmac_fpe fpe; + int ret; + + ret = stmmac_fpe_configure_get(priv, priv->ioaddr, &fpe); + if (ret) { + fpcmd->fp_supported = 0; + fpcmd->supported_queues_mask = 0; + fpcmd->preemptible_queues_mask = 0; + fpcmd->min_frag_size = 0; + + return 0; + } + + fpcmd->fp_supported = 1; + fpcmd->fp_status = priv->plat->fpe_cfg->enable; + fpcmd->fp_active = fpe.enable; + fpcmd->supported_queues_mask = GENMASK(priv->plat->tx_queues_to_use - 1, + 0); + fpcmd->preemptible_queues_mask = fpe.p_queues; + /* Queue 0 is always preemption when preemption is active. */ + if (fpcmd->fp_active) + fpcmd->preemptible_queues_mask |= 1; + fpcmd->min_frag_size = (fpe.fragsize + 1) * 64 - 4; + + return 0; +} + static const struct ethtool_ops stmmac_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_MAX_FRAMES, @@ -1300,6 +1403,9 @@ static const struct ethtool_ops stmmac_ethtool_ops = { .set_tunable = stmmac_set_tunable, .get_link_ksettings = stmmac_ethtool_get_link_ksettings, .set_link_ksettings = stmmac_ethtool_set_link_ksettings, + .set_preempt = stmmac_set_preempt, + .get_preempt = stmmac_get_preempt, + .reset_preempt = stmmac_reset_preempt, }; void stmmac_set_ethtool_ops(struct net_device *netdev) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index e7dcf9b2d3a4..673761ba3507 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -969,6 +969,12 @@ static void stmmac_fpe_link_state_handle(struct stmmac_priv *priv, bool is_up) } else { *lo_state = FPE_STATE_OFF; *lp_state = FPE_STATE_OFF; + priv->plat->fpe_cfg->enable = false; + stmmac_fpe_configure(priv, priv->ioaddr, + fpe_cfg, + priv->plat->tx_queues_to_use, + priv->plat->rx_queues_to_use, + false, NULL); } } @@ -2455,12 +2461,14 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) /* We are sharing with slow path and stop XSK TX desc submission when * available TX ring is less than threshold. */ - if (unlikely(stmmac_tx_avail(priv, queue) < STMMAC_TX_XSK_AVAIL) || - !netif_carrier_ok(priv->dev)) { + if (unlikely(stmmac_tx_avail(priv, queue) < STMMAC_TX_XSK_AVAIL)) { work_done = false; break; } + if (!netif_carrier_ok(priv->dev)) + break; + if (!xsk_tx_peek_desc(pool, &xdp_desc)) break; @@ -6993,7 +7001,9 @@ static const struct net_device_ops stmmac_netdev_ops = { .ndo_fix_features = stmmac_fix_features, .ndo_set_features = stmmac_set_features, .ndo_set_rx_mode = stmmac_set_rx_mode, +#ifndef CONFIG_NET_SCH_MULTIQ .ndo_tx_timeout = stmmac_tx_timeout, +#endif .ndo_eth_ioctl = stmmac_ioctl, .ndo_get_stats64 = stmmac_get_stats64, .ndo_setup_tc = stmmac_setup_tc, @@ -7145,6 +7155,7 @@ static void stmmac_napi_add(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); u32 queue, maxq; + char name[NAPINAMSIZ]; maxq = max(priv->plat->rx_queues_to_use, priv->plat->tx_queues_to_use); @@ -7156,16 +7167,22 @@ static void stmmac_napi_add(struct net_device *dev) spin_lock_init(&ch->lock); if (queue < priv->plat->rx_queues_to_use) { - netif_napi_add(dev, &ch->rx_napi, stmmac_napi_poll_rx); + snprintf(name, NAPINAMSIZ, "rx-%d", queue); + netif_napi_add_named(dev, &ch->rx_napi, stmmac_napi_poll_rx, + NAPI_POLL_WEIGHT, name); } if (queue < priv->plat->tx_queues_to_use) { - netif_napi_add_tx(dev, &ch->tx_napi, - stmmac_napi_poll_tx); + snprintf(name, NAPINAMSIZ, "tx-%d", queue); + netif_napi_add_tx_named(dev, &ch->tx_napi, + stmmac_napi_poll_tx, + NAPI_POLL_WEIGHT, name); } if (queue < priv->plat->rx_queues_to_use && queue < priv->plat->tx_queues_to_use) { - netif_napi_add(dev, &ch->rxtx_napi, - stmmac_napi_poll_rxtx); + snprintf(name, NAPINAMSIZ, "zc-%d", queue); + netif_napi_add_named(dev, &ch->rxtx_napi, + stmmac_napi_poll_rxtx, + NAPI_POLL_WEIGHT, name); } } } @@ -7251,7 +7268,6 @@ static void stmmac_fpe_lp_task(struct work_struct *work) enum stmmac_fpe_state *lo_state = &fpe_cfg->lo_fpe_state; enum stmmac_fpe_state *lp_state = &fpe_cfg->lp_fpe_state; bool *hs_enable = &fpe_cfg->hs_enable; - bool *enable = &fpe_cfg->enable; int retries = 20; while (retries-- > 0) { @@ -7265,7 +7281,7 @@ static void stmmac_fpe_lp_task(struct work_struct *work) fpe_cfg, priv->plat->tx_queues_to_use, priv->plat->rx_queues_to_use, - *enable); + true, NULL); netdev_info(priv->dev, "configured FPE\n"); @@ -7773,7 +7789,7 @@ int stmmac_suspend(struct device *dev) stmmac_fpe_configure(priv, priv->ioaddr, priv->plat->fpe_cfg, priv->plat->tx_queues_to_use, - priv->plat->rx_queues_to_use, false); + priv->plat->rx_queues_to_use, false, NULL); stmmac_fpe_handshake(priv, false); stmmac_fpe_stop_wq(priv); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c index 77245f856dd0..176edb91f40f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c @@ -923,7 +923,7 @@ static int tc_setup_taprio(struct stmmac_priv *priv, struct plat_stmmacenet_data *plat = priv->plat; struct timespec64 time, current_time, qopt_time; ktime_t current_time_ns; - bool fpe = false; + struct stmmac_fpe fpe; int i, ret = 0; u64 ctr; @@ -995,6 +995,8 @@ static int tc_setup_taprio(struct stmmac_priv *priv, priv->plat->est->enable = qopt->cmd == TAPRIO_CMD_REPLACE; mutex_unlock(&priv->plat->est->lock); + stmmac_fpe_configure_get(priv, priv->ioaddr, &fpe); + for (i = 0; i < size; i++) { s64 delta_ns = qopt->entries[i].interval; u32 gates = qopt->entries[i].gate_mask; @@ -1006,16 +1008,18 @@ static int tc_setup_taprio(struct stmmac_priv *priv, switch (qopt->entries[i].command) { case TC_TAPRIO_CMD_SET_GATES: - if (fpe) + if (fpe.enable) return -EINVAL; break; case TC_TAPRIO_CMD_SET_AND_HOLD: + if (!fpe.enable) + return -EINVAL; gates |= BIT(0); - fpe = true; break; case TC_TAPRIO_CMD_SET_AND_RELEASE: + if (!fpe.enable) + return -EINVAL; gates &= ~BIT(0); - fpe = true; break; default: return -EOPNOTSUPP; @@ -1042,16 +1046,6 @@ static int tc_setup_taprio(struct stmmac_priv *priv, priv->plat->est->ctr[0] = do_div(ctr, NSEC_PER_SEC); priv->plat->est->ctr[1] = (u32)ctr; - if (fpe && !priv->dma_cap.fpesel) { - mutex_unlock(&priv->plat->est->lock); - return -EOPNOTSUPP; - } - - /* Actual FPE register configuration will be done after FPE handshake - * is success. - */ - priv->plat->fpe_cfg->enable = fpe; - ret = stmmac_est_configure(priv, priv->ioaddr, priv->plat->est, priv->plat->clk_ptp_rate); mutex_unlock(&priv->plat->est->lock); @@ -1062,11 +1056,6 @@ static int tc_setup_taprio(struct stmmac_priv *priv, netdev_info(priv->dev, "configured EST\n"); - if (fpe) { - stmmac_fpe_handshake(priv, true); - netdev_info(priv->dev, "start FPE handshake\n"); - } - return 0; disable: @@ -1078,17 +1067,6 @@ static int tc_setup_taprio(struct stmmac_priv *priv, mutex_unlock(&priv->plat->est->lock); } - priv->plat->fpe_cfg->enable = false; - stmmac_fpe_configure(priv, priv->ioaddr, - priv->plat->fpe_cfg, - priv->plat->tx_queues_to_use, - priv->plat->rx_queues_to_use, - false); - netdev_info(priv->dev, "disabled FPE\n"); - - stmmac_fpe_handshake(priv, false); - netdev_info(priv->dev, "stop FPE handshake\n"); - return ret; } diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 95a9c35e293c..6eff60426a08 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -36,7 +36,8 @@ #include #include -#define PHY_STATE_TIME HZ +#define PHY_STATE_TIME HZ +#define PHY_STATE_TIME_MS 100 #define PHY_STATE_STR(_state) \ case PHY_##_state: \ @@ -1544,7 +1545,7 @@ void phy_state_machine(struct work_struct *work) */ mutex_lock(&phydev->lock); if (phy_polling_mode(phydev) && phy_is_started(phydev)) - phy_queue_state_machine(phydev, PHY_STATE_TIME); + phy_queue_state_machine(phydev, (PHY_STATE_TIME_MS * HZ) / 1000); mutex_unlock(&phydev->lock); } diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index 288c6f1c6979..64d3846b93be 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -60,6 +60,7 @@ config NFC_VIRTUAL_NCI If unsure, say N. +source "drivers/nfc/pn5xx/Kconfig" source "drivers/nfc/fdp/Kconfig" source "drivers/nfc/pn544/Kconfig" source "drivers/nfc/pn533/Kconfig" diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index 7b1bfde1d971..62d495c57a15 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -3,6 +3,7 @@ # Makefile for nfc devices # +obj-$(CONFIG_NFC_NXP_PN5XX) += pn5xx/ obj-$(CONFIG_NFC_FDP) += fdp/ obj-$(CONFIG_NFC_PN544) += pn544/ obj-$(CONFIG_NFC_MICROREAD) += microread/ diff --git a/drivers/nfc/pn5xx/Kconfig b/drivers/nfc/pn5xx/Kconfig new file mode 100644 index 000000000000..10db8c403866 --- /dev/null +++ b/drivers/nfc/pn5xx/Kconfig @@ -0,0 +1,12 @@ +config NFC_NXP_PN5XX + tristate "NXP PN5XX based driver" + depends on I2C + select CRC_CCITT + help + NXP PN5XX driver based on I2C. + This is a driver to provides I2C access to PN5xx and PN7120 NFC + Controller devices + + To compile this driver as a module, choose m here. The module will + be called pn5xx_i2c. + Say N if unsure. diff --git a/drivers/nfc/pn5xx/Makefile b/drivers/nfc/pn5xx/Makefile new file mode 100644 index 000000000000..b31bbb51423a --- /dev/null +++ b/drivers/nfc/pn5xx/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for PN5xx NFC driver +# + +obj-$(CONFIG_NFC_NXP_PN5XX) += pn5xx_i2c.o diff --git a/drivers/nfc/pn5xx/README.md b/drivers/nfc/pn5xx/README.md new file mode 100644 index 000000000000..c0c8d5c73757 --- /dev/null +++ b/drivers/nfc/pn5xx/README.md @@ -0,0 +1,2 @@ +# nxp-pn5xx +NXP's NFC Open Source Kernel mode driver diff --git a/drivers/nfc/pn5xx/pn5xx_i2c.c b/drivers/nfc/pn5xx/pn5xx_i2c.c new file mode 100644 index 000000000000..4b85b12aa7ff --- /dev/null +++ b/drivers/nfc/pn5xx/pn5xx_i2c.c @@ -0,0 +1,669 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2010 Trusted Logic S.A. + * Copyright 2015,2019-2023 NXP + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pn5xx_i2c.h" +#include +#include +#include +#include + +#include + +#define MAX_BUFFER_SIZE 512 + +#define MODE_OFF 0 +#define MODE_RUN 1 +#define MODE_FW 2 + +/* pn7120, pn548, pn547 and pn544 are supported */ +#define CHIP "pn544" +#define DRIVER_CARD "PN54x NFC" +#define DRIVER_DESC "NFC driver for PN54x Family" + +#ifndef CONFIG_OF +#define CONFIG_OF +#endif + +struct pn54x_dev { + wait_queue_head_t read_wq; + struct mutex read_mutex; + struct i2c_client *client; + struct miscdevice pn54x_device; + int ven_gpio; + int firm_gpio; + int irq_gpio; + int clkreq_gpio; + struct regulator *pvdd_reg; + struct regulator *vbat_reg; + struct regulator *pmuvcc_reg; + struct regulator *sevdd_reg; + bool irq_enabled; + spinlock_t irq_enabled_lock; +}; + +static struct task_struct *test_task; +/* Interrupt control and handler */ +static void pn54x_disable_irq(struct pn54x_dev *pn54x_dev) +{ + unsigned long flags; + + spin_lock_irqsave(&pn54x_dev->irq_enabled_lock, flags); + if (pn54x_dev->irq_enabled) { + disable_irq_nosync(pn54x_dev->client->irq); + pn54x_dev->irq_enabled = false; + } + spin_unlock_irqrestore(&pn54x_dev->irq_enabled_lock, flags); +} + +static irqreturn_t pn54x_dev_irq_handler(int irq, void *dev_id) +{ + struct pn54x_dev *pn54x_dev = dev_id; + + pn54x_disable_irq(pn54x_dev); + + /* Wake up waiting readers */ + wake_up(&pn54x_dev->read_wq); + + return IRQ_HANDLED; +} + +static struct pn54x_dev *gpn54x_dev = NULL; +static int pn54x_dev_loop_handler(void *data) +{ + struct pn54x_dev *pn54x_dev = (struct pn54x_dev *)gpn54x_dev; + + printk("In pn54x_dev_loop_handler...%x \n", data); + while (1) { + /* Wake up waiting readers */ + wake_up(&pn54x_dev->read_wq); + msleep(200); + } + + return 0; +} + +/* private functions */ +static int pn544_enable(struct pn54x_dev *dev, int mode) +{ + int r; + + /* + * turn on the regulators + * -- if the regulators were specified, they're required + */ + if (dev->pvdd_reg != NULL) { + r = regulator_enable(dev->pvdd_reg); + if (r < 0) { + pr_err("%s: not able to enable pvdd\n", __func__); + return r; + } + } + if (dev->vbat_reg != NULL) { + r = regulator_enable(dev->vbat_reg); + if (r < 0) { + pr_err("%s: not able to enable vbat\n", __func__); + goto enable_exit0; + } + } + if (dev->pmuvcc_reg != NULL) { + r = regulator_enable(dev->pmuvcc_reg); + if (r < 0) { + pr_err("%s: not able to enable pmuvcc\n", __func__); + goto enable_exit1; + } + } + if (dev->sevdd_reg != NULL) { + r = regulator_enable(dev->sevdd_reg); + if (r < 0) { + pr_err("%s: not able to enable sevdd\n", __func__); + goto enable_exit2; + } + } + + if (MODE_RUN == mode) { + printk("%s power on\n", __func__); + if (gpio_is_valid(dev->firm_gpio)) + gpio_set_value_cansleep(dev->firm_gpio, 0); + msleep(100); + } else if (MODE_FW == mode) { + /* power on with firmware download (requires hw reset) */ + pr_info("%s power on with firmware\n", __func__); + msleep(20); + if (gpio_is_valid(dev->firm_gpio)) { + gpio_set_value(dev->firm_gpio, 1); + } else { + pr_err("%s Unused Firm GPIO %d\n", __func__, mode); + return GPIO_UNUSED; + } + msleep(20); + msleep(100); + msleep(20); + } else { + pr_err("%s bad arg %d\n", __func__, mode); + return -EINVAL; + } + + return 0; + +enable_exit2: + if (dev->pmuvcc_reg) + regulator_disable(dev->pmuvcc_reg); +enable_exit1: + if (dev->vbat_reg) + regulator_disable(dev->vbat_reg); +enable_exit0: + if (dev->pvdd_reg) + regulator_disable(dev->pvdd_reg); + + return r; +} + +static void pn544_disable(struct pn54x_dev *dev) +{ + /* power off */ + printk("%s power off\n", __func__); + if (gpio_is_valid(dev->firm_gpio)) + gpio_set_value_cansleep(dev->firm_gpio, 0); + msleep(100); + + if (dev->sevdd_reg) + regulator_disable(dev->sevdd_reg); + if (dev->pmuvcc_reg) + regulator_disable(dev->pmuvcc_reg); + if (dev->vbat_reg) + regulator_disable(dev->vbat_reg); + if (dev->pvdd_reg) + regulator_disable(dev->pvdd_reg); +} + +/* driver functions */ +static ssize_t pn54x_dev_read(struct file *filp, char __user *buf, + size_t count, loff_t *offset) +{ + struct pn54x_dev *pn54x_dev = filp->private_data; + char tmp[MAX_BUFFER_SIZE]; + int ret; + + if (count > MAX_BUFFER_SIZE) + count = MAX_BUFFER_SIZE; + + mutex_lock(&pn54x_dev->read_mutex); + + /* Read data */ + ret = i2c_master_recv(pn54x_dev->client, tmp, count); + + mutex_unlock(&pn54x_dev->read_mutex); + + /* + * pn54x seems to be slow in handling I2C read requests + * so add 1ms delay after recv operation + */ + udelay(1000); + + if (ret < 0) + return ret; + + if (ret > count) { + pr_err("%s: received too many bytes from i2c (%d)\n", + __func__, ret); + return -EIO; + } + if (copy_to_user(buf, tmp, ret)) { + pr_err("%s : failed to copy to user space\n", __func__); + return -EFAULT; + } + return ret; + +fail: + mutex_unlock(&pn54x_dev->read_mutex); + return ret; +} + +static ssize_t pn54x_dev_write(struct file *filp, const char __user *buf, + size_t count, loff_t *offset) +{ + struct pn54x_dev *pn54x_dev; + char tmp[MAX_BUFFER_SIZE]; + int ret; + + pn54x_dev = filp->private_data; + + if (count > MAX_BUFFER_SIZE) + count = MAX_BUFFER_SIZE; + + if (copy_from_user(tmp, buf, count)) { + pr_err("%s : failed to copy from user space\n", __func__); + return -EFAULT; + } + + pr_debug("%s : writing %zu bytes.\n", __func__, count); + /* Write data */ + ret = i2c_master_send(pn54x_dev->client, tmp, count); + if (ret != count) { + pr_err("%s : i2c_master_send returned %d\n", __func__, ret); + pr_err("I2C addr is 0x%02X, name is %s\n", + pn54x_dev->client->addr, pn54x_dev->client->name); + ret = -EIO; + } + + /* + * pn54x seems to be slow in handling I2C write requests + * so add 1ms delay after I2C send oparation + */ + udelay(1000); + + return ret; +} + +static int pn54x_dev_open(struct inode *inode, struct file *filp) +{ + struct pn54x_dev *pn54x_dev = container_of(filp->private_data, + struct pn54x_dev, + pn54x_device); + + filp->private_data = pn54x_dev; + + pr_info("%s : %d,%d\n", __func__, imajor(inode), iminor(inode)); + + return 0; +} + +static int pn54x_dev_release(struct inode *inode, struct file *filp) +{ + pr_info("%s : closing %d,%d\n", __func__, imajor(inode), iminor(inode)); + + return 0; +} + +static long pn54x_dev_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct pn54x_dev *pn54x_dev = filp->private_data; + + pr_info("%s, cmd=%d, arg=%lu\n", __func__, cmd, arg); + switch (cmd) { + case PN544_SET_PWR: + if (arg == 2) { + /* power on w/FW */ + if (pn544_enable(pn54x_dev, arg) == GPIO_UNUSED) + return GPIO_UNUSED; + } else if (arg == 1) { + /* power on */ + pn544_enable(pn54x_dev, arg); + } else if (arg == 0) { + /* power off */ + pn544_disable(pn54x_dev); + } else { + pr_err("%s bad SET_PWR arg %lu\n", __func__, arg); + return -EINVAL; + } + break; + case PN54X_CLK_REQ: + if (arg == 1) { + if (gpio_is_valid(pn54x_dev->clkreq_gpio)) { + gpio_set_value(pn54x_dev->clkreq_gpio, 1); + } else { + pr_err("%s Unused Clkreq GPIO %lu\n", + __func__, arg); + return GPIO_UNUSED; + } + } else if (arg == 0) { + if (gpio_is_valid(pn54x_dev->clkreq_gpio)) { + gpio_set_value(pn54x_dev->clkreq_gpio, 0); + } else { + pr_err("%s Unused Clkreq GPIO %lu\n", + __func__, arg); + return GPIO_UNUSED; + } + } else { + pr_err("%s bad CLK_REQ arg %lu\n", __func__, arg); + return -EINVAL; + } + break; + default: + pr_err("%s bad ioctl %u\n", __func__, cmd); + return -EINVAL; + } + + return 0; +} + +static const struct file_operations pn54x_dev_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = pn54x_dev_read, + .write = pn54x_dev_write, + .open = pn54x_dev_open, + .release = pn54x_dev_release, + .unlocked_ioctl = pn54x_dev_ioctl, +}; + +/* Handlers for alternative sources of platform_data */ +#ifdef CONFIG_OF +/* Translate OpenFirmware node properties into platform_data */ +static int pn54x_get_pdata(struct device *dev, + struct pn544_i2c_platform_data *pdata) +{ + struct device_node *node; + int val; + + /* make sure there is actually a device tree node */ + node = dev->of_node; + if (!node) + return -ENODEV; + + memset(pdata, 0, sizeof(*pdata)); + + /* + * read the dev tree data + * firm pin - controls firmware download - OPTIONAL + */ + val = of_get_named_gpio(node, "firmware-gpios", 0); + if (val >= 0) { + pdata->firm_gpio = val; + } else { + pdata->firm_gpio = GPIO_UNUSED; + dev_warn(dev, "FIRM GPIO error getting from OF node\n"); + } + + /* clkreq pin - controls the clock to the PN547 - OPTIONAL */ + val = of_get_named_gpio(node, "nxp,pn54x-clkreq", 0); + if (val >= 0) { + pdata->clkreq_gpio = val; + } else { + pdata->clkreq_gpio = GPIO_UNUSED; + dev_warn(dev, + "CLKREQ GPIO error getting from OF node\n"); + } + + /* + * handle the regulator lines - these are optional + * PVdd - pad Vdd (544, 547) + * Vbat - Battery (544, 547) + * PMUVcc - UICC Power (544, 547) + * SEVdd - SE Power (544) + * + * Will attempt to load a matching Regulator Resource for each + * If no resource is provided, then the input will not be controlled + * Example: if only PVdd is provided, it is the only one that will be + * turned on/off. + */ + pdata->pvdd_reg = regulator_get(dev, "nxp,pn54x-pvdd"); + if (IS_ERR(pdata->pvdd_reg)) { + pr_err("%s: could not get nxp,pn54x-pvdd, rc=%ld\n", + __func__, PTR_ERR(pdata->pvdd_reg)); + pdata->pvdd_reg = NULL; + } + + pdata->vbat_reg = regulator_get(dev, "nxp,pn54x-vbat"); + if (IS_ERR(pdata->vbat_reg)) { + pr_err("%s: could not get nxp,pn54x-vbat, rc=%ld\n", + __func__, PTR_ERR(pdata->vbat_reg)); + pdata->vbat_reg = NULL; + } + + pdata->pmuvcc_reg = regulator_get(dev, "nxp,pn54x-pmuvcc"); + if (IS_ERR(pdata->pmuvcc_reg)) { + pr_err("%s: could not get nxp,pn54x-pmuvcc, rc=%ld\n", + __func__, PTR_ERR(pdata->pmuvcc_reg)); + pdata->pmuvcc_reg = NULL; + } + + pdata->sevdd_reg = regulator_get(dev, "nxp,pn54x-sevdd"); + if (IS_ERR(pdata->sevdd_reg)) { + pr_err("%s: could not get nxp,pn54x-sevdd, rc=%ld\n", + __func__, PTR_ERR(pdata->sevdd_reg)); + pdata->sevdd_reg = NULL; + } + + return 0; +} +#else +static int pn54x_get_pdata(struct device *dev, + struct pn544_i2c_platform_data *pdata) +{ + pdata = dev->platform_data; + return 0; +} +#endif + +/* pn54x_probe */ +#ifdef KERNEL_3_4_AND_OLDER +static int __devinit pn54x_probe(struct i2c_client *client) +#else +static int pn54x_probe(struct i2c_client *client) +#endif +{ + int ret; + /* gpio values, from board file or DT */ + struct pn544_i2c_platform_data *pdata; + struct pn544_i2c_platform_data tmp_pdata; + /* internal device specific data */ + struct pn54x_dev *pn54x_dev; + + pr_info("%s\n", __func__); + + /* + * ---- retrieve the platform data ---- + * If the dev.platform_data is NULL, then + * attempt to read from the device tree + */ + if (!client->dev.platform_data) { + ret = pn54x_get_pdata(&(client->dev), &tmp_pdata); + if (ret) + return ret; + + pdata = &tmp_pdata; + } else { + pdata = client->dev.platform_data; + } + + if (pdata == NULL) { + pr_err("%s : nfc probe fail\n", __func__); + return -ENODEV; + } + + /* validate the the adapter has basic I2C functionality */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("%s : need I2C_FUNC_I2C\n", __func__); + return -ENODEV; + } + + if (gpio_is_valid(pdata->firm_gpio)) { + pr_info("%s: request firm_gpio %d\n", + __func__, pdata->firm_gpio); + ret = gpio_request(pdata->firm_gpio, "nfc_firm"); + if (ret) { + pr_err("%s :not able to get GPIO firm_gpio\n", + __func__); + goto err_firm; + } + } + + if (gpio_is_valid(pdata->clkreq_gpio)) { + pr_info("%s: request clkreq_gpio %d\n", + __func__, pdata->clkreq_gpio); + ret = gpio_request(pdata->clkreq_gpio, "nfc_clkreq"); + if (ret) { + pr_err("%s :not able to get GPIO clkreq_gpio\n", + __func__); + goto err_clkreq; + } + } + + /* allocate the pn54x driver information structure */ + pn54x_dev = kzalloc(sizeof(*pn54x_dev), GFP_KERNEL); + if (pn54x_dev == NULL) { + dev_err(&client->dev, + "failed to allocate memory for module data\n"); + ret = -ENOMEM; + goto err_exit; + } + + /* store the platform data in the driver info struct */ + pn54x_dev->firm_gpio = pdata->firm_gpio; + pn54x_dev->clkreq_gpio = pdata->clkreq_gpio; + pn54x_dev->pvdd_reg = pdata->pvdd_reg; + pn54x_dev->vbat_reg = pdata->vbat_reg; + pn54x_dev->pmuvcc_reg = pdata->pmuvcc_reg; + pn54x_dev->sevdd_reg = pdata->sevdd_reg; + + pn54x_dev->client = client; + + if (gpio_is_valid(pn54x_dev->firm_gpio)) { + ret = gpio_direction_output(pn54x_dev->firm_gpio, 0); + if (ret < 0) { + pr_err("%s : not able to set firm_gpio as output\n", + __func__); + goto err_exit; + } + } + + if (gpio_is_valid(pn54x_dev->clkreq_gpio)) { + ret = gpio_direction_output(pn54x_dev->clkreq_gpio, 0); + if (ret < 0) { + pr_err("%s : not able to set clkreq_gpio as output\n", + __func__); + goto err_exit; + } + } + + /* init mutex and queues */ + init_waitqueue_head(&pn54x_dev->read_wq); + mutex_init(&pn54x_dev->read_mutex); + spin_lock_init(&pn54x_dev->irq_enabled_lock); + + /* register as a misc device - character based with one entry point */ + pn54x_dev->pn54x_device.minor = MISC_DYNAMIC_MINOR; + pn54x_dev->pn54x_device.name = CHIP; + pn54x_dev->pn54x_device.fops = &pn54x_dev_fops; + ret = misc_register(&pn54x_dev->pn54x_device); + if (ret) { + pr_err("%s : misc_register failed\n", __FILE__); + goto err_misc_register; + } + + /* + * request irq. the irq is set whenever the chip has data available + * for reading. it is cleared when all data has been read. + */ + i2c_set_clientdata(client, pn54x_dev); + + return 0; + +err_request_irq_failed: + misc_deregister(&pn54x_dev->pn54x_device); +err_misc_register: +err_exit: + if (gpio_is_valid(pdata->clkreq_gpio)) + gpio_free(pdata->clkreq_gpio); +err_clkreq: + if (gpio_is_valid(pdata->firm_gpio)) + gpio_free(pdata->firm_gpio); +err_firm: +err_ven: + return ret; +} + +#ifdef KERNEL_3_4_AND_OLDER +static int __devexit pn54x_remove(struct i2c_client *client) +#else +static void pn54x_remove(struct i2c_client *client) +#endif +{ + struct pn54x_dev *pn54x_dev; + + pr_info("%s\n", __func__); + + pn54x_dev = i2c_get_clientdata(client); + free_irq(client->irq, pn54x_dev); + misc_deregister(&pn54x_dev->pn54x_device); + mutex_destroy(&pn54x_dev->read_mutex); + if (gpio_is_valid(pn54x_dev->firm_gpio)) + gpio_free(pn54x_dev->firm_gpio); + if (gpio_is_valid(pn54x_dev->clkreq_gpio)) + gpio_free(pn54x_dev->clkreq_gpio); + regulator_put(pn54x_dev->pvdd_reg); + regulator_put(pn54x_dev->vbat_reg); + regulator_put(pn54x_dev->pmuvcc_reg); + regulator_put(pn54x_dev->sevdd_reg); + + kfree(pn54x_dev); + +#ifdef KERNEL_3_4_AND_OLDER + return 0; +#else + return; +#endif +} + +#ifdef CONFIG_OF +static struct of_device_id pn54x_dt_match[] = { + { .compatible = "nxp,pn547", }, + { .compatible = "nxp,pn544", }, + {}, +}; +MODULE_DEVICE_TABLE(of, pn54x_dt_match); +#endif + +static const struct i2c_device_id pn54x_id[] = { + { "pn547", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, pn54x_id); + +static struct i2c_driver pn54x_driver = { + .id_table = pn54x_id, + .probe = pn54x_probe, +#ifdef KERNEL_3_4_AND_OLDER + .remove = __devexit_p(pn54x_remove), +#else + .remove = pn54x_remove, +#endif + .driver = { + .owner = THIS_MODULE, + .name = "pn544", + .of_match_table = pn54x_dt_match, + }, +}; + +/* module load/unload record keeping */ +static int __init pn54x_dev_init(void) +{ + pr_info("%s\n", __func__); + return i2c_add_driver(&pn54x_driver); +} + +static void __exit pn54x_dev_exit(void) +{ + pr_info("%s\n", __func__); + i2c_del_driver(&pn54x_driver); +} + +module_init(pn54x_dev_init); +module_exit(pn54x_dev_exit); + +MODULE_AUTHOR("Sylvain Fonteneau"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/nfc/pn5xx/pn5xx_i2c.h b/drivers/nfc/pn5xx/pn5xx_i2c.h new file mode 100644 index 000000000000..4b53655007e0 --- /dev/null +++ b/drivers/nfc/pn5xx/pn5xx_i2c.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2010 Trusted Logic S.A. + * Copyright 2015,2019-2023 NXP + * + */ + +#define PN544_MAGIC 0xE9 + +/* + * PN544 power control via ioctl + * PN544_SET_PWR(0): power off + * PN544_SET_PWR(1): power on + * PN544_SET_PWR(2): reset and power on with firmware download enabled + */ + +#define PWR_OFF 0 +#define PWR_ON 1 +#define PWR_FW 2 + +#define CLK_OFF 0 +#define CLK_ON 1 + +#define GPIO_UNUSED -1 + +#define PN544_SET_PWR _IOW(PN544_MAGIC, 0x01, unsigned int) +#define PN54X_CLK_REQ _IOW(PN544_MAGIC, 0x02, unsigned int) + +struct pn544_i2c_platform_data { + unsigned int irq_gpio; + unsigned int ven_gpio; + unsigned int firm_gpio; + unsigned int clkreq_gpio; + struct regulator *pvdd_reg; + struct regulator *vbat_reg; + struct regulator *pmuvcc_reg; + struct regulator *sevdd_reg; +}; diff --git a/drivers/nfc/pn5xx/sample_devicetree.txt b/drivers/nfc/pn5xx/sample_devicetree.txt new file mode 100644 index 000000000000..0e81959052fc --- /dev/null +++ b/drivers/nfc/pn5xx/sample_devicetree.txt @@ -0,0 +1,17 @@ +Example: + +&i2c{ + + status = "okay"; + + pn547: pn547@29 { + + compatible = "nxp,pn547"; + + reg = <0x29>; + clock-frequency = <400000>; + + interrupt-gpios = <&gpio2 17 0>; + enable-gpios = <&gpio4 21 0>; + }; +}; diff --git a/drivers/pmdomain/imx/scu-pd.c b/drivers/pmdomain/imx/scu-pd.c index 38f3cdd21042..97d1a537917a 100644 --- a/drivers/pmdomain/imx/scu-pd.c +++ b/drivers/pmdomain/imx/scu-pd.c @@ -171,6 +171,7 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { { "esai1", IMX_SC_R_ESAI_1, 1, false, 0 }, { "spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 }, { "spdif1", IMX_SC_R_SPDIF_1, 1, false, 0 }, + { "gpt", IMX_SC_R_GPT_5, 4, true, 5 }, { "sai", IMX_SC_R_SAI_0, 3, true, 0 }, { "sai3", IMX_SC_R_SAI_3, 1, false, 0 }, { "sai4", IMX_SC_R_SAI_4, 1, false, 0 }, diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c index 91cc6ffa0095..f32e633a3f65 100644 --- a/drivers/ptp/ptp_chardev.c +++ b/drivers/ptp/ptp_chardev.c @@ -121,6 +121,7 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) struct ptp_clock_time *pct; unsigned int i, pin_index; struct ptp_pin_desc pd; + struct ptp_convert_timestamps convert_ts; struct timespec64 ts; int enable, err = 0; @@ -422,6 +423,28 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) mutex_unlock(&ptp->pincfg_mux); break; + case PTP_CONVERT_TIMESTAMPS: + if (copy_from_user(&convert_ts, (void __user *)arg, sizeof(convert_ts))) { + err = -EFAULT; + break; + } + + if (convert_ts.n_ts > PTP_MAX_CONVERT_TS_NUM) { + err = -EINVAL; + break; + } + + if (ptp->is_virtual_clock) + err = ptp_vclock_convert_timestamps(ptp, convert_ts.src_ts, convert_ts.n_ts, + convert_ts.dst_phc_index, convert_ts.dst_ts); + else + err = ptp_clock_convert_timestamps(ptp, convert_ts.src_ts, convert_ts.n_ts, + convert_ts.dst_phc_index, convert_ts.dst_ts); + + if (!err && copy_to_user((void __user *)arg, &convert_ts, sizeof(convert_ts))) + err = -EFAULT; + + break; default: err = -ENOTTY; break; diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c index 9a50bfb56453..b857cfca55b7 100644 --- a/drivers/ptp/ptp_clock.c +++ b/drivers/ptp/ptp_clock.c @@ -201,6 +201,25 @@ static void ptp_aux_kworker(struct kthread_work *work) kthread_queue_delayed_work(ptp->kworker, &ptp->aux_work, delay); } +/* Convert from physical to virtual time domains. */ +int ptp_clock_convert_timestamps(struct ptp_clock *ptp, + struct ptp_clock_time *src_ts, unsigned int n_ts, int dst_phc_index, + struct ptp_clock_time *dst_ts) +{ + int rc; + + /* If the destination time domain is the same as the source, quick return. */ + if (ptp->index == dst_phc_index) { + memcpy(dst_ts, src_ts, n_ts * sizeof(struct ptp_clock_time)); + return 0; + } + + rc = ptp_vclock_convert_from_hw_timestamps(ptp, src_ts, n_ts, + dst_phc_index, dst_ts); + + return rc; +} + /* public interface */ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info, diff --git a/drivers/ptp/ptp_private.h b/drivers/ptp/ptp_private.h index b8d4f61f14be..03136915423f 100644 --- a/drivers/ptp/ptp_private.h +++ b/drivers/ptp/ptp_private.h @@ -141,6 +141,24 @@ extern const struct attribute_group *ptp_groups[]; int ptp_populate_pin_groups(struct ptp_clock *ptp); void ptp_cleanup_pin_groups(struct ptp_clock *ptp); +/* + * see ptp_vclock.c + */ + struct ptp_vclock *ptp_vclock_register(struct ptp_clock *pclock); void ptp_vclock_unregister(struct ptp_vclock *vclock); +int ptp_vclock_convert_from_hw_timestamps(struct ptp_clock *ptp, + struct ptp_clock_time *src_ts, unsigned int n_ts, int dst_phc_index, + struct ptp_clock_time *dst_ts); +int ptp_vclock_convert_timestamps(struct ptp_clock *ptp, struct ptp_clock_time *src_ts, + unsigned int n_ts, int dst_phc_index, + struct ptp_clock_time *dst_ts); + +/* + * see ptp_clock.c + */ + +int ptp_clock_convert_timestamps(struct ptp_clock *ptp, + struct ptp_clock_time *src_ts, unsigned int n_ts, int dst_phc_index, + struct ptp_clock_time *dst_ts); #endif diff --git a/drivers/ptp/ptp_sysfs.c b/drivers/ptp/ptp_sysfs.c index aefc06ae5d09..dd762d776de8 100644 --- a/drivers/ptp/ptp_sysfs.c +++ b/drivers/ptp/ptp_sysfs.c @@ -220,14 +220,21 @@ static ssize_t n_vclocks_store(struct device *dev, /* Need to create more vclocks */ if (num > ptp->n_vclocks) { - for (i = 0; i < num - ptp->n_vclocks; i++) { + unsigned int nb_vclocks = num - ptp->n_vclocks; + + for (i = 0; i < nb_vclocks; i++) { vclock = ptp_vclock_register(ptp); - if (!vclock) + if (!vclock) { + dev_err(dev, "failed to create more than %u (out of %u) virtual clocks for ptp%d\n", + i, nb_vclocks, ptp->index); goto out; + } - *(ptp->vclock_index + ptp->n_vclocks + i) = + *(ptp->vclock_index + ptp->n_vclocks) = vclock->clock->index; + ptp->n_vclocks++; + dev_info(dev, "new virtual clock ptp%d\n", vclock->clock->index); } @@ -241,6 +248,8 @@ static ssize_t n_vclocks_store(struct device *dev, for (i = 1; i <= ptp->n_vclocks - num; i++) *(ptp->vclock_index + ptp->n_vclocks - i) = -1; + + ptp->n_vclocks = num; } /* Need to inform about changed physical clock behavior */ @@ -251,7 +260,6 @@ static ssize_t n_vclocks_store(struct device *dev, dev_info(dev, "guarantee physical clock free running\n"); } - ptp->n_vclocks = num; mutex_unlock(&ptp->n_vclocks_mux); return count; diff --git a/drivers/ptp/ptp_vclock.c b/drivers/ptp/ptp_vclock.c index dcf752c9e045..bb26d2535218 100644 --- a/drivers/ptp/ptp_vclock.c +++ b/drivers/ptp/ptp_vclock.c @@ -40,6 +40,24 @@ static void ptp_vclock_hash_del(struct ptp_vclock *vclock) synchronize_rcu(); } +/* This function and its return value (the vclock pointer) must be used + * inside the same RCU read critical section + */ +static struct ptp_vclock *ptp_vclock_lookup(int vclock_index) +{ + unsigned int hash = vclock_index % HASH_SIZE(vclock_hash); + struct ptp_vclock *vclock; + + hlist_for_each_entry_rcu(vclock, &vclock_hash[hash], vclock_hash_node) { + if (vclock->clock->index != vclock_index) + continue; + + return vclock; + } + + return NULL; +} + static int ptp_vclock_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) { struct ptp_vclock *vclock = info_to_vclock(ptp); @@ -154,6 +172,182 @@ static long ptp_vclock_refresh(struct ptp_clock_info *ptp) return PTP_VCLOCK_REFRESH_INTERVAL; } +/* Convert from virtual clock to physical time domain */ +static u64 ptp_vclock_to_hw_time(const struct timecounter *tc, u64 nsec) +{ + u64 ns_hw = tc->cycle_last; + u64 delta; + + /* TODO implement a less costly conversion using a shift/mult rather + * than an integer division ? + */ + if (nsec > tc->nsec) { + delta = nsec - tc->nsec; + delta <<= tc->cc->shift; + ns_hw += div_u64(delta, tc->cc->mult); + } else { + delta = tc->nsec - nsec; + delta <<= tc->cc->shift; + ns_hw -= div_u64(delta, tc->cc->mult); + } + + return ns_hw; +} + +static inline s64 ptp_clock_time_to_ns(const struct ptp_clock_time *ptp_time) +{ + struct timespec64 ts; + + ts.tv_sec = ptp_time->sec; + ts.tv_nsec = ptp_time->nsec; + + return timespec64_to_ns(&ts); +} + +static inline struct ptp_clock_time ns_to_ptp_clock_time(s64 nsec) +{ + struct ptp_clock_time ptp_time = { 0 }; + struct timespec64 ts; + + ts = ns_to_timespec64(nsec); + + ptp_time.sec = ts.tv_sec; + ptp_time.nsec = ts.tv_nsec; + + return ptp_time; +} + +/* This function converts from a virtual domain to a destination + * clock domain (either virtual or physical) + */ +int ptp_vclock_convert_timestamps(struct ptp_clock *ptp, struct ptp_clock_time *src_ts, + unsigned int n_ts, int dst_phc_index, + struct ptp_clock_time *dst_ts) +{ + unsigned int hash = dst_phc_index % HASH_SIZE(vclock_hash); + struct ptp_vclock *vclock = info_to_vclock(ptp->info); + struct ptp_vclock *vclock_dst; + int i, rc = 0; + u64 dst_ns; + + /* The destination clock domain is the same as the source, early exit. */ + if (dst_phc_index == vclock->clock->index) { + memcpy(dst_ts, src_ts, n_ts * sizeof(struct ptp_clock_time)); + goto out; + } + + rcu_read_lock(); + + vclock_dst = ptp_vclock_lookup(dst_phc_index); + + if (vclock_dst) { + /* Check that both virtual clocks share the same physical parent. */ + if (vclock_dst->pclock != vclock->pclock) { + rc = -EINVAL; + goto out_unlock_rcu; + } + + if (mutex_lock_interruptible(&vclock->lock)) { + rc = -ERESTARTSYS; + goto out_unlock_rcu; + } + + if (mutex_lock_interruptible(&vclock_dst->lock)) { + mutex_unlock(&vclock->lock); + rc = -ERESTARTSYS; + goto out_unlock_rcu; + } + + for (i = 0; i < n_ts; i++) { + /* Convert from source virtual to physical time domain. */ + dst_ns = ptp_vclock_to_hw_time(&vclock->tc, + ptp_clock_time_to_ns(src_ts + i)); + /* Convert from physical to destination virtual time domain. */ + dst_ns = timecounter_cyc2time(&vclock_dst->tc, dst_ns); + + *(dst_ts + i) = ns_to_ptp_clock_time(dst_ns); + } + + mutex_unlock(&vclock_dst->lock); + mutex_unlock(&vclock->lock); + } else { + /* Check that the destination physical clock is the parent of the source + * virtual clock. + */ + if (vclock->pclock->index != dst_phc_index) { + rc = -EINVAL; + goto out_unlock_rcu; + } + + if (mutex_lock_interruptible(&vclock->lock)) { + rc = -EINTR; + goto out_unlock_rcu; + } + + /* Convert from virtual to physical time domain . */ + for (i = 0; i < n_ts; i++) { + dst_ns = ptp_vclock_to_hw_time(&vclock->tc, + ptp_clock_time_to_ns(src_ts + i)); + *(dst_ts + i) = ns_to_ptp_clock_time(dst_ns); + } + + mutex_unlock(&vclock->lock); + + } + +out_unlock_rcu: + rcu_read_unlock(); + +out: + return rc; +} + +/* Convert from physical to virtual time domain. */ +int ptp_vclock_convert_from_hw_timestamps(struct ptp_clock *ptp, struct ptp_clock_time *src_ts, + unsigned int n_ts, int dst_vclock_index, + struct ptp_clock_time *dst_ts) +{ + unsigned int hash = dst_vclock_index % HASH_SIZE(vclock_hash); + struct ptp_vclock *vclock; + int i, rc = 0; + u64 dst_ns; + + rcu_read_lock(); + + vclock = ptp_vclock_lookup(dst_vclock_index); + + /* Check that dst_vclock_index point to a virtual clock. */ + if (!vclock) { + rc = -EINVAL; + goto out_unlock_rcu; + } + + /* Check that the source physical clock is the parent of the + * destination virtual clock. + */ + if (vclock->pclock != ptp) { + rc = -EINVAL; + goto out_unlock_rcu; + } + + if (mutex_lock_interruptible(&vclock->lock)) { + rc = -ERESTARTSYS; + goto out_unlock_rcu; + } + + for (i = 0; i < n_ts; i++) { + dst_ns = timecounter_cyc2time(&vclock->tc, ptp_clock_time_to_ns(src_ts + i)); + *(dst_ts + i) = ns_to_ptp_clock_time(dst_ns); + } + + mutex_unlock(&vclock->lock); + +out_unlock_rcu: + rcu_read_unlock(); + + return rc; +} + static const struct ptp_clock_info ptp_vclock_info = { .owner = THIS_MODULE, .name = "ptp virtual clock", @@ -276,17 +470,15 @@ ktime_t ptp_convert_timestamp(const ktime_t *hwtstamp, int vclock_index) rcu_read_lock(); - hlist_for_each_entry_rcu(vclock, &vclock_hash[hash], vclock_hash_node) { - if (vclock->clock->index != vclock_index) - continue; - + vclock = ptp_vclock_lookup(vclock_index); + if (vclock) { if (mutex_lock_interruptible(&vclock->lock)) - break; + goto out_unlock_rcu; vclock_ns = timecounter_cyc2time(&vclock->tc, ns); mutex_unlock(&vclock->lock); - break; } +out_unlock_rcu: rcu_read_unlock(); return ns_to_ktime(vclock_ns); diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index 5e15dd127bde..449ff92129cf 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -6,7 +6,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -20,11 +22,16 @@ #include #include #include +#include #include #include #include #include +#include + +#include + #include "remoteproc_elf_helpers.h" #include "remoteproc_internal.h" @@ -73,6 +80,8 @@ #define IMX_SIP_RPROC_STARTED 0x01 #define IMX_SIP_RPROC_STOP 0x02 +#define IMX_SIP_CPU_OFF 0xC200000D + #define IMX_SC_IRQ_GROUP_REBOOTED 5 /** @@ -125,6 +134,8 @@ struct imx_rproc { struct device_link **pd_dev_link; u32 startup_delay; struct sys_off_data data; + cpumask_t cpus; + cpumask_t offlined_cpus; }; static const struct imx_rproc_att imx_rproc_att_imx95_m7[] = { @@ -157,6 +168,9 @@ static const struct imx_rproc_att imx_rproc_att_imx93[] = { { 0x80000000, 0x80000000, 0x10000000, 0 }, { 0x90000000, 0x80000000, 0x10000000, 0 }, + { 0xa0000000, 0xa0000000, 0x10000000, 0 }, + { 0xb0000000, 0xa0000000, 0x10000000, 0 }, + { 0xC0000000, 0xC0000000, 0x10000000, 0 }, { 0xD0000000, 0xC0000000, 0x10000000, 0 }, }; @@ -384,12 +398,102 @@ static const struct imx_rproc_dcfg imx_rproc_cfg_imx93 = { .method = IMX_RPROC_SMC, }; +static const struct imx_rproc_dcfg imx_rproc_cfg_psci = { + .att = NULL, + .att_size = 0, + .method = IMX_RPROC_PSCI, +}; + static const struct imx_rproc_dcfg imx_rproc_cfg_imx95_m7 = { .att = imx_rproc_att_imx95_m7, .att_size = ARRAY_SIZE(imx_rproc_att_imx95_m7), .method = IMX_RPROC_SMC, }; +static int imx_rproc_psci_start(struct rproc *rproc) +{ + struct imx_rproc *priv = rproc->priv; + struct device *dev = priv->dev; + unsigned int cpu; + int ret; + + if (cpumask_empty(&priv->cpus)) { + dev_err(dev, "No CPU Core assigned!\n"); + return -ENODEV; + } + + for_each_cpu(cpu, &priv->cpus) { + if (cpu_online(cpu)) { + ret = remove_cpu(cpu); + if (ret) + goto err; + cpumask_set_cpu(cpu, &priv->offlined_cpus); + } + } + + cpu = cpumask_first(&priv->cpus); + ret = psci_ops.cpu_on(cpu_logical_map(cpu), rproc->bootaddr); + if (ret) { + dev_err(dev, "Boot failed on CPU Core %d\n", cpu); + goto err; + } + + return 0; + +err: + for_each_cpu(cpu, &priv->cpus) { + if (!cpu_online(cpu) && add_cpu(cpu) == 0) + cpumask_clear_cpu(cpu, &priv->offlined_cpus); + } + + return ret; +} + +static int imx_rproc_psci_stop(struct rproc *rproc) +{ + struct imx_rproc *priv = rproc->priv; + struct device *dev = priv->dev; + struct arm_smccc_res res; + unsigned int cpu; + unsigned long start, end; + int err; + + for_each_cpu(cpu, &priv->cpus) { + /* Check CPU status */ + err = psci_ops.affinity_info(cpu_logical_map(cpu), 0); + if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) + continue; + + /* Bring CPU to be off */ + arm_smccc_smc(IMX_SIP_CPU_OFF, cpu, 0, + 0, 0, 0, 0, 0, &res); + start = jiffies; + end = start + msecs_to_jiffies(100); + do { + err = psci_ops.affinity_info(cpu_logical_map(cpu), 0); + if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) { + pr_info("CPU%d is killed (polled %d ms)\n", cpu, + jiffies_to_msecs(jiffies - start)); + break; + } + + usleep_range(100, 1000); + } while (time_before(jiffies, end)); + } + + /* Return back freed CPU Core to Linux kernel */ + for_each_cpu(cpu, &priv->cpus) { + if (cpumask_test_cpu(cpu, &priv->offlined_cpus)) { + if (add_cpu(cpu) != 0) + dev_err(dev, "Failed to bring CPU %d back to be online", cpu); + cpumask_clear_cpu(cpu, &priv->offlined_cpus); + } + } + + return 0; +} + + static int imx_rproc_start(struct rproc *rproc) { struct imx_rproc *priv = rproc->priv; @@ -424,6 +528,9 @@ static int imx_rproc_start(struct rproc *rproc) case IMX_RPROC_SCU_API: ret = imx_sc_pm_cpu_start(priv->ipc_handle, priv->rsrc_id, true, priv->entry); break; + case IMX_RPROC_PSCI: + ret = imx_rproc_psci_start(rproc); + break; default: return -EOPNOTSUPP; } @@ -470,6 +577,9 @@ static int imx_rproc_stop(struct rproc *rproc) case IMX_RPROC_SCU_API: ret = imx_sc_pm_cpu_start(priv->ipc_handle, priv->rsrc_id, false, priv->entry); break; + case IMX_RPROC_PSCI: + ret = imx_rproc_psci_stop(rproc); + break; default: return -EOPNOTSUPP; } @@ -488,6 +598,12 @@ static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da, const struct imx_rproc_dcfg *dcfg = priv->dcfg; int i; + /* No need to translate for Cortex-A Core */ + if (dcfg->method == IMX_RPROC_PSCI) { + *sys = da; + return 0; + } + /* parse address translation table */ for (i = 0; i < dcfg->att_size; i++) { const struct imx_rproc_att *att = &dcfg->att[i]; @@ -558,7 +674,13 @@ static int imx_rproc_mem_alloc(struct rproc *rproc, void *va; dev_dbg(dev, "map memory: %p+%zx\n", &mem->dma, mem->len); - va = ioremap_wc(mem->dma, mem->len); + if (dev_is_dma_coherent(dev) && + !strncmp(mem->name, "vdev0vring", strlen("vdev0vring"))) { + va = ioremap_cache(mem->dma, mem->len); + } else { + va = ioremap_wc(mem->dma, mem->len); + } + if (IS_ERR_OR_NULL(va)) { dev_err(dev, "Unable to map memory region: %p+%zx\n", &mem->dma, mem->len); @@ -588,19 +710,15 @@ static int imx_rproc_prepare(struct rproc *rproc) struct rproc_mem_entry *mem; struct reserved_mem *rmem; u32 da; + int index = 0; /* Register associated reserved memory regions */ of_phandle_iterator_init(&it, np, "memory-region", NULL, 0); while (of_phandle_iterator_next(&it) == 0) { - /* - * Ignore the first memory region which will be used vdev buffer. - * No need to do extra handlings, rproc_add_virtio_dev will handle it. - */ - if (!strcmp(it.node->name, "vdev0buffer")) - continue; - - if (!strcmp(it.node->name, "rsc-table")) + if (!strcmp(it.node->name, "rsc-table")) { + index++; continue; + } rmem = of_reserved_mem_lookup(it.node); if (!rmem) { @@ -612,19 +730,28 @@ static int imx_rproc_prepare(struct rproc *rproc) /* No need to translate pa to da, i.MX use same map */ da = rmem->base; - /* Register memory region */ - mem = rproc_mem_entry_init(priv->dev, NULL, (dma_addr_t)rmem->base, rmem->size, da, - imx_rproc_mem_alloc, imx_rproc_mem_release, - it.node->name); + if (strcmp(it.node->name, "vdev0buffer")) { + /* Register memory region */ + mem = rproc_mem_entry_init(priv->dev, NULL, (dma_addr_t)rmem->base, + rmem->size, da, imx_rproc_mem_alloc, + imx_rproc_mem_release, it.node->name); - if (mem) { - rproc_coredump_add_segment(rproc, da, rmem->size); + if (mem) { + rproc_coredump_add_segment(rproc, da, rmem->size); + } } else { + + /* Register reserved memory for vdev buffer alloc */ + mem = rproc_of_resm_mem_entry_init(priv->dev, index, rmem->size, + rmem->base, it.node->name); + } + + if (!mem) { of_node_put(it.node); return -ENOMEM; } - rproc_add_carveout(rproc, mem); + index++; } return 0; @@ -1046,6 +1173,25 @@ static int imx_rproc_detect_mode(struct imx_rproc *priv) case IMX_RPROC_NONE: priv->rproc->state = RPROC_DETACHED; return 0; + case IMX_RPROC_PSCI: + unsigned int cpu; + int cpu_aff; + + priv->rproc->state = RPROC_DETACHED; + for_each_cpu(cpu, &priv->cpus) { + cpu_aff = psci_ops.affinity_info(cpu_logical_map(cpu), 0); + if (cpu_aff == PSCI_0_2_AFFINITY_LEVEL_OFF) { + priv->rproc->state = RPROC_OFFLINE; + break; + } + + /* in psci on state but not running Linux */ + if (cpu_online(cpu)) { + priv->rproc->state = RPROC_OFFLINE; + break; + } + } + return 0; case IMX_RPROC_SMC: arm_smccc_smc(IMX_SIP_RPROC, IMX_SIP_RPROC_STARTED, 0, 0, 0, 0, 0, 0, &res); if (res.a0) @@ -1210,10 +1356,12 @@ static int imx_rproc_probe(struct platform_device *pdev) struct imx_rproc *priv; struct rproc *rproc; const struct imx_rproc_dcfg *dcfg; + unsigned int cpus; + unsigned long cpus_bits; int ret; - /* set some other name then imx */ - rproc = rproc_alloc(dev, "imx-rproc", &imx_rproc_ops, + /* set node name to be remote processor name */ + rproc = rproc_alloc(dev, np->name, &imx_rproc_ops, NULL, sizeof(*priv)); if (!rproc) return -ENOMEM; @@ -1249,6 +1397,17 @@ static int imx_rproc_probe(struct platform_device *pdev) goto err_put_mbox; } + /* Init priv->cpus before detect mode */ + ret = of_property_read_u32(dev->of_node, "fsl,cpus-bits", &cpus); + if (ret) { + cpumask_clear(&priv->cpus); + } else { + cpus_bits = cpus; + bitmap_copy(cpumask_bits(&priv->cpus), &cpus_bits, + min((unsigned int)nr_cpumask_bits, + (unsigned int)sizeof(unsigned long))); + } + ret = imx_rproc_detect_mode(priv); if (ret) goto err_put_mbox; @@ -1332,6 +1491,7 @@ static const struct of_device_id imx_rproc_of_match[] = { { .compatible = "fsl,imx8ulp-cm33", .data = &imx_rproc_cfg_imx8ulp }, { .compatible = "fsl,imx93-cm33", .data = &imx_rproc_cfg_imx93 }, { .compatible = "fsl,imx95-cm7", .data = &imx_rproc_cfg_imx95_m7 }, + { .compatible = "fsl,imx-rproc-psci", .data = &imx_rproc_cfg_psci }, {}, }; MODULE_DEVICE_TABLE(of, imx_rproc_of_match); diff --git a/drivers/remoteproc/imx_rproc.h b/drivers/remoteproc/imx_rproc.h index 79a1b8956d14..052d43fe563a 100644 --- a/drivers/remoteproc/imx_rproc.h +++ b/drivers/remoteproc/imx_rproc.h @@ -24,6 +24,8 @@ enum imx_rproc_method { IMX_RPROC_SMC, /* Through System Control Unit API */ IMX_RPROC_SCU_API, + /* Through ARM64 PSCI */ + IMX_RPROC_PSCI, }; struct imx_rproc_dcfg { diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 695cce218e8c..3104f28d871d 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -38,6 +38,7 @@ #include #include #include +#include #include "remoteproc_internal.h" @@ -522,6 +523,9 @@ static int rproc_handle_vdev(struct rproc *rproc, void *ptr, return PTR_ERR(pdev); } + /* inherit parent's dma_coherent */ + pdev->dev.dma_coherent = dev_is_dma_coherent(dev); + return 0; } @@ -1285,7 +1289,7 @@ static int rproc_start(struct rproc *rproc, const struct firmware *fw) * that any subsequent changes will be applied to the loaded version. */ loaded_table = rproc_find_loaded_rsc_table(rproc, fw); - if (loaded_table) { + if (loaded_table && rproc->cached_table) { memcpy(loaded_table, rproc->cached_table, rproc->table_sz); rproc->table_ptr = loaded_table; } @@ -2463,6 +2467,8 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, rproc->dev.type = &rproc_type; rproc->dev.class = &rproc_class; rproc->dev.driver_data = rproc; + /* inherit parent's dma_coherent */ + rproc->dev.dma_coherent = dev_is_dma_coherent(dev); idr_init(&rproc->notifyids); rproc->name = kstrdup_const(name, GFP_KERNEL); diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig index afba55506632..968ddd1fac18 100644 --- a/drivers/rpmsg/Kconfig +++ b/drivers/rpmsg/Kconfig @@ -81,10 +81,17 @@ config RPMSG_VIRTIO select RPMSG_NS select VIRTIO +config RPMSG_PERF + tristate "RPMSG performance test driver" + depends on RPMSG + default m + help + Say Y here to enable the RPMSG performance test driver. + config HAVE_IMX_RPMSG bool "IMX RPMSG driver on the AMP SOCs" default y - depends on IMX_MBOX + depends on IMX_MBOX || GENERIC_SOFTWARE_MAILBOX select RPMSG_VIRTIO help Say y here to enable support for the iMX Rpmsg Driver providing @@ -118,4 +125,11 @@ config IMX_RPMSG_TTY executed by the command "echo > ", thus remote M core would receive the string. +config RPMSG_8M_BUF + bool "RPMSG 8M bytes buffer support" + default n + depends on HAVE_IMX_RPMSG + help + Say y here to enable support for 8M bytes Vring buffer for rpmsg. + endmenu diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile index 7c4ee52e92c4..f64fa4d0ada2 100644 --- a/drivers/rpmsg/Makefile +++ b/drivers/rpmsg/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_RPMSG) += rpmsg_core.o obj-$(CONFIG_RPMSG_CHAR) += rpmsg_char.o +obj-$(CONFIG_RPMSG_PERF) += rpmsg_perf.o obj-$(CONFIG_RPMSG_CTRL) += rpmsg_ctrl.o obj-$(CONFIG_RPMSG_NS) += rpmsg_ns.o obj-$(CONFIG_RPMSG_MTK_SCP) += mtk_rpmsg.o diff --git a/drivers/rpmsg/imx_rpmsg.c b/drivers/rpmsg/imx_rpmsg.c index 8d4ecd7360fd..b4277d5cfe37 100644 --- a/drivers/rpmsg/imx_rpmsg.c +++ b/drivers/rpmsg/imx_rpmsg.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2019 NXP + * Copyright 2019,2023 NXP */ #include @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -163,8 +164,12 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, return ERR_PTR(-ENOMEM); /* ioremap'ing normal memory, so we cast away sparse's complaints */ - rpvq->addr = (__force void *) ioremap(virdev->vring[index], - RPMSG_RING_SIZE); + if (of_dma_is_coherent(dev->of_node)) + rpvq->addr = (__force void *)ioremap_cache(virdev->vring[index], + RPMSG_RING_SIZE); + else + rpvq->addr = (__force void *)ioremap(virdev->vring[index], + RPMSG_RING_SIZE); if (!rpvq->addr) { err = -ENOMEM; goto free_rpvq; @@ -466,6 +471,13 @@ static int imx_rpmsg_rxdb_channel_init(struct imx_rpmsg_vproc *rpdev) return ret; } +static int imx_rpmsg_rxdb_channel_deinit(struct imx_rpmsg_vproc *rpdev) +{ + mbox_free_channel(rpdev->rxdb_ch); + + return 0; +} + static void imx_rpmsg_rx_callback(struct mbox_client *c, void *msg) { int buf_space; @@ -531,6 +543,14 @@ static int imx_rpmsg_xtr_channel_init(struct imx_rpmsg_vproc *rpdev) return ret; } +static int imx_rpmsg_xtr_channel_deinit(struct imx_rpmsg_vproc *rpdev) +{ + mbox_free_channel(rpdev->tx_ch); + mbox_free_channel(rpdev->rx_ch); + + return 0; +} + static int imx_rpmsg_probe(struct platform_device *pdev) { int j, ret = 0; @@ -653,6 +673,36 @@ static int imx_rpmsg_probe(struct platform_device *pdev) return ret; } +static int imx_rpmsg_remove(struct platform_device *pdev) +{ + struct imx_rpmsg_vproc *rpdev = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int i; + +#ifdef CONFIG_IMX_SCU + if (rpdev->variant == IMX8QXP || rpdev->variant == IMX8QM) { + imx_scu_irq_unregister_notifier(&rpdev->proc_nb); + imx_scu_irq_group_enable(IMX_SC_IRQ_GROUP_REBOOTED, + BIT(rpdev->mub_partition), false); + } +#endif + imx_rpmsg_rxdb_channel_deinit(rpdev); + + for (i = 0; i < rpdev->vdev_nums; i++) { + unregister_virtio_device(&rpdev->ivdev[i]->vdev); + kfree(rpdev->ivdev[i]); + } + + if (rpdev->flags & SPECIFIC_DMA_POOL) + of_reserved_mem_device_release(dev); + + imx_rpmsg_xtr_channel_deinit(rpdev); + + cancel_delayed_work_sync(&rpdev->rpmsg_work); + + return 0; +} + static struct platform_driver imx_rpmsg_driver = { .driver = { .owner = THIS_MODULE, @@ -660,6 +710,7 @@ static struct platform_driver imx_rpmsg_driver = { .of_match_table = imx_rpmsg_dt_ids, }, .probe = imx_rpmsg_probe, + .remove = imx_rpmsg_remove, }; static int __init imx_rpmsg_init(void) diff --git a/drivers/rpmsg/imx_rpmsg_tty.c b/drivers/rpmsg/imx_rpmsg_tty.c index aaf343fc1e54..431a33afc0ad 100644 --- a/drivers/rpmsg/imx_rpmsg_tty.c +++ b/drivers/rpmsg/imx_rpmsg_tty.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2019 NXP + * Copyright 2019,2023 NXP */ #include @@ -13,8 +13,24 @@ #include #include +#ifdef CONFIG_RPMSG_8M_BUF +struct rpmsg_hdr { + __rpmsg32 src; + __rpmsg32 dst; + __rpmsg32 reserved; + __rpmsg16 len; + __rpmsg16 flags; + u8 data[]; +} __packed; + +#define MAX_RPMSG_BUF_SIZE (512 * 2) +/* this needs to be less then (RPMSG_BUF_SIZE - sizeof(struct rpmsg_hdr)) */ +#define RPMSG_MAX_SIZE (MAX_RPMSG_BUF_SIZE - sizeof(struct rpmsg_hdr)) +#else /* this needs to be less then (RPMSG_BUF_SIZE - sizeof(struct rpmsg_hdr)) */ #define RPMSG_MAX_SIZE 256 +#endif + #define MSG "hello world!" /* diff --git a/drivers/rpmsg/rpmsg_perf.c b/drivers/rpmsg/rpmsg_perf.c new file mode 100644 index 000000000000..f58b15b2ea8d --- /dev/null +++ b/drivers/rpmsg/rpmsg_perf.c @@ -0,0 +1,545 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2023 NXP + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rpmsg_internal.h" + +#define RPMSG_PERF_AS_SENDER_IOCTL _IO(0xb5, 0x5) +#define RPMSG_PERF_AS_RECEIVER_IOCTL _IO(0xb5, 0x6) +#define RPMSG_PERF_AS_RECEIVER_END_ACK_IOCTL _IO(0xb5, 0x7) +#define RPMSG_PERF_GET_RUNNING_STA_IOCTL _IO(0xb5, 0x8) + +#define RPMSG_DEV_MAX (MINORMASK + 1) + +static dev_t rpmsg_major; + +static DEFINE_IDA(rpmsg_perf_minor_ida); + +#define dev_to_eptdev(dev) container_of(dev, struct rpmsg_eptdev, dev) +#define cdev_to_eptdev(i_cdev) container_of(i_cdev, struct rpmsg_eptdev, cdev) + +struct packet_header { + uint32_t preamble; + bool no_copy; + uint32_t packet_size; + uint32_t packet_cnt; + uint32_t test_time; /* unit: second */ +}; + +enum { + RPMSG_PERF_PREAMBLE_SENDER_START = 0xBECAACEA, + RPMSG_PERF_PREAMBLE_SENDER_END = 0xBECAACEB, + RPMSG_PERF_PREAMBLE_SENDER_END_ACK = 0xBECAACEC, + RPMSG_PERF_PREAMBLE_RECEIVER_START = 0xBECAACED, + RPMSG_PERF_PREAMBLE_RECEIVER_END = 0xBECAACEE, + RPMSG_PERF_PREAMBLE_RECEIVER_END_ACK = 0xBECAACEF, +}; + +enum dev_state { + RPMSG_DEV_IDLE, + RPMSG_DEV_SENDING, + RPMSG_DEV_RECEIVING, +}; + +struct test_statistic { + uint32_t recv_packet_cnt; + uint32_t send_packet_cnt; + uint32_t packet_size; + uint32_t test_time; +}; + +/** + * struct rpmsg_eptdev - endpoint device context + * @dev: endpoint device + * @cdev: cdev for the endpoint device + * @rpdev: underlaying rpmsg device + * @chinfo: info used to open the endpoint + * @ept_lock: synchronization of @ept modifications + * @ept: rpmsg endpoint reference, when open + * @default_ept: set to channel default endpoint if the default endpoint should be re-used + * on device open to prevent endpoint address update. + */ +struct rpmsg_eptdev { + struct device dev; + struct cdev cdev; + + struct rpmsg_device *rpdev; + struct rpmsg_channel_info chinfo; + + struct mutex ept_lock; + struct rpmsg_endpoint *ept; + struct rpmsg_endpoint *default_ept; + + enum dev_state state; + struct test_statistic statistic; + + struct packet_header param; +}; + +static int rpmsg_perf_ept_cb(struct rpmsg_device *rpdev, void *buf, int len, + void *priv, u32 addr) +{ + struct packet_header *hdr = (struct packet_header *)buf; + struct rpmsg_eptdev *eptdev = priv; + struct test_statistic *statistic = &eptdev->statistic; + uint32_t rate; + + switch (hdr->preamble) { + case RPMSG_PERF_PREAMBLE_SENDER_END_ACK: + if (eptdev->state == RPMSG_DEV_SENDING) { + eptdev->state = RPMSG_DEV_IDLE; + rate = statistic->send_packet_cnt / + statistic->test_time / 1000; + pr_info("packet size: %u, sent packets: %u, time: %u s, rate: %u kpps\n", + statistic->packet_size, + statistic->send_packet_cnt, + statistic->test_time, rate); + statistic->packet_size = 0; + statistic->send_packet_cnt = 0; + } + break; + default: + break; + } + statistic->recv_packet_cnt++; + + return 0; +} + +static int rpmsg_perf_eptdev_open(struct inode *inode, struct file *filp) +{ + struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev); + struct rpmsg_endpoint *ept; + struct rpmsg_device *rpdev = eptdev->rpdev; + struct device *dev = &eptdev->dev; + + mutex_lock(&eptdev->ept_lock); + if (eptdev->ept) { + mutex_unlock(&eptdev->ept_lock); + return -EBUSY; + } + + get_device(dev); + + /* + * If the default_ept is set, the rpmsg device default endpoint is used. + * Else a new endpoint is created on open that will be destroyed on release. + */ + if (eptdev->default_ept) + ept = eptdev->default_ept; + else + ept = rpmsg_create_ept(rpdev, rpmsg_perf_ept_cb, eptdev, eptdev->chinfo); + + if (!ept) { + dev_err(dev, "failed to open %s\n", eptdev->chinfo.name); + put_device(dev); + mutex_unlock(&eptdev->ept_lock); + return -EINVAL; + } + + eptdev->ept = ept; + filp->private_data = eptdev; + mutex_unlock(&eptdev->ept_lock); + + return 0; +} + +static int rpmsg_perf_eptdev_release(struct inode *inode, struct file *filp) +{ + struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev); + struct device *dev = &eptdev->dev; + + /* Close the endpoint, if it's not already destroyed by the parent */ + mutex_lock(&eptdev->ept_lock); + if (eptdev->ept) { + if (!eptdev->default_ept) + rpmsg_destroy_ept(eptdev->ept); + eptdev->ept = NULL; + } + mutex_unlock(&eptdev->ept_lock); + + put_device(dev); + + return 0; +} + +static int rpmsg_perf_sender_thread(void *p) +{ + struct rpmsg_eptdev *eptdev = (struct rpmsg_eptdev *)p; + struct packet_header hdr = eptdev->param; + unsigned long timeout; + uint8_t *data; + uint32_t packet_len; + int ret; + + hdr.preamble = RPMSG_PERF_PREAMBLE_SENDER_START; + packet_len = hdr.packet_size; + eptdev->statistic.send_packet_cnt = 0; + + ret = rpmsg_sendto(eptdev->ept, &hdr, sizeof(hdr), eptdev->chinfo.dst); + if (ret) { + pr_err("failed to send packet header\n"); + return ret; + } + + /* Prepare data packets */ + data = kmalloc(packet_len, GFP_KERNEL); + if (data == NULL) { + pr_err("allocate data buffer failure\n"); + return -ENOMEM; + } + memset(data, 0, packet_len); + + udelay(100); + timeout = jiffies + msecs_to_jiffies(hdr.test_time * 1000); + + do { + do { + ret = rpmsg_trysendto(eptdev->ept, data, packet_len, + eptdev->chinfo.dst); + } while (ret != 0); + eptdev->statistic.send_packet_cnt++; + } while (time_before(jiffies, timeout)); + + memset(&hdr, 0, sizeof(hdr)); + hdr.preamble = RPMSG_PERF_PREAMBLE_SENDER_END; + + ret = rpmsg_sendto(eptdev->ept, &hdr, sizeof(hdr), eptdev->chinfo.dst); + if (ret) { + pr_err("failed to send RPMSG_PERF_PREAMBLE_SENDER_END packet\n"); + } + + kfree(data); + + return ret; +} + +static long rpmsg_perf_eptdev_ioctl(struct file *fp, unsigned int cmd, + unsigned long arg) +{ + struct rpmsg_eptdev *eptdev = fp->private_data; + struct test_statistic *statistic = &eptdev->statistic; + struct task_struct *sender_thread_h; + struct packet_header hdr = {0}; + uint32_t packet_cnt; + uint32_t rate; + uint32_t status; + long ret; + + switch (cmd) { + case RPMSG_PERF_GET_RUNNING_STA_IOCTL: + status = eptdev->state; + if (copy_to_user((char __user *)arg, &status, sizeof(status))) { + pr_err("copy_to_user() failed\n"); + return -EFAULT; + } + break; + case RPMSG_PERF_AS_SENDER_IOCTL: + if (eptdev->state != RPMSG_DEV_IDLE) + return -EINVAL; + + eptdev->state = RPMSG_DEV_SENDING; + ret = copy_from_user((void *)&hdr, (const void __user *)arg, + sizeof(struct packet_header)); + if (ret) { + pr_err("copy_from_user() failed\n"); + return -EFAULT; + } + + eptdev->param.no_copy = hdr.no_copy; + eptdev->param.packet_size = hdr.packet_size; + eptdev->param.test_time = hdr.test_time; + statistic->packet_size = hdr.packet_size; + statistic->test_time = hdr.test_time; + + sender_thread_h = kthread_run(rpmsg_perf_sender_thread, + eptdev, "rpmsg_sender"); + if (IS_ERR(sender_thread_h)) { + pr_err("failed to create sender thread\n"); + return -EFAULT; + } + break; + case RPMSG_PERF_AS_RECEIVER_IOCTL: + if (eptdev->state != RPMSG_DEV_IDLE) + return -EINVAL; + + eptdev->state = RPMSG_DEV_RECEIVING; + ret = copy_from_user((void *)&hdr, (const void __user *)arg, + sizeof(struct packet_header)); + if (ret) { + pr_err("copy_from_user() failed\n"); + return -EFAULT; + } + hdr.preamble = RPMSG_PERF_PREAMBLE_RECEIVER_START; + statistic->test_time = hdr.test_time; + statistic->packet_size = hdr.packet_size; + statistic->recv_packet_cnt = 0; + + ret = rpmsg_sendto(eptdev->ept, &hdr, sizeof(hdr), + eptdev->chinfo.dst); + if (ret) + pr_err("failed to send RPMSG_PERF_PREAMBLE_RECEIVER_START packet\n"); + break; + case RPMSG_PERF_AS_RECEIVER_END_ACK_IOCTL: + if (eptdev->state != RPMSG_DEV_RECEIVING) + return -EINVAL; + + packet_cnt = statistic->recv_packet_cnt - 1; + rate = packet_cnt / statistic->test_time / 1000; + pr_info("packet size: %u, received packets: %u, time: %u s, rate: %u kpps\n", + statistic->packet_size, packet_cnt, + statistic->test_time, rate); + + hdr.preamble = RPMSG_PERF_PREAMBLE_RECEIVER_END_ACK; + hdr.packet_cnt = packet_cnt; + ret = rpmsg_sendto(eptdev->ept, &hdr, sizeof(hdr), + eptdev->chinfo.dst); + if (ret) { + pr_err("failed to send RPMSG_PERF_PREAMBLE_RECEIVER_END_ACK packet\n"); + return ret; + } + eptdev->state = RPMSG_DEV_IDLE; + statistic->test_time = 0; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct file_operations rpmsg_perf_eptdev_fops = { + .owner = THIS_MODULE, + .open = rpmsg_perf_eptdev_open, + .release = rpmsg_perf_eptdev_release, + .unlocked_ioctl = rpmsg_perf_eptdev_ioctl, + .compat_ioctl = compat_ptr_ioctl, +}; + +static ssize_t name_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", eptdev->chinfo.name); +} +static DEVICE_ATTR_RO(name); + +static ssize_t src_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", eptdev->chinfo.src); +} +static DEVICE_ATTR_RO(src); + +static ssize_t dst_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", eptdev->chinfo.dst); +} +static DEVICE_ATTR_RO(dst); + +static struct attribute *rpmsg_perf_eptdev_attrs[] = { + &dev_attr_name.attr, + &dev_attr_src.attr, + &dev_attr_dst.attr, + NULL +}; +ATTRIBUTE_GROUPS(rpmsg_perf_eptdev); + +static void rpmsg_eptdev_release_device(struct device *dev) +{ + struct rpmsg_eptdev *eptdev = dev_to_eptdev(dev); + + ida_simple_remove(&rpmsg_perf_minor_ida, MINOR(eptdev->dev.devt)); + kfree(eptdev); +} + +static struct rpmsg_eptdev *rpmsg_perf_eptdev_alloc(struct rpmsg_device *rpdev, + struct device *parent) +{ + struct rpmsg_eptdev *eptdev; + struct device *dev; + + eptdev = kzalloc(sizeof(*eptdev), GFP_KERNEL); + if (!eptdev) + return ERR_PTR(-ENOMEM); + + eptdev->state = RPMSG_DEV_IDLE; + + dev = &eptdev->dev; + eptdev->rpdev = rpdev; + + mutex_init(&eptdev->ept_lock); + + device_initialize(dev); + dev->class = rpmsg_class; + dev->parent = parent; + dev->groups = rpmsg_perf_eptdev_groups; + dev_set_drvdata(dev, eptdev); + + cdev_init(&eptdev->cdev, &rpmsg_perf_eptdev_fops); + eptdev->cdev.owner = THIS_MODULE; + + return eptdev; +} + +static int rpmsg_perf_eptdev_add(struct rpmsg_eptdev *eptdev, + struct rpmsg_channel_info chinfo) +{ + struct device *dev = &eptdev->dev; + int dst = eptdev->rpdev->dst; + int ret; + + eptdev->chinfo = chinfo; + + ret = ida_simple_get(&rpmsg_perf_minor_ida, 0, + RPMSG_DEV_MAX, GFP_KERNEL); + if (ret < 0) + goto free_eptdev; + dev->devt = MKDEV(MAJOR(rpmsg_major), ret); + + dev->id = dst; + dev_set_name(dev, "rpmsg-perf%d", dst); + + ret = cdev_device_add(&eptdev->cdev, &eptdev->dev); + if (ret) + goto free_minor_ida; + + /* We can now rely on the release function for cleanup */ + dev->release = rpmsg_eptdev_release_device; + + return ret; + +free_minor_ida: + ida_simple_remove(&rpmsg_perf_minor_ida, MINOR(dev->devt)); +free_eptdev: + put_device(dev); + kfree(eptdev); + + return ret; +} + +static int rpmsg_perf_probe(struct rpmsg_device *rpdev) +{ + struct rpmsg_channel_info chinfo; + struct rpmsg_eptdev *eptdev; + struct device *dev = &rpdev->dev; + + memcpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE); + chinfo.src = rpdev->src; + chinfo.dst = rpdev->dst; + + eptdev = rpmsg_perf_eptdev_alloc(rpdev, dev); + if (IS_ERR(eptdev)) + return PTR_ERR(eptdev); + + /* Set the default_ept to the rpmsg device endpoint */ + eptdev->default_ept = rpdev->ept; + + /* + * The rpmsg_perf_ept_cb uses *priv parameter to get its + * rpmsg_eptdev context. Stored it in default_ept *priv field. + */ + eptdev->default_ept->priv = eptdev; + + return rpmsg_perf_eptdev_add(eptdev, chinfo); +} + +static int rpmsg_perf_eptdev_chrdev_destroy(struct device *dev, void *data) +{ + struct rpmsg_eptdev *eptdev = dev_to_eptdev(dev); + + mutex_lock(&eptdev->ept_lock); + if (eptdev->ept) { + /* The default endpoint is released by the rpmsg core */ + if (!eptdev->default_ept) + rpmsg_destroy_ept(eptdev->ept); + eptdev->ept = NULL; + } + mutex_unlock(&eptdev->ept_lock); + + cdev_device_del(&eptdev->cdev, &eptdev->dev); + put_device(&eptdev->dev); + + return 0; +} + +static void rpmsg_perf_remove(struct rpmsg_device *rpdev) +{ + int ret; + + ret = device_for_each_child(&rpdev->dev, NULL, + rpmsg_perf_eptdev_chrdev_destroy); + if (ret) + dev_warn(&rpdev->dev, "failed to destroy endpoints: %d\n", ret); +} + +static struct rpmsg_device_id rpmsg_perf_id_table[] = { + { .name = "rpmsg-perf" }, + { }, +}; + +static struct rpmsg_driver rpmsg_perf_driver = { + .probe = rpmsg_perf_probe, + .remove = rpmsg_perf_remove, + .callback = rpmsg_perf_ept_cb, + .id_table = rpmsg_perf_id_table, + .drv.name = "rpmsg_perf", +}; + +static int rpmsg_perf_init(void) +{ + int ret; + + ret = alloc_chrdev_region(&rpmsg_major, 0, RPMSG_DEV_MAX, "rpmsg_perf"); + if (ret < 0) { + pr_err("failed to allocate char dev region\n"); + return ret; + } + + ret = register_rpmsg_driver(&rpmsg_perf_driver); + if (ret < 0) { + pr_err("rpmsg: failed to register rpmsg raw driver\n"); + goto free_region; + } + + return 0; + +free_region: + unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX); + + return ret; +} +postcore_initcall(rpmsg_perf_init); + +static void rpmsg_perf_exit(void) +{ + unregister_rpmsg_driver(&rpmsg_perf_driver); + unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX); +} +module_exit(rpmsg_perf_exit); + +MODULE_ALIAS("rpmsg:rpmsg_perf"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index f1af0f674615..40ee347fcc9b 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -127,8 +127,13 @@ struct virtio_rpmsg_channel { * can change this without changing anything in the firmware of the remote * processor. */ +#ifdef CONFIG_RPMSG_8M_BUF +#define MAX_RPMSG_NUM_BUFS (1024 * 8) +#define MAX_RPMSG_BUF_SIZE (512 * 2) +#else #define MAX_RPMSG_NUM_BUFS (512) #define MAX_RPMSG_BUF_SIZE (512) +#endif /* * Local addresses are dynamically allocated on-demand. diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 882d0c850c10..80a8b479c90f 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1835,6 +1835,7 @@ config RTC_DRV_IMX_SM config RTC_DRV_IMX_RPMSG tristate "NXP RPMSG RTC support" depends on OF + depends on RPMSG help If you say yes here you get support for the NXP RPMSG RTC module. diff --git a/drivers/staging/fsl_qbman/qman_driver.c b/drivers/staging/fsl_qbman/qman_driver.c index a752faa4e85d..2f17d4a510e2 100644 --- a/drivers/staging/fsl_qbman/qman_driver.c +++ b/drivers/staging/fsl_qbman/qman_driver.c @@ -1,4 +1,5 @@ /* Copyright 2008-2012 Freescale Semiconductor, Inc. + * Copyright 2019-2023 NXP * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -723,6 +724,50 @@ static int qman_online_cpu(unsigned int cpu) #endif /* CONFIG_HOTPLUG_CPU */ +#ifdef CONFIG_FSL_DPAA_ETHERCAT +__init void qman_ethercat_portal_init(int cpu, bool need_cleanup) +{ + struct qm_portal_config *pcfg; + struct qman_portal *p; + + pcfg = get_pcfg(&unused_pcfgs); + if (pcfg) { + pcfg->public_cfg.cpu = cpu; + pcfg->public_cfg.is_shared = 0; + + pcfg->iommu_domain = NULL; + portal_set_cpu(pcfg, pcfg->public_cfg.cpu); + p = qman_create_affine_portal_ethercat(pcfg, NULL, cpu, need_cleanup); + if (p) { + pr_info("Qman portal %sinitialised, cpu %d\n", + pcfg->public_cfg.is_shared ? "(shared) " : "", + pcfg->public_cfg.cpu); + } else { + pr_crit("Qman portal failure on cpu %d\n", + pcfg->public_cfg.cpu); + } + return; + } +} + +static u32 qman_affine_last_cpu; + +u32 qman_get_affine_last_cpu(void) +{ + return qman_affine_last_cpu; +} + +__init void qman_ethercat_portal_init_on_cpu(bool need_cleanup) +{ + int cpu = 0; + + for_each_online_cpu(cpu) { + qman_affine_last_cpu = cpu; + qman_ethercat_portal_init(cpu, need_cleanup); + } +} +#endif + __init int qman_init(void) { struct cpumask slave_cpus; @@ -897,6 +942,10 @@ __init int qman_init(void) qman_enable_irqs(); } +#ifdef CONFIG_FSL_DPAA_ETHERCAT + qman_ethercat_portal_init_on_cpu(need_cleanup); +#endif + return 0; } diff --git a/drivers/staging/fsl_qbman/qman_high.c b/drivers/staging/fsl_qbman/qman_high.c index d1cbfaa987fe..feb47294a02f 100644 --- a/drivers/staging/fsl_qbman/qman_high.c +++ b/drivers/staging/fsl_qbman/qman_high.c @@ -1,4 +1,5 @@ /* Copyright 2008-2012 Freescale Semiconductor, Inc. + * Copyright 2019-2023 NXP * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -802,6 +803,30 @@ struct qman_portal *qman_create_portal( return NULL; } +#ifdef CONFIG_FSL_DPAA_ETHERCAT +static struct qman_portal ethercat_portal[NR_CPUS]; +static u16 ethercat_channel[NR_CPUS]; +static DEFINE_SPINLOCK(ethercat_mask_lock); + +struct qman_portal *qman_create_affine_portal_ethercat + (const struct qm_portal_config *config, + const struct qman_cgrs *cgrs, int cpu, + bool need_cleanup) +{ + struct qman_portal *res; + struct qman_portal *portal = NULL; + + portal = ðercat_portal[cpu]; + res = qman_create_portal(portal, config, cgrs, need_cleanup); + if (res) { + spin_lock(ðercat_mask_lock); + ethercat_channel[cpu] = config->public_cfg.channel; + spin_unlock(ðercat_mask_lock); + } + return res; +} +#endif + struct qman_portal *qman_create_affine_portal( const struct qm_portal_config *config, const struct qman_cgrs *cgrs, @@ -1468,6 +1493,20 @@ void *qman_get_affine_portal(int cpu) } EXPORT_SYMBOL(qman_get_affine_portal); +#ifdef CONFIG_FSL_DPAA_ETHERCAT +u16 qman_affine_channel_ethercat(int cpu) +{ + return ethercat_channel[cpu]; +} +EXPORT_SYMBOL(qman_affine_channel_ethercat); + +void *qman_get_affine_portal_ethercat(int cpu) +{ + return ðercat_portal[cpu]; +} +EXPORT_SYMBOL(qman_get_affine_portal_ethercat); +#endif + int qman_p_poll_dqrr(struct qman_portal *p, unsigned int limit) { int ret; diff --git a/drivers/staging/fsl_qbman/qman_private.h b/drivers/staging/fsl_qbman/qman_private.h index 615d5a135702..9803d6990353 100644 --- a/drivers/staging/fsl_qbman/qman_private.h +++ b/drivers/staging/fsl_qbman/qman_private.h @@ -1,4 +1,5 @@ /* Copyright 2008-2012 Freescale Semiconductor, Inc. + * Copyright 2019-2023 NXP * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -233,6 +234,12 @@ struct qman_portal *qman_create_portal( const struct qman_cgrs *cgrs, bool need_cleanup); +#ifdef CONFIG_FSL_DPAA_ETHERCAT +struct qman_portal *qman_create_affine_portal_ethercat + (const struct qm_portal_config *config, + const struct qman_cgrs *cgrs, int cpu, + bool need_cleanup); +#endif struct qman_portal *qman_create_affine_portal( const struct qm_portal_config *config, const struct qman_cgrs *cgrs, diff --git a/drivers/tty/rpmsg_tty.c b/drivers/tty/rpmsg_tty.c index 60a2915f5cfe..7f3f0373c9cf 100644 --- a/drivers/tty/rpmsg_tty.c +++ b/drivers/tty/rpmsg_tty.c @@ -1,13 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2021 STMicroelectronics - All Rights Reserved + * Copyright 2023 NXP * * The rpmsg tty driver implements serial communication on the RPMsg bus to makes * possible for user-space programs to send and receive rpmsg messages as a standard * tty protocol. * - * The remote processor can instantiate a new tty by requesting a "rpmsg-tty" RPMsg service. - * The "rpmsg-tty" service is directly used for data exchange. No flow control is implemented yet. + * The remote processor can instantiate a new tty by requesting a "srtm-uart-channel" or "rpmsg-tty" RPMsg service. + * The "srtm-uart-channel" or "rpmsg-tty" service is directly used for data exchange. No flow control is implemented yet. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -17,6 +18,43 @@ #include #include #include +#include +#include + +/* + * The srtm (simplified real time message) protocol for uart: + * + * +---------------+-------------------------------+ + * | Byte Offset | Content | + * +---------------+---+---+---+---+---+---+---+---+ + * | 0 | Category | + * +---------------+---+---+---+---+---+---+---+---+ + * | 1 ~ 2 | Version | + * +---------------+---+---+---+---+---+---+---+---+ + * | 3 | Type | + * +---------------+---+---+---+---+---+---+---+---+ + * | 4 | Command | + * +---------------+---+---+---+---+---+---+---+---+ + * | 5 | Priority | + * +---------------+---+---+---+---+---+---+---+---+ + * | 6 | Reserved1 | + * +---------------+---+---+---+---+---+---+---+---+ + * | 7 | Reserved2 | + * +---------------+---+---+---+---+---+---+---+---+ + * | 8 | Reserved3 | + * +---------------+---+---+---+---+---+---+---+---+ + * | 9 | Reserved4 | + * +---------------+---+---+---+---+---+---+---+---+ + * | 10 | UART BUS ID | + * +---------------+---+---+---+---+---+---+---+---+ + * | 11 | reserved/ret code | + * +---------------+---+---+---+---+---+---+---+---+ + * | 12 ~ 13 | Flags | + * +---------------+---+---+---+---+---+---+---+---+ + * | 14 ~ 495 | Data | + * +---------------+---+---+---+---+---+---+---+---+ + * UART BUS ID: real uart instance, such as: the soc has UART0(UART BUS ID = 0), UART1(UART BUS ID = 1), UART2(UART BUS ID = 2), ... , UARTx(UART BUS ID = x) + */ #define RPMSG_TTY_NAME "ttyRPMSG" #define MAX_TTY_RPMSG 32 @@ -29,18 +67,72 @@ static struct tty_driver *rpmsg_tty_driver; struct rpmsg_tty_port { struct tty_port port; /* TTY port data */ int id; /* TTY rpmsg index */ + int bus_id; /* used for srtm uart protocol, which real uart will be used, such as, uart0, uart1, uart2, uart3... */ + int flags; /* used for srtm uart protocol */ + u16 srtm_uart_msg_data_max_sz; /* used for srtm uart protocol */ + bool use_srtm_uart_protocol; /* used for srtm uart protocol */ struct rpmsg_device *rpdev; /* rpmsg device */ }; +struct srtm_uart_msg_header { + struct imx_srtm_head common; + u8 bus_id; /* The bus_id is used when send data from acore to mcore; The bus_id is useless when acore received data that from mcore*/ + union { + u8 reserved; /* used in request packet */ + u8 retCode; /* used in response packet */ + }; + u16 flags; +} __packed __aligned[1]; + +struct srtm_uart_msg { + struct srtm_uart_msg_header header; + /* srtm uart Payload Start */ + u8 data[1]; +} __packed __aligned(1); + +struct imx_srtm_uart_data_structure +{ + bool use_srtm_uart_protocol; +}; + +const static struct imx_srtm_uart_data_structure imx_srtm_uart_data = { + .use_srtm_uart_protocol = true, +}; + +const static struct imx_srtm_uart_data_structure rpmsg_tty_data = { + .use_srtm_uart_protocol = false, +}; + static int rpmsg_tty_cb(struct rpmsg_device *rpdev, void *data, int len, void *priv, u32 src) { struct rpmsg_tty_port *cport = dev_get_drvdata(&rpdev->dev); + struct srtm_uart_msg *msg = (struct srtm_uart_msg *)data; int copied; + u8 *payload = data; + int payload_len = len; - if (!len) + if (!payload_len) return -EINVAL; - copied = tty_insert_flip_string(&cport->port, data, len); - if (copied != len) + if (cport->use_srtm_uart_protocol) { + if (payload_len < (sizeof(struct srtm_uart_msg))) + return -EINVAL; + payload_len -= (sizeof(struct srtm_uart_msg) - 1); /* 1: msg->data[0] */ + + if (msg->header.common.type != IMX_SRTM_TYPE_RESPONSE && msg->header.common.type != IMX_SRTM_TYPE_NOTIFY) { + return -EINVAL; + } + + if (payload_len > cport->srtm_uart_msg_data_max_sz) { + dev_err(&rpdev->dev, + "%s failed: data length greater than %d, len=%d\n", + __func__, cport->srtm_uart_msg_data_max_sz, payload_len); + return -EINVAL; + } + payload = &msg->data[0]; + } + + copied = tty_insert_flip_string(&cport->port, payload, payload_len); + if (copied != payload_len) dev_err_ratelimited(&rpdev->dev, "Trunc buffer: available space is %d\n", copied); tty_flip_buffer_push(&cport->port); @@ -78,27 +170,53 @@ static ssize_t rpmsg_tty_write(struct tty_struct *tty, const u8 *buf, { struct rpmsg_tty_port *cport = tty->driver_data; struct rpmsg_device *rpdev; - int msg_max_size, msg_size; + int msg_max_size, msg_size = -1; + struct srtm_uart_msg *msg = NULL; int ret; + void *data = NULL; + int data_len = 0; rpdev = cport->rpdev; - msg_max_size = rpmsg_get_mtu(rpdev->ept); + msg_max_size = cport->use_srtm_uart_protocol ? (cport->srtm_uart_msg_data_max_sz) : (rpmsg_get_mtu(rpdev->ept)); if (msg_max_size < 0) return msg_max_size; msg_size = min_t(unsigned int, len, msg_max_size); + if (cport->use_srtm_uart_protocol) { + data_len = msg_size + sizeof(struct srtm_uart_msg) - 1; /* srtm uart msg header + data len */ + msg = kmalloc(data_len, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + memset(msg, 0, data_len); + msg->header.common.cate = IMX_SRTM_CATEGORY_UART; + msg->header.common.major = IMX_SRTM_VER_UART; + msg->header.common.minor = IMX_SRTM_VER_UART >> 8; + msg->header.common.type = IMX_SRTM_TYPE_NOTIFY; + msg->header.common.cmd = IMX_SRTM_UART_COMMAND_SEND; + msg->header.common.reserved[0] = IMX_SRTM_UART_PRIORITY; + msg->header.bus_id = cport->bus_id & 0xFF; + msg->header.flags = cport->flags & 0xFFFF; + memcpy(&msg->data[0], buf, msg_size); + data = (void *)msg; + } else { + data = (void *)buf; + data_len = msg_size; + } /* * Use rpmsg_trysend instead of rpmsg_send to send the message so the caller is not * hung until a rpmsg buffer is available. In such case rpmsg_trysend returns -ENOMEM. */ - ret = rpmsg_trysend(rpdev->ept, (void *)buf, msg_size); + ret = rpmsg_trysend(rpdev->ept, data, data_len); if (ret) { dev_dbg_ratelimited(&rpdev->dev, "rpmsg_send failed: %d\n", ret); - return ret; + msg_size = ret; } + kfree(msg); + return msg_size; } @@ -106,8 +224,10 @@ static unsigned int rpmsg_tty_write_room(struct tty_struct *tty) { struct rpmsg_tty_port *cport = tty->driver_data; int size; + struct rpmsg_device *rpdev; - size = rpmsg_get_mtu(cport->rpdev->ept); + rpdev = cport->rpdev; + size = cport->use_srtm_uart_protocol ? (cport->srtm_uart_msg_data_max_sz) : (rpmsg_get_mtu(rpdev->ept)); if (size < 0) return 0; @@ -173,7 +293,12 @@ static int rpmsg_tty_probe(struct rpmsg_device *rpdev) struct rpmsg_tty_port *cport; struct device *dev = &rpdev->dev; struct device *tty_dev; + struct device_node *np; + struct imx_srtm_uart_data_structure *srtm_uart_data = NULL; + struct srtm_uart_msg *msg = NULL; int ret; + int data_len = 0; + char buf[64]; cport = rpmsg_tty_alloc_cport(); if (IS_ERR(cport)) @@ -182,6 +307,23 @@ static int rpmsg_tty_probe(struct rpmsg_device *rpdev) tty_port_init(&cport->port); cport->port.ops = &rpmsg_tty_port_ops; + cport->rpdev = rpdev; + + srtm_uart_data = (struct imx_srtm_uart_data_structure *)rpdev->id.driver_data; + if (srtm_uart_data && srtm_uart_data->use_srtm_uart_protocol == true) { + cport->bus_id = 0xFF; /* mcore directly print the data that received from acore */ + cport->use_srtm_uart_protocol = true; + snprintf(buf, sizeof(buf), "uart-rpbus-%d", cport->id); + np = of_find_node_by_name(NULL, buf); + if (np && of_device_is_compatible(np, "fsl,uart-rpbus") && of_device_is_available(np)) { + of_property_read_u32(np, "bus_id", &cport->bus_id); /* mcore will use the id as uart instance, then write data to the real uart instance */ + of_property_read_u32(np, "flags", &cport->flags); + } + cport->srtm_uart_msg_data_max_sz = rpmsg_get_mtu(rpdev->ept) - (sizeof(struct srtm_uart_msg) - 1); /* 1: data[1] of struct srtm_uart_msg */ + } else { + cport->use_srtm_uart_protocol = false; + } + tty_dev = tty_port_register_device(&cport->port, rpmsg_tty_driver, cport->id, dev); if (IS_ERR(tty_dev)) { @@ -190,9 +332,28 @@ static int rpmsg_tty_probe(struct rpmsg_device *rpdev) return ret; } - cport->rpdev = rpdev; - - dev_set_drvdata(dev, cport); + dev_set_drvdata(dev, (void *)cport); + + /* Say hello to remote to acknowleage each other */ + if (cport->use_srtm_uart_protocol) { + data_len = sizeof(struct srtm_uart_msg); /* srtm uart msg header */ + msg = kmalloc(data_len, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + memset(msg, 0, data_len); + msg->header.common.cate = IMX_SRTM_CATEGORY_UART; + msg->header.common.major = IMX_SRTM_VER_UART; + msg->header.common.minor = IMX_SRTM_VER_UART >> 8; + msg->header.common.type = IMX_SRTM_TYPE_NOTIFY; + msg->header.common.cmd = IMX_SRTM_UART_COMMAND_HELLO; + msg->header.common.reserved[0] = IMX_SRTM_UART_PRIORITY; + msg->header.bus_id = cport->bus_id & 0xFF; + msg->header.flags = cport->flags & 0xFFFF; + + rpmsg_send(rpdev->ept, (void *)msg, data_len); + kfree(msg); + } dev_dbg(dev, "New channel: 0x%x -> 0x%x: " RPMSG_TTY_NAME "%d\n", rpdev->src, rpdev->dst, cport->id); @@ -215,7 +376,8 @@ static void rpmsg_tty_remove(struct rpmsg_device *rpdev) } static struct rpmsg_device_id rpmsg_driver_tty_id_table[] = { - { .name = "rpmsg-tty" }, + { .name = "srtm-uart-channel", .driver_data = (kernel_ulong_t)&imx_srtm_uart_data, }, + { .name = "rpmsg-tty", .driver_data = (kernel_ulong_t)&rpmsg_tty_data, }, { }, }; MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_tty_id_table); @@ -284,5 +446,6 @@ module_init(rpmsg_tty_init); module_exit(rpmsg_tty_exit); MODULE_AUTHOR("Arnaud Pouliquen "); +MODULE_AUTHOR("Biwen Li "); MODULE_DESCRIPTION("remote processor messaging tty driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/21285.c b/drivers/tty/serial/21285.c index d756fcc884cb..4de0c975ebdc 100644 --- a/drivers/tty/serial/21285.c +++ b/drivers/tty/serial/21285.c @@ -185,14 +185,14 @@ static void serial21285_break_ctl(struct uart_port *port, int break_state) unsigned long flags; unsigned int h_lcr; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); h_lcr = *CSR_H_UBRLCR; if (break_state) h_lcr |= H_UBRLCR_BREAK; else h_lcr &= ~H_UBRLCR_BREAK; *CSR_H_UBRLCR = h_lcr; - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static int serial21285_startup(struct uart_port *port) @@ -272,7 +272,7 @@ serial21285_set_termios(struct uart_port *port, struct ktermios *termios, if (port->fifosize) h_lcr |= H_UBRLCR_FIFO; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* * Update the per-port timeout. @@ -309,7 +309,7 @@ serial21285_set_termios(struct uart_port *port, struct ktermios *termios, *CSR_H_UBRLCR = h_lcr; *CSR_UARTCON = 1; - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *serial21285_type(struct uart_port *port) diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c index 4a9e71b2dbbc..021949f252f8 100644 --- a/drivers/tty/serial/8250/8250_aspeed_vuart.c +++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c @@ -288,9 +288,9 @@ static void aspeed_vuart_set_throttle(struct uart_port *port, bool throttle) struct uart_8250_port *up = up_to_u8250p(port); unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); __aspeed_vuart_set_throttle(up, throttle); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void aspeed_vuart_throttle(struct uart_port *port) @@ -340,7 +340,7 @@ static int aspeed_vuart_handle_irq(struct uart_port *port) if (iir & UART_IIR_NO_INT) return 0; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); lsr = serial_port_in(port, UART_LSR); diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c index 9afd5979c9e0..db23b3a02aef 100644 --- a/drivers/tty/serial/8250/8250_bcm7271.c +++ b/drivers/tty/serial/8250/8250_bcm7271.c @@ -567,7 +567,7 @@ static irqreturn_t brcmuart_isr(int irq, void *dev_id) if (interrupts == 0) return IRQ_NONE; - spin_lock_irqsave(&up->lock, flags); + uart_port_lock_irqsave(up, &flags); /* Clear all interrupts */ udma_writel(priv, REGS_DMA_ISR, UDMA_INTR_CLEAR, interrupts); @@ -581,7 +581,7 @@ static irqreturn_t brcmuart_isr(int irq, void *dev_id) if ((rval | tval) == 0) dev_warn(dev, "Spurious interrupt: 0x%x\n", interrupts); - spin_unlock_irqrestore(&up->lock, flags); + uart_port_unlock_irqrestore(up, flags); return IRQ_HANDLED; } @@ -608,10 +608,10 @@ static int brcmuart_startup(struct uart_port *port) * * Synchronize UART_IER access against the console. */ - spin_lock_irq(&port->lock); + uart_port_lock_irq(port); up->ier &= ~UART_IER_RDI; serial_port_out(port, UART_IER, up->ier); - spin_unlock_irq(&port->lock); + uart_port_unlock_irq(port); priv->tx_running = false; priv->dma.rx_dma = NULL; @@ -629,7 +629,7 @@ static void brcmuart_shutdown(struct uart_port *port) struct brcmuart_priv *priv = up->port.private_data; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); priv->shutdown = true; if (priv->dma_enabled) { stop_rx_dma(up); @@ -645,7 +645,7 @@ static void brcmuart_shutdown(struct uart_port *port) */ up->dma = NULL; - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); serial8250_do_shutdown(port); } @@ -807,7 +807,7 @@ static int brcmuart_handle_irq(struct uart_port *p) * interrupt but there is no data ready. */ if (((iir & UART_IIR_ID) == UART_IIR_RX_TIMEOUT) && !(priv->shutdown)) { - spin_lock_irqsave(&p->lock, flags); + uart_port_lock_irqsave(p, &flags); status = serial_port_in(p, UART_LSR); if ((status & UART_LSR_DR) == 0) { @@ -832,7 +832,7 @@ static int brcmuart_handle_irq(struct uart_port *p) handled = 1; } - spin_unlock_irqrestore(&p->lock, flags); + uart_port_unlock_irqrestore(p, flags); if (handled) return 1; } @@ -850,7 +850,7 @@ static enum hrtimer_restart brcmuart_hrtimer_func(struct hrtimer *t) if (priv->shutdown) return HRTIMER_NORESTART; - spin_lock_irqsave(&p->lock, flags); + uart_port_lock_irqsave(p, &flags); status = serial_port_in(p, UART_LSR); /* @@ -874,7 +874,7 @@ static enum hrtimer_restart brcmuart_hrtimer_func(struct hrtimer *t) status |= UART_MCR_RTS; serial_port_out(p, UART_MCR, status); } - spin_unlock_irqrestore(&p->lock, flags); + uart_port_unlock_irqrestore(p, flags); return HRTIMER_NORESTART; } @@ -1173,10 +1173,10 @@ static int __maybe_unused brcmuart_suspend(struct device *dev) * This will prevent resume from enabling RTS before the * baud rate has been restored. */ - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); priv->saved_mctrl = port->mctrl; port->mctrl &= ~TIOCM_RTS; - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); serial8250_suspend_port(priv->line); clk_disable_unprepare(priv->baud_mux_clk); @@ -1215,10 +1215,10 @@ static int __maybe_unused brcmuart_resume(struct device *dev) if (priv->saved_mctrl & TIOCM_RTS) { /* Restore RTS */ - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); port->mctrl |= TIOCM_RTS; port->ops->set_mctrl(port, port->mctrl); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } return 0; diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 3449f8790e46..30434718fad8 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -259,7 +259,7 @@ static void serial8250_backup_timeout(struct timer_list *t) unsigned int iir, ier = 0, lsr; unsigned long flags; - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); /* * Must disable interrupts or else we risk racing with the interrupt @@ -292,7 +292,7 @@ static void serial8250_backup_timeout(struct timer_list *t) if (up->port.irq) serial_out(up, UART_IER, ier); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); /* Standard timer interval plus 0.2s to keep the port running */ mod_timer(&up->timer, @@ -592,6 +592,7 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev) #ifdef CONFIG_SERIAL_8250_CONSOLE +#ifdef CONFIG_SERIAL_8250_LEGACY_CONSOLE static void univ8250_console_write(struct console *co, const char *s, unsigned int count) { @@ -599,6 +600,37 @@ static void univ8250_console_write(struct console *co, const char *s, serial8250_console_write(up, s, count); } +#else +static bool univ8250_console_write_atomic(struct console *co, + struct nbcon_write_context *wctxt) +{ + struct uart_8250_port *up = &serial8250_ports[co->index]; + + return serial8250_console_write_atomic(up, wctxt); +} + +static bool univ8250_console_write_thread(struct console *co, + struct nbcon_write_context *wctxt) +{ + struct uart_8250_port *up = &serial8250_ports[co->index]; + + return serial8250_console_write_thread(up, wctxt); +} + +static void univ8250_console_driver_enter(struct console *con, unsigned long *flags) +{ + struct uart_port *up = &serial8250_ports[con->index].port; + + __uart_port_lock_irqsave(up, flags); +} + +static void univ8250_console_driver_exit(struct console *con, unsigned long flags) +{ + struct uart_port *up = &serial8250_ports[con->index].port; + + __uart_port_unlock_irqrestore(up, flags); +} +#endif /* CONFIG_SERIAL_8250_LEGACY_CONSOLE */ static int univ8250_console_setup(struct console *co, char *options) { @@ -698,12 +730,20 @@ static int univ8250_console_match(struct console *co, char *name, int idx, static struct console univ8250_console = { .name = "ttyS", +#ifdef CONFIG_SERIAL_8250_LEGACY_CONSOLE .write = univ8250_console_write, + .flags = CON_PRINTBUFFER | CON_ANYTIME, +#else + .write_atomic = univ8250_console_write_atomic, + .write_thread = univ8250_console_write_thread, + .driver_enter = univ8250_console_driver_enter, + .driver_exit = univ8250_console_driver_exit, + .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_NBCON, +#endif .device = uart_console_device, .setup = univ8250_console_setup, .exit = univ8250_console_exit, .match = univ8250_console_match, - .flags = CON_PRINTBUFFER | CON_ANYTIME, .index = -1, .data = &serial8250_reg, }; @@ -992,11 +1032,11 @@ static void serial_8250_overrun_backoff_work(struct work_struct *work) struct uart_port *port = &up->port; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); up->ier |= UART_IER_RLSI | UART_IER_RDI; up->port.read_status_mask |= UART_LSR_DR; serial_out(up, UART_IER, up->ier); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /** @@ -1194,9 +1234,9 @@ void serial8250_unregister_port(int line) if (uart->em485) { unsigned long flags; - spin_lock_irqsave(&uart->port.lock, flags); + uart_port_lock_irqsave(&uart->port, &flags); serial8250_em485_destroy(uart); - spin_unlock_irqrestore(&uart->port.lock, flags); + uart_port_unlock_irqrestore(&uart->port, flags); } uart_remove_one_port(&serial8250_reg, &uart->port); diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index 7fa66501792d..8b30ca8fdd3f 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -22,7 +22,7 @@ static void __dma_tx_complete(void *param) dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr, UART_XMIT_SIZE, DMA_TO_DEVICE); - spin_lock_irqsave(&p->port.lock, flags); + uart_port_lock_irqsave(&p->port, &flags); dma->tx_running = 0; @@ -35,7 +35,7 @@ static void __dma_tx_complete(void *param) if (ret || !dma->tx_running) serial8250_set_THRI(p); - spin_unlock_irqrestore(&p->port.lock, flags); + uart_port_unlock_irqrestore(&p->port, flags); } static void __dma_rx_complete(struct uart_8250_port *p) @@ -70,7 +70,7 @@ static void dma_rx_complete(void *param) struct uart_8250_dma *dma = p->dma; unsigned long flags; - spin_lock_irqsave(&p->port.lock, flags); + uart_port_lock_irqsave(&p->port, &flags); if (dma->rx_running) __dma_rx_complete(p); @@ -80,7 +80,7 @@ static void dma_rx_complete(void *param) */ if (!dma->rx_running && (serial_lsr_in(p) & UART_LSR_DR)) p->dma->rx_dma(p); - spin_unlock_irqrestore(&p->port.lock, flags); + uart_port_unlock_irqrestore(&p->port, flags); } int serial8250_tx_dma(struct uart_8250_port *p) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 8aed33be2ebf..5367bcc6256c 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -290,20 +290,20 @@ static int dw8250_handle_irq(struct uart_port *p) * so we limit the workaround only to non-DMA mode. */ if (!up->dma && rx_timeout) { - spin_lock_irqsave(&p->lock, flags); + uart_port_lock_irqsave(p, &flags); status = serial_lsr_in(up); if (!(status & (UART_LSR_DR | UART_LSR_BI))) (void) p->serial_in(p, UART_RX); - spin_unlock_irqrestore(&p->lock, flags); + uart_port_unlock_irqrestore(p, flags); } /* Manually stop the Rx DMA transfer when acting as flow controller */ if (quirks & DW_UART_QUIRK_IS_DMA_FC && up->dma && up->dma->rx_running && rx_timeout) { - spin_lock_irqsave(&p->lock, flags); + uart_port_lock_irqsave(p, &flags); status = serial_lsr_in(up); - spin_unlock_irqrestore(&p->lock, flags); + uart_port_unlock_irqrestore(p, flags); if (status & (UART_LSR_DR | UART_LSR_BI)) { dw8250_writel_ext(p, RZN1_UART_RDMACR, 0); diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 27430fdd9e76..17be6ad24a0f 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -243,9 +243,9 @@ static int xr17v35x_startup(struct uart_port *port) * * Synchronize UART_IER access against the console. */ - spin_lock_irq(&port->lock); + uart_port_lock_irq(port); serial_port_out(port, UART_IER, 0); - spin_unlock_irq(&port->lock); + uart_port_unlock_irq(port); return serial8250_do_startup(port); } diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c index 6af4e1c1210a..f522eb5026c9 100644 --- a/drivers/tty/serial/8250/8250_fsl.c +++ b/drivers/tty/serial/8250/8250_fsl.c @@ -30,11 +30,11 @@ int fsl8250_handle_irq(struct uart_port *port) unsigned int iir; struct uart_8250_port *up = up_to_u8250p(port); - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); iir = port->serial_in(port, UART_IIR); if (iir & UART_IIR_NO_INT) { - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); return 0; } @@ -54,7 +54,7 @@ int fsl8250_handle_irq(struct uart_port *port) if (unlikely(up->lsr_saved_flags & UART_LSR_BI)) { up->lsr_saved_flags &= ~UART_LSR_BI; port->serial_in(port, UART_RX); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); return 1; } diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index 28f9a2679a20..33699e86eb52 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -102,7 +102,7 @@ static void mtk8250_dma_rx_complete(void *param) if (data->rx_status == DMA_RX_SHUTDOWN) return; - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); total = dma->rx_size - state.residue; @@ -128,7 +128,7 @@ static void mtk8250_dma_rx_complete(void *param) mtk8250_rx_dma(up); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } static void mtk8250_rx_dma(struct uart_8250_port *up) @@ -372,7 +372,7 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, * Ok, we're now changing the port state. Do it with * interrupts disabled. */ - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* * Update the per-port timeout. @@ -420,7 +420,7 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, if (uart_console(port)) up->port.cons->cflag = termios->c_cflag; - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); /* Don't rewrite B0 */ if (tty_termios_baud_rate(termios)) tty_termios_encode_baud_rate(termios, baud, baud); diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 8f472a2080ff..78fc1f17d5e2 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -405,7 +405,7 @@ static void omap_8250_set_termios(struct uart_port *port, * interrupts disabled. */ pm_runtime_get_sync(port->dev); - spin_lock_irq(&port->lock); + uart_port_lock_irq(port); /* * Update the per-port timeout. @@ -508,7 +508,7 @@ static void omap_8250_set_termios(struct uart_port *port, } omap8250_restore_regs(up); - spin_unlock_irq(&up->port.lock); + uart_port_unlock_irq(&up->port); pm_runtime_mark_last_busy(port->dev); pm_runtime_put_autosuspend(port->dev); @@ -533,7 +533,7 @@ static void omap_8250_pm(struct uart_port *port, unsigned int state, pm_runtime_get_sync(port->dev); /* Synchronize UART_IER access against the console. */ - spin_lock_irq(&port->lock); + uart_port_lock_irq(port); serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); efr = serial_in(up, UART_EFR); @@ -545,7 +545,7 @@ static void omap_8250_pm(struct uart_port *port, unsigned int state, serial_out(up, UART_EFR, efr); serial_out(up, UART_LCR, 0); - spin_unlock_irq(&port->lock); + uart_port_unlock_irq(port); pm_runtime_mark_last_busy(port->dev); pm_runtime_put_autosuspend(port->dev); @@ -676,7 +676,7 @@ static irqreturn_t omap8250_irq(int irq, void *dev_id) unsigned long delay; /* Synchronize UART_IER access against the console. */ - spin_lock(&port->lock); + uart_port_lock(port); up->ier = port->serial_in(port, UART_IER); if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) { port->ops->stop_rx(port); @@ -686,7 +686,7 @@ static irqreturn_t omap8250_irq(int irq, void *dev_id) */ cancel_delayed_work(&up->overrun_backoff); } - spin_unlock(&port->lock); + uart_port_unlock(port); delay = msecs_to_jiffies(up->overrun_backoff_time_ms); schedule_delayed_work(&up->overrun_backoff, delay); @@ -733,10 +733,10 @@ static int omap_8250_startup(struct uart_port *port) } /* Synchronize UART_IER access against the console. */ - spin_lock_irq(&port->lock); + uart_port_lock_irq(port); up->ier = UART_IER_RLSI | UART_IER_RDI; serial_out(up, UART_IER, up->ier); - spin_unlock_irq(&port->lock); + uart_port_unlock_irq(port); #ifdef CONFIG_PM up->capabilities |= UART_CAP_RPM; @@ -749,9 +749,9 @@ static int omap_8250_startup(struct uart_port *port) serial_out(up, UART_OMAP_WER, priv->wer); if (up->dma && !(priv->habit & UART_HAS_EFR2)) { - spin_lock_irq(&port->lock); + uart_port_lock_irq(port); up->dma->rx_dma(up); - spin_unlock_irq(&port->lock); + uart_port_unlock_irq(port); } enable_irq(up->port.irq); @@ -777,10 +777,10 @@ static void omap_8250_shutdown(struct uart_port *port) serial_out(up, UART_OMAP_EFR2, 0x0); /* Synchronize UART_IER access against the console. */ - spin_lock_irq(&port->lock); + uart_port_lock_irq(port); up->ier = 0; serial_out(up, UART_IER, 0); - spin_unlock_irq(&port->lock); + uart_port_unlock_irq(port); disable_irq_nosync(up->port.irq); dev_pm_clear_wake_irq(port->dev); @@ -805,10 +805,10 @@ static void omap_8250_throttle(struct uart_port *port) pm_runtime_get_sync(port->dev); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); port->ops->stop_rx(port); priv->throttled = true; - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); pm_runtime_mark_last_busy(port->dev); pm_runtime_put_autosuspend(port->dev); @@ -823,14 +823,14 @@ static void omap_8250_unthrottle(struct uart_port *port) pm_runtime_get_sync(port->dev); /* Synchronize UART_IER access against the console. */ - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); priv->throttled = false; if (up->dma) up->dma->rx_dma(up); up->ier |= UART_IER_RLSI | UART_IER_RDI; port->read_status_mask |= UART_LSR_DR; serial_out(up, UART_IER, up->ier); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); pm_runtime_mark_last_busy(port->dev); pm_runtime_put_autosuspend(port->dev); @@ -974,7 +974,7 @@ static void __dma_rx_complete(void *param) unsigned long flags; /* Synchronize UART_IER access against the console. */ - spin_lock_irqsave(&p->port.lock, flags); + uart_port_lock_irqsave(&p->port, &flags); /* * If the tx status is not DMA_COMPLETE, then this is a delayed @@ -983,7 +983,7 @@ static void __dma_rx_complete(void *param) */ if (dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state) != DMA_COMPLETE) { - spin_unlock_irqrestore(&p->port.lock, flags); + uart_port_unlock_irqrestore(&p->port, flags); return; } __dma_rx_do_complete(p); @@ -994,7 +994,7 @@ static void __dma_rx_complete(void *param) omap_8250_rx_dma(p); } - spin_unlock_irqrestore(&p->port.lock, flags); + uart_port_unlock_irqrestore(&p->port, flags); } static void omap_8250_rx_dma_flush(struct uart_8250_port *p) @@ -1099,7 +1099,7 @@ static void omap_8250_dma_tx_complete(void *param) dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr, UART_XMIT_SIZE, DMA_TO_DEVICE); - spin_lock_irqsave(&p->port.lock, flags); + uart_port_lock_irqsave(&p->port, &flags); dma->tx_running = 0; @@ -1128,7 +1128,7 @@ static void omap_8250_dma_tx_complete(void *param) serial8250_set_THRI(p); } - spin_unlock_irqrestore(&p->port.lock, flags); + uart_port_unlock_irqrestore(&p->port, flags); } static int omap_8250_tx_dma(struct uart_8250_port *p) @@ -1294,7 +1294,7 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) return IRQ_HANDLED; } - spin_lock(&port->lock); + uart_port_lock(port); status = serial_port_in(port, UART_LSR); @@ -1774,15 +1774,15 @@ static int omap8250_runtime_resume(struct device *dev) up = serial8250_get_port(priv->line); if (up && omap8250_lost_context(up)) { - spin_lock_irq(&up->port.lock); + uart_port_lock_irq(&up->port); omap8250_restore_regs(up); - spin_unlock_irq(&up->port.lock); + uart_port_unlock_irq(&up->port); } if (up && up->dma && up->dma->rxchan && !(priv->habit & UART_HAS_EFR2)) { - spin_lock_irq(&up->port.lock); + uart_port_lock_irq(&up->port); omap_8250_rx_dma(up); - spin_unlock_irq(&up->port.lock); + uart_port_unlock_irq(&up->port); } priv->latency = priv->calc_latency; diff --git a/drivers/tty/serial/8250/8250_pci1xxxx.c b/drivers/tty/serial/8250/8250_pci1xxxx.c index a3b25779d921..53e238c8cc89 100644 --- a/drivers/tty/serial/8250/8250_pci1xxxx.c +++ b/drivers/tty/serial/8250/8250_pci1xxxx.c @@ -225,10 +225,10 @@ static bool pci1xxxx_port_suspend(int line) if (port->suspended == 0 && port->dev) { wakeup_mask = readb(up->port.membase + UART_WAKE_MASK_REG); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); port->mctrl &= ~TIOCM_OUT2; port->ops->set_mctrl(port, port->mctrl); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); ret = (wakeup_mask & UART_WAKE_SRCS) != UART_WAKE_SRCS; } @@ -251,10 +251,10 @@ static void pci1xxxx_port_resume(int line) writeb(UART_WAKE_SRCS, port->membase + UART_WAKE_REG); if (port->suspended == 0) { - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); port->mctrl |= TIOCM_OUT2; port->ops->set_mctrl(port, port->mctrl); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } mutex_unlock(&tport->mutex); } diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index a17803da83f8..2d4e775cd7cb 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -557,6 +557,11 @@ static int serial8250_em485_init(struct uart_8250_port *p) if (!p->em485) return -ENOMEM; +#ifndef CONFIG_SERIAL_8250_LEGACY_CONSOLE + if (uart_console(&p->port)) + dev_warn(p->port.dev, "no atomic printing for rs485 consoles\n"); +#endif + hrtimer_init(&p->em485->stop_tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hrtimer_init(&p->em485->start_tx_timer, CLOCK_MONOTONIC, @@ -689,7 +694,7 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) if (p->capabilities & UART_CAP_SLEEP) { /* Synchronize UART_IER access against the console. */ - spin_lock_irq(&p->port.lock); + uart_port_lock_irq(&p->port); if (p->capabilities & UART_CAP_EFR) { lcr = serial_in(p, UART_LCR); efr = serial_in(p, UART_EFR); @@ -703,13 +708,17 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) serial_out(p, UART_EFR, efr); serial_out(p, UART_LCR, lcr); } - spin_unlock_irq(&p->port.lock); + uart_port_unlock_irq(&p->port); } serial8250_rpm_put(p); } -static void serial8250_clear_IER(struct uart_8250_port *up) +/* + * Only to be used by write_atomic() and the legacy write(), which do not + * require port lock. + */ +static void __serial8250_clear_IER(struct uart_8250_port *up) { if (up->capabilities & UART_CAP_UUE) serial_out(up, UART_IER, UART_IER_UUE); @@ -717,6 +726,14 @@ static void serial8250_clear_IER(struct uart_8250_port *up) serial_out(up, UART_IER, 0); } +static inline void serial8250_clear_IER(struct uart_8250_port *up) +{ + /* Port locked to synchronize UART_IER access against the console. */ + lockdep_assert_held_once(&up->port.lock); + + __serial8250_clear_IER(up); +} + #ifdef CONFIG_SERIAL_8250_RSA /* * Attempts to turn on the RSA FIFO. Returns zero on failure. @@ -746,9 +763,9 @@ static void enable_rsa(struct uart_8250_port *up) { if (up->port.type == PORT_RSA) { if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) { - spin_lock_irq(&up->port.lock); + uart_port_lock_irq(&up->port); __enable_rsa(up); - spin_unlock_irq(&up->port.lock); + uart_port_unlock_irq(&up->port); } if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) serial_out(up, UART_RSA_FRR, 0); @@ -768,7 +785,7 @@ static void disable_rsa(struct uart_8250_port *up) if (up->port.type == PORT_RSA && up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) { - spin_lock_irq(&up->port.lock); + uart_port_lock_irq(&up->port); mode = serial_in(up, UART_RSA_MSR); result = !(mode & UART_RSA_MSR_FIFO); @@ -781,7 +798,7 @@ static void disable_rsa(struct uart_8250_port *up) if (result) up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16; - spin_unlock_irq(&up->port.lock); + uart_port_unlock_irq(&up->port); } } #endif /* CONFIG_SERIAL_8250_RSA */ @@ -1172,7 +1189,7 @@ static void autoconfig(struct uart_8250_port *up) * * Synchronize UART_IER access against the console. */ - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); up->capabilities = 0; up->bugs = 0; @@ -1211,7 +1228,7 @@ static void autoconfig(struct uart_8250_port *up) /* * We failed; there's nothing here */ - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); DEBUG_AUTOCONF("IER test failed (%02x, %02x) ", scratch2, scratch3); goto out; @@ -1235,7 +1252,7 @@ static void autoconfig(struct uart_8250_port *up) status1 = serial_in(up, UART_MSR) & UART_MSR_STATUS_BITS; serial8250_out_MCR(up, save_mcr); if (status1 != (UART_MSR_DCD | UART_MSR_CTS)) { - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); DEBUG_AUTOCONF("LOOP test failed (%02x) ", status1); goto out; @@ -1304,7 +1321,7 @@ static void autoconfig(struct uart_8250_port *up) serial8250_clear_IER(up); out_unlock: - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); /* * Check if the device is a Fintek F81216A @@ -1341,9 +1358,9 @@ static void autoconfig_irq(struct uart_8250_port *up) probe_irq_off(probe_irq_on()); save_mcr = serial8250_in_MCR(up); /* Synchronize UART_IER access against the console. */ - spin_lock_irq(&port->lock); + uart_port_lock_irq(port); save_ier = serial_in(up, UART_IER); - spin_unlock_irq(&port->lock); + uart_port_unlock_irq(port); serial8250_out_MCR(up, UART_MCR_OUT1 | UART_MCR_OUT2); irqs = probe_irq_on(); @@ -1356,9 +1373,9 @@ static void autoconfig_irq(struct uart_8250_port *up) UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); } /* Synchronize UART_IER access against the console. */ - spin_lock_irq(&port->lock); + uart_port_lock_irq(port); serial_out(up, UART_IER, UART_IER_ALL_INTR); - spin_unlock_irq(&port->lock); + uart_port_unlock_irq(port); serial_in(up, UART_LSR); serial_in(up, UART_RX); serial_in(up, UART_IIR); @@ -1369,9 +1386,9 @@ static void autoconfig_irq(struct uart_8250_port *up) serial8250_out_MCR(up, save_mcr); /* Synchronize UART_IER access against the console. */ - spin_lock_irq(&port->lock); + uart_port_lock_irq(port); serial_out(up, UART_IER, save_ier); - spin_unlock_irq(&port->lock); + uart_port_unlock_irq(port); if (port->flags & UPF_FOURPORT) outb_p(save_ICP, ICP); @@ -1436,13 +1453,13 @@ static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t) unsigned long flags; serial8250_rpm_get(p); - spin_lock_irqsave(&p->port.lock, flags); + uart_port_lock_irqsave(&p->port, &flags); if (em485->active_timer == &em485->stop_tx_timer) { p->rs485_stop_tx(p); em485->active_timer = NULL; em485->tx_stopped = true; } - spin_unlock_irqrestore(&p->port.lock, flags); + uart_port_unlock_irqrestore(&p->port, flags); serial8250_rpm_put(p); return HRTIMER_NORESTART; @@ -1624,12 +1641,12 @@ static enum hrtimer_restart serial8250_em485_handle_start_tx(struct hrtimer *t) struct uart_8250_port *p = em485->port; unsigned long flags; - spin_lock_irqsave(&p->port.lock, flags); + uart_port_lock_irqsave(&p->port, &flags); if (em485->active_timer == &em485->start_tx_timer) { __start_tx(&p->port); em485->active_timer = NULL; } - spin_unlock_irqrestore(&p->port.lock, flags); + uart_port_unlock_irqrestore(&p->port, flags); return HRTIMER_NORESTART; } @@ -1912,7 +1929,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) if (iir & UART_IIR_NO_INT) return 0; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); status = serial_lsr_in(up); @@ -1982,9 +1999,9 @@ static int serial8250_tx_threshold_handle_irq(struct uart_port *port) if ((iir & UART_IIR_ID) == UART_IIR_THRI) { struct uart_8250_port *up = up_to_u8250p(port); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); serial8250_tx_chars(up); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } iir = serial_port_in(port, UART_IIR); @@ -1999,10 +2016,10 @@ static unsigned int serial8250_tx_empty(struct uart_port *port) serial8250_rpm_get(up); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); if (!serial8250_tx_dma_running(up) && uart_lsr_tx_empty(serial_lsr_in(up))) result = TIOCSER_TEMT; - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); serial8250_rpm_put(up); @@ -2064,13 +2081,13 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state) unsigned long flags; serial8250_rpm_get(up); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); if (break_state == -1) up->lcr |= UART_LCR_SBC; else up->lcr &= ~UART_LCR_SBC; serial_port_out(port, UART_LCR, up->lcr); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); serial8250_rpm_put(up); } @@ -2205,7 +2222,7 @@ int serial8250_do_startup(struct uart_port *port) * * Synchronize UART_IER access against the console. */ - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); up->acr = 0; serial_port_out(port, UART_LCR, UART_LCR_CONF_MODE_B); serial_port_out(port, UART_EFR, UART_EFR_ECB); @@ -2215,7 +2232,7 @@ int serial8250_do_startup(struct uart_port *port) serial_port_out(port, UART_LCR, UART_LCR_CONF_MODE_B); serial_port_out(port, UART_EFR, UART_EFR_ECB); serial_port_out(port, UART_LCR, 0); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } if (port->type == PORT_DA830) { @@ -2224,10 +2241,10 @@ int serial8250_do_startup(struct uart_port *port) * * Synchronize UART_IER access against the console. */ - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); serial_port_out(port, UART_IER, 0); serial_port_out(port, UART_DA830_PWREMU_MGMT, 0); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); mdelay(10); /* Enable Tx, Rx and free run mode */ @@ -2341,7 +2358,7 @@ int serial8250_do_startup(struct uart_port *port) * * Synchronize UART_IER access against the console. */ - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); wait_for_xmitr(up, UART_LSR_THRE); serial_port_out_sync(port, UART_IER, UART_IER_THRI); @@ -2353,7 +2370,7 @@ int serial8250_do_startup(struct uart_port *port) iir = serial_port_in(port, UART_IIR); serial_port_out(port, UART_IER, 0); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); if (port->irqflags & IRQF_SHARED) enable_irq(port->irq); @@ -2376,7 +2393,7 @@ int serial8250_do_startup(struct uart_port *port) */ serial_port_out(port, UART_LCR, UART_LCR_WLEN8); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); if (up->port.flags & UPF_FOURPORT) { if (!up->port.irq) up->port.mctrl |= TIOCM_OUT1; @@ -2422,7 +2439,7 @@ int serial8250_do_startup(struct uart_port *port) } dont_test_tx_en: - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); /* * Clear the interrupt registers again for luck, and clear the @@ -2493,17 +2510,17 @@ void serial8250_do_shutdown(struct uart_port *port) * * Synchronize UART_IER access against the console. */ - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); up->ier = 0; serial_port_out(port, UART_IER, 0); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); synchronize_irq(port->irq); if (up->dma) serial8250_release_dma(up); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); if (port->flags & UPF_FOURPORT) { /* reset interrupts on the AST Fourport board */ inb((port->iobase & 0xfe0) | 0x1f); @@ -2512,7 +2529,7 @@ void serial8250_do_shutdown(struct uart_port *port) port->mctrl &= ~TIOCM_OUT2; serial8250_set_mctrl(port, port->mctrl); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); /* * Disable break condition and FIFOs @@ -2748,14 +2765,14 @@ void serial8250_update_uartclk(struct uart_port *port, unsigned int uartclk) quot = serial8250_get_divisor(port, baud, &frac); serial8250_rpm_get(up); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); uart_update_timeout(port, termios->c_cflag, baud); serial8250_set_divisor(port, baud, quot, frac); serial_port_out(port, UART_LCR, up->lcr); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); serial8250_rpm_put(up); out_unlock: @@ -2792,7 +2809,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, * Synchronize UART_IER access against the console. */ serial8250_rpm_get(up); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); up->lcr = cval; /* Save computed LCR */ @@ -2895,7 +2912,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, serial_port_out(port, UART_FCR, up->fcr); /* set fcr */ } serial8250_set_mctrl(port, port->mctrl); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); serial8250_rpm_put(up); /* Don't rewrite B0 */ @@ -2918,15 +2935,15 @@ void serial8250_do_set_ldisc(struct uart_port *port, struct ktermios *termios) { if (termios->c_line == N_PPS) { port->flags |= UPF_HARDPPS_CD; - spin_lock_irq(&port->lock); + uart_port_lock_irq(port); serial8250_enable_ms(port); - spin_unlock_irq(&port->lock); + uart_port_unlock_irq(port); } else { port->flags &= ~UPF_HARDPPS_CD; if (!UART_ENABLE_MS(port, termios->c_cflag)) { - spin_lock_irq(&port->lock); + uart_port_lock_irq(port); serial8250_disable_ms(port); - spin_unlock_irq(&port->lock); + uart_port_unlock_irq(port); } } } @@ -3322,6 +3339,11 @@ static void serial8250_console_putchar(struct uart_port *port, unsigned char ch) wait_for_xmitr(up, UART_LSR_THRE); serial_port_out(port, UART_TX, ch); + + if (ch == '\n') + up->console_newline_needed = false; + else + up->console_newline_needed = true; } /* @@ -3350,6 +3372,7 @@ static void serial8250_console_restore(struct uart_8250_port *up) serial8250_out_MCR(up, up->mcr | UART_MCR_DTR | UART_MCR_RTS); } +#ifdef CONFIG_SERIAL_8250_LEGACY_CONSOLE /* * Print a string to the serial port using the device FIFO * @@ -3400,15 +3423,15 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, touch_nmi_watchdog(); if (oops_in_progress) - locked = spin_trylock_irqsave(&port->lock, flags); + locked = uart_port_trylock_irqsave(port, &flags); else - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* * First save the IER then disable the interrupts */ ier = serial_port_in(port, UART_IER); - serial8250_clear_IER(up); + __serial8250_clear_IER(up); /* check scratch reg to see if port powered off during system sleep */ if (up->canary && (up->canary != serial_port_in(port, UART_SCR))) { @@ -3472,8 +3495,137 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, serial8250_modem_status(up); if (locked) - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } +#else +bool serial8250_console_write_thread(struct uart_8250_port *up, + struct nbcon_write_context *wctxt) +{ + struct uart_8250_em485 *em485 = up->em485; + struct uart_port *port = &up->port; + bool done = false; + unsigned int ier; + + touch_nmi_watchdog(); + + if (!nbcon_enter_unsafe(wctxt)) + return false; + + /* First save IER then disable the interrupts. */ + ier = serial_port_in(port, UART_IER); + serial8250_clear_IER(up); + + /* Check scratch reg if port powered off during system sleep. */ + if (up->canary && (up->canary != serial_port_in(port, UART_SCR))) { + serial8250_console_restore(up); + up->canary = 0; + } + + if (em485) { + if (em485->tx_stopped) + up->rs485_start_tx(up); + mdelay(port->rs485.delay_rts_before_send); + } + + if (nbcon_exit_unsafe(wctxt)) { + int len = READ_ONCE(wctxt->len); + int i; + + /* + * Write out the message. Toggle unsafe for each byte in order + * to give another (higher priority) context the opportunity + * for a friendly takeover. If such a takeover occurs, this + * context must reacquire ownership in order to perform final + * actions (such as re-enabling the interrupts). + * + * IMPORTANT: wctxt->outbuf and wctxt->len are no longer valid + * after a reacquire so writing the message must be + * aborted. + */ + for (i = 0; i < len; i++) { + if (!nbcon_enter_unsafe(wctxt)) { + nbcon_reacquire(wctxt); + break; + } + + uart_console_write(port, wctxt->outbuf + i, 1, serial8250_console_putchar); + + if (!nbcon_exit_unsafe(wctxt)) { + nbcon_reacquire(wctxt); + break; + } + } + done = (i == len); + } else { + nbcon_reacquire(wctxt); + } + + while (!nbcon_enter_unsafe(wctxt)) + nbcon_reacquire(wctxt); + + /* Finally, wait for transmitter to become empty and restore IER. */ + wait_for_xmitr(up, UART_LSR_BOTH_EMPTY); + if (em485) { + mdelay(port->rs485.delay_rts_after_send); + if (em485->tx_stopped) + up->rs485_stop_tx(up); + } + serial_port_out(port, UART_IER, ier); + + /* + * The receive handling will happen properly because the receive ready + * bit will still be set; it is not cleared on read. However, modem + * control will not, we must call it if we have saved something in the + * saved flags while processing with interrupts off. + */ + if (up->msr_saved_flags) + serial8250_modem_status(up); + + /* Success if no handover/takeover and message fully printed. */ + return (nbcon_exit_unsafe(wctxt) && done); +} + +bool serial8250_console_write_atomic(struct uart_8250_port *up, + struct nbcon_write_context *wctxt) +{ + struct uart_port *port = &up->port; + unsigned int ier; + + /* Atomic console not supported for rs485 mode. */ + if (up->em485) + return false; + + touch_nmi_watchdog(); + + if (!nbcon_enter_unsafe(wctxt)) + return false; + + /* + * First save IER then disable the interrupts. The special variant to + * clear IER is used because atomic printing may occur without holding + * the port lock. + */ + ier = serial_port_in(port, UART_IER); + __serial8250_clear_IER(up); + + /* Check scratch reg if port powered off during system sleep. */ + if (up->canary && (up->canary != serial_port_in(port, UART_SCR))) { + serial8250_console_restore(up); + up->canary = 0; + } + + if (up->console_newline_needed) + uart_console_write(port, "\n", 1, serial8250_console_putchar); + uart_console_write(port, wctxt->outbuf, wctxt->len, serial8250_console_putchar); + + /* Finally, wait for transmitter to become empty and restore IER. */ + wait_for_xmitr(up, UART_LSR_BOTH_EMPTY); + serial_port_out(port, UART_IER, ier); + + /* Success if no handover/takeover. */ + return nbcon_exit_unsafe(wctxt); +} +#endif /* CONFIG_SERIAL_8250_LEGACY_CONSOLE */ static unsigned int probe_baud(struct uart_port *port) { @@ -3492,6 +3644,7 @@ static unsigned int probe_baud(struct uart_port *port) int serial8250_console_setup(struct uart_port *port, char *options, bool probe) { + struct uart_8250_port *up = up_to_u8250p(port); int baud = 9600; int bits = 8; int parity = 'n'; @@ -3501,6 +3654,8 @@ int serial8250_console_setup(struct uart_port *port, char *options, bool probe) if (!port->iobase && !port->membase) return -ENODEV; + up->console_newline_needed = false; + if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else if (probe) diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index 5fab4c978891..7090b251dd4d 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -147,14 +147,14 @@ static irqreturn_t altera_jtaguart_interrupt(int irq, void *data) isr = (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) >> ALTERA_JTAGUART_CONTROL_RI_OFF) & port->read_status_mask; - spin_lock(&port->lock); + uart_port_lock(port); if (isr & ALTERA_JTAGUART_CONTROL_RE_MSK) altera_jtaguart_rx_chars(port); if (isr & ALTERA_JTAGUART_CONTROL_WE_MSK) altera_jtaguart_tx_chars(port); - spin_unlock(&port->lock); + uart_port_unlock(port); return IRQ_RETVAL(isr); } @@ -180,14 +180,14 @@ static int altera_jtaguart_startup(struct uart_port *port) return ret; } - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Enable RX interrupts now */ port->read_status_mask = ALTERA_JTAGUART_CONTROL_RE_MSK; writel(port->read_status_mask, port->membase + ALTERA_JTAGUART_CONTROL_REG); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return 0; } @@ -196,14 +196,14 @@ static void altera_jtaguart_shutdown(struct uart_port *port) { unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Disable all interrupts now */ port->read_status_mask = 0; writel(port->read_status_mask, port->membase + ALTERA_JTAGUART_CONTROL_REG); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); free_irq(port->irq, port); } @@ -264,33 +264,33 @@ static void altera_jtaguart_console_putc(struct uart_port *port, unsigned char c unsigned long flags; u32 status; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); while (!altera_jtaguart_tx_space(port, &status)) { - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); if ((status & ALTERA_JTAGUART_CONTROL_AC_MSK) == 0) { return; /* no connection activity */ } cpu_relax(); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); } writel(c, port->membase + ALTERA_JTAGUART_DATA_REG); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } #else static void altera_jtaguart_console_putc(struct uart_port *port, unsigned char c) { unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); while (!altera_jtaguart_tx_space(port, NULL)) { - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); cpu_relax(); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); } writel(c, port->membase + ALTERA_JTAGUART_DATA_REG); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } #endif diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index a9c41942190c..77835ac68df2 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -164,13 +164,13 @@ static void altera_uart_break_ctl(struct uart_port *port, int break_state) struct altera_uart *pp = container_of(port, struct altera_uart, port); unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); if (break_state == -1) pp->imr |= ALTERA_UART_CONTROL_TRBK_MSK; else pp->imr &= ~ALTERA_UART_CONTROL_TRBK_MSK; altera_uart_update_ctrl_reg(pp); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void altera_uart_set_termios(struct uart_port *port, @@ -187,10 +187,10 @@ static void altera_uart_set_termios(struct uart_port *port, tty_termios_copy_hw(termios, old); tty_termios_encode_baud_rate(termios, baud, baud); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); uart_update_timeout(port, termios->c_cflag, baud); altera_uart_writel(port, baudclk, ALTERA_UART_DIVISOR_REG); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); /* * FIXME: port->read_status_mask and port->ignore_status_mask @@ -264,12 +264,12 @@ static irqreturn_t altera_uart_interrupt(int irq, void *data) isr = altera_uart_readl(port, ALTERA_UART_STATUS_REG) & pp->imr; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); if (isr & ALTERA_UART_STATUS_RRDY_MSK) altera_uart_rx_chars(port); if (isr & ALTERA_UART_STATUS_TRDY_MSK) altera_uart_tx_chars(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return IRQ_RETVAL(isr); } @@ -313,13 +313,13 @@ static int altera_uart_startup(struct uart_port *port) } } - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Enable RX interrupts now */ pp->imr = ALTERA_UART_CONTROL_RRDY_MSK; altera_uart_update_ctrl_reg(pp); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return 0; } @@ -329,13 +329,13 @@ static void altera_uart_shutdown(struct uart_port *port) struct altera_uart *pp = container_of(port, struct altera_uart, port); unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Disable all interrupts now */ pp->imr = 0; altera_uart_update_ctrl_reg(pp); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); if (port->irq) free_irq(port->irq, port); diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c index b5a7404cbacb..eabbf8afc9b5 100644 --- a/drivers/tty/serial/amba-pl010.c +++ b/drivers/tty/serial/amba-pl010.c @@ -207,7 +207,7 @@ static irqreturn_t pl010_int(int irq, void *dev_id) unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; int handled = 0; - spin_lock(&port->lock); + uart_port_lock(port); status = readb(port->membase + UART010_IIR); if (status) { @@ -228,7 +228,7 @@ static irqreturn_t pl010_int(int irq, void *dev_id) handled = 1; } - spin_unlock(&port->lock); + uart_port_unlock(port); return IRQ_RETVAL(handled); } @@ -270,14 +270,14 @@ static void pl010_break_ctl(struct uart_port *port, int break_state) unsigned long flags; unsigned int lcr_h; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); lcr_h = readb(port->membase + UART010_LCRH); if (break_state == -1) lcr_h |= UART01x_LCRH_BRK; else lcr_h &= ~UART01x_LCRH_BRK; writel(lcr_h, port->membase + UART010_LCRH); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static int pl010_startup(struct uart_port *port) @@ -385,7 +385,7 @@ pl010_set_termios(struct uart_port *port, struct ktermios *termios, if (port->fifosize > 1) lcr_h |= UART01x_LCRH_FEN; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* * Update the per-port timeout. @@ -438,22 +438,22 @@ pl010_set_termios(struct uart_port *port, struct ktermios *termios, writel(lcr_h, port->membase + UART010_LCRH); writel(old_cr, port->membase + UART010_CR); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void pl010_set_ldisc(struct uart_port *port, struct ktermios *termios) { if (termios->c_line == N_PPS) { port->flags |= UPF_HARDPPS_CD; - spin_lock_irq(&port->lock); + uart_port_lock_irq(port); pl010_enable_ms(port); - spin_unlock_irq(&port->lock); + uart_port_unlock_irq(port); } else { port->flags &= ~UPF_HARDPPS_CD; if (!UART_ENABLE_MS(port, termios->c_cflag)) { - spin_lock_irq(&port->lock); + uart_port_lock_irq(port); pl010_disable_ms(port); - spin_unlock_irq(&port->lock); + uart_port_unlock_irq(port); } } } diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 362bbcdece0d..9cd660edb165 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -347,9 +347,9 @@ static int pl011_fifo_to_tty(struct uart_amba_port *uap) flag = TTY_FRAME; } - spin_unlock(&uap->port.lock); + uart_port_unlock(&uap->port); sysrq = uart_handle_sysrq_char(&uap->port, ch & 255); - spin_lock(&uap->port.lock); + uart_port_lock(&uap->port); if (!sysrq) uart_insert_char(&uap->port, ch, UART011_DR_OE, ch, flag); @@ -544,7 +544,7 @@ static void pl011_dma_tx_callback(void *data) unsigned long flags; u16 dmacr; - spin_lock_irqsave(&uap->port.lock, flags); + uart_port_lock_irqsave(&uap->port, &flags); if (uap->dmatx.queued) dma_unmap_single(dmatx->chan->device->dev, dmatx->dma, dmatx->len, DMA_TO_DEVICE); @@ -565,7 +565,7 @@ static void pl011_dma_tx_callback(void *data) if (!(dmacr & UART011_TXDMAE) || uart_tx_stopped(&uap->port) || uart_circ_empty(&uap->port.state->xmit)) { uap->dmatx.queued = false; - spin_unlock_irqrestore(&uap->port.lock, flags); + uart_port_unlock_irqrestore(&uap->port, flags); return; } @@ -576,7 +576,7 @@ static void pl011_dma_tx_callback(void *data) */ pl011_start_tx_pio(uap); - spin_unlock_irqrestore(&uap->port.lock, flags); + uart_port_unlock_irqrestore(&uap->port, flags); } /* @@ -1004,7 +1004,7 @@ static void pl011_dma_rx_callback(void *data) * routine to flush out the secondary DMA buffer while * we immediately trigger the next DMA job. */ - spin_lock_irq(&uap->port.lock); + uart_port_lock_irq(&uap->port); /* * Rx data can be taken by the UART interrupts during * the DMA irq handler. So we check the residue here. @@ -1020,7 +1020,7 @@ static void pl011_dma_rx_callback(void *data) ret = pl011_dma_rx_trigger_dma(uap); pl011_dma_rx_chars(uap, pending, lastbuf, false); - spin_unlock_irq(&uap->port.lock); + uart_port_unlock_irq(&uap->port); /* * Do this check after we picked the DMA chars so we don't * get some IRQ immediately from RX. @@ -1086,11 +1086,11 @@ static void pl011_dma_rx_poll(struct timer_list *t) if (jiffies_to_msecs(jiffies - dmarx->last_jiffies) > uap->dmarx.poll_timeout) { - spin_lock_irqsave(&uap->port.lock, flags); + uart_port_lock_irqsave(&uap->port, &flags); pl011_dma_rx_stop(uap); uap->im |= UART011_RXIM; pl011_write(uap->im, uap, REG_IMSC); - spin_unlock_irqrestore(&uap->port.lock, flags); + uart_port_unlock_irqrestore(&uap->port, flags); uap->dmarx.running = false; dmaengine_terminate_all(rxchan); @@ -1186,10 +1186,10 @@ static void pl011_dma_shutdown(struct uart_amba_port *uap) while (pl011_read(uap, REG_FR) & uap->vendor->fr_busy) cpu_relax(); - spin_lock_irq(&uap->port.lock); + uart_port_lock_irq(&uap->port); uap->dmacr &= ~(UART011_DMAONERR | UART011_RXDMAE | UART011_TXDMAE); pl011_write(uap->dmacr, uap, REG_DMACR); - spin_unlock_irq(&uap->port.lock); + uart_port_unlock_irq(&uap->port); if (uap->using_tx_dma) { /* In theory, this should already be done by pl011_dma_flush_buffer */ @@ -1400,9 +1400,9 @@ static void pl011_throttle_rx(struct uart_port *port) { unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); pl011_stop_rx(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void pl011_enable_ms(struct uart_port *port) @@ -1420,7 +1420,7 @@ __acquires(&uap->port.lock) { pl011_fifo_to_tty(uap); - spin_unlock(&uap->port.lock); + uart_port_unlock(&uap->port); tty_flip_buffer_push(&uap->port.state->port); /* * If we were temporarily out of DMA mode for a while, @@ -1445,7 +1445,7 @@ __acquires(&uap->port.lock) #endif } } - spin_lock(&uap->port.lock); + uart_port_lock(&uap->port); } static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c, @@ -1551,7 +1551,7 @@ static irqreturn_t pl011_int(int irq, void *dev_id) unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; int handled = 0; - spin_lock_irqsave(&uap->port.lock, flags); + uart_port_lock_irqsave(&uap->port, &flags); status = pl011_read(uap, REG_RIS) & uap->im; if (status) { do { @@ -1581,7 +1581,7 @@ static irqreturn_t pl011_int(int irq, void *dev_id) handled = 1; } - spin_unlock_irqrestore(&uap->port.lock, flags); + uart_port_unlock_irqrestore(&uap->port, flags); return IRQ_RETVAL(handled); } @@ -1653,14 +1653,14 @@ static void pl011_break_ctl(struct uart_port *port, int break_state) unsigned long flags; unsigned int lcr_h; - spin_lock_irqsave(&uap->port.lock, flags); + uart_port_lock_irqsave(&uap->port, &flags); lcr_h = pl011_read(uap, REG_LCRH_TX); if (break_state == -1) lcr_h |= UART01x_LCRH_BRK; else lcr_h &= ~UART01x_LCRH_BRK; pl011_write(lcr_h, uap, REG_LCRH_TX); - spin_unlock_irqrestore(&uap->port.lock, flags); + uart_port_unlock_irqrestore(&uap->port, flags); } #ifdef CONFIG_CONSOLE_POLL @@ -1799,7 +1799,7 @@ static void pl011_enable_interrupts(struct uart_amba_port *uap) unsigned long flags; unsigned int i; - spin_lock_irqsave(&uap->port.lock, flags); + uart_port_lock_irqsave(&uap->port, &flags); /* Clear out any spuriously appearing RX interrupts */ pl011_write(UART011_RTIS | UART011_RXIS, uap, REG_ICR); @@ -1821,7 +1821,7 @@ static void pl011_enable_interrupts(struct uart_amba_port *uap) if (!pl011_dma_rx_running(uap)) uap->im |= UART011_RXIM; pl011_write(uap->im, uap, REG_IMSC); - spin_unlock_irqrestore(&uap->port.lock, flags); + uart_port_unlock_irqrestore(&uap->port, flags); } static void pl011_unthrottle_rx(struct uart_port *port) @@ -1829,7 +1829,7 @@ static void pl011_unthrottle_rx(struct uart_port *port) struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); unsigned long flags; - spin_lock_irqsave(&uap->port.lock, flags); + uart_port_lock_irqsave(&uap->port, &flags); uap->im = UART011_RTIM; if (!pl011_dma_rx_running(uap)) @@ -1837,7 +1837,7 @@ static void pl011_unthrottle_rx(struct uart_port *port) pl011_write(uap->im, uap, REG_IMSC); - spin_unlock_irqrestore(&uap->port.lock, flags); + uart_port_unlock_irqrestore(&uap->port, flags); } static int pl011_startup(struct uart_port *port) @@ -1857,7 +1857,7 @@ static int pl011_startup(struct uart_port *port) pl011_write(uap->vendor->ifls, uap, REG_IFLS); - spin_lock_irq(&uap->port.lock); + uart_port_lock_irq(&uap->port); cr = pl011_read(uap, REG_CR); cr &= UART011_CR_RTS | UART011_CR_DTR; @@ -1868,7 +1868,7 @@ static int pl011_startup(struct uart_port *port) pl011_write(cr, uap, REG_CR); - spin_unlock_irq(&uap->port.lock); + uart_port_unlock_irq(&uap->port); /* * initialise the old status of the modem signals @@ -1929,12 +1929,12 @@ static void pl011_disable_uart(struct uart_amba_port *uap) unsigned int cr; uap->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS); - spin_lock_irq(&uap->port.lock); + uart_port_lock_irq(&uap->port); cr = pl011_read(uap, REG_CR); cr &= UART011_CR_RTS | UART011_CR_DTR; cr |= UART01x_CR_UARTEN | UART011_CR_TXE; pl011_write(cr, uap, REG_CR); - spin_unlock_irq(&uap->port.lock); + uart_port_unlock_irq(&uap->port); /* * disable break condition and fifos @@ -1946,14 +1946,14 @@ static void pl011_disable_uart(struct uart_amba_port *uap) static void pl011_disable_interrupts(struct uart_amba_port *uap) { - spin_lock_irq(&uap->port.lock); + uart_port_lock_irq(&uap->port); /* mask all interrupts and clear all pending ones */ uap->im = 0; pl011_write(uap->im, uap, REG_IMSC); pl011_write(0xffff, uap, REG_ICR); - spin_unlock_irq(&uap->port.lock); + uart_port_unlock_irq(&uap->port); } static void pl011_shutdown(struct uart_port *port) @@ -2098,7 +2098,7 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, bits = tty_get_frame_size(termios->c_cflag); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* * Update the per-port timeout. @@ -2172,7 +2172,7 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, old_cr |= UART011_CR_RXE; pl011_write(old_cr, uap, REG_CR); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void @@ -2190,10 +2190,10 @@ sbsa_uart_set_termios(struct uart_port *port, struct ktermios *termios, termios->c_cflag &= ~(CMSPAR | CRTSCTS); termios->c_cflag |= CS8 | CLOCAL; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); uart_update_timeout(port, CS8, uap->fixed_baud); pl011_setup_status_masks(port, termios); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *pl011_type(struct uart_port *port) @@ -2328,13 +2328,10 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) clk_enable(uap->clk); - local_irq_save(flags); - if (uap->port.sysrq) - locked = 0; - else if (oops_in_progress) - locked = spin_trylock(&uap->port.lock); + if (uap->port.sysrq || oops_in_progress) + locked = uart_port_trylock_irqsave(&uap->port, &flags); else - spin_lock(&uap->port.lock); + uart_port_lock_irqsave(&uap->port, &flags); /* * First save the CR then disable the interrupts @@ -2360,8 +2357,7 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) pl011_write(old_cr, uap, REG_CR); if (locked) - spin_unlock(&uap->port.lock); - local_irq_restore(flags); + uart_port_unlock_irqrestore(&uap->port, flags); clk_disable(uap->clk); } diff --git a/drivers/tty/serial/apbuart.c b/drivers/tty/serial/apbuart.c index d3cb341f2c55..364599f256db 100644 --- a/drivers/tty/serial/apbuart.c +++ b/drivers/tty/serial/apbuart.c @@ -133,7 +133,7 @@ static irqreturn_t apbuart_int(int irq, void *dev_id) struct uart_port *port = dev_id; unsigned int status; - spin_lock(&port->lock); + uart_port_lock(port); status = UART_GET_STATUS(port); if (status & UART_STATUS_DR) @@ -141,7 +141,7 @@ static irqreturn_t apbuart_int(int irq, void *dev_id) if (status & UART_STATUS_THE) apbuart_tx_chars(port); - spin_unlock(&port->lock); + uart_port_unlock(port); return IRQ_HANDLED; } @@ -228,7 +228,7 @@ static void apbuart_set_termios(struct uart_port *port, if (termios->c_cflag & CRTSCTS) cr |= UART_CTRL_FL; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Update the per-port timeout. */ uart_update_timeout(port, termios->c_cflag, baud); @@ -251,7 +251,7 @@ static void apbuart_set_termios(struct uart_port *port, UART_PUT_SCAL(port, quot); UART_PUT_CTRL(port, cr); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *apbuart_type(struct uart_port *port) diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index 924c1a89347c..ffd234673177 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -133,9 +133,9 @@ static unsigned int ar933x_uart_tx_empty(struct uart_port *port) unsigned long flags; unsigned int rdata; - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); rdata = ar933x_uart_read(up, AR933X_UART_DATA_REG); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); return (rdata & AR933X_UART_DATA_TX_CSR) ? 0 : TIOCSER_TEMT; } @@ -220,14 +220,14 @@ static void ar933x_uart_break_ctl(struct uart_port *port, int break_state) container_of(port, struct ar933x_uart_port, port); unsigned long flags; - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); if (break_state == -1) ar933x_uart_rmw_set(up, AR933X_UART_CS_REG, AR933X_UART_CS_TX_BREAK); else ar933x_uart_rmw_clear(up, AR933X_UART_CS_REG, AR933X_UART_CS_TX_BREAK); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } /* @@ -318,7 +318,7 @@ static void ar933x_uart_set_termios(struct uart_port *port, * Ok, we're now changing the port state. Do it with * interrupts disabled. */ - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); /* disable the UART */ ar933x_uart_rmw_clear(up, AR933X_UART_CS_REG, @@ -352,7 +352,7 @@ static void ar933x_uart_set_termios(struct uart_port *port, AR933X_UART_CS_IF_MODE_M << AR933X_UART_CS_IF_MODE_S, AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); if (tty_termios_baud_rate(new)) tty_termios_encode_baud_rate(new, baud, baud); @@ -450,7 +450,7 @@ static irqreturn_t ar933x_uart_interrupt(int irq, void *dev_id) if ((status & AR933X_UART_CS_HOST_INT) == 0) return IRQ_NONE; - spin_lock(&up->port.lock); + uart_port_lock(&up->port); status = ar933x_uart_read(up, AR933X_UART_INT_REG); status &= ar933x_uart_read(up, AR933X_UART_INT_EN_REG); @@ -468,7 +468,7 @@ static irqreturn_t ar933x_uart_interrupt(int irq, void *dev_id) ar933x_uart_tx_chars(up); } - spin_unlock(&up->port.lock); + uart_port_unlock(&up->port); return IRQ_HANDLED; } @@ -485,7 +485,7 @@ static int ar933x_uart_startup(struct uart_port *port) if (ret) return ret; - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); /* Enable HOST interrupts */ ar933x_uart_rmw_set(up, AR933X_UART_CS_REG, @@ -498,7 +498,7 @@ static int ar933x_uart_startup(struct uart_port *port) /* Enable RX interrupts */ ar933x_uart_start_rx_interrupt(up); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); return 0; } @@ -632,9 +632,9 @@ static void ar933x_uart_console_write(struct console *co, const char *s, if (up->port.sysrq) locked = 0; else if (oops_in_progress) - locked = spin_trylock(&up->port.lock); + locked = uart_port_trylock(&up->port); else - spin_lock(&up->port.lock); + uart_port_lock(&up->port); /* * First save the IER then disable the interrupts @@ -654,7 +654,7 @@ static void ar933x_uart_console_write(struct console *co, const char *s, ar933x_uart_write(up, AR933X_UART_INT_REG, AR933X_UART_INT_ALLINTS); if (locked) - spin_unlock(&up->port.lock); + uart_port_unlock(&up->port); local_irq_restore(flags); } diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c index ad4ae19b6ce3..1aa5b2b49c26 100644 --- a/drivers/tty/serial/arc_uart.c +++ b/drivers/tty/serial/arc_uart.c @@ -279,9 +279,9 @@ static irqreturn_t arc_serial_isr(int irq, void *dev_id) if (status & RXIENB) { /* already in ISR, no need of xx_irqsave */ - spin_lock(&port->lock); + uart_port_lock(port); arc_serial_rx_chars(port, status); - spin_unlock(&port->lock); + uart_port_unlock(port); } if ((status & TXIENB) && (status & TXEMPTY)) { @@ -291,12 +291,12 @@ static irqreturn_t arc_serial_isr(int irq, void *dev_id) */ UART_TX_IRQ_DISABLE(port); - spin_lock(&port->lock); + uart_port_lock(port); if (!uart_tx_stopped(port)) arc_serial_tx_chars(port); - spin_unlock(&port->lock); + uart_port_unlock(port); } return IRQ_HANDLED; @@ -366,7 +366,7 @@ arc_serial_set_termios(struct uart_port *port, struct ktermios *new, uartl = hw_val & 0xFF; uarth = (hw_val >> 8) & 0xFF; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); UART_ALL_IRQ_DISABLE(port); @@ -391,7 +391,7 @@ arc_serial_set_termios(struct uart_port *port, struct ktermios *new, uart_update_timeout(port, new->c_cflag, baud); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *arc_serial_type(struct uart_port *port) @@ -521,9 +521,9 @@ static void arc_serial_console_write(struct console *co, const char *s, struct uart_port *port = &arc_uart_ports[co->index].port; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); uart_console_write(port, s, count, arc_serial_console_putchar); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static struct console arc_console = { diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index bcca5627afac..5a3f2fc476f9 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -861,7 +861,7 @@ static void atmel_complete_tx_dma(void *arg) struct dma_chan *chan = atmel_port->chan_tx; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); if (chan) dmaengine_terminate_all(chan); @@ -893,7 +893,7 @@ static void atmel_complete_tx_dma(void *arg) atmel_port->tx_done_mask); } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void atmel_release_tx_dma(struct uart_port *port) @@ -1711,9 +1711,9 @@ static void atmel_tasklet_rx_func(struct tasklet_struct *t) struct uart_port *port = &atmel_port->uart; /* The interrupt handler does not take the lock */ - spin_lock(&port->lock); + uart_port_lock(port); atmel_port->schedule_rx(port); - spin_unlock(&port->lock); + uart_port_unlock(port); } static void atmel_tasklet_tx_func(struct tasklet_struct *t) @@ -1723,9 +1723,9 @@ static void atmel_tasklet_tx_func(struct tasklet_struct *t) struct uart_port *port = &atmel_port->uart; /* The interrupt handler does not take the lock */ - spin_lock(&port->lock); + uart_port_lock(port); atmel_port->schedule_tx(port); - spin_unlock(&port->lock); + uart_port_unlock(port); } static void atmel_init_property(struct atmel_uart_port *atmel_port, @@ -2175,7 +2175,7 @@ static void atmel_set_termios(struct uart_port *port, } else mode |= ATMEL_US_PAR_NONE; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); port->read_status_mask = ATMEL_US_OVRE; if (termios->c_iflag & INPCK) @@ -2377,22 +2377,22 @@ static void atmel_set_termios(struct uart_port *port, else atmel_disable_ms(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void atmel_set_ldisc(struct uart_port *port, struct ktermios *termios) { if (termios->c_line == N_PPS) { port->flags |= UPF_HARDPPS_CD; - spin_lock_irq(&port->lock); + uart_port_lock_irq(port); atmel_enable_ms(port); - spin_unlock_irq(&port->lock); + uart_port_unlock_irq(port); } else { port->flags &= ~UPF_HARDPPS_CD; if (!UART_ENABLE_MS(port, termios->c_cflag)) { - spin_lock_irq(&port->lock); + uart_port_lock_irq(port); atmel_disable_ms(port); - spin_unlock_irq(&port->lock); + uart_port_unlock_irq(port); } } } diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index 44c27e5cefbc..b104c36ce5c0 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -201,7 +201,7 @@ static void bcm_uart_break_ctl(struct uart_port *port, int ctl) unsigned long flags; unsigned int val; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); val = bcm_uart_readl(port, UART_CTL_REG); if (ctl) @@ -210,7 +210,7 @@ static void bcm_uart_break_ctl(struct uart_port *port, int ctl) val &= ~UART_CTL_XMITBRK_MASK; bcm_uart_writel(port, val, UART_CTL_REG); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /* @@ -335,7 +335,7 @@ static irqreturn_t bcm_uart_interrupt(int irq, void *dev_id) unsigned int irqstat; port = dev_id; - spin_lock(&port->lock); + uart_port_lock(port); irqstat = bcm_uart_readl(port, UART_IR_REG); if (irqstat & UART_RX_INT_STAT) @@ -356,7 +356,7 @@ static irqreturn_t bcm_uart_interrupt(int irq, void *dev_id) estat & UART_EXTINP_DCD_MASK); } - spin_unlock(&port->lock); + uart_port_unlock(port); return IRQ_HANDLED; } @@ -454,9 +454,9 @@ static void bcm_uart_shutdown(struct uart_port *port) { unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); bcm_uart_writel(port, 0, UART_IR_REG); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); bcm_uart_disable(port); bcm_uart_flush(port); @@ -473,7 +473,7 @@ static void bcm_uart_set_termios(struct uart_port *port, struct ktermios *new, unsigned long flags; int tries; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Drain the hot tub fully before we power it off for the winter. */ for (tries = 3; !bcm_uart_tx_empty(port) && tries; tries--) @@ -549,7 +549,7 @@ static void bcm_uart_set_termios(struct uart_port *port, struct ktermios *new, uart_update_timeout(port, new->c_cflag, baud); bcm_uart_enable(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /* @@ -715,9 +715,9 @@ static void bcm_console_write(struct console *co, const char *s, /* bcm_uart_interrupt() already took the lock */ locked = 0; } else if (oops_in_progress) { - locked = spin_trylock(&port->lock); + locked = uart_port_trylock(port); } else { - spin_lock(&port->lock); + uart_port_lock(port); locked = 1; } @@ -728,7 +728,7 @@ static void bcm_console_write(struct console *co, const char *s, wait_for_xmitr(port); if (locked) - spin_unlock(&port->lock); + uart_port_unlock(port); local_irq_restore(flags); } diff --git a/drivers/tty/serial/cpm_uart.c b/drivers/tty/serial/cpm_uart.c index 626423022d62..be4af6eda4c2 100644 --- a/drivers/tty/serial/cpm_uart.c +++ b/drivers/tty/serial/cpm_uart.c @@ -569,7 +569,7 @@ static void cpm_uart_set_termios(struct uart_port *port, if ((termios->c_cflag & CREAD) == 0) port->read_status_mask &= ~BD_SC_EMPTY; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); if (IS_SMC(pinfo)) { unsigned int bits = tty_get_frame_size(termios->c_cflag); @@ -609,7 +609,7 @@ static void cpm_uart_set_termios(struct uart_port *port, clk_set_rate(pinfo->clk, baud); else cpm_setbrg(pinfo->brg - 1, baud); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *cpm_uart_type(struct uart_port *port) @@ -1386,9 +1386,9 @@ static void cpm_uart_console_write(struct console *co, const char *s, cpm_uart_early_write(pinfo, s, count, true); local_irq_restore(flags); } else { - spin_lock_irqsave(&pinfo->port.lock, flags); + uart_port_lock_irqsave(&pinfo->port, &flags); cpm_uart_early_write(pinfo, s, count, true); - spin_unlock_irqrestore(&pinfo->port.lock, flags); + uart_port_unlock_irqrestore(&pinfo->port, flags); } } diff --git a/drivers/tty/serial/digicolor-usart.c b/drivers/tty/serial/digicolor-usart.c index 128b5479e813..5004125f3045 100644 --- a/drivers/tty/serial/digicolor-usart.c +++ b/drivers/tty/serial/digicolor-usart.c @@ -133,7 +133,7 @@ static void digicolor_uart_rx(struct uart_port *port) { unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); while (1) { u8 status, ch, ch_flag; @@ -172,7 +172,7 @@ static void digicolor_uart_rx(struct uart_port *port) ch_flag); } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); tty_flip_buffer_push(&port->state->port); } @@ -185,7 +185,7 @@ static void digicolor_uart_tx(struct uart_port *port) if (digicolor_uart_tx_full(port)) return; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); if (port->x_char) { writeb_relaxed(port->x_char, port->membase + UA_EMI_REC); @@ -211,7 +211,7 @@ static void digicolor_uart_tx(struct uart_port *port) uart_write_wakeup(port); out: - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static irqreturn_t digicolor_uart_int(int irq, void *dev_id) @@ -333,7 +333,7 @@ static void digicolor_uart_set_termios(struct uart_port *port, port->ignore_status_mask |= UA_STATUS_OVERRUN_ERR | UA_STATUS_PARITY_ERR | UA_STATUS_FRAME_ERR; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); uart_update_timeout(port, termios->c_cflag, baud); @@ -341,7 +341,7 @@ static void digicolor_uart_set_termios(struct uart_port *port, writeb_relaxed(divisor & 0xff, port->membase + UA_HBAUD_LO); writeb_relaxed(divisor >> 8, port->membase + UA_HBAUD_HI); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *digicolor_uart_type(struct uart_port *port) @@ -398,14 +398,14 @@ static void digicolor_uart_console_write(struct console *co, const char *c, int locked = 1; if (oops_in_progress) - locked = spin_trylock_irqsave(&port->lock, flags); + locked = uart_port_trylock_irqsave(port, &flags); else - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); uart_console_write(port, c, n, digicolor_uart_console_putchar); if (locked) - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); /* Wait for transmitter to become empty */ do { diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c index 667f52e83277..6df7af9edc1c 100644 --- a/drivers/tty/serial/dz.c +++ b/drivers/tty/serial/dz.c @@ -268,9 +268,9 @@ static inline void dz_transmit_chars(struct dz_mux *mux) } /* If nothing to do or stopped or hardware stopped. */ if (uart_circ_empty(xmit) || uart_tx_stopped(&dport->port)) { - spin_lock(&dport->port.lock); + uart_port_lock(&dport->port); dz_stop_tx(&dport->port); - spin_unlock(&dport->port.lock); + uart_port_unlock(&dport->port); return; } @@ -287,9 +287,9 @@ static inline void dz_transmit_chars(struct dz_mux *mux) /* Are we are done. */ if (uart_circ_empty(xmit)) { - spin_lock(&dport->port.lock); + uart_port_lock(&dport->port); dz_stop_tx(&dport->port); - spin_unlock(&dport->port.lock); + uart_port_unlock(&dport->port); } } @@ -415,14 +415,14 @@ static int dz_startup(struct uart_port *uport) return ret; } - spin_lock_irqsave(&dport->port.lock, flags); + uart_port_lock_irqsave(&dport->port, &flags); /* Enable interrupts. */ tmp = dz_in(dport, DZ_CSR); tmp |= DZ_RIE | DZ_TIE; dz_out(dport, DZ_CSR, tmp); - spin_unlock_irqrestore(&dport->port.lock, flags); + uart_port_unlock_irqrestore(&dport->port, flags); return 0; } @@ -443,9 +443,9 @@ static void dz_shutdown(struct uart_port *uport) int irq_guard; u16 tmp; - spin_lock_irqsave(&dport->port.lock, flags); + uart_port_lock_irqsave(&dport->port, &flags); dz_stop_tx(&dport->port); - spin_unlock_irqrestore(&dport->port.lock, flags); + uart_port_unlock_irqrestore(&dport->port, flags); irq_guard = atomic_add_return(-1, &mux->irq_guard); if (!irq_guard) { @@ -491,14 +491,14 @@ static void dz_break_ctl(struct uart_port *uport, int break_state) unsigned long flags; unsigned short tmp, mask = 1 << dport->port.line; - spin_lock_irqsave(&uport->lock, flags); + uart_port_lock_irqsave(uport, &flags); tmp = dz_in(dport, DZ_TCR); if (break_state) tmp |= mask; else tmp &= ~mask; dz_out(dport, DZ_TCR, tmp); - spin_unlock_irqrestore(&uport->lock, flags); + uart_port_unlock_irqrestore(uport, flags); } static int dz_encode_baud_rate(unsigned int baud) @@ -608,7 +608,7 @@ static void dz_set_termios(struct uart_port *uport, struct ktermios *termios, if (termios->c_cflag & CREAD) cflag |= DZ_RXENAB; - spin_lock_irqsave(&dport->port.lock, flags); + uart_port_lock_irqsave(&dport->port, &flags); uart_update_timeout(uport, termios->c_cflag, baud); @@ -631,7 +631,7 @@ static void dz_set_termios(struct uart_port *uport, struct ktermios *termios, if (termios->c_iflag & IGNBRK) dport->port.ignore_status_mask |= DZ_BREAK; - spin_unlock_irqrestore(&dport->port.lock, flags); + uart_port_unlock_irqrestore(&dport->port, flags); } /* @@ -645,12 +645,12 @@ static void dz_pm(struct uart_port *uport, unsigned int state, struct dz_port *dport = to_dport(uport); unsigned long flags; - spin_lock_irqsave(&dport->port.lock, flags); + uart_port_lock_irqsave(&dport->port, &flags); if (state < 3) dz_start_tx(&dport->port); else dz_stop_tx(&dport->port); - spin_unlock_irqrestore(&dport->port.lock, flags); + uart_port_unlock_irqrestore(&dport->port, flags); } @@ -811,7 +811,7 @@ static void dz_console_putchar(struct uart_port *uport, unsigned char ch) unsigned short csr, tcr, trdy, mask; int loops = 10000; - spin_lock_irqsave(&dport->port.lock, flags); + uart_port_lock_irqsave(&dport->port, &flags); csr = dz_in(dport, DZ_CSR); dz_out(dport, DZ_CSR, csr & ~DZ_TIE); tcr = dz_in(dport, DZ_TCR); @@ -819,7 +819,7 @@ static void dz_console_putchar(struct uart_port *uport, unsigned char ch) mask = tcr; dz_out(dport, DZ_TCR, mask); iob(); - spin_unlock_irqrestore(&dport->port.lock, flags); + uart_port_unlock_irqrestore(&dport->port, flags); do { trdy = dz_in(dport, DZ_CSR); diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index 249cb380c3c6..7fa809a405e8 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -203,7 +203,7 @@ static irqreturn_t linflex_txint(int irq, void *dev_id) struct circ_buf *xmit = &sport->state->xmit; unsigned long flags; - spin_lock_irqsave(&sport->lock, flags); + uart_port_lock_irqsave(sport, &flags); if (sport->x_char) { linflex_put_char(sport, sport->x_char); @@ -217,7 +217,7 @@ static irqreturn_t linflex_txint(int irq, void *dev_id) linflex_transmit_buffer(sport); out: - spin_unlock_irqrestore(&sport->lock, flags); + uart_port_unlock_irqrestore(sport, flags); return IRQ_HANDLED; } @@ -230,7 +230,7 @@ static irqreturn_t linflex_rxint(int irq, void *dev_id) unsigned char rx; bool brk; - spin_lock_irqsave(&sport->lock, flags); + uart_port_lock_irqsave(sport, &flags); status = readl(sport->membase + UARTSR); while (status & LINFLEXD_UARTSR_RMB) { @@ -266,7 +266,7 @@ static irqreturn_t linflex_rxint(int irq, void *dev_id) } } - spin_unlock_irqrestore(&sport->lock, flags); + uart_port_unlock_irqrestore(sport, flags); tty_flip_buffer_push(port); @@ -369,11 +369,11 @@ static int linflex_startup(struct uart_port *port) int ret = 0; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); linflex_setup_watermark(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); ret = devm_request_irq(port->dev, port->irq, linflex_int, 0, DRIVER_NAME, port); @@ -386,14 +386,14 @@ static void linflex_shutdown(struct uart_port *port) unsigned long ier; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* disable interrupts */ ier = readl(port->membase + LINIER); ier &= ~(LINFLEXD_LINIER_DRIE | LINFLEXD_LINIER_DTIE); writel(ier, port->membase + LINIER); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); devm_free_irq(port->dev, port->irq, port); } @@ -474,7 +474,7 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios, cr &= ~LINFLEXD_UARTCR_PCE; } - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); port->read_status_mask = 0; @@ -507,7 +507,7 @@ linflex_set_termios(struct uart_port *port, struct ktermios *termios, writel(cr1, port->membase + LINCR1); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *linflex_type(struct uart_port *port) @@ -646,14 +646,14 @@ linflex_console_write(struct console *co, const char *s, unsigned int count) if (sport->sysrq) locked = 0; else if (oops_in_progress) - locked = spin_trylock_irqsave(&sport->lock, flags); + locked = uart_port_trylock_irqsave(sport, &flags); else - spin_lock_irqsave(&sport->lock, flags); + uart_port_lock_irqsave(sport, &flags); linflex_string_write(sport, s, count); if (locked) - spin_unlock_irqrestore(&sport->lock, flags); + uart_port_unlock_irqrestore(sport, flags); } /* diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 11f6fabce051..1f7478c72e66 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -547,9 +547,9 @@ static void lpuart_dma_tx_complete(void *arg) struct dma_chan *chan = sport->dma_tx_chan; unsigned long flags; - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); if (!sport->dma_tx_in_progress) { - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); return; } @@ -558,7 +558,7 @@ static void lpuart_dma_tx_complete(void *arg) uart_xmit_advance(&sport->port, sport->dma_tx_bytes); sport->dma_tx_in_progress = false; - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&sport->port); @@ -568,12 +568,12 @@ static void lpuart_dma_tx_complete(void *arg) return; } - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); if (!lpuart_stopped_or_empty(&sport->port)) lpuart_dma_tx(sport); - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); } static dma_addr_t lpuart_dma_datareg_addr(struct lpuart_port *sport) @@ -666,7 +666,7 @@ static int lpuart_poll_init(struct uart_port *port) sport->port.fifosize = 0; - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); /* Disable Rx & Tx */ writeb(0, sport->port.membase + UARTCR2); @@ -690,7 +690,7 @@ static int lpuart_poll_init(struct uart_port *port) /* Enable Rx and Tx */ writeb(UARTCR2_RE | UARTCR2_TE, sport->port.membase + UARTCR2); - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); return 0; } @@ -718,7 +718,7 @@ static int lpuart32_poll_init(struct uart_port *port) sport->port.fifosize = 0; - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); /* Disable Rx & Tx */ lpuart32_write(&sport->port, 0, UARTCTRL); @@ -739,7 +739,7 @@ static int lpuart32_poll_init(struct uart_port *port) /* Enable Rx and Tx */ lpuart32_write(&sport->port, UARTCTRL_RE | UARTCTRL_TE, UARTCTRL); - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); return 0; } @@ -902,9 +902,9 @@ static unsigned int lpuart32_tx_empty(struct uart_port *port) static void lpuart_txint(struct lpuart_port *sport) { - spin_lock(&sport->port.lock); + uart_port_lock(&sport->port); lpuart_transmit_buffer(sport); - spin_unlock(&sport->port.lock); + uart_port_unlock(&sport->port); } static void lpuart_rxint(struct lpuart_port *sport) @@ -913,7 +913,7 @@ static void lpuart_rxint(struct lpuart_port *sport) struct tty_port *port = &sport->port.state->port; unsigned char rx, sr; - spin_lock(&sport->port.lock); + uart_port_lock(&sport->port); while (!(readb(sport->port.membase + UARTSFIFO) & UARTSFIFO_RXEMPT)) { flg = TTY_NORMAL; @@ -979,9 +979,9 @@ static void lpuart_rxint(struct lpuart_port *sport) static void lpuart32_txint(struct lpuart_port *sport) { - spin_lock(&sport->port.lock); + uart_port_lock(&sport->port); lpuart32_transmit_buffer(sport); - spin_unlock(&sport->port.lock); + uart_port_unlock(&sport->port); } static void lpuart32_rxint(struct lpuart_port *sport) @@ -991,7 +991,7 @@ static void lpuart32_rxint(struct lpuart_port *sport) unsigned long rx, sr; bool is_break; - spin_lock(&sport->port.lock); + uart_port_lock(&sport->port); while (!(lpuart32_read(&sport->port, UARTFIFO) & UARTFIFO_RXEMPT)) { flg = TTY_NORMAL; @@ -1193,12 +1193,12 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport) async_tx_ack(sport->dma_rx_desc); - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); dmastat = dmaengine_tx_status(chan, sport->dma_rx_cookie, &state); if (dmastat == DMA_ERROR) { dev_err(sport->port.dev, "Rx DMA transfer failed!\n"); - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); return; } @@ -1267,7 +1267,7 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport) dma_sync_sg_for_device(chan->device->dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE); - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); tty_flip_buffer_push(port); if (!sport->dma_idle_int) @@ -1358,9 +1358,9 @@ static void lpuart_timer_func(struct timer_list *t) mod_timer(&sport->lpuart_timer, jiffies + sport->dma_rx_timeout); - if (spin_trylock_irqsave(&sport->port.lock, flags)) { + if (uart_port_trylock_irqsave(&sport->port, &flags)) { sport->last_residue = state.residue; - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); } } @@ -1838,14 +1838,14 @@ static void lpuart_hw_setup(struct lpuart_port *sport) { unsigned long flags; - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); lpuart_setup_watermark_enable(sport); lpuart_rx_dma_startup(sport); lpuart_tx_dma_startup(sport); - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); } static int lpuart_startup(struct uart_port *port) @@ -1895,7 +1895,7 @@ static void lpuart32_hw_setup(struct lpuart_port *sport) { unsigned long flags; - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); lpuart32_hw_disable(sport); @@ -1905,7 +1905,7 @@ static void lpuart32_hw_setup(struct lpuart_port *sport) lpuart32_setup_watermark_enable(sport); lpuart32_configure(sport); - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); } static int lpuart32_startup(struct uart_port *port) @@ -1977,7 +1977,7 @@ static void lpuart_shutdown(struct uart_port *port) unsigned char temp; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* disable Rx/Tx and interrupts */ temp = readb(port->membase + UARTCR2); @@ -1985,7 +1985,7 @@ static void lpuart_shutdown(struct uart_port *port) UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_RIE); writeb(temp, port->membase + UARTCR2); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); lpuart_dma_shutdown(sport); } @@ -1997,7 +1997,7 @@ static void lpuart32_shutdown(struct uart_port *port) unsigned long temp; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* clear status */ temp = lpuart32_read(&sport->port, UARTSTAT); @@ -2014,7 +2014,7 @@ static void lpuart32_shutdown(struct uart_port *port) UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE | UARTCTRL_SBK); lpuart32_write(port, temp, UARTCTRL); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); lpuart_dma_shutdown(sport); } @@ -2114,7 +2114,7 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, if (old && sport->lpuart_dma_rx_use) lpuart_dma_rx_free(&sport->port); - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); sport->port.read_status_mask = 0; if (termios->c_iflag & INPCK) @@ -2169,7 +2169,7 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, sport->lpuart_dma_rx_use = false; } - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); } static void __lpuart32_serial_setbrg(struct uart_port *port, @@ -2358,7 +2358,7 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, if (old && sport->lpuart_dma_rx_use) lpuart_dma_rx_free(&sport->port); - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); sport->port.read_status_mask = 0; if (termios->c_iflag & INPCK) @@ -2416,7 +2416,7 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, sport->lpuart_dma_rx_use = false; } - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); } static const char *lpuart_type(struct uart_port *port) @@ -2534,9 +2534,9 @@ lpuart_console_write(struct console *co, const char *s, unsigned int count) int locked = 1; if (oops_in_progress) - locked = spin_trylock_irqsave(&sport->port.lock, flags); + locked = uart_port_trylock_irqsave(&sport->port, &flags); else - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); /* first save CR2 and then disable interrupts */ cr2 = old_cr2 = readb(sport->port.membase + UARTCR2); @@ -2552,7 +2552,7 @@ lpuart_console_write(struct console *co, const char *s, unsigned int count) writeb(old_cr2, sport->port.membase + UARTCR2); if (locked) - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); } static void @@ -2564,9 +2564,9 @@ lpuart32_console_write(struct console *co, const char *s, unsigned int count) int locked = 1; if (oops_in_progress) - locked = spin_trylock_irqsave(&sport->port.lock, flags); + locked = uart_port_trylock_irqsave(&sport->port, &flags); else - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); /* first save CR2 and then disable interrupts */ cr = old_cr = lpuart32_read(&sport->port, UARTCTRL); @@ -2582,7 +2582,7 @@ lpuart32_console_write(struct console *co, const char *s, unsigned int count) lpuart32_write(&sport->port, old_cr, UARTCTRL); if (locked) - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); } /* @@ -3158,7 +3158,7 @@ static int lpuart_suspend(struct device *dev) uart_suspend_port(&lpuart_reg, &sport->port); if (lpuart_uport_is_active(sport)) { - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); if (lpuart_is_32(sport)) { /* disable Rx/Tx and interrupts */ temp = lpuart32_read(&sport->port, UARTCTRL); @@ -3170,7 +3170,7 @@ static int lpuart_suspend(struct device *dev) temp &= ~(UARTCR2_TE | UARTCR2_TIE | UARTCR2_TCIE); writeb(temp, sport->port.membase + UARTCR2); } - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); if (sport->lpuart_dma_rx_use) { /* @@ -3183,7 +3183,7 @@ static int lpuart_suspend(struct device *dev) lpuart_dma_rx_free(&sport->port); /* Disable Rx DMA to use UART port as wakeup source */ - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); if (lpuart_is_32(sport)) { temp = lpuart32_read(&sport->port, UARTBAUD); lpuart32_write(&sport->port, temp & ~UARTBAUD_RDMAE, @@ -3192,11 +3192,11 @@ static int lpuart_suspend(struct device *dev) writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_RDMAS, sport->port.membase + UARTCR5); } - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); } if (sport->lpuart_dma_tx_use) { - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); if (lpuart_is_32(sport)) { temp = lpuart32_read(&sport->port, UARTBAUD); temp &= ~UARTBAUD_TDMAE; @@ -3206,7 +3206,7 @@ static int lpuart_suspend(struct device *dev) temp &= ~UARTCR5_TDMAS; writeb(temp, sport->port.membase + UARTCR5); } - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); sport->dma_tx_in_progress = false; dmaengine_terminate_sync(sport->dma_tx_chan); } diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c index 819f957b6b84..a75eafbcbea3 100644 --- a/drivers/tty/serial/icom.c +++ b/drivers/tty/serial/icom.c @@ -929,7 +929,7 @@ static inline void check_modem_status(struct icom_port *icom_port) char delta_status; unsigned char status; - spin_lock(&icom_port->uart_port.lock); + uart_port_lock(&icom_port->uart_port); /*modem input register */ status = readb(&icom_port->dram->isr); @@ -951,7 +951,7 @@ static inline void check_modem_status(struct icom_port *icom_port) port.delta_msr_wait); old_status = status; } - spin_unlock(&icom_port->uart_port.lock); + uart_port_unlock(&icom_port->uart_port); } static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port) @@ -1093,7 +1093,7 @@ static void process_interrupt(u16 port_int_reg, struct icom_port *icom_port) { - spin_lock(&icom_port->uart_port.lock); + uart_port_lock(&icom_port->uart_port); trace(icom_port, "INTERRUPT", port_int_reg); if (port_int_reg & (INT_XMIT_COMPLETED | INT_XMIT_DISABLED)) @@ -1102,7 +1102,7 @@ static void process_interrupt(u16 port_int_reg, if (port_int_reg & INT_RCV_COMPLETED) recv_interrupt(port_int_reg, icom_port); - spin_unlock(&icom_port->uart_port.lock); + uart_port_unlock(&icom_port->uart_port); } static irqreturn_t icom_interrupt(int irq, void *dev_id) @@ -1186,14 +1186,14 @@ static unsigned int icom_tx_empty(struct uart_port *port) int ret; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); if (le16_to_cpu(icom_port->statStg->xmit[0].flags) & SA_FLAGS_READY_TO_XMIT) ret = TIOCSER_TEMT; else ret = 0; - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return ret; } @@ -1276,7 +1276,7 @@ static void icom_send_xchar(struct uart_port *port, char ch) /* wait .1 sec to send char */ for (index = 0; index < 10; index++) { - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); xdata = readb(&icom_port->dram->xchar); if (xdata == 0x00) { trace(icom_port, "QUICK_WRITE", 0); @@ -1284,10 +1284,10 @@ static void icom_send_xchar(struct uart_port *port, char ch) /* flush write operation */ xdata = readb(&icom_port->dram->xchar); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); break; } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); msleep(10); } } @@ -1307,7 +1307,7 @@ static void icom_break(struct uart_port *port, int break_state) unsigned char cmdReg; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); trace(icom_port, "BREAK", 0); cmdReg = readb(&icom_port->dram->CmdReg); if (break_state == -1) { @@ -1315,7 +1315,7 @@ static void icom_break(struct uart_port *port, int break_state) } else { writeb(cmdReg & ~CMD_SND_BREAK, &icom_port->dram->CmdReg); } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static int icom_open(struct uart_port *port) @@ -1365,7 +1365,7 @@ static void icom_set_termios(struct uart_port *port, struct ktermios *termios, unsigned long offset; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); trace(icom_port, "CHANGE_SPEED", 0); cflag = termios->c_cflag; @@ -1516,7 +1516,7 @@ static void icom_set_termios(struct uart_port *port, struct ktermios *termios, trace(icom_port, "XR_ENAB", 0); writeb(CMD_XMIT_RCV_ENABLE, &icom_port->dram->CmdReg); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *icom_type(struct uart_port *port) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 70641b53e008..88a45ba65123 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -596,7 +596,7 @@ static void imx_uart_dma_tx_callback(void *data) unsigned long flags; u32 ucr1; - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE); @@ -621,7 +621,7 @@ static void imx_uart_dma_tx_callback(void *data) imx_uart_writel(sport, ucr4, UCR4); } - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); } /* called with port.lock taken and irqs off */ @@ -795,11 +795,11 @@ static irqreturn_t imx_uart_rtsint(int irq, void *dev_id) struct imx_port *sport = dev_id; irqreturn_t ret; - spin_lock(&sport->port.lock); + uart_port_lock(&sport->port); ret = __imx_uart_rtsint(irq, dev_id); - spin_unlock(&sport->port.lock); + uart_port_unlock(&sport->port); return ret; } @@ -808,9 +808,9 @@ static irqreturn_t imx_uart_txint(int irq, void *dev_id) { struct imx_port *sport = dev_id; - spin_lock(&sport->port.lock); + uart_port_lock(&sport->port); imx_uart_transmit_buffer(sport); - spin_unlock(&sport->port.lock); + uart_port_unlock(&sport->port); return IRQ_HANDLED; } @@ -924,11 +924,11 @@ static irqreturn_t imx_uart_rxint(int irq, void *dev_id) struct imx_port *sport = dev_id; irqreturn_t ret; - spin_lock(&sport->port.lock); + uart_port_lock(&sport->port); ret = __imx_uart_rxint(irq, dev_id); - spin_unlock(&sport->port.lock); + uart_port_unlock(&sport->port); return ret; } @@ -991,7 +991,7 @@ static irqreturn_t imx_uart_int(int irq, void *dev_id) unsigned int usr1, usr2, ucr1, ucr2, ucr3, ucr4; irqreturn_t ret = IRQ_NONE; - spin_lock(&sport->port.lock); + uart_port_lock(&sport->port); usr1 = imx_uart_readl(sport, USR1); usr2 = imx_uart_readl(sport, USR2); @@ -1061,7 +1061,7 @@ static irqreturn_t imx_uart_int(int irq, void *dev_id) ret = IRQ_HANDLED; } - spin_unlock(&sport->port.lock); + uart_port_unlock(&sport->port); return ret; } @@ -1144,7 +1144,7 @@ static void imx_uart_break_ctl(struct uart_port *port, int break_state) unsigned long flags; u32 ucr1; - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); ucr1 = imx_uart_readl(sport, UCR1) & ~UCR1_SNDBRK; @@ -1153,7 +1153,7 @@ static void imx_uart_break_ctl(struct uart_port *port, int break_state) imx_uart_writel(sport, ucr1, UCR1); - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); } /* @@ -1166,9 +1166,9 @@ static void imx_uart_timeout(struct timer_list *t) unsigned long flags; if (sport->port.state) { - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); imx_uart_mctrl_check(sport); - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT); } @@ -1198,9 +1198,9 @@ static void imx_uart_dma_rx_callback(void *data) status = dmaengine_tx_status(chan, sport->rx_cookie, &state); if (status == DMA_ERROR) { - spin_lock(&sport->port.lock); + uart_port_lock(&sport->port); imx_uart_clear_rx_errors(sport); - spin_unlock(&sport->port.lock); + uart_port_unlock(&sport->port); return; } @@ -1229,9 +1229,9 @@ static void imx_uart_dma_rx_callback(void *data) r_bytes = rx_ring->head - rx_ring->tail; /* If we received something, check for 0xff flood */ - spin_lock(&sport->port.lock); + uart_port_lock(&sport->port); imx_uart_check_flood(sport, imx_uart_readl(sport, USR2)); - spin_unlock(&sport->port.lock); + uart_port_unlock(&sport->port); if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ)) { @@ -1504,7 +1504,7 @@ static int imx_uart_startup(struct uart_port *port) if (!uart_console(port) && imx_uart_dma_init(sport) == 0) dma_is_inited = 1; - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); /* Reset fifo's and state machines */ imx_uart_soft_reset(sport); @@ -1578,7 +1578,7 @@ static int imx_uart_startup(struct uart_port *port) imx_uart_disable_loopback_rs485(sport); - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); return 0; } @@ -1603,21 +1603,21 @@ static void imx_uart_shutdown(struct uart_port *port) sport->dma_is_rxing = 0; } - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); imx_uart_stop_tx(port); imx_uart_stop_rx(port); imx_uart_disable_dma(sport); - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); imx_uart_dma_exit(sport); } mctrl_gpio_disable_ms(sport->gpios); - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); ucr2 = imx_uart_readl(sport, UCR2); ucr2 &= ~(UCR2_TXEN | UCR2_ATEN); imx_uart_writel(sport, ucr2, UCR2); - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); /* * Stop our timer. @@ -1628,7 +1628,7 @@ static void imx_uart_shutdown(struct uart_port *port) * Disable all interrupts, port and break condition. */ - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); ucr1 = imx_uart_readl(sport, UCR1); ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_RXDMAEN | @@ -1650,7 +1650,7 @@ static void imx_uart_shutdown(struct uart_port *port) ucr4 &= ~UCR4_TCEN; imx_uart_writel(sport, ucr4, UCR4); - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); clk_disable_unprepare(sport->clk_per); clk_disable_unprepare(sport->clk_ipg); @@ -1713,7 +1713,7 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16); quot = uart_get_divisor(port, baud); - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); /* * Read current UCR2 and save it for future use, then clear all the bits @@ -1841,7 +1841,7 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) imx_uart_enable_ms(&sport->port); - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); } static const char *imx_uart_type(struct uart_port *port) @@ -1903,7 +1903,7 @@ static int imx_uart_poll_init(struct uart_port *port) imx_uart_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); /* * Be careful about the order of enabling bits here. First enable the @@ -1931,7 +1931,7 @@ static int imx_uart_poll_init(struct uart_port *port) imx_uart_writel(sport, ucr1 | UCR1_RRDYEN, UCR1); imx_uart_writel(sport, ucr2 | UCR2_ATEN, UCR2); - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); return 0; } @@ -2051,9 +2051,9 @@ imx_uart_console_write(struct console *co, const char *s, unsigned int count) if (sport->port.sysrq) locked = 0; else if (oops_in_progress) - locked = spin_trylock_irqsave(&sport->port.lock, flags); + locked = uart_port_trylock_irqsave(&sport->port, &flags); else - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); /* * First, save UCR1/2/3 and then disable interrupts @@ -2081,7 +2081,7 @@ imx_uart_console_write(struct console *co, const char *s, unsigned int count) imx_uart_ucrs_restore(sport, &old_ucr); if (locked) - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); } /* @@ -2239,10 +2239,10 @@ static enum hrtimer_restart imx_trigger_start_tx(struct hrtimer *t) struct imx_port *sport = container_of(t, struct imx_port, trigger_start_tx); unsigned long flags; - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); if (sport->tx_state == WAIT_AFTER_RTS) imx_uart_start_tx(&sport->port); - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); return HRTIMER_NORESTART; } @@ -2252,10 +2252,10 @@ static enum hrtimer_restart imx_trigger_stop_tx(struct hrtimer *t) struct imx_port *sport = container_of(t, struct imx_port, trigger_stop_tx); unsigned long flags; - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); if (sport->tx_state == WAIT_AFTER_SEND) imx_uart_stop_tx(&sport->port); - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); return HRTIMER_NORESTART; } @@ -2583,9 +2583,9 @@ static void imx_uart_restore_context(struct imx_port *sport) { unsigned long flags; - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); if (!sport->context_saved) { - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); return; } @@ -2600,7 +2600,7 @@ static void imx_uart_restore_context(struct imx_port *sport) imx_uart_writel(sport, sport->saved_reg[2], UCR3); imx_uart_writel(sport, sport->saved_reg[3], UCR4); sport->context_saved = false; - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); } static void imx_uart_save_context(struct imx_port *sport) @@ -2608,7 +2608,7 @@ static void imx_uart_save_context(struct imx_port *sport) unsigned long flags; /* Save necessary regs */ - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); sport->saved_reg[0] = imx_uart_readl(sport, UCR1); sport->saved_reg[1] = imx_uart_readl(sport, UCR2); sport->saved_reg[2] = imx_uart_readl(sport, UCR3); @@ -2620,7 +2620,7 @@ static void imx_uart_save_context(struct imx_port *sport) sport->saved_reg[8] = imx_uart_readl(sport, UBMR); sport->saved_reg[9] = imx_uart_readl(sport, IMX21_UTS); sport->context_saved = true; - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); } static void imx_uart_enable_wakeup(struct imx_port *sport, bool on) diff --git a/drivers/tty/serial/ip22zilog.c b/drivers/tty/serial/ip22zilog.c index 845ff706bc59..320b29cd4683 100644 --- a/drivers/tty/serial/ip22zilog.c +++ b/drivers/tty/serial/ip22zilog.c @@ -432,7 +432,7 @@ static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id) unsigned char r3; bool push = false; - spin_lock(&up->port.lock); + uart_port_lock(&up->port); r3 = read_zsreg(channel, R3); /* Channel A */ @@ -448,7 +448,7 @@ static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id) if (r3 & CHATxIP) ip22zilog_transmit_chars(up, channel); } - spin_unlock(&up->port.lock); + uart_port_unlock(&up->port); if (push) tty_flip_buffer_push(&up->port.state->port); @@ -458,7 +458,7 @@ static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id) channel = ZILOG_CHANNEL_FROM_PORT(&up->port); push = false; - spin_lock(&up->port.lock); + uart_port_lock(&up->port); if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { writeb(RES_H_IUS, &channel->control); ZSDELAY(); @@ -471,7 +471,7 @@ static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id) if (r3 & CHBTxIP) ip22zilog_transmit_chars(up, channel); } - spin_unlock(&up->port.lock); + uart_port_unlock(&up->port); if (push) tty_flip_buffer_push(&up->port.state->port); @@ -504,11 +504,11 @@ static unsigned int ip22zilog_tx_empty(struct uart_port *port) unsigned char status; unsigned int ret; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); status = ip22zilog_read_channel_status(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); if (status & Tx_BUF_EMP) ret = TIOCSER_TEMT; @@ -664,7 +664,7 @@ static void ip22zilog_break_ctl(struct uart_port *port, int break_state) else clear_bits |= SND_BRK; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); new_reg = (up->curregs[R5] | set_bits) & ~clear_bits; if (new_reg != up->curregs[R5]) { @@ -674,7 +674,7 @@ static void ip22zilog_break_ctl(struct uart_port *port, int break_state) write_zsreg(channel, R5, up->curregs[R5]); } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void __ip22zilog_reset(struct uart_ip22zilog_port *up) @@ -735,9 +735,9 @@ static int ip22zilog_startup(struct uart_port *port) if (ZS_IS_CONS(up)) return 0; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); __ip22zilog_startup(up); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return 0; } @@ -775,7 +775,7 @@ static void ip22zilog_shutdown(struct uart_port *port) if (ZS_IS_CONS(up)) return; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); channel = ZILOG_CHANNEL_FROM_PORT(port); @@ -788,7 +788,7 @@ static void ip22zilog_shutdown(struct uart_port *port) up->curregs[R5] &= ~SND_BRK; ip22zilog_maybe_update_regs(up, channel); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /* Shared by TTY driver and serial console setup. The port lock is held @@ -880,7 +880,7 @@ ip22zilog_set_termios(struct uart_port *port, struct ktermios *termios, baud = uart_get_baud_rate(port, termios, old, 1200, 76800); - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); @@ -894,7 +894,7 @@ ip22zilog_set_termios(struct uart_port *port, struct ktermios *termios, ip22zilog_maybe_update_regs(up, ZILOG_CHANNEL_FROM_PORT(port)); uart_update_timeout(port, termios->c_cflag, baud); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } static const char *ip22zilog_type(struct uart_port *port) @@ -1016,10 +1016,10 @@ ip22zilog_console_write(struct console *con, const char *s, unsigned int count) struct uart_ip22zilog_port *up = &ip22zilog_port_table[con->index]; unsigned long flags; - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); uart_console_write(&up->port, s, count, ip22zilog_put_char); udelay(2); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } static int __init ip22zilog_console_setup(struct console *con, char *options) @@ -1034,13 +1034,13 @@ static int __init ip22zilog_console_setup(struct console *con, char *options) printk(KERN_INFO "Console: ttyS%d (IP22-Zilog)\n", con->index); - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); up->curregs[R15] |= BRKIE; __ip22zilog_startup(up); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c index 0c78f66276cd..2bd640428970 100644 --- a/drivers/tty/serial/jsm/jsm_neo.c +++ b/drivers/tty/serial/jsm/jsm_neo.c @@ -816,9 +816,9 @@ static void neo_parse_isr(struct jsm_board *brd, u32 port) /* Parse any modem signal changes */ jsm_dbg(INTR, &ch->ch_bd->pci_dev, "MOD_STAT: sending to parse_modem_sigs\n"); - spin_lock_irqsave(&ch->uart_port.lock, lock_flags); + uart_port_lock_irqsave(&ch->uart_port, &lock_flags); neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr)); - spin_unlock_irqrestore(&ch->uart_port.lock, lock_flags); + uart_port_unlock_irqrestore(&ch->uart_port, lock_flags); } } diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c index 222afc270c88..ce0fef7e2c66 100644 --- a/drivers/tty/serial/jsm/jsm_tty.c +++ b/drivers/tty/serial/jsm/jsm_tty.c @@ -152,14 +152,14 @@ static void jsm_tty_send_xchar(struct uart_port *port, char ch) container_of(port, struct jsm_channel, uart_port); struct ktermios *termios; - spin_lock_irqsave(&port->lock, lock_flags); + uart_port_lock_irqsave(port, &lock_flags); termios = &port->state->port.tty->termios; if (ch == termios->c_cc[VSTART]) channel->ch_bd->bd_ops->send_start_character(channel); if (ch == termios->c_cc[VSTOP]) channel->ch_bd->bd_ops->send_stop_character(channel); - spin_unlock_irqrestore(&port->lock, lock_flags); + uart_port_unlock_irqrestore(port, lock_flags); } static void jsm_tty_stop_rx(struct uart_port *port) @@ -176,13 +176,13 @@ static void jsm_tty_break(struct uart_port *port, int break_state) struct jsm_channel *channel = container_of(port, struct jsm_channel, uart_port); - spin_lock_irqsave(&port->lock, lock_flags); + uart_port_lock_irqsave(port, &lock_flags); if (break_state == -1) channel->ch_bd->bd_ops->send_break(channel); else channel->ch_bd->bd_ops->clear_break(channel); - spin_unlock_irqrestore(&port->lock, lock_flags); + uart_port_unlock_irqrestore(port, lock_flags); } static int jsm_tty_open(struct uart_port *port) @@ -241,7 +241,7 @@ static int jsm_tty_open(struct uart_port *port) channel->ch_cached_lsr = 0; channel->ch_stops_sent = 0; - spin_lock_irqsave(&port->lock, lock_flags); + uart_port_lock_irqsave(port, &lock_flags); termios = &port->state->port.tty->termios; channel->ch_c_cflag = termios->c_cflag; channel->ch_c_iflag = termios->c_iflag; @@ -261,7 +261,7 @@ static int jsm_tty_open(struct uart_port *port) jsm_carrier(channel); channel->ch_open_count++; - spin_unlock_irqrestore(&port->lock, lock_flags); + uart_port_unlock_irqrestore(port, lock_flags); jsm_dbg(OPEN, &channel->ch_bd->pci_dev, "finish\n"); return 0; @@ -307,7 +307,7 @@ static void jsm_tty_set_termios(struct uart_port *port, struct jsm_channel *channel = container_of(port, struct jsm_channel, uart_port); - spin_lock_irqsave(&port->lock, lock_flags); + uart_port_lock_irqsave(port, &lock_flags); channel->ch_c_cflag = termios->c_cflag; channel->ch_c_iflag = termios->c_iflag; channel->ch_c_oflag = termios->c_oflag; @@ -317,7 +317,7 @@ static void jsm_tty_set_termios(struct uart_port *port, channel->ch_bd->bd_ops->param(channel); jsm_carrier(channel); - spin_unlock_irqrestore(&port->lock, lock_flags); + uart_port_unlock_irqrestore(port, lock_flags); } static const char *jsm_tty_type(struct uart_port *port) diff --git a/drivers/tty/serial/liteuart.c b/drivers/tty/serial/liteuart.c index d881cdd2a58f..a25ab1efe38f 100644 --- a/drivers/tty/serial/liteuart.c +++ b/drivers/tty/serial/liteuart.c @@ -139,13 +139,13 @@ static irqreturn_t liteuart_interrupt(int irq, void *data) * if polling, the context would be "in_serving_softirq", so use * irq[save|restore] spin_lock variants to cover all possibilities */ - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); isr = litex_read8(port->membase + OFF_EV_PENDING) & uart->irq_reg; if (isr & EV_RX) liteuart_rx_chars(port); if (isr & EV_TX) liteuart_tx_chars(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return IRQ_RETVAL(isr); } @@ -195,10 +195,10 @@ static int liteuart_startup(struct uart_port *port) } } - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* only enabling rx irqs during startup */ liteuart_update_irq_reg(port, true, EV_RX); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); if (!port->irq) { timer_setup(&uart->timer, liteuart_timer, 0); @@ -213,9 +213,9 @@ static void liteuart_shutdown(struct uart_port *port) struct liteuart_port *uart = to_liteuart_port(port); unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); liteuart_update_irq_reg(port, false, EV_RX | EV_TX); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); if (port->irq) free_irq(port->irq, port); @@ -229,13 +229,13 @@ static void liteuart_set_termios(struct uart_port *port, struct ktermios *new, unsigned int baud; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* update baudrate */ baud = uart_get_baud_rate(port, new, old, 0, 460800); uart_update_timeout(port, new->c_cflag, baud); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *liteuart_type(struct uart_port *port) @@ -382,9 +382,9 @@ static void liteuart_console_write(struct console *co, const char *s, uart = (struct liteuart_port *)xa_load(&liteuart_array, co->index); port = &uart->port; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); uart_console_write(port, s, count, liteuart_putchar); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static int liteuart_console_setup(struct console *co, char *options) diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c index b38fe4728c26..5149a947b7fe 100644 --- a/drivers/tty/serial/lpc32xx_hs.c +++ b/drivers/tty/serial/lpc32xx_hs.c @@ -140,15 +140,15 @@ static void lpc32xx_hsuart_console_write(struct console *co, const char *s, if (up->port.sysrq) locked = 0; else if (oops_in_progress) - locked = spin_trylock(&up->port.lock); + locked = uart_port_trylock(&up->port); else - spin_lock(&up->port.lock); + uart_port_lock(&up->port); uart_console_write(&up->port, s, count, lpc32xx_hsuart_console_putchar); wait_for_xmit_empty(&up->port); if (locked) - spin_unlock(&up->port.lock); + uart_port_unlock(&up->port); local_irq_restore(flags); } @@ -298,7 +298,7 @@ static irqreturn_t serial_lpc32xx_interrupt(int irq, void *dev_id) struct tty_port *tport = &port->state->port; u32 status; - spin_lock(&port->lock); + uart_port_lock(port); /* Read UART status and clear latched interrupts */ status = readl(LPC32XX_HSUART_IIR(port->membase)); @@ -333,7 +333,7 @@ static irqreturn_t serial_lpc32xx_interrupt(int irq, void *dev_id) __serial_lpc32xx_tx(port); } - spin_unlock(&port->lock); + uart_port_unlock(port); return IRQ_HANDLED; } @@ -404,14 +404,14 @@ static void serial_lpc32xx_break_ctl(struct uart_port *port, unsigned long flags; u32 tmp; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); tmp = readl(LPC32XX_HSUART_CTRL(port->membase)); if (break_state != 0) tmp |= LPC32XX_HSU_BREAK; else tmp &= ~LPC32XX_HSU_BREAK; writel(tmp, LPC32XX_HSUART_CTRL(port->membase)); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /* port->lock is not held. */ @@ -421,7 +421,7 @@ static int serial_lpc32xx_startup(struct uart_port *port) unsigned long flags; u32 tmp; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); __serial_uart_flush(port); @@ -441,7 +441,7 @@ static int serial_lpc32xx_startup(struct uart_port *port) lpc32xx_loopback_set(port->mapbase, 0); /* get out of loopback mode */ - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); retval = request_irq(port->irq, serial_lpc32xx_interrupt, 0, MODNAME, port); @@ -458,7 +458,7 @@ static void serial_lpc32xx_shutdown(struct uart_port *port) u32 tmp; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); tmp = LPC32XX_HSU_TX_TL8B | LPC32XX_HSU_RX_TL32B | LPC32XX_HSU_OFFSET(20) | LPC32XX_HSU_TMO_INACT_4B; @@ -466,7 +466,7 @@ static void serial_lpc32xx_shutdown(struct uart_port *port) lpc32xx_loopback_set(port->mapbase, 1); /* go to loopback mode */ - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); free_irq(port->irq, port); } @@ -491,7 +491,7 @@ static void serial_lpc32xx_set_termios(struct uart_port *port, quot = __serial_get_clock_div(port->uartclk, baud); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Ignore characters? */ tmp = readl(LPC32XX_HSUART_CTRL(port->membase)); @@ -505,7 +505,7 @@ static void serial_lpc32xx_set_termios(struct uart_port *port, uart_update_timeout(port, termios->c_cflag, baud); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); /* Don't rewrite B0 */ if (tty_termios_baud_rate(termios)) diff --git a/drivers/tty/serial/ma35d1_serial.c b/drivers/tty/serial/ma35d1_serial.c index 99225f1e02ac..faccd772c68c 100644 --- a/drivers/tty/serial/ma35d1_serial.c +++ b/drivers/tty/serial/ma35d1_serial.c @@ -269,16 +269,16 @@ static void receive_chars(struct uart_ma35d1_port *up) if (uart_handle_sysrq_char(&up->port, ch)) continue; - spin_lock(&up->port.lock); + uart_port_lock(&up->port); uart_insert_char(&up->port, fsr, MA35_FSR_RX_OVER_IF, ch, flag); - spin_unlock(&up->port.lock); + uart_port_unlock(&up->port); fsr = serial_in(up, MA35_FSR_REG); } while (!(fsr & MA35_FSR_RX_EMPTY) && (max_count-- > 0)); - spin_lock(&up->port.lock); + uart_port_lock(&up->port); tty_flip_buffer_push(&up->port.state->port); - spin_unlock(&up->port.lock); + uart_port_unlock(&up->port); } static irqreturn_t ma35d1serial_interrupt(int irq, void *dev_id) @@ -364,14 +364,14 @@ static void ma35d1serial_break_ctl(struct uart_port *port, int break_state) unsigned long flags; u32 lcr; - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); lcr = serial_in(up, MA35_LCR_REG); if (break_state != 0) lcr |= MA35_LCR_BREAK; else lcr &= ~MA35_LCR_BREAK; serial_out(up, MA35_LCR_REG, lcr); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } static int ma35d1serial_startup(struct uart_port *port) @@ -441,7 +441,7 @@ static void ma35d1serial_set_termios(struct uart_port *port, * Ok, we're now changing the port state. Do it with * interrupts disabled. */ - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); up->port.read_status_mask = MA35_FSR_RX_OVER_IF; if (termios->c_iflag & INPCK) @@ -475,7 +475,7 @@ static void ma35d1serial_set_termios(struct uart_port *port, serial_out(up, MA35_LCR_REG, lcr); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } static const char *ma35d1serial_type(struct uart_port *port) @@ -568,9 +568,9 @@ static void ma35d1serial_console_write(struct console *co, const char *s, u32 co if (up->port.sysrq) locked = 0; else if (oops_in_progress) - locked = spin_trylock_irqsave(&up->port.lock, flags); + locked = uart_port_trylock_irqsave(&up->port, &flags); else - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); /* * First save the IER then disable the interrupts @@ -584,7 +584,7 @@ static void ma35d1serial_console_write(struct console *co, const char *s, u32 co serial_out(up, MA35_IER_REG, ier); if (locked) - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } static int __init ma35d1serial_console_setup(struct console *co, char *options) diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c index aea29b4e6567..ee40af20a08f 100644 --- a/drivers/tty/serial/mcf.c +++ b/drivers/tty/serial/mcf.c @@ -135,12 +135,12 @@ static void mcf_break_ctl(struct uart_port *port, int break_state) { unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); if (break_state == -1) writeb(MCFUART_UCR_CMDBREAKSTART, port->membase + MCFUART_UCR); else writeb(MCFUART_UCR_CMDBREAKSTOP, port->membase + MCFUART_UCR); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /****************************************************************************/ @@ -150,7 +150,7 @@ static int mcf_startup(struct uart_port *port) struct mcf_uart *pp = container_of(port, struct mcf_uart, port); unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Reset UART, get it into known state... */ writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); @@ -164,7 +164,7 @@ static int mcf_startup(struct uart_port *port) pp->imr = MCFUART_UIR_RXREADY; writeb(pp->imr, port->membase + MCFUART_UIMR); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return 0; } @@ -176,7 +176,7 @@ static void mcf_shutdown(struct uart_port *port) struct mcf_uart *pp = container_of(port, struct mcf_uart, port); unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Disable all interrupts now */ pp->imr = 0; @@ -186,7 +186,7 @@ static void mcf_shutdown(struct uart_port *port) writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /****************************************************************************/ @@ -252,7 +252,7 @@ static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, mr2 |= MCFUART_MR2_TXCTS; } - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); if (port->rs485.flags & SER_RS485_ENABLED) { dev_dbg(port->dev, "Setting UART to RS485\n"); mr2 |= MCFUART_MR2_TXRTS; @@ -273,7 +273,7 @@ static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, port->membase + MCFUART_UCSR); writeb(MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE, port->membase + MCFUART_UCR); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /****************************************************************************/ @@ -350,7 +350,7 @@ static irqreturn_t mcf_interrupt(int irq, void *data) isr = readb(port->membase + MCFUART_UISR) & pp->imr; - spin_lock(&port->lock); + uart_port_lock(port); if (isr & MCFUART_UIR_RXREADY) { mcf_rx_chars(pp); ret = IRQ_HANDLED; @@ -359,7 +359,7 @@ static irqreturn_t mcf_interrupt(int irq, void *data) mcf_tx_chars(pp); ret = IRQ_HANDLED; } - spin_unlock(&port->lock); + uart_port_unlock(port); return ret; } diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c index d2502aaa3e8c..8048fa542fc4 100644 --- a/drivers/tty/serial/men_z135_uart.c +++ b/drivers/tty/serial/men_z135_uart.c @@ -392,7 +392,7 @@ static irqreturn_t men_z135_intr(int irq, void *data) if (!irq_id) goto out; - spin_lock(&port->lock); + uart_port_lock(port); /* It's save to write to IIR[7:6] RXC[9:8] */ iowrite8(irq_id, port->membase + MEN_Z135_STAT_REG); @@ -418,7 +418,7 @@ static irqreturn_t men_z135_intr(int irq, void *data) handled = true; } - spin_unlock(&port->lock); + uart_port_unlock(port); out: return IRQ_RETVAL(handled); } @@ -708,7 +708,7 @@ static void men_z135_set_termios(struct uart_port *port, baud = uart_get_baud_rate(port, termios, old, 0, uart_freq / 16); - spin_lock_irq(&port->lock); + uart_port_lock_irq(port); if (tty_termios_baud_rate(termios)) tty_termios_encode_baud_rate(termios, baud, baud); @@ -716,7 +716,7 @@ static void men_z135_set_termios(struct uart_port *port, iowrite32(bd_reg, port->membase + MEN_Z135_BAUD_REG); uart_update_timeout(port, termios->c_cflag, baud); - spin_unlock_irq(&port->lock); + uart_port_unlock_irq(port); } static const char *men_z135_type(struct uart_port *port) diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c index 9388b9ddea3b..4c1d2089a0bb 100644 --- a/drivers/tty/serial/meson_uart.c +++ b/drivers/tty/serial/meson_uart.c @@ -129,14 +129,14 @@ static void meson_uart_shutdown(struct uart_port *port) free_irq(port->irq, port); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); val = readl(port->membase + AML_UART_CONTROL); val &= ~AML_UART_RX_EN; val &= ~(AML_UART_RX_INT_EN | AML_UART_TX_INT_EN); writel(val, port->membase + AML_UART_CONTROL); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void meson_uart_start_tx(struct uart_port *port) @@ -238,7 +238,7 @@ static irqreturn_t meson_uart_interrupt(int irq, void *dev_id) { struct uart_port *port = (struct uart_port *)dev_id; - spin_lock(&port->lock); + uart_port_lock(port); if (!(readl(port->membase + AML_UART_STATUS) & AML_UART_RX_EMPTY)) meson_receive_chars(port); @@ -248,7 +248,7 @@ static irqreturn_t meson_uart_interrupt(int irq, void *dev_id) meson_uart_start_tx(port); } - spin_unlock(&port->lock); + uart_port_unlock(port); return IRQ_HANDLED; } @@ -284,7 +284,7 @@ static int meson_uart_startup(struct uart_port *port) u32 val; int ret = 0; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); val = readl(port->membase + AML_UART_CONTROL); val |= AML_UART_CLEAR_ERR; @@ -301,7 +301,7 @@ static int meson_uart_startup(struct uart_port *port) val = (AML_UART_RECV_IRQ(1) | AML_UART_XMIT_IRQ(port->fifosize / 2)); writel(val, port->membase + AML_UART_MISC); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); ret = request_irq(port->irq, meson_uart_interrupt, 0, port->name, port); @@ -341,7 +341,7 @@ static void meson_uart_set_termios(struct uart_port *port, unsigned long flags; u32 val; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); cflags = termios->c_cflag; iflags = termios->c_iflag; @@ -405,7 +405,7 @@ static void meson_uart_set_termios(struct uart_port *port, AML_UART_FRAME_ERR; uart_update_timeout(port, termios->c_cflag, baud); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static int meson_uart_verify_port(struct uart_port *port, @@ -464,14 +464,14 @@ static int meson_uart_poll_get_char(struct uart_port *port) u32 c; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); if (readl(port->membase + AML_UART_STATUS) & AML_UART_RX_EMPTY) c = NO_POLL_CHAR; else c = readl(port->membase + AML_UART_RFIFO); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return c; } @@ -482,7 +482,7 @@ static void meson_uart_poll_put_char(struct uart_port *port, unsigned char c) u32 reg; int ret; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Wait until FIFO is empty or timeout */ ret = readl_poll_timeout_atomic(port->membase + AML_UART_STATUS, reg, @@ -506,7 +506,7 @@ static void meson_uart_poll_put_char(struct uart_port *port, unsigned char c) dev_err(port->dev, "Timeout waiting for UART TX EMPTY\n"); out: - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } #endif /* CONFIG_CONSOLE_POLL */ @@ -563,9 +563,9 @@ static void meson_serial_port_write(struct uart_port *port, const char *s, if (port->sysrq) { locked = 0; } else if (oops_in_progress) { - locked = spin_trylock(&port->lock); + locked = uart_port_trylock(port); } else { - spin_lock(&port->lock); + uart_port_lock(port); locked = 1; } @@ -577,7 +577,7 @@ static void meson_serial_port_write(struct uart_port *port, const char *s, writel(val, port->membase + AML_UART_CONTROL); if (locked) - spin_unlock(&port->lock); + uart_port_unlock(port); local_irq_restore(flags); } diff --git a/drivers/tty/serial/milbeaut_usio.c b/drivers/tty/serial/milbeaut_usio.c index 70a910085e93..db3b81f2aa57 100644 --- a/drivers/tty/serial/milbeaut_usio.c +++ b/drivers/tty/serial/milbeaut_usio.c @@ -207,9 +207,9 @@ static irqreturn_t mlb_usio_rx_irq(int irq, void *dev_id) { struct uart_port *port = dev_id; - spin_lock(&port->lock); + uart_port_lock(port); mlb_usio_rx_chars(port); - spin_unlock(&port->lock); + uart_port_unlock(port); return IRQ_HANDLED; } @@ -218,10 +218,10 @@ static irqreturn_t mlb_usio_tx_irq(int irq, void *dev_id) { struct uart_port *port = dev_id; - spin_lock(&port->lock); + uart_port_lock(port); if (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI) mlb_usio_tx_chars(port); - spin_unlock(&port->lock); + uart_port_unlock(port); return IRQ_HANDLED; } @@ -267,7 +267,7 @@ static int mlb_usio_startup(struct uart_port *port) escr = readb(port->membase + MLB_USIO_REG_ESCR); if (of_property_read_bool(port->dev->of_node, "auto-flow-control")) escr |= MLB_USIO_ESCR_FLWEN; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); writeb(0, port->membase + MLB_USIO_REG_SCR); writeb(escr, port->membase + MLB_USIO_REG_ESCR); writeb(MLB_USIO_SCR_UPCL, port->membase + MLB_USIO_REG_SCR); @@ -282,7 +282,7 @@ static int mlb_usio_startup(struct uart_port *port) writeb(MLB_USIO_SCR_TXE | MLB_USIO_SCR_RIE | MLB_USIO_SCR_TBIE | MLB_USIO_SCR_RXE, port->membase + MLB_USIO_REG_SCR); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return 0; } @@ -337,7 +337,7 @@ static void mlb_usio_set_termios(struct uart_port *port, else quot = 0; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); uart_update_timeout(port, termios->c_cflag, baud); port->read_status_mask = MLB_USIO_SSR_ORE | MLB_USIO_SSR_RDRF | MLB_USIO_SSR_TDRE; @@ -367,7 +367,7 @@ static void mlb_usio_set_termios(struct uart_port *port, writew(BIT(12), port->membase + MLB_USIO_REG_FBYTE); writeb(MLB_USIO_SCR_RIE | MLB_USIO_SCR_RXE | MLB_USIO_SCR_TBIE | MLB_USIO_SCR_TXE, port->membase + MLB_USIO_REG_SCR); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *mlb_usio_type(struct uart_port *port) diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index 916507b8f31d..a252465e745f 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -1096,14 +1096,14 @@ static void mpc52xx_uart_break_ctl(struct uart_port *port, int ctl) { unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); if (ctl == -1) psc_ops->command(port, MPC52xx_PSC_START_BRK); else psc_ops->command(port, MPC52xx_PSC_STOP_BRK); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static int @@ -1214,7 +1214,7 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, } /* Get the lock */ - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Do our best to flush TX & RX, so we don't lose anything */ /* But we don't wait indefinitely ! */ @@ -1250,7 +1250,7 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, psc_ops->command(port, MPC52xx_PSC_RX_ENABLE); /* We're all set, release the lock */ - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char * @@ -1477,11 +1477,11 @@ mpc52xx_uart_int(int irq, void *dev_id) struct uart_port *port = dev_id; irqreturn_t ret; - spin_lock(&port->lock); + uart_port_lock(port); ret = psc_ops->handle_irq(port); - spin_unlock(&port->lock); + uart_port_unlock(port); return ret; } diff --git a/drivers/tty/serial/mps2-uart.c b/drivers/tty/serial/mps2-uart.c index ea5a7911cb15..2a4c09f3a834 100644 --- a/drivers/tty/serial/mps2-uart.c +++ b/drivers/tty/serial/mps2-uart.c @@ -188,12 +188,12 @@ static irqreturn_t mps2_uart_rxirq(int irq, void *data) if (unlikely(!(irqflag & UARTn_INT_RX))) return IRQ_NONE; - spin_lock(&port->lock); + uart_port_lock(port); mps2_uart_write8(port, UARTn_INT_RX, UARTn_INT); mps2_uart_rx_chars(port); - spin_unlock(&port->lock); + uart_port_unlock(port); return IRQ_HANDLED; } @@ -206,12 +206,12 @@ static irqreturn_t mps2_uart_txirq(int irq, void *data) if (unlikely(!(irqflag & UARTn_INT_TX))) return IRQ_NONE; - spin_lock(&port->lock); + uart_port_lock(port); mps2_uart_write8(port, UARTn_INT_TX, UARTn_INT); mps2_uart_tx_chars(port); - spin_unlock(&port->lock); + uart_port_unlock(port); return IRQ_HANDLED; } @@ -222,7 +222,7 @@ static irqreturn_t mps2_uart_oerrirq(int irq, void *data) struct uart_port *port = data; u8 irqflag = mps2_uart_read8(port, UARTn_INT); - spin_lock(&port->lock); + uart_port_lock(port); if (irqflag & UARTn_INT_RX_OVERRUN) { struct tty_port *tport = &port->state->port; @@ -244,7 +244,7 @@ static irqreturn_t mps2_uart_oerrirq(int irq, void *data) handled = IRQ_HANDLED; } - spin_unlock(&port->lock); + uart_port_unlock(port); return handled; } @@ -356,12 +356,12 @@ mps2_uart_set_termios(struct uart_port *port, struct ktermios *termios, bauddiv = DIV_ROUND_CLOSEST(port->uartclk, baud); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); uart_update_timeout(port, termios->c_cflag, baud); mps2_uart_write32(port, bauddiv, UARTn_BAUDDIV); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); if (tty_termios_baud_rate(termios)) tty_termios_encode_baud_rate(termios, baud, baud); diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 90953e679e38..597264b546fd 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -444,7 +444,7 @@ static void msm_complete_tx_dma(void *args) unsigned int count; u32 val; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Already stopped */ if (!dma->count) @@ -476,7 +476,7 @@ static void msm_complete_tx_dma(void *args) msm_handle_tx(port); done: - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count) @@ -549,7 +549,7 @@ static void msm_complete_rx_dma(void *args) unsigned long flags; u32 val; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Already stopped */ if (!dma->count) @@ -587,16 +587,16 @@ static void msm_complete_rx_dma(void *args) if (!(port->read_status_mask & MSM_UART_SR_RX_BREAK)) flag = TTY_NORMAL; - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); sysrq = uart_handle_sysrq_char(port, dma->virt[i]); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); if (!sysrq) tty_insert_flip_char(tport, dma->virt[i], flag); } msm_start_rx_dma(msm_port); done: - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); if (count) tty_flip_buffer_push(tport); @@ -762,9 +762,9 @@ static void msm_handle_rx_dm(struct uart_port *port, unsigned int misr) if (!(port->read_status_mask & MSM_UART_SR_RX_BREAK)) flag = TTY_NORMAL; - spin_unlock(&port->lock); + uart_port_unlock(port); sysrq = uart_handle_sysrq_char(port, buf[i]); - spin_lock(&port->lock); + uart_port_lock(port); if (!sysrq) tty_insert_flip_char(tport, buf[i], flag); } @@ -824,9 +824,9 @@ static void msm_handle_rx(struct uart_port *port) else if (sr & MSM_UART_SR_PAR_FRAME_ERR) flag = TTY_FRAME; - spin_unlock(&port->lock); + uart_port_unlock(port); sysrq = uart_handle_sysrq_char(port, c); - spin_lock(&port->lock); + uart_port_lock(port); if (!sysrq) tty_insert_flip_char(tport, c, flag); } @@ -951,7 +951,7 @@ static irqreturn_t msm_uart_irq(int irq, void *dev_id) unsigned int misr; u32 val; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); misr = msm_read(port, MSM_UART_MISR); msm_write(port, 0, MSM_UART_IMR); /* disable interrupt */ @@ -983,7 +983,7 @@ static irqreturn_t msm_uart_irq(int irq, void *dev_id) msm_handle_delta_cts(port); msm_write(port, msm_port->imr, MSM_UART_IMR); /* restore interrupt */ - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return IRQ_HANDLED; } @@ -1128,13 +1128,13 @@ static int msm_set_baud_rate(struct uart_port *port, unsigned int baud, unsigned long flags, rate; flags = *saved_flags; - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); entry = msm_find_best_baud(port, baud, &rate); clk_set_rate(msm_port->clk, rate); baud = rate / 16 / entry->divisor; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); *saved_flags = flags; port->uartclk = rate; @@ -1266,7 +1266,7 @@ static void msm_set_termios(struct uart_port *port, struct ktermios *termios, unsigned long flags; unsigned int baud, mr; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); if (dma->chan) /* Terminate if any */ msm_stop_dma(port, dma); @@ -1338,7 +1338,7 @@ static void msm_set_termios(struct uart_port *port, struct ktermios *termios, /* Try to use DMA */ msm_start_rx_dma(msm_port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *msm_type(struct uart_port *port) @@ -1620,9 +1620,9 @@ static void __msm_console_write(struct uart_port *port, const char *s, if (port->sysrq) locked = 0; else if (oops_in_progress) - locked = spin_trylock(&port->lock); + locked = uart_port_trylock(port); else - spin_lock(&port->lock); + uart_port_lock(port); if (is_uartdm) msm_reset_dm_count(port, count); @@ -1661,7 +1661,7 @@ static void __msm_console_write(struct uart_port *port, const char *s, } if (locked) - spin_unlock(&port->lock); + uart_port_unlock(port); local_irq_restore(flags); } diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c index ea924e9b913b..0255646bc175 100644 --- a/drivers/tty/serial/mvebu-uart.c +++ b/drivers/tty/serial/mvebu-uart.c @@ -187,9 +187,9 @@ static unsigned int mvebu_uart_tx_empty(struct uart_port *port) unsigned long flags; unsigned int st; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); st = readl(port->membase + UART_STAT); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return (st & STAT_TX_EMP) ? TIOCSER_TEMT : 0; } @@ -249,14 +249,14 @@ static void mvebu_uart_break_ctl(struct uart_port *port, int brk) unsigned int ctl; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); ctl = readl(port->membase + UART_CTRL(port)); if (brk == -1) ctl |= CTRL_SND_BRK_SEQ; else ctl &= ~CTRL_SND_BRK_SEQ; writel(ctl, port->membase + UART_CTRL(port)); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void mvebu_uart_rx_chars(struct uart_port *port, unsigned int status) @@ -540,7 +540,7 @@ static void mvebu_uart_set_termios(struct uart_port *port, unsigned long flags; unsigned int baud, min_baud, max_baud; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); port->read_status_mask = STAT_RX_RDY(port) | STAT_OVR_ERR | STAT_TX_RDY(port) | STAT_TX_FIFO_FUL; @@ -589,7 +589,7 @@ static void mvebu_uart_set_termios(struct uart_port *port, uart_update_timeout(port, termios->c_cflag, baud); } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *mvebu_uart_type(struct uart_port *port) @@ -735,9 +735,9 @@ static void mvebu_uart_console_write(struct console *co, const char *s, int locked = 1; if (oops_in_progress) - locked = spin_trylock_irqsave(&port->lock, flags); + locked = uart_port_trylock_irqsave(port, &flags); else - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); ier = readl(port->membase + UART_CTRL(port)) & CTRL_BRK_INT; intr = readl(port->membase + UART_INTR(port)) & @@ -758,7 +758,7 @@ static void mvebu_uart_console_write(struct console *co, const char *s, } if (locked) - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static int mvebu_uart_console_setup(struct console *co, char *options) diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 135a838f517a..1097fca22307 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -390,10 +390,10 @@ static void serial_omap_throttle(struct uart_port *port) struct uart_omap_port *up = to_uart_omap_port(port); unsigned long flags; - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); up->ier &= ~(UART_IER_RLSI | UART_IER_RDI); serial_out(up, UART_IER, up->ier); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } static void serial_omap_unthrottle(struct uart_port *port) @@ -401,10 +401,10 @@ static void serial_omap_unthrottle(struct uart_port *port) struct uart_omap_port *up = to_uart_omap_port(port); unsigned long flags; - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); up->ier |= UART_IER_RLSI | UART_IER_RDI; serial_out(up, UART_IER, up->ier); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } static unsigned int check_modem_status(struct uart_omap_port *up) @@ -527,7 +527,7 @@ static irqreturn_t serial_omap_irq(int irq, void *dev_id) irqreturn_t ret = IRQ_NONE; int max_count = 256; - spin_lock(&up->port.lock); + uart_port_lock(&up->port); do { iir = serial_in(up, UART_IIR); @@ -563,7 +563,7 @@ static irqreturn_t serial_omap_irq(int irq, void *dev_id) } } while (max_count--); - spin_unlock(&up->port.lock); + uart_port_unlock(&up->port); tty_flip_buffer_push(&up->port.state->port); @@ -579,9 +579,9 @@ static unsigned int serial_omap_tx_empty(struct uart_port *port) unsigned int ret = 0; dev_dbg(up->port.dev, "serial_omap_tx_empty+%d\n", up->port.line); - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); return ret; } @@ -647,13 +647,13 @@ static void serial_omap_break_ctl(struct uart_port *port, int break_state) unsigned long flags; dev_dbg(up->port.dev, "serial_omap_break_ctl+%d\n", up->port.line); - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); if (break_state == -1) up->lcr |= UART_LCR_SBC; else up->lcr &= ~UART_LCR_SBC; serial_out(up, UART_LCR, up->lcr); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } static int serial_omap_startup(struct uart_port *port) @@ -701,13 +701,13 @@ static int serial_omap_startup(struct uart_port *port) * Now, initialize the UART */ serial_out(up, UART_LCR, UART_LCR_WLEN8); - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); /* * Most PC uarts need OUT2 raised to enable interrupts. */ up->port.mctrl |= TIOCM_OUT2; serial_omap_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); up->msr_saved_flags = 0; /* @@ -742,10 +742,10 @@ static void serial_omap_shutdown(struct uart_port *port) up->ier = 0; serial_out(up, UART_IER, 0); - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); up->port.mctrl &= ~TIOCM_OUT2; serial_omap_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); /* * Disable break condition and FIFOs @@ -815,7 +815,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, * Ok, we're now changing the port state. Do it with * interrupts disabled. */ - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); /* * Update the per-port timeout. @@ -1013,7 +1013,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, serial_omap_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); dev_dbg(up->port.dev, "serial_omap_set_termios+%d\n", up->port.line); } @@ -1212,13 +1212,10 @@ serial_omap_console_write(struct console *co, const char *s, unsigned int ier; int locked = 1; - local_irq_save(flags); - if (up->port.sysrq) - locked = 0; - else if (oops_in_progress) - locked = spin_trylock(&up->port.lock); + if (up->port.sysrq || oops_in_progress) + locked = uart_port_trylock_irqsave(&up->port, &flags); else - spin_lock(&up->port.lock); + uart_port_lock_irqsave(&up->port, &flags); /* * First save the IER then disable the interrupts @@ -1245,8 +1242,7 @@ serial_omap_console_write(struct console *co, const char *s, check_modem_status(up); if (locked) - spin_unlock(&up->port.lock); - local_irq_restore(flags); + uart_port_unlock_irqrestore(&up->port, flags); } static int __init diff --git a/drivers/tty/serial/owl-uart.c b/drivers/tty/serial/owl-uart.c index e99970a9437f..919f5e5aa0f1 100644 --- a/drivers/tty/serial/owl-uart.c +++ b/drivers/tty/serial/owl-uart.c @@ -125,12 +125,12 @@ static unsigned int owl_uart_tx_empty(struct uart_port *port) u32 val; unsigned int ret; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); val = owl_uart_read(port, OWL_UART_STAT); ret = (val & OWL_UART_STAT_TFES) ? TIOCSER_TEMT : 0; - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return ret; } @@ -232,7 +232,7 @@ static irqreturn_t owl_uart_irq(int irq, void *dev_id) unsigned long flags; u32 stat; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); stat = owl_uart_read(port, OWL_UART_STAT); @@ -246,7 +246,7 @@ static irqreturn_t owl_uart_irq(int irq, void *dev_id) stat |= OWL_UART_STAT_RIP | OWL_UART_STAT_TIP; owl_uart_write(port, stat, OWL_UART_STAT); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return IRQ_HANDLED; } @@ -256,14 +256,14 @@ static void owl_uart_shutdown(struct uart_port *port) u32 val; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); val = owl_uart_read(port, OWL_UART_CTL); val &= ~(OWL_UART_CTL_TXIE | OWL_UART_CTL_RXIE | OWL_UART_CTL_TXDE | OWL_UART_CTL_RXDE | OWL_UART_CTL_EN); owl_uart_write(port, val, OWL_UART_CTL); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); free_irq(port->irq, port); } @@ -279,7 +279,7 @@ static int owl_uart_startup(struct uart_port *port) if (ret) return ret; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); val = owl_uart_read(port, OWL_UART_STAT); val |= OWL_UART_STAT_RIP | OWL_UART_STAT_TIP @@ -291,7 +291,7 @@ static int owl_uart_startup(struct uart_port *port) val |= OWL_UART_CTL_EN; owl_uart_write(port, val, OWL_UART_CTL); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return 0; } @@ -311,7 +311,7 @@ static void owl_uart_set_termios(struct uart_port *port, u32 ctl; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); ctl = owl_uart_read(port, OWL_UART_CTL); @@ -371,7 +371,7 @@ static void owl_uart_set_termios(struct uart_port *port, uart_update_timeout(port, termios->c_cflag, baud); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void owl_uart_release_port(struct uart_port *port) @@ -515,9 +515,9 @@ static void owl_uart_port_write(struct uart_port *port, const char *s, if (port->sysrq) locked = 0; else if (oops_in_progress) - locked = spin_trylock(&port->lock); + locked = uart_port_trylock(port); else { - spin_lock(&port->lock); + uart_port_lock(port); locked = 1; } @@ -541,7 +541,7 @@ static void owl_uart_port_write(struct uart_port *port, const char *s, owl_uart_write(port, old_ctl, OWL_UART_CTL); if (locked) - spin_unlock(&port->lock); + uart_port_unlock(port); local_irq_restore(flags); } diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index cc83b772b7ca..436cc6d52a11 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -1347,7 +1347,7 @@ static void pch_uart_set_termios(struct uart_port *port, baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); spin_lock_irqsave(&priv->lock, flags); - spin_lock(&port->lock); + uart_port_lock(port); uart_update_timeout(port, termios->c_cflag, baud); rtn = pch_uart_hal_set_line(priv, baud, parity, bits, stb); @@ -1360,7 +1360,7 @@ static void pch_uart_set_termios(struct uart_port *port, tty_termios_encode_baud_rate(termios, baud, baud); out: - spin_unlock(&port->lock); + uart_port_unlock(port); spin_unlock_irqrestore(&priv->lock, flags); } @@ -1581,10 +1581,10 @@ pch_console_write(struct console *co, const char *s, unsigned int count) port_locked = 0; } else if (oops_in_progress) { priv_locked = spin_trylock(&priv->lock); - port_locked = spin_trylock(&priv->port.lock); + port_locked = uart_port_trylock(&priv->port); } else { spin_lock(&priv->lock); - spin_lock(&priv->port.lock); + uart_port_lock(&priv->port); } /* @@ -1604,7 +1604,7 @@ pch_console_write(struct console *co, const char *s, unsigned int count) iowrite8(ier, priv->membase + UART_IER); if (port_locked) - spin_unlock(&priv->port.lock); + uart_port_unlock(&priv->port); if (priv_locked) spin_unlock(&priv->lock); local_irq_restore(flags); diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c index e308d5022b3f..3a95bf5d55d3 100644 --- a/drivers/tty/serial/pic32_uart.c +++ b/drivers/tty/serial/pic32_uart.c @@ -243,7 +243,7 @@ static void pic32_uart_break_ctl(struct uart_port *port, int ctl) struct pic32_sport *sport = to_pic32_sport(port); unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); if (ctl) pic32_uart_writel(sport, PIC32_SET(PIC32_UART_STA), @@ -252,7 +252,7 @@ static void pic32_uart_break_ctl(struct uart_port *port, int ctl) pic32_uart_writel(sport, PIC32_CLR(PIC32_UART_STA), PIC32_UART_STA_UTXBRK); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /* get port type in string format */ @@ -274,7 +274,7 @@ static void pic32_uart_do_rx(struct uart_port *port) */ max_count = PIC32_UART_RX_FIFO_DEPTH; - spin_lock(&port->lock); + uart_port_lock(port); tty = &port->state->port; @@ -331,7 +331,7 @@ static void pic32_uart_do_rx(struct uart_port *port) } while (--max_count); - spin_unlock(&port->lock); + uart_port_unlock(port); tty_flip_buffer_push(tty); } @@ -410,9 +410,9 @@ static irqreturn_t pic32_uart_tx_interrupt(int irq, void *dev_id) struct uart_port *port = dev_id; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); pic32_uart_do_tx(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return IRQ_HANDLED; } @@ -580,9 +580,9 @@ static void pic32_uart_shutdown(struct uart_port *port) unsigned long flags; /* disable uart */ - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); pic32_uart_dsbl_and_mask(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); clk_disable_unprepare(sport->clk); /* free all 3 interrupts for this UART */ @@ -604,7 +604,7 @@ static void pic32_uart_set_termios(struct uart_port *port, unsigned int quot; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* disable uart and mask all interrupts while changing speed */ pic32_uart_dsbl_and_mask(port); @@ -672,7 +672,7 @@ static void pic32_uart_set_termios(struct uart_port *port, /* enable uart */ pic32_uart_en_and_unmask(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /* serial core request to claim uart iomem */ diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c index 29bc80d39e8b..77691fbbf779 100644 --- a/drivers/tty/serial/pmac_zilog.c +++ b/drivers/tty/serial/pmac_zilog.c @@ -245,9 +245,9 @@ static bool pmz_receive_chars(struct uart_pmac_port *uap) #endif /* USE_CTRL_O_SYSRQ */ if (uap->port.sysrq) { int swallow; - spin_unlock(&uap->port.lock); + uart_port_unlock(&uap->port); swallow = uart_handle_sysrq_char(&uap->port, ch); - spin_lock(&uap->port.lock); + uart_port_lock(&uap->port); if (swallow) goto next_char; } @@ -421,7 +421,7 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id) uap_a = pmz_get_port_A(uap); uap_b = uap_a->mate; - spin_lock(&uap_a->port.lock); + uart_port_lock(&uap_a->port); r3 = read_zsreg(uap_a, R3); /* Channel A */ @@ -442,14 +442,14 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id) rc = IRQ_HANDLED; } skip_a: - spin_unlock(&uap_a->port.lock); + uart_port_unlock(&uap_a->port); if (push) tty_flip_buffer_push(&uap->port.state->port); if (!uap_b) goto out; - spin_lock(&uap_b->port.lock); + uart_port_lock(&uap_b->port); push = false; if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { if (!ZS_IS_OPEN(uap_b)) { @@ -467,7 +467,7 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id) rc = IRQ_HANDLED; } skip_b: - spin_unlock(&uap_b->port.lock); + uart_port_unlock(&uap_b->port); if (push) tty_flip_buffer_push(&uap->port.state->port); @@ -483,9 +483,9 @@ static inline u8 pmz_peek_status(struct uart_pmac_port *uap) unsigned long flags; u8 status; - spin_lock_irqsave(&uap->port.lock, flags); + uart_port_lock_irqsave(&uap->port, &flags); status = read_zsreg(uap, R0); - spin_unlock_irqrestore(&uap->port.lock, flags); + uart_port_unlock_irqrestore(&uap->port, flags); return status; } @@ -671,7 +671,7 @@ static void pmz_break_ctl(struct uart_port *port, int break_state) else clear_bits |= SND_BRK; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); new_reg = (uap->curregs[R5] | set_bits) & ~clear_bits; if (new_reg != uap->curregs[R5]) { @@ -679,7 +679,7 @@ static void pmz_break_ctl(struct uart_port *port, int break_state) write_zsreg(uap, R5, uap->curregs[R5]); } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } #ifdef CONFIG_PPC_PMAC @@ -851,18 +851,18 @@ static void pmz_irda_reset(struct uart_pmac_port *uap) { unsigned long flags; - spin_lock_irqsave(&uap->port.lock, flags); + uart_port_lock_irqsave(&uap->port, &flags); uap->curregs[R5] |= DTR; write_zsreg(uap, R5, uap->curregs[R5]); zssync(uap); - spin_unlock_irqrestore(&uap->port.lock, flags); + uart_port_unlock_irqrestore(&uap->port, flags); msleep(110); - spin_lock_irqsave(&uap->port.lock, flags); + uart_port_lock_irqsave(&uap->port, &flags); uap->curregs[R5] &= ~DTR; write_zsreg(uap, R5, uap->curregs[R5]); zssync(uap); - spin_unlock_irqrestore(&uap->port.lock, flags); + uart_port_unlock_irqrestore(&uap->port, flags); msleep(10); } @@ -882,9 +882,9 @@ static int pmz_startup(struct uart_port *port) * initialize the chip */ if (!ZS_IS_CONS(uap)) { - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); pwr_delay = __pmz_startup(uap); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } sprintf(uap->irq_name, PMACZILOG_NAME"%d", uap->port.line); if (request_irq(uap->port.irq, pmz_interrupt, IRQF_SHARED, @@ -907,9 +907,9 @@ static int pmz_startup(struct uart_port *port) pmz_irda_reset(uap); /* Enable interrupt requests for the channel */ - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); pmz_interrupt_control(uap, 1); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return 0; } @@ -919,7 +919,7 @@ static void pmz_shutdown(struct uart_port *port) struct uart_pmac_port *uap = to_pmz(port); unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Disable interrupt requests for the channel */ pmz_interrupt_control(uap, 0); @@ -934,19 +934,19 @@ static void pmz_shutdown(struct uart_port *port) pmz_maybe_update_regs(uap); } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); /* Release interrupt handler */ free_irq(uap->port.irq, uap); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); uap->flags &= ~PMACZILOG_FLAG_IS_OPEN; if (!ZS_IS_CONS(uap)) pmz_set_scc_power(uap, 0); /* Shut the chip down */ - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /* Shared by TTY driver and serial console setup. The port lock is held @@ -1233,7 +1233,7 @@ static void pmz_set_termios(struct uart_port *port, struct ktermios *termios, struct uart_pmac_port *uap = to_pmz(port); unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Disable IRQs on the port */ pmz_interrupt_control(uap, 0); @@ -1245,7 +1245,7 @@ static void pmz_set_termios(struct uart_port *port, struct ktermios *termios, if (ZS_IS_OPEN(uap)) pmz_interrupt_control(uap, 1); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *pmz_type(struct uart_port *port) @@ -1882,7 +1882,7 @@ static void pmz_console_write(struct console *con, const char *s, unsigned int c struct uart_pmac_port *uap = &pmz_ports[con->index]; unsigned long flags; - spin_lock_irqsave(&uap->port.lock, flags); + uart_port_lock_irqsave(&uap->port, &flags); /* Turn of interrupts and enable the transmitter. */ write_zsreg(uap, R1, uap->curregs[1] & ~TxINT_ENAB); @@ -1894,7 +1894,7 @@ static void pmz_console_write(struct console *con, const char *s, unsigned int c write_zsreg(uap, R1, uap->curregs[1]); /* Don't disable the transmitter. */ - spin_unlock_irqrestore(&uap->port.lock, flags); + uart_port_unlock_irqrestore(&uap->port, flags); } /* diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index 73c60f5ea027..46e70e155aab 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -225,14 +225,14 @@ static inline irqreturn_t serial_pxa_irq(int irq, void *dev_id) iir = serial_in(up, UART_IIR); if (iir & UART_IIR_NO_INT) return IRQ_NONE; - spin_lock(&up->port.lock); + uart_port_lock(&up->port); lsr = serial_in(up, UART_LSR); if (lsr & UART_LSR_DR) receive_chars(up, &lsr); check_modem_status(up); if (lsr & UART_LSR_THRE) transmit_chars(up); - spin_unlock(&up->port.lock); + uart_port_unlock(&up->port); return IRQ_HANDLED; } @@ -242,9 +242,9 @@ static unsigned int serial_pxa_tx_empty(struct uart_port *port) unsigned long flags; unsigned int ret; - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); return ret; } @@ -295,13 +295,13 @@ static void serial_pxa_break_ctl(struct uart_port *port, int break_state) struct uart_pxa_port *up = (struct uart_pxa_port *)port; unsigned long flags; - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); if (break_state == -1) up->lcr |= UART_LCR_SBC; else up->lcr &= ~UART_LCR_SBC; serial_out(up, UART_LCR, up->lcr); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } static int serial_pxa_startup(struct uart_port *port) @@ -346,10 +346,10 @@ static int serial_pxa_startup(struct uart_port *port) */ serial_out(up, UART_LCR, UART_LCR_WLEN8); - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); up->port.mctrl |= TIOCM_OUT2; serial_pxa_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); /* * Finally, enable interrupts. Note: Modem status interrupts @@ -383,10 +383,10 @@ static void serial_pxa_shutdown(struct uart_port *port) up->ier = 0; serial_out(up, UART_IER, 0); - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); up->port.mctrl &= ~TIOCM_OUT2; serial_pxa_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); /* * Disable break condition and FIFOs @@ -434,7 +434,7 @@ serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios, * Ok, we're now changing the port state. Do it with * interrupts disabled. */ - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); /* * Ensure the port will be enabled. @@ -504,7 +504,7 @@ serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios, up->lcr = cval; /* Save LCR */ serial_pxa_set_mctrl(&up->port, up->port.mctrl); serial_out(up, UART_FCR, fcr); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } static void @@ -608,9 +608,9 @@ serial_pxa_console_write(struct console *co, const char *s, unsigned int count) if (up->port.sysrq) locked = 0; else if (oops_in_progress) - locked = spin_trylock(&up->port.lock); + locked = uart_port_trylock(&up->port); else - spin_lock(&up->port.lock); + uart_port_lock(&up->port); /* * First save the IER then disable the interrupts @@ -628,7 +628,7 @@ serial_pxa_console_write(struct console *co, const char *s, unsigned int count) serial_out(up, UART_IER, ier); if (locked) - spin_unlock(&up->port.lock); + uart_port_unlock(&up->port); local_irq_restore(flags); clk_disable(up->clk); diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 2e1b1c827dfe..549909644011 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -482,9 +482,9 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, uport = &port->uport; if (oops_in_progress) - locked = spin_trylock_irqsave(&uport->lock, flags); + locked = uart_port_trylock_irqsave(uport, &flags); else - spin_lock_irqsave(&uport->lock, flags); + uart_port_lock_irqsave(uport, &flags); geni_status = readl(uport->membase + SE_GENI_STATUS); @@ -520,7 +520,7 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, qcom_geni_serial_setup_tx(uport, port->tx_remaining); if (locked) - spin_unlock_irqrestore(&uport->lock, flags); + uart_port_unlock_irqrestore(uport, flags); } static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) @@ -972,7 +972,7 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) if (uport->suspended) return IRQ_NONE; - spin_lock(&uport->lock); + uart_port_lock(uport); m_irq_status = readl(uport->membase + SE_GENI_M_IRQ_STATUS); s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS); diff --git a/drivers/tty/serial/rda-uart.c b/drivers/tty/serial/rda-uart.c index be5c842b5ba9..d824c8318f33 100644 --- a/drivers/tty/serial/rda-uart.c +++ b/drivers/tty/serial/rda-uart.c @@ -139,12 +139,12 @@ static unsigned int rda_uart_tx_empty(struct uart_port *port) unsigned int ret; u32 val; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); val = rda_uart_read(port, RDA_UART_STATUS); ret = (val & RDA_UART_TX_FIFO_MASK) ? TIOCSER_TEMT : 0; - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return ret; } @@ -246,7 +246,7 @@ static void rda_uart_set_termios(struct uart_port *port, unsigned int baud; u32 irq_mask; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); baud = uart_get_baud_rate(port, termios, old, 9600, port->uartclk / 4); rda_uart_change_baudrate(rda_port, baud); @@ -325,7 +325,7 @@ static void rda_uart_set_termios(struct uart_port *port, /* update the per-port timeout */ uart_update_timeout(port, termios->c_cflag, baud); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void rda_uart_send_chars(struct uart_port *port) @@ -408,7 +408,7 @@ static irqreturn_t rda_interrupt(int irq, void *dev_id) unsigned long flags; u32 val, irq_mask; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Clear IRQ cause */ val = rda_uart_read(port, RDA_UART_IRQ_CAUSE); @@ -425,7 +425,7 @@ static irqreturn_t rda_interrupt(int irq, void *dev_id) rda_uart_send_chars(port); } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return IRQ_HANDLED; } @@ -436,16 +436,16 @@ static int rda_uart_startup(struct uart_port *port) int ret; u32 val; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); rda_uart_write(port, 0, RDA_UART_IRQ_MASK); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); ret = request_irq(port->irq, rda_interrupt, IRQF_NO_SUSPEND, "rda-uart", port); if (ret) return ret; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); val = rda_uart_read(port, RDA_UART_CTRL); val |= RDA_UART_ENABLE; @@ -456,7 +456,7 @@ static int rda_uart_startup(struct uart_port *port) val |= (RDA_UART_RX_DATA_AVAILABLE | RDA_UART_RX_TIMEOUT); rda_uart_write(port, val, RDA_UART_IRQ_MASK); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return 0; } @@ -466,7 +466,7 @@ static void rda_uart_shutdown(struct uart_port *port) unsigned long flags; u32 val; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); rda_uart_stop_tx(port); rda_uart_stop_rx(port); @@ -475,7 +475,7 @@ static void rda_uart_shutdown(struct uart_port *port) val &= ~RDA_UART_ENABLE; rda_uart_write(port, val, RDA_UART_CTRL); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *rda_uart_type(struct uart_port *port) @@ -515,7 +515,7 @@ static void rda_uart_config_port(struct uart_port *port, int flags) rda_uart_request_port(port); } - spin_lock_irqsave(&port->lock, irq_flags); + uart_port_lock_irqsave(port, &irq_flags); /* Clear mask, so no surprise interrupts. */ rda_uart_write(port, 0, RDA_UART_IRQ_MASK); @@ -523,7 +523,7 @@ static void rda_uart_config_port(struct uart_port *port, int flags) /* Clear status register */ rda_uart_write(port, 0, RDA_UART_STATUS); - spin_unlock_irqrestore(&port->lock, irq_flags); + uart_port_unlock_irqrestore(port, irq_flags); } static void rda_uart_release_port(struct uart_port *port) @@ -597,9 +597,9 @@ static void rda_uart_port_write(struct uart_port *port, const char *s, if (port->sysrq) { locked = 0; } else if (oops_in_progress) { - locked = spin_trylock(&port->lock); + locked = uart_port_trylock(port); } else { - spin_lock(&port->lock); + uart_port_lock(port); locked = 1; } @@ -615,7 +615,7 @@ static void rda_uart_port_write(struct uart_port *port, const char *s, rda_uart_write(port, old_irq_mask, RDA_UART_IRQ_MASK); if (locked) - spin_unlock(&port->lock); + uart_port_unlock(port); local_irq_restore(flags); } diff --git a/drivers/tty/serial/rp2.c b/drivers/tty/serial/rp2.c index de220ac8ca54..d46a81cddfcd 100644 --- a/drivers/tty/serial/rp2.c +++ b/drivers/tty/serial/rp2.c @@ -276,9 +276,9 @@ static unsigned int rp2_uart_tx_empty(struct uart_port *port) * But the TXEMPTY bit doesn't seem to work unless the TX IRQ is * enabled. */ - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); tx_fifo_bytes = readw(up->base + RP2_TX_FIFO_COUNT); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); return tx_fifo_bytes ? 0 : TIOCSER_TEMT; } @@ -323,10 +323,10 @@ static void rp2_uart_break_ctl(struct uart_port *port, int break_state) { unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); rp2_rmw(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_BREAK_m, break_state ? RP2_TXRX_CTL_BREAK_m : 0); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void rp2_uart_enable_ms(struct uart_port *port) @@ -383,7 +383,7 @@ static void rp2_uart_set_termios(struct uart_port *port, struct ktermios *new, if (tty_termios_baud_rate(new)) tty_termios_encode_baud_rate(new, baud, baud); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* ignore all characters if CREAD is not set */ port->ignore_status_mask = (new->c_cflag & CREAD) ? 0 : RP2_DUMMY_READ; @@ -391,7 +391,7 @@ static void rp2_uart_set_termios(struct uart_port *port, struct ktermios *new, __rp2_uart_set_termios(up, new->c_cflag, new->c_iflag, baud_div); uart_update_timeout(port, new->c_cflag, baud); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void rp2_rx_chars(struct rp2_uart_port *up) @@ -440,7 +440,7 @@ static void rp2_ch_interrupt(struct rp2_uart_port *up) { u32 status; - spin_lock(&up->port.lock); + uart_port_lock(&up->port); /* * The IRQ status bits are clear-on-write. Other status bits in @@ -456,7 +456,7 @@ static void rp2_ch_interrupt(struct rp2_uart_port *up) if (status & RP2_CHAN_STAT_MS_CHANGED_MASK) wake_up_interruptible(&up->port.state->port.delta_msr_wait); - spin_unlock(&up->port.lock); + uart_port_unlock(&up->port); } static int rp2_asic_interrupt(struct rp2_card *card, unsigned int asic_id) @@ -516,10 +516,10 @@ static void rp2_uart_shutdown(struct uart_port *port) rp2_uart_break_ctl(port, 0); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); rp2_mask_ch_irq(up, up->idx, 0); rp2_rmw(up, RP2_CHAN_STAT, 0, 0); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *rp2_uart_type(struct uart_port *port) diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c index ad011f1e2f4d..be7bcd75d9f4 100644 --- a/drivers/tty/serial/sa1100.c +++ b/drivers/tty/serial/sa1100.c @@ -115,9 +115,9 @@ static void sa1100_timeout(struct timer_list *t) unsigned long flags; if (sport->port.state) { - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); sa1100_mctrl_check(sport); - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT); } @@ -247,7 +247,7 @@ static irqreturn_t sa1100_int(int irq, void *dev_id) struct sa1100_port *sport = dev_id; unsigned int status, pass_counter = 0; - spin_lock(&sport->port.lock); + uart_port_lock(&sport->port); status = UART_GET_UTSR0(sport); status &= SM_TO_UTSR0(sport->port.read_status_mask) | ~UTSR0_TFS; do { @@ -276,7 +276,7 @@ static irqreturn_t sa1100_int(int irq, void *dev_id) status &= SM_TO_UTSR0(sport->port.read_status_mask) | ~UTSR0_TFS; } while (status & (UTSR0_TFS | UTSR0_RFS | UTSR0_RID)); - spin_unlock(&sport->port.lock); + uart_port_unlock(&sport->port); return IRQ_HANDLED; } @@ -321,14 +321,14 @@ static void sa1100_break_ctl(struct uart_port *port, int break_state) unsigned long flags; unsigned int utcr3; - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); utcr3 = UART_GET_UTCR3(sport); if (break_state == -1) utcr3 |= UTCR3_BRK; else utcr3 &= ~UTCR3_BRK; UART_PUT_UTCR3(sport, utcr3); - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); } static int sa1100_startup(struct uart_port *port) @@ -354,9 +354,9 @@ static int sa1100_startup(struct uart_port *port) /* * Enable modem status interrupts */ - spin_lock_irq(&sport->port.lock); + uart_port_lock_irq(&sport->port); sa1100_enable_ms(&sport->port); - spin_unlock_irq(&sport->port.lock); + uart_port_unlock_irq(&sport->port); return 0; } @@ -423,7 +423,7 @@ sa1100_set_termios(struct uart_port *port, struct ktermios *termios, del_timer_sync(&sport->timer); - spin_lock_irqsave(&sport->port.lock, flags); + uart_port_lock_irqsave(&sport->port, &flags); sport->port.read_status_mask &= UTSR0_TO_SM(UTSR0_TFS); sport->port.read_status_mask |= UTSR1_TO_SM(UTSR1_ROR); @@ -485,7 +485,7 @@ sa1100_set_termios(struct uart_port *port, struct ktermios *termios, if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) sa1100_enable_ms(&sport->port); - spin_unlock_irqrestore(&sport->port.lock, flags); + uart_port_unlock_irqrestore(&sport->port, flags); } static const char *sa1100_type(struct uart_port *port) diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c index 5a4d88e13471..a82b65155f6e 100644 --- a/drivers/tty/serial/samsung_tty.c +++ b/drivers/tty/serial/samsung_tty.c @@ -248,7 +248,7 @@ static void s3c24xx_serial_rx_enable(struct uart_port *port) unsigned int ucon, ufcon; int count = 10000; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); while (--count && !s3c24xx_serial_txempty_nofifo(port)) udelay(100); @@ -262,7 +262,7 @@ static void s3c24xx_serial_rx_enable(struct uart_port *port) wr_regl(port, S3C2410_UCON, ucon); ourport->rx_enabled = 1; - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void s3c24xx_serial_rx_disable(struct uart_port *port) @@ -271,14 +271,14 @@ static void s3c24xx_serial_rx_disable(struct uart_port *port) unsigned long flags; unsigned int ucon; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); ucon = rd_regl(port, S3C2410_UCON); ucon &= ~S3C2410_UCON_RXIRQMODE; wr_regl(port, S3C2410_UCON, ucon); ourport->rx_enabled = 0; - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void s3c24xx_serial_stop_tx(struct uart_port *port) @@ -344,7 +344,7 @@ static void s3c24xx_serial_tx_dma_complete(void *args) dma->tx_transfer_addr, dma->tx_size, DMA_TO_DEVICE); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); uart_xmit_advance(port, count); ourport->tx_in_progress = 0; @@ -353,7 +353,7 @@ static void s3c24xx_serial_tx_dma_complete(void *args) uart_write_wakeup(port); s3c24xx_serial_start_next_tx(ourport); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void enable_tx_dma(struct s3c24xx_uart_port *ourport) @@ -619,7 +619,7 @@ static void s3c24xx_serial_rx_dma_complete(void *args) received = dma->rx_bytes_requested - state.residue; async_tx_ack(dma->rx_desc); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); if (received) s3c24xx_uart_copy_rx_to_tty(ourport, t, received); @@ -631,7 +631,7 @@ static void s3c24xx_serial_rx_dma_complete(void *args) s3c64xx_start_rx_dma(ourport); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void s3c64xx_start_rx_dma(struct s3c24xx_uart_port *ourport) @@ -722,7 +722,7 @@ static irqreturn_t s3c24xx_serial_rx_chars_dma(void *dev_id) utrstat = rd_regl(port, S3C2410_UTRSTAT); rd_regl(port, S3C2410_UFSTAT); - spin_lock(&port->lock); + uart_port_lock(port); if (!(utrstat & S3C2410_UTRSTAT_TIMEOUT)) { s3c64xx_start_rx_dma(ourport); @@ -751,7 +751,7 @@ static irqreturn_t s3c24xx_serial_rx_chars_dma(void *dev_id) wr_regl(port, S3C2410_UTRSTAT, S3C2410_UTRSTAT_TIMEOUT); finish: - spin_unlock(&port->lock); + uart_port_unlock(port); return IRQ_HANDLED; } @@ -849,9 +849,9 @@ static irqreturn_t s3c24xx_serial_rx_chars_pio(void *dev_id) struct s3c24xx_uart_port *ourport = dev_id; struct uart_port *port = &ourport->port; - spin_lock(&port->lock); + uart_port_lock(port); s3c24xx_serial_rx_drain_fifo(ourport); - spin_unlock(&port->lock); + uart_port_unlock(port); return IRQ_HANDLED; } @@ -932,11 +932,11 @@ static irqreturn_t s3c24xx_serial_tx_irq(int irq, void *id) struct s3c24xx_uart_port *ourport = id; struct uart_port *port = &ourport->port; - spin_lock(&port->lock); + uart_port_lock(port); s3c24xx_serial_tx_chars(ourport); - spin_unlock(&port->lock); + uart_port_unlock(port); return IRQ_HANDLED; } @@ -1032,7 +1032,7 @@ static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state) unsigned long flags; unsigned int ucon; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); ucon = rd_regl(port, S3C2410_UCON); @@ -1043,7 +1043,7 @@ static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state) wr_regl(port, S3C2410_UCON, ucon); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p) @@ -1302,7 +1302,7 @@ static int s3c64xx_serial_startup(struct uart_port *port) ourport->rx_enabled = 1; ourport->tx_enabled = 0; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); ufcon = rd_regl(port, S3C2410_UFCON); ufcon |= S3C2410_UFCON_RESETRX | S5PV210_UFCON_RXTRIG8; @@ -1312,7 +1312,7 @@ static int s3c64xx_serial_startup(struct uart_port *port) enable_rx_pio(ourport); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); /* Enable Rx Interrupt */ s3c24xx_clear_bit(port, S3C64XX_UINTM_RXD, S3C64XX_UINTM); @@ -1340,7 +1340,7 @@ static int apple_s5l_serial_startup(struct uart_port *port) ourport->rx_enabled = 1; ourport->tx_enabled = 0; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); ufcon = rd_regl(port, S3C2410_UFCON); ufcon |= S3C2410_UFCON_RESETRX | S5PV210_UFCON_RXTRIG8; @@ -1350,7 +1350,7 @@ static int apple_s5l_serial_startup(struct uart_port *port) enable_rx_pio(ourport); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); /* Enable Rx Interrupt */ s3c24xx_set_bit(port, APPLE_S5L_UCON_RXTHRESH_ENA, S3C2410_UCON); @@ -1625,7 +1625,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, ulcon |= S3C2410_LCON_PNONE; } - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); dev_dbg(port->dev, "setting ulcon to %08x, brddiv to %d, udivslot %08x\n", @@ -1683,7 +1683,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, if ((termios->c_cflag & CREAD) == 0) port->ignore_status_mask |= RXSTAT_DUMMY_READ; - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *s3c24xx_serial_type(struct uart_port *port) @@ -2375,14 +2375,14 @@ s3c24xx_serial_console_write(struct console *co, const char *s, if (cons_uart->sysrq) locked = false; else if (oops_in_progress) - locked = spin_trylock_irqsave(&cons_uart->lock, flags); + locked = uart_port_trylock_irqsave(cons_uart, &flags); else - spin_lock_irqsave(&cons_uart->lock, flags); + uart_port_lock_irqsave(cons_uart, &flags); uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar); if (locked) - spin_unlock_irqrestore(&cons_uart->lock, flags); + uart_port_unlock_irqrestore(cons_uart, flags); } /* Shouldn't be __init, as it can be instantiated from other module */ diff --git a/drivers/tty/serial/sb1250-duart.c b/drivers/tty/serial/sb1250-duart.c index f3cd69346482..dbec29d9a6c3 100644 --- a/drivers/tty/serial/sb1250-duart.c +++ b/drivers/tty/serial/sb1250-duart.c @@ -610,7 +610,7 @@ static void sbd_set_termios(struct uart_port *uport, struct ktermios *termios, else aux &= ~M_DUART_CTS_CHNG_ENA; - spin_lock(&uport->lock); + uart_port_lock(uport); if (sport->tx_stopped) command |= M_DUART_TX_DIS; @@ -632,7 +632,7 @@ static void sbd_set_termios(struct uart_port *uport, struct ktermios *termios, write_sbdchn(sport, R_DUART_CMD, command); - spin_unlock(&uport->lock); + uart_port_unlock(uport); } @@ -839,22 +839,22 @@ static void sbd_console_write(struct console *co, const char *s, unsigned int mask; /* Disable transmit interrupts and enable the transmitter. */ - spin_lock_irqsave(&uport->lock, flags); + uart_port_lock_irqsave(uport, &flags); mask = read_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2)); write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), mask & ~M_DUART_IMR_TX); write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_EN); - spin_unlock_irqrestore(&uport->lock, flags); + uart_port_unlock_irqrestore(uport, flags); uart_console_write(&sport->port, s, count, sbd_console_putchar); /* Restore transmit interrupts and the transmitter enable. */ - spin_lock_irqsave(&uport->lock, flags); + uart_port_lock_irqsave(uport, &flags); sbd_line_drain(sport); if (sport->tx_stopped) write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_DIS); write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), mask); - spin_unlock_irqrestore(&uport->lock, flags); + uart_port_unlock_irqrestore(uport, flags); } static int __init sbd_console_setup(struct console *co, char *options) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 7a9924d9b294..be9ae5cdd1b8 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -817,6 +817,7 @@ static void sc16is7xx_tx_proc(struct kthread_work *ws) { struct uart_port *port = &(to_sc16is7xx_one(ws, tx_work)->port); struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + unsigned long flags; if ((port->rs485.flags & SER_RS485_ENABLED) && (port->rs485.delay_rts_before_send > 0)) @@ -825,6 +826,10 @@ static void sc16is7xx_tx_proc(struct kthread_work *ws) mutex_lock(&one->efr_lock); sc16is7xx_handle_tx(port); mutex_unlock(&one->efr_lock); + + uart_port_lock_irqsave(port, &flags); + sc16is7xx_ier_set(port, SC16IS7XX_IER_THRI_BIT); + uart_port_unlock_irqrestore(port, flags); } static void sc16is7xx_reconf_rs485(struct uart_port *port) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index d4ec943cb8e9..6d4006b41975 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -411,7 +411,7 @@ static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud) divisor = DIV_ROUND_CLOSEST(rate, baud * 16); } - spin_lock_irqsave(&tup->uport.lock, flags); + uart_port_lock_irqsave(&tup->uport, &flags); lcr = tup->lcr_shadow; lcr |= UART_LCR_DLAB; tegra_uart_write(tup, lcr, UART_LCR); @@ -424,7 +424,7 @@ static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud) /* Dummy read to ensure the write is posted */ tegra_uart_read(tup, UART_SCR); - spin_unlock_irqrestore(&tup->uport.lock, flags); + uart_port_unlock_irqrestore(&tup->uport, flags); tup->current_baud = baud; @@ -522,13 +522,13 @@ static void tegra_uart_tx_dma_complete(void *args) dmaengine_tx_status(tup->tx_dma_chan, tup->tx_cookie, &state); count = tup->tx_bytes_requested - state.residue; async_tx_ack(tup->tx_dma_desc); - spin_lock_irqsave(&tup->uport.lock, flags); + uart_port_lock_irqsave(&tup->uport, &flags); uart_xmit_advance(&tup->uport, count); tup->tx_in_progress = 0; if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&tup->uport); tegra_uart_start_next_tx(tup); - spin_unlock_irqrestore(&tup->uport.lock, flags); + uart_port_unlock_irqrestore(&tup->uport, flags); } static int tegra_uart_start_tx_dma(struct tegra_uart_port *tup, @@ -598,13 +598,13 @@ static unsigned int tegra_uart_tx_empty(struct uart_port *u) unsigned int ret = 0; unsigned long flags; - spin_lock_irqsave(&u->lock, flags); + uart_port_lock_irqsave(u, &flags); if (!tup->tx_in_progress) { unsigned long lsr = tegra_uart_read(tup, UART_LSR); if ((lsr & TX_EMPTY_STATUS) == TX_EMPTY_STATUS) ret = TIOCSER_TEMT; } - spin_unlock_irqrestore(&u->lock, flags); + uart_port_unlock_irqrestore(u, flags); return ret; } @@ -727,7 +727,7 @@ static void tegra_uart_rx_dma_complete(void *args) struct dma_tx_state state; enum dma_status status; - spin_lock_irqsave(&u->lock, flags); + uart_port_lock_irqsave(u, &flags); status = dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state); @@ -749,7 +749,7 @@ static void tegra_uart_rx_dma_complete(void *args) set_rts(tup, true); done: - spin_unlock_irqrestore(&u->lock, flags); + uart_port_unlock_irqrestore(u, flags); } static void tegra_uart_terminate_rx_dma(struct tegra_uart_port *tup) @@ -836,7 +836,7 @@ static irqreturn_t tegra_uart_isr(int irq, void *data) bool is_rx_int = false; unsigned long flags; - spin_lock_irqsave(&u->lock, flags); + uart_port_lock_irqsave(u, &flags); while (1) { iir = tegra_uart_read(tup, UART_IIR); if (iir & UART_IIR_NO_INT) { @@ -852,7 +852,7 @@ static irqreturn_t tegra_uart_isr(int irq, void *data) } else if (is_rx_start) { tegra_uart_start_rx_dma(tup); } - spin_unlock_irqrestore(&u->lock, flags); + uart_port_unlock_irqrestore(u, flags); return IRQ_HANDLED; } @@ -969,11 +969,11 @@ static void tegra_uart_hw_deinit(struct tegra_uart_port *tup) } } - spin_lock_irqsave(&tup->uport.lock, flags); + uart_port_lock_irqsave(&tup->uport, &flags); /* Reset the Rx and Tx FIFOs */ tegra_uart_fifo_reset(tup, UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR); tup->current_baud = 0; - spin_unlock_irqrestore(&tup->uport.lock, flags); + uart_port_unlock_irqrestore(&tup->uport, flags); tup->rx_in_progress = 0; tup->tx_in_progress = 0; @@ -1292,7 +1292,7 @@ static void tegra_uart_set_termios(struct uart_port *u, int ret; max_divider *= 16; - spin_lock_irqsave(&u->lock, flags); + uart_port_lock_irqsave(u, &flags); /* Changing configuration, it is safe to stop any rx now */ if (tup->rts_active) @@ -1341,7 +1341,7 @@ static void tegra_uart_set_termios(struct uart_port *u, baud = uart_get_baud_rate(u, termios, oldtermios, parent_clk_rate/max_divider, parent_clk_rate/16); - spin_unlock_irqrestore(&u->lock, flags); + uart_port_unlock_irqrestore(u, flags); ret = tegra_set_baudrate(tup, baud); if (ret < 0) { dev_err(tup->uport.dev, "Failed to set baud rate\n"); @@ -1349,7 +1349,7 @@ static void tegra_uart_set_termios(struct uart_port *u, } if (tty_termios_baud_rate(termios)) tty_termios_encode_baud_rate(termios, baud, baud); - spin_lock_irqsave(&u->lock, flags); + uart_port_lock_irqsave(u, &flags); /* Flow control */ if (termios->c_cflag & CRTSCTS) { @@ -1382,7 +1382,7 @@ static void tegra_uart_set_termios(struct uart_port *u, if (termios->c_iflag & IGNBRK) tup->uport.ignore_status_mask |= UART_LSR_BI; - spin_unlock_irqrestore(&u->lock, flags); + uart_port_unlock_irqrestore(u, flags); } static const char *tegra_uart_type(struct uart_port *u) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index ed8798fdf522..f0cd976e22e2 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -79,7 +79,7 @@ static inline void uart_port_deref(struct uart_port *uport) ({ \ struct uart_port *__uport = uart_port_ref(state); \ if (__uport) \ - spin_lock_irqsave(&__uport->lock, flags); \ + uart_port_lock_irqsave(__uport, &flags); \ __uport; \ }) @@ -87,7 +87,7 @@ static inline void uart_port_deref(struct uart_port *uport) ({ \ struct uart_port *__uport = uport; \ if (__uport) { \ - spin_unlock_irqrestore(&__uport->lock, flags); \ + uart_port_unlock_irqrestore(__uport, flags); \ uart_port_deref(__uport); \ } \ }) @@ -179,12 +179,12 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) unsigned long flags; unsigned int old; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); old = port->mctrl; port->mctrl = (old & ~clear) | set; if (old != port->mctrl && !(port->rs485.flags & SER_RS485_ENABLED)) port->ops->set_mctrl(port, port->mctrl); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } #define uart_set_mctrl(port, set) uart_update_mctrl(port, set, 0) @@ -219,7 +219,7 @@ static void uart_change_line_settings(struct tty_struct *tty, struct uart_state /* * Set modem status enables based on termios cflag */ - spin_lock_irq(&uport->lock); + uart_port_lock_irq(uport); if (termios->c_cflag & CRTSCTS) uport->status |= UPSTAT_CTS_ENABLE; else @@ -240,7 +240,7 @@ static void uart_change_line_settings(struct tty_struct *tty, struct uart_state else __uart_start(state); } - spin_unlock_irq(&uport->lock); + uart_port_unlock_irq(uport); } /* @@ -715,11 +715,11 @@ static void uart_send_xchar(struct tty_struct *tty, char ch) if (port->ops->send_xchar) port->ops->send_xchar(port, ch); else { - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); port->x_char = ch; if (ch) port->ops->start_tx(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } uart_port_deref(port); } @@ -1106,9 +1106,9 @@ static int uart_tiocmget(struct tty_struct *tty) if (!tty_io_error(tty)) { result = uport->mctrl; - spin_lock_irq(&uport->lock); + uart_port_lock_irq(uport); result |= uport->ops->get_mctrl(uport); - spin_unlock_irq(&uport->lock); + uart_port_unlock_irq(uport); } out: mutex_unlock(&port->mutex); @@ -1244,16 +1244,16 @@ static int uart_wait_modem_status(struct uart_state *state, unsigned long arg) uport = uart_port_ref(state); if (!uport) return -EIO; - spin_lock_irq(&uport->lock); + uart_port_lock_irq(uport); memcpy(&cprev, &uport->icount, sizeof(struct uart_icount)); uart_enable_ms(uport); - spin_unlock_irq(&uport->lock); + uart_port_unlock_irq(uport); add_wait_queue(&port->delta_msr_wait, &wait); for (;;) { - spin_lock_irq(&uport->lock); + uart_port_lock_irq(uport); memcpy(&cnow, &uport->icount, sizeof(struct uart_icount)); - spin_unlock_irq(&uport->lock); + uart_port_unlock_irq(uport); set_current_state(TASK_INTERRUPTIBLE); @@ -1298,9 +1298,9 @@ static int uart_get_icount(struct tty_struct *tty, uport = uart_port_ref(state); if (!uport) return -EIO; - spin_lock_irq(&uport->lock); + uart_port_lock_irq(uport); memcpy(&cnow, &uport->icount, sizeof(struct uart_icount)); - spin_unlock_irq(&uport->lock); + uart_port_unlock_irq(uport); uart_port_deref(uport); icount->cts = cnow.cts; @@ -1453,9 +1453,9 @@ static int uart_rs485_config(struct uart_port *port) uart_set_rs485_termination(port, rs485); uart_set_rs485_rx_during_tx(port, rs485); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); ret = port->rs485_config(port, NULL, rs485); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); if (ret) { memset(rs485, 0, sizeof(*rs485)); /* unset GPIOs */ @@ -1472,9 +1472,9 @@ static int uart_get_rs485_config(struct uart_port *port, unsigned long flags; struct serial_rs485 aux; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); aux = port->rs485; - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); if (copy_to_user(rs485, &aux, sizeof(aux))) return -EFAULT; @@ -1502,7 +1502,7 @@ static int uart_set_rs485_config(struct tty_struct *tty, struct uart_port *port, uart_set_rs485_termination(port, &rs485); uart_set_rs485_rx_during_tx(port, &rs485); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); ret = port->rs485_config(port, &tty->termios, &rs485); if (!ret) { port->rs485 = rs485; @@ -1511,7 +1511,7 @@ static int uart_set_rs485_config(struct tty_struct *tty, struct uart_port *port, if (!(rs485.flags & SER_RS485_ENABLED)) port->ops->set_mctrl(port, port->mctrl); } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); if (ret) { /* restore old GPIO settings */ gpiod_set_value_cansleep(port->rs485_term_gpio, @@ -1536,9 +1536,9 @@ static int uart_get_iso7816_config(struct uart_port *port, if (!port->iso7816_config) return -ENOTTY; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); aux = port->iso7816; - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); if (copy_to_user(iso7816, &aux, sizeof(aux))) return -EFAULT; @@ -1567,9 +1567,9 @@ static int uart_set_iso7816_config(struct uart_port *port, if (iso7816.reserved[i]) return -EINVAL; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); ret = port->iso7816_config(port, &iso7816); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); if (ret) return ret; @@ -1786,9 +1786,9 @@ static void uart_tty_port_shutdown(struct tty_port *port) if (WARN(!uport, "detached port still initialized!\n")) return; - spin_lock_irq(&uport->lock); + uart_port_lock_irq(uport); uport->ops->stop_rx(uport); - spin_unlock_irq(&uport->lock); + uart_port_unlock_irq(uport); serial_base_port_shutdown(uport); uart_port_shutdown(port); @@ -1803,11 +1803,11 @@ static void uart_tty_port_shutdown(struct tty_port *port) /* * Free the transmit buffer. */ - spin_lock_irq(&uport->lock); + uart_port_lock_irq(uport); uart_circ_clear(&state->xmit); buf = state->xmit.buf; state->xmit.buf = NULL; - spin_unlock_irq(&uport->lock); + uart_port_unlock_irq(uport); free_page((unsigned long)buf); @@ -1950,10 +1950,10 @@ static bool uart_carrier_raised(struct tty_port *port) */ if (WARN_ON(!uport)) return true; - spin_lock_irq(&uport->lock); + uart_port_lock_irq(uport); uart_enable_ms(uport); mctrl = uport->ops->get_mctrl(uport); - spin_unlock_irq(&uport->lock); + uart_port_unlock_irq(uport); uart_port_deref(uport); return mctrl & TIOCM_CAR; @@ -2070,9 +2070,9 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) pm_state = state->pm_state; if (pm_state != UART_PM_STATE_ON) uart_change_pm(state, UART_PM_STATE_ON); - spin_lock_irq(&uport->lock); + uart_port_lock_irq(uport); status = uport->ops->get_mctrl(uport); - spin_unlock_irq(&uport->lock); + uart_port_unlock_irq(uport); if (pm_state != UART_PM_STATE_ON) uart_change_pm(state, pm_state); @@ -2411,9 +2411,9 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) */ if (!console_suspend_enabled && uart_console(uport)) { if (uport->ops->start_rx) { - spin_lock_irq(&uport->lock); + uart_port_lock_irq(uport); uport->ops->stop_rx(uport); - spin_unlock_irq(&uport->lock); + uart_port_unlock_irq(uport); } goto unlock; } @@ -2428,7 +2428,7 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) tty_port_set_suspended(port, true); tty_port_set_initialized(port, false); - spin_lock_irq(&uport->lock); + uart_port_lock_irq(uport); ops->stop_tx(uport); if (!(uport->rs485.flags & SER_RS485_ENABLED)) ops->set_mctrl(uport, 0); @@ -2436,7 +2436,7 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) mctrl = uport->mctrl; uport->mctrl = 0; ops->stop_rx(uport); - spin_unlock_irq(&uport->lock); + uart_port_unlock_irq(uport); /* * Wait for the transmitter to empty. @@ -2508,9 +2508,9 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) uart_change_pm(state, UART_PM_STATE_ON); uport->ops->set_termios(uport, &termios, NULL); if (!console_suspend_enabled && uport->ops->start_rx) { - spin_lock_irq(&uport->lock); + uart_port_lock_irq(uport); uport->ops->start_rx(uport); - spin_unlock_irq(&uport->lock); + uart_port_unlock_irq(uport); } if (console_suspend_enabled) console_start(uport->cons); @@ -2521,10 +2521,10 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) int ret; uart_change_pm(state, UART_PM_STATE_ON); - spin_lock_irq(&uport->lock); + uart_port_lock_irq(uport); if (!(uport->rs485.flags & SER_RS485_ENABLED)) ops->set_mctrl(uport, 0); - spin_unlock_irq(&uport->lock); + uart_port_unlock_irq(uport); if (console_suspend_enabled || !uart_console(uport)) { /* Protected by port mutex for now */ struct tty_struct *tty = port->tty; @@ -2534,11 +2534,11 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) if (tty) uart_change_line_settings(tty, state, NULL); uart_rs485_config(uport); - spin_lock_irq(&uport->lock); + uart_port_lock_irq(uport); if (!(uport->rs485.flags & SER_RS485_ENABLED)) ops->set_mctrl(uport, uport->mctrl); ops->start_tx(uport); - spin_unlock_irq(&uport->lock); + uart_port_unlock_irq(uport); tty_port_set_initialized(port, true); } else { /* @@ -2650,11 +2650,11 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state, * keep the DTR setting that is set in uart_set_options() * We probably don't need a spinlock around this, but */ - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); port->mctrl &= TIOCM_DTR; if (!(port->rs485.flags & SER_RS485_ENABLED)) port->ops->set_mctrl(port, port->mctrl); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); uart_rs485_config(port); diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c index 7d5aaa8d422b..e51ca593ab86 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.c +++ b/drivers/tty/serial/serial_mctrl_gpio.c @@ -184,7 +184,7 @@ static irqreturn_t mctrl_gpio_irq_handle(int irq, void *context) mctrl_gpio_get(gpios, &mctrl); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); mctrl_diff = mctrl ^ gpios->mctrl_prev; gpios->mctrl_prev = mctrl; @@ -205,7 +205,7 @@ static irqreturn_t mctrl_gpio_irq_handle(int irq, void *context) wake_up_interruptible(&port->state->port.delta_msr_wait); } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return IRQ_HANDLED; } diff --git a/drivers/tty/serial/serial_port.c b/drivers/tty/serial/serial_port.c index 469ad26cde48..66fd117d8aea 100644 --- a/drivers/tty/serial/serial_port.c +++ b/drivers/tty/serial/serial_port.c @@ -38,14 +38,14 @@ static int serial_port_runtime_resume(struct device *dev) goto out; /* Flush any pending TX for the port */ - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); if (!port_dev->tx_enabled) goto unlock; if (__serial_port_busy(port)) port->ops->start_tx(port); unlock: - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); out: pm_runtime_mark_last_busy(dev); diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c index be08fb6f749c..eaa980722455 100644 --- a/drivers/tty/serial/serial_txx9.c +++ b/drivers/tty/serial/serial_txx9.c @@ -335,13 +335,13 @@ static irqreturn_t serial_txx9_interrupt(int irq, void *dev_id) unsigned int status; while (1) { - spin_lock(&up->lock); + uart_port_lock(up); status = sio_in(up, TXX9_SIDISR); if (!(sio_in(up, TXX9_SIDICR) & TXX9_SIDICR_TIE)) status &= ~TXX9_SIDISR_TDIS; if (!(status & (TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS | TXX9_SIDISR_TOUT))) { - spin_unlock(&up->lock); + uart_port_unlock(up); break; } @@ -353,7 +353,7 @@ static irqreturn_t serial_txx9_interrupt(int irq, void *dev_id) sio_mask(up, TXX9_SIDISR, TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS | TXX9_SIDISR_TOUT); - spin_unlock(&up->lock); + uart_port_unlock(up); if (pass_counter++ > PASS_LIMIT) break; @@ -367,9 +367,9 @@ static unsigned int serial_txx9_tx_empty(struct uart_port *up) unsigned long flags; unsigned int ret; - spin_lock_irqsave(&up->lock, flags); + uart_port_lock_irqsave(up, &flags); ret = (sio_in(up, TXX9_SICISR) & TXX9_SICISR_TXALS) ? TIOCSER_TEMT : 0; - spin_unlock_irqrestore(&up->lock, flags); + uart_port_unlock_irqrestore(up, flags); return ret; } @@ -399,12 +399,12 @@ static void serial_txx9_break_ctl(struct uart_port *up, int break_state) { unsigned long flags; - spin_lock_irqsave(&up->lock, flags); + uart_port_lock_irqsave(up, &flags); if (break_state == -1) sio_set(up, TXX9_SIFLCR, TXX9_SIFLCR_TBRK); else sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_TBRK); - spin_unlock_irqrestore(&up->lock, flags); + uart_port_unlock_irqrestore(up, flags); } #if defined(CONFIG_SERIAL_TXX9_CONSOLE) || defined(CONFIG_CONSOLE_POLL) @@ -517,9 +517,9 @@ static int serial_txx9_startup(struct uart_port *up) /* * Now, initialize the UART */ - spin_lock_irqsave(&up->lock, flags); + uart_port_lock_irqsave(up, &flags); serial_txx9_set_mctrl(up, up->mctrl); - spin_unlock_irqrestore(&up->lock, flags); + uart_port_unlock_irqrestore(up, flags); /* Enable RX/TX */ sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_RSDE | TXX9_SIFLCR_TSDE); @@ -541,9 +541,9 @@ static void serial_txx9_shutdown(struct uart_port *up) */ sio_out(up, TXX9_SIDICR, 0); /* disable all intrs */ - spin_lock_irqsave(&up->lock, flags); + uart_port_lock_irqsave(up, &flags); serial_txx9_set_mctrl(up, up->mctrl); - spin_unlock_irqrestore(&up->lock, flags); + uart_port_unlock_irqrestore(up, flags); /* * Disable break condition @@ -625,7 +625,7 @@ serial_txx9_set_termios(struct uart_port *up, struct ktermios *termios, * Ok, we're now changing the port state. Do it with * interrupts disabled. */ - spin_lock_irqsave(&up->lock, flags); + uart_port_lock_irqsave(up, &flags); /* * Update the per-port timeout. @@ -676,7 +676,7 @@ serial_txx9_set_termios(struct uart_port *up, struct ktermios *termios, sio_out(up, TXX9_SIFCR, fcr); serial_txx9_set_mctrl(up, up->mctrl); - spin_unlock_irqrestore(&up->lock, flags); + uart_port_unlock_irqrestore(up, flags); } static void diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index f793624fd501..2559c97812fa 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1205,7 +1205,7 @@ static void sci_dma_tx_complete(void *arg) dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); uart_xmit_advance(port, s->tx_dma_len); @@ -1229,7 +1229,7 @@ static void sci_dma_tx_complete(void *arg) } } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /* Locking: called with port lock held */ @@ -1325,7 +1325,7 @@ static void sci_dma_rx_complete(void *arg) dev_dbg(port->dev, "%s(%d) active cookie %d\n", __func__, port->line, s->active_rx); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); active = sci_dma_rx_find_active(s); if (active >= 0) @@ -1352,20 +1352,20 @@ static void sci_dma_rx_complete(void *arg) dma_async_issue_pending(chan); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); dev_dbg(port->dev, "%s: cookie %d #%d, new active cookie %d\n", __func__, s->cookie_rx[active], active, s->active_rx); return; fail: - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); /* Switch to PIO */ - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); dmaengine_terminate_async(chan); sci_dma_rx_chan_invalidate(s); sci_dma_rx_reenable_irq(s); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void sci_dma_tx_release(struct sci_port *s) @@ -1414,13 +1414,13 @@ static int sci_dma_rx_submit(struct sci_port *s, bool port_lock_held) fail: /* Switch to PIO */ if (!port_lock_held) - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); if (i) dmaengine_terminate_async(chan); sci_dma_rx_chan_invalidate(s); sci_start_rx(port); if (!port_lock_held) - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return -EAGAIN; } @@ -1442,14 +1442,14 @@ static void sci_dma_tx_work_fn(struct work_struct *work) * transmit till the end, and then the rest. Take the port lock to get a * consistent xmit buffer state. */ - spin_lock_irq(&port->lock); + uart_port_lock_irq(port); head = xmit->head; tail = xmit->tail; buf = s->tx_dma_addr + tail; s->tx_dma_len = CIRC_CNT_TO_END(head, tail, UART_XMIT_SIZE); if (!s->tx_dma_len) { /* Transmit buffer has been flushed */ - spin_unlock_irq(&port->lock); + uart_port_unlock_irq(port); return; } @@ -1457,7 +1457,7 @@ static void sci_dma_tx_work_fn(struct work_struct *work) DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { - spin_unlock_irq(&port->lock); + uart_port_unlock_irq(port); dev_warn(port->dev, "Failed preparing Tx DMA descriptor\n"); goto switch_to_pio; } @@ -1469,12 +1469,12 @@ static void sci_dma_tx_work_fn(struct work_struct *work) desc->callback_param = s; s->cookie_tx = dmaengine_submit(desc); if (dma_submit_error(s->cookie_tx)) { - spin_unlock_irq(&port->lock); + uart_port_unlock_irq(port); dev_warn(port->dev, "Failed submitting Tx DMA descriptor\n"); goto switch_to_pio; } - spin_unlock_irq(&port->lock); + uart_port_unlock_irq(port); dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n", __func__, xmit->buf, tail, head, s->cookie_tx); @@ -1482,10 +1482,10 @@ static void sci_dma_tx_work_fn(struct work_struct *work) return; switch_to_pio: - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); s->chan_tx = NULL; sci_start_tx(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return; } @@ -1502,17 +1502,17 @@ static enum hrtimer_restart sci_dma_rx_timer_fn(struct hrtimer *t) dev_dbg(port->dev, "DMA Rx timed out\n"); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); active = sci_dma_rx_find_active(s); if (active < 0) { - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return HRTIMER_NORESTART; } status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state); if (status == DMA_COMPLETE) { - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); dev_dbg(port->dev, "Cookie %d #%d has already completed\n", s->active_rx, active); @@ -1530,7 +1530,7 @@ static enum hrtimer_restart sci_dma_rx_timer_fn(struct hrtimer *t) */ status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state); if (status == DMA_COMPLETE) { - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); dev_dbg(port->dev, "Transaction complete after DMA engine was stopped"); return HRTIMER_NORESTART; } @@ -1551,7 +1551,7 @@ static enum hrtimer_restart sci_dma_rx_timer_fn(struct hrtimer *t) sci_dma_rx_reenable_irq(s); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return HRTIMER_NORESTART; } @@ -1775,9 +1775,9 @@ static irqreturn_t sci_tx_interrupt(int irq, void *ptr) struct uart_port *port = ptr; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); sci_transmit_chars(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return IRQ_HANDLED; } @@ -1791,11 +1791,11 @@ static irqreturn_t sci_tx_end_interrupt(int irq, void *ptr) if (port->type != PORT_SCI) return sci_tx_interrupt(irq, ptr); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); ctrl = serial_port_in(port, SCSCR); ctrl &= ~(SCSCR_TE | SCSCR_TEIE); serial_port_out(port, SCSCR, ctrl); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return IRQ_HANDLED; } @@ -2192,7 +2192,7 @@ static void sci_break_ctl(struct uart_port *port, int break_state) return; } - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); scsptr = serial_port_in(port, SCSPTR); scscr = serial_port_in(port, SCSCR); @@ -2206,7 +2206,7 @@ static void sci_break_ctl(struct uart_port *port, int break_state) serial_port_out(port, SCSPTR, scsptr); serial_port_out(port, SCSCR, scscr); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static int sci_startup(struct uart_port *port) @@ -2238,7 +2238,7 @@ static void sci_shutdown(struct uart_port *port) s->autorts = false; mctrl_gpio_disable_ms(to_sci_port(port)->gpios); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); sci_stop_rx(port); sci_stop_tx(port); /* @@ -2248,7 +2248,7 @@ static void sci_shutdown(struct uart_port *port) scr = serial_port_in(port, SCSCR); serial_port_out(port, SCSCR, scr & (SCSCR_CKE1 | SCSCR_CKE0 | s->hscif_tot)); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); #ifdef CONFIG_SERIAL_SH_SCI_DMA if (s->chan_rx_saved) { @@ -2550,7 +2550,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, serial_port_out(port, SCCKS, sccks); } - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); sci_reset(port); @@ -2672,7 +2672,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, if ((termios->c_cflag & CREAD) != 0) sci_start_rx(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); sci_port_disable(s); @@ -3057,9 +3057,9 @@ static void serial_console_write(struct console *co, const char *s, if (port->sysrq) locked = 0; else if (oops_in_progress) - locked = spin_trylock_irqsave(&port->lock, flags); + locked = uart_port_trylock_irqsave(port, &flags); else - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* first save SCSCR then disable interrupts, keep clock source */ ctrl = serial_port_in(port, SCSCR); @@ -3079,7 +3079,7 @@ static void serial_console_write(struct console *co, const char *s, serial_port_out(port, SCSCR, ctrl); if (locked) - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static int serial_console_setup(struct console *co, char *options) diff --git a/drivers/tty/serial/sifive.c b/drivers/tty/serial/sifive.c index d195c5de52e7..b296e57a9dee 100644 --- a/drivers/tty/serial/sifive.c +++ b/drivers/tty/serial/sifive.c @@ -521,11 +521,11 @@ static irqreturn_t sifive_serial_irq(int irq, void *dev_id) struct sifive_serial_port *ssp = dev_id; u32 ip; - spin_lock(&ssp->port.lock); + uart_port_lock(&ssp->port); ip = __ssp_readl(ssp, SIFIVE_SERIAL_IP_OFFS); if (!ip) { - spin_unlock(&ssp->port.lock); + uart_port_unlock(&ssp->port); return IRQ_NONE; } @@ -534,7 +534,7 @@ static irqreturn_t sifive_serial_irq(int irq, void *dev_id) if (ip & SIFIVE_SERIAL_IP_TXWM_MASK) __ssp_transmit_chars(ssp); - spin_unlock(&ssp->port.lock); + uart_port_unlock(&ssp->port); return IRQ_HANDLED; } @@ -653,7 +653,7 @@ static void sifive_serial_set_termios(struct uart_port *port, ssp->port.uartclk / 16); __ssp_update_baud_rate(ssp, rate); - spin_lock_irqsave(&ssp->port.lock, flags); + uart_port_lock_irqsave(&ssp->port, &flags); /* Update the per-port timeout */ uart_update_timeout(port, termios->c_cflag, rate); @@ -670,7 +670,7 @@ static void sifive_serial_set_termios(struct uart_port *port, if (v != old_v) __ssp_writel(v, SIFIVE_SERIAL_RXCTRL_OFFS, ssp); - spin_unlock_irqrestore(&ssp->port.lock, flags); + uart_port_unlock_irqrestore(&ssp->port, flags); } static void sifive_serial_release_port(struct uart_port *port) @@ -795,9 +795,9 @@ static void sifive_serial_console_write(struct console *co, const char *s, if (ssp->port.sysrq) locked = 0; else if (oops_in_progress) - locked = spin_trylock(&ssp->port.lock); + locked = uart_port_trylock(&ssp->port); else - spin_lock(&ssp->port.lock); + uart_port_lock(&ssp->port); ier = __ssp_readl(ssp, SIFIVE_SERIAL_IE_OFFS); __ssp_writel(0, SIFIVE_SERIAL_IE_OFFS, ssp); @@ -807,7 +807,7 @@ static void sifive_serial_console_write(struct console *co, const char *s, __ssp_writel(ier, SIFIVE_SERIAL_IE_OFFS, ssp); if (locked) - spin_unlock(&ssp->port.lock); + uart_port_unlock(&ssp->port); local_irq_restore(flags); } diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index f328fa57231f..f257525f9299 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -247,7 +247,7 @@ static void sprd_complete_tx_dma(void *data) struct circ_buf *xmit = &port->state->xmit; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); dma_unmap_single(port->dev, sp->tx_dma.phys_addr, sp->tx_dma.trans_len, DMA_TO_DEVICE); @@ -260,7 +260,7 @@ static void sprd_complete_tx_dma(void *data) sprd_tx_dma_config(port)) sp->tx_dma.trans_len = 0; - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static int sprd_uart_dma_submit(struct uart_port *port, @@ -429,13 +429,13 @@ static void sprd_complete_rx_dma(void *data) enum dma_status status; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); status = dmaengine_tx_status(sp->rx_dma.chn, sp->rx_dma.cookie, &state); if (status != DMA_COMPLETE) { sprd_stop_rx(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return; } @@ -449,7 +449,7 @@ static void sprd_complete_rx_dma(void *data) if (sprd_start_dma_rx(port)) sprd_stop_rx(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static int sprd_start_dma_rx(struct uart_port *port) @@ -638,12 +638,12 @@ static irqreturn_t sprd_handle_irq(int irq, void *dev_id) struct uart_port *port = dev_id; unsigned int ims; - spin_lock(&port->lock); + uart_port_lock(port); ims = serial_in(port, SPRD_IMSR); if (!ims) { - spin_unlock(&port->lock); + uart_port_unlock(port); return IRQ_NONE; } @@ -660,7 +660,7 @@ static irqreturn_t sprd_handle_irq(int irq, void *dev_id) if (ims & SPRD_IMSR_TX_FIFO_EMPTY) sprd_tx(port); - spin_unlock(&port->lock); + uart_port_unlock(port); return IRQ_HANDLED; } @@ -727,13 +727,13 @@ static int sprd_startup(struct uart_port *port) serial_out(port, SPRD_CTL1, fc); /* enable interrupt */ - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); ien = serial_in(port, SPRD_IEN); ien |= SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT; if (!sp->rx_dma.enable) ien |= SPRD_IEN_RX_FULL; serial_out(port, SPRD_IEN, ien); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return 0; } @@ -793,7 +793,7 @@ static void sprd_set_termios(struct uart_port *port, struct ktermios *termios, lcr |= SPRD_LCR_EVEN_PAR; } - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* update the per-port timeout */ uart_update_timeout(port, termios->c_cflag, baud); @@ -837,7 +837,7 @@ static void sprd_set_termios(struct uart_port *port, struct ktermios *termios, fc |= RX_TOUT_THLD_DEF | RX_HFC_THLD_DEF; serial_out(port, SPRD_CTL1, fc); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); /* Don't rewrite B0 */ if (tty_termios_baud_rate(termios)) @@ -974,9 +974,9 @@ static void sprd_console_write(struct console *co, const char *s, if (port->sysrq) locked = 0; else if (oops_in_progress) - locked = spin_trylock_irqsave(&port->lock, flags); + locked = uart_port_trylock_irqsave(port, &flags); else - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); uart_console_write(port, s, count, sprd_console_putchar); @@ -984,7 +984,7 @@ static void sprd_console_write(struct console *co, const char *s, wait_for_xmitr(port); if (locked) - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static int sprd_console_setup(struct console *co, char *options) diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c index 92b9f6894006..a821f5d76a26 100644 --- a/drivers/tty/serial/st-asc.c +++ b/drivers/tty/serial/st-asc.c @@ -319,7 +319,7 @@ static irqreturn_t asc_interrupt(int irq, void *ptr) struct uart_port *port = ptr; u32 status; - spin_lock(&port->lock); + uart_port_lock(port); status = asc_in(port, ASC_STA); @@ -334,7 +334,7 @@ static irqreturn_t asc_interrupt(int irq, void *ptr) asc_transmit_chars(port); } - spin_unlock(&port->lock); + uart_port_unlock(port); return IRQ_HANDLED; } @@ -452,10 +452,10 @@ static void asc_pm(struct uart_port *port, unsigned int state, * we can come to turning it off. Note this is not called with * the port spinlock held. */ - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); ctl = asc_in(port, ASC_CTL) & ~ASC_CTL_RUN; asc_out(port, ASC_CTL, ctl); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); clk_disable_unprepare(ascport->clk); break; } @@ -480,7 +480,7 @@ static void asc_set_termios(struct uart_port *port, struct ktermios *termios, baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); cflag = termios->c_cflag; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* read control register */ ctrl_val = asc_in(port, ASC_CTL); @@ -594,7 +594,7 @@ static void asc_set_termios(struct uart_port *port, struct ktermios *termios, /* write final value and enable port */ asc_out(port, ASC_CTL, (ctrl_val | ASC_CTL_RUN)); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *asc_type(struct uart_port *port) @@ -849,9 +849,9 @@ static void asc_console_write(struct console *co, const char *s, unsigned count) if (port->sysrq) locked = 0; /* asc_interrupt has already claimed the lock */ else if (oops_in_progress) - locked = spin_trylock_irqsave(&port->lock, flags); + locked = uart_port_trylock_irqsave(port, &flags); else - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* * Disable interrupts so we don't get the IRQ line bouncing @@ -869,7 +869,7 @@ static void asc_console_write(struct console *co, const char *s, unsigned count) asc_out(port, ASC_INTEN, intenable); if (locked) - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static int asc_console_setup(struct console *co, char *options) diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 9ef90bb30a47..b963f9ccb070 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -535,7 +535,7 @@ static void stm32_usart_rx_dma_complete(void *arg) unsigned int size; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); size = stm32_usart_receive_chars(port, false); uart_unlock_and_check_sysrq_irqrestore(port, flags); if (size) @@ -641,9 +641,9 @@ static void stm32_usart_tx_dma_complete(void *arg) stm32_usart_tx_dma_terminate(stm32port); /* Let's see if we have pending data to send */ - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); stm32_usart_transmit_chars(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void stm32_usart_tx_interrupt_enable(struct uart_port *port) @@ -892,7 +892,7 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) if (!stm32_port->throttled) { if (((sr & USART_SR_RXNE) && !stm32_usart_rx_dma_started(stm32_port)) || ((sr & USART_SR_ERR_MASK) && stm32_usart_rx_dma_started(stm32_port))) { - spin_lock(&port->lock); + uart_port_lock(port); size = stm32_usart_receive_chars(port, false); uart_unlock_and_check_sysrq(port); if (size) @@ -902,15 +902,15 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) } if ((sr & USART_SR_TXE) && !(stm32_port->tx_ch)) { - spin_lock(&port->lock); + uart_port_lock(port); stm32_usart_transmit_chars(port); - spin_unlock(&port->lock); + uart_port_unlock(port); ret = IRQ_HANDLED; } /* Receiver timeout irq for DMA RX */ if (stm32_usart_rx_dma_started(stm32_port) && !stm32_port->throttled) { - spin_lock(&port->lock); + uart_port_lock(port); size = stm32_usart_receive_chars(port, false); uart_unlock_and_check_sysrq(port); if (size) @@ -999,7 +999,7 @@ static void stm32_usart_throttle(struct uart_port *port) const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* * Pause DMA transfer, so the RX data gets queued into the FIFO. @@ -1012,7 +1012,7 @@ static void stm32_usart_throttle(struct uart_port *port) stm32_usart_clr_bits(port, ofs->cr3, stm32_port->cr3_irq); stm32_port->throttled = true; - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /* Unthrottle the remote, the input buffer can now accept data. */ @@ -1022,7 +1022,7 @@ static void stm32_usart_unthrottle(struct uart_port *port) const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); stm32_usart_set_bits(port, ofs->cr1, stm32_port->cr1_irq); if (stm32_port->cr3_irq) stm32_usart_set_bits(port, ofs->cr3, stm32_port->cr3_irq); @@ -1036,7 +1036,7 @@ static void stm32_usart_unthrottle(struct uart_port *port) if (stm32_port->rx_ch) stm32_usart_rx_dma_start_or_resume(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /* Receive stop */ @@ -1165,7 +1165,7 @@ static void stm32_usart_set_termios(struct uart_port *port, baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 8); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr, isr, @@ -1356,7 +1356,7 @@ static void stm32_usart_set_termios(struct uart_port *port, writel_relaxed(cr1, port->membase + ofs->cr1); stm32_usart_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); /* Handle modem control interrupts */ if (UART_ENABLE_MS(port, termios->c_cflag)) @@ -1406,9 +1406,9 @@ static void stm32_usart_pm(struct uart_port *port, unsigned int state, pm_runtime_get_sync(port->dev); break; case UART_PM_STATE_OFF: - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); stm32_usart_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); pm_runtime_put_sync(port->dev); break; } @@ -1891,9 +1891,9 @@ static void stm32_usart_console_write(struct console *co, const char *s, int locked = 1; if (oops_in_progress) - locked = spin_trylock_irqsave(&port->lock, flags); + locked = uart_port_trylock_irqsave(port, &flags); else - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Save and disable interrupts, enable the transmitter */ old_cr1 = readl_relaxed(port->membase + ofs->cr1); @@ -1907,7 +1907,7 @@ static void stm32_usart_console_write(struct console *co, const char *s, writel_relaxed(old_cr1, port->membase + ofs->cr1); if (locked) - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static int stm32_usart_console_setup(struct console *co, char *options) @@ -2042,7 +2042,7 @@ static int __maybe_unused stm32_usart_serial_en_wakeup(struct uart_port *port, * low-power mode. */ if (stm32_port->rx_ch) { - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Poll data from DMA RX buffer if any */ if (!stm32_usart_rx_dma_pause(stm32_port)) size += stm32_usart_receive_chars(port, true); diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index c671d674bce4..5bfc0040f17b 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -217,10 +217,10 @@ static irqreturn_t sunhv_interrupt(int irq, void *dev_id) struct tty_port *tport; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); tport = receive_chars(port); transmit_chars(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); if (tport) tty_flip_buffer_push(tport); @@ -271,7 +271,7 @@ static void sunhv_send_xchar(struct uart_port *port, char ch) if (ch == __DISABLED_CHAR) return; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); while (limit-- > 0) { long status = sun4v_con_putchar(ch); @@ -280,7 +280,7 @@ static void sunhv_send_xchar(struct uart_port *port, char ch) udelay(1); } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /* port->lock held by caller. */ @@ -295,7 +295,7 @@ static void sunhv_break_ctl(struct uart_port *port, int break_state) unsigned long flags; int limit = 10000; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); while (limit-- > 0) { long status = sun4v_con_putchar(CON_BREAK); @@ -304,7 +304,7 @@ static void sunhv_break_ctl(struct uart_port *port, int break_state) udelay(1); } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } } @@ -328,7 +328,7 @@ static void sunhv_set_termios(struct uart_port *port, struct ktermios *termios, unsigned int iflag, cflag; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); iflag = termios->c_iflag; cflag = termios->c_cflag; @@ -343,7 +343,7 @@ static void sunhv_set_termios(struct uart_port *port, struct ktermios *termios, uart_update_timeout(port, cflag, (port->uartclk / (16 * quot))); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *sunhv_type(struct uart_port *port) @@ -437,9 +437,9 @@ static void sunhv_console_write_paged(struct console *con, const char *s, unsign int locked = 1; if (port->sysrq || oops_in_progress) - locked = spin_trylock_irqsave(&port->lock, flags); + locked = uart_port_trylock_irqsave(port, &flags); else - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); while (n > 0) { unsigned long ra = __pa(con_write_page); @@ -470,7 +470,7 @@ static void sunhv_console_write_paged(struct console *con, const char *s, unsign } if (locked) - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static inline void sunhv_console_putchar(struct uart_port *port, char c) @@ -492,9 +492,9 @@ static void sunhv_console_write_bychar(struct console *con, const char *s, unsig int i, locked = 1; if (port->sysrq || oops_in_progress) - locked = spin_trylock_irqsave(&port->lock, flags); + locked = uart_port_trylock_irqsave(port, &flags); else - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); for (i = 0; i < n; i++) { if (*s == '\n') @@ -503,7 +503,7 @@ static void sunhv_console_write_bychar(struct console *con, const char *s, unsig } if (locked) - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static struct console sunhv_console = { diff --git a/drivers/tty/serial/sunplus-uart.c b/drivers/tty/serial/sunplus-uart.c index 3aacd5eb414c..4251f4e1ba99 100644 --- a/drivers/tty/serial/sunplus-uart.c +++ b/drivers/tty/serial/sunplus-uart.c @@ -184,7 +184,7 @@ static void sunplus_break_ctl(struct uart_port *port, int ctl) unsigned long flags; unsigned int lcr; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); lcr = readl(port->membase + SUP_UART_LCR); @@ -195,7 +195,7 @@ static void sunplus_break_ctl(struct uart_port *port, int ctl) writel(lcr, port->membase + SUP_UART_LCR); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void transmit_chars(struct uart_port *port) @@ -277,7 +277,7 @@ static irqreturn_t sunplus_uart_irq(int irq, void *args) struct uart_port *port = args; unsigned int isc; - spin_lock(&port->lock); + uart_port_lock(port); isc = readl(port->membase + SUP_UART_ISC); @@ -287,7 +287,7 @@ static irqreturn_t sunplus_uart_irq(int irq, void *args) if (isc & SUP_UART_ISC_TX) transmit_chars(port); - spin_unlock(&port->lock); + uart_port_unlock(port); return IRQ_HANDLED; } @@ -302,14 +302,14 @@ static int sunplus_startup(struct uart_port *port) if (ret) return ret; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* isc define Bit[7:4] int setting, Bit[3:0] int status * isc register will clean Bit[3:0] int status after read * only do a write to Bit[7:4] int setting */ isc |= SUP_UART_ISC_RXM; writel(isc, port->membase + SUP_UART_ISC); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return 0; } @@ -318,13 +318,13 @@ static void sunplus_shutdown(struct uart_port *port) { unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* isc define Bit[7:4] int setting, Bit[3:0] int status * isc register will clean Bit[3:0] int status after read * only do a write to Bit[7:4] int setting */ writel(0, port->membase + SUP_UART_ISC); /* disable all interrupt */ - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); free_irq(port->irq, port); } @@ -372,7 +372,7 @@ static void sunplus_set_termios(struct uart_port *port, lcr |= UART_LCR_EPAR; } - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); uart_update_timeout(port, termios->c_cflag, baud); @@ -407,7 +407,7 @@ static void sunplus_set_termios(struct uart_port *port, writel(div_l, port->membase + SUP_UART_DIV_L); writel(lcr, port->membase + SUP_UART_LCR); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void sunplus_set_ldisc(struct uart_port *port, struct ktermios *termios) @@ -517,15 +517,15 @@ static void sunplus_console_write(struct console *co, if (sunplus_console_ports[co->index]->port.sysrq) locked = 0; else if (oops_in_progress) - locked = spin_trylock(&sunplus_console_ports[co->index]->port.lock); + locked = uart_port_trylock(&sunplus_console_ports[co->index]->port); else - spin_lock(&sunplus_console_ports[co->index]->port.lock); + uart_port_lock(&sunplus_console_ports[co->index]->port); uart_console_write(&sunplus_console_ports[co->index]->port, s, count, sunplus_uart_console_putchar); if (locked) - spin_unlock(&sunplus_console_ports[co->index]->port.lock); + uart_port_unlock(&sunplus_console_ports[co->index]->port); local_irq_restore(flags); } diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c index 40eeaf835bba..6aa51a6f8063 100644 --- a/drivers/tty/serial/sunsab.c +++ b/drivers/tty/serial/sunsab.c @@ -310,7 +310,7 @@ static irqreturn_t sunsab_interrupt(int irq, void *dev_id) unsigned long flags; unsigned char gis; - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); status.stat = 0; gis = readb(&up->regs->r.gis) >> up->gis_shift; @@ -331,7 +331,7 @@ static irqreturn_t sunsab_interrupt(int irq, void *dev_id) transmit_chars(up, &status); } - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); if (port) tty_flip_buffer_push(port); @@ -473,12 +473,12 @@ static void sunsab_send_xchar(struct uart_port *port, char ch) if (ch == __DISABLED_CHAR) return; - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); sunsab_tec_wait(up); writeb(ch, &up->regs->w.tic); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } /* port->lock held by caller. */ @@ -499,7 +499,7 @@ static void sunsab_break_ctl(struct uart_port *port, int break_state) unsigned long flags; unsigned char val; - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); val = up->cached_dafo; if (break_state) @@ -512,7 +512,7 @@ static void sunsab_break_ctl(struct uart_port *port, int break_state) if (test_bit(SAB82532_XPR, &up->irqflags)) sunsab_tx_idle(up); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } /* port->lock is not held. */ @@ -527,7 +527,7 @@ static int sunsab_startup(struct uart_port *port) if (err) return err; - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); /* * Wait for any commands or immediate characters @@ -582,7 +582,7 @@ static int sunsab_startup(struct uart_port *port) set_bit(SAB82532_ALLS, &up->irqflags); set_bit(SAB82532_XPR, &up->irqflags); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); return 0; } @@ -594,7 +594,7 @@ static void sunsab_shutdown(struct uart_port *port) container_of(port, struct uart_sunsab_port, port); unsigned long flags; - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); /* Disable Interrupts */ up->interrupt_mask0 = 0xff; @@ -628,7 +628,7 @@ static void sunsab_shutdown(struct uart_port *port) writeb(tmp, &up->regs->rw.ccr0); #endif - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); free_irq(up->port.irq, up); } @@ -779,9 +779,9 @@ static void sunsab_set_termios(struct uart_port *port, struct ktermios *termios, unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000); unsigned int quot = uart_get_divisor(port, baud); - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); sunsab_convert_to_sab(up, termios->c_cflag, termios->c_iflag, baud, quot); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } static const char *sunsab_type(struct uart_port *port) @@ -857,15 +857,15 @@ static void sunsab_console_write(struct console *con, const char *s, unsigned n) int locked = 1; if (up->port.sysrq || oops_in_progress) - locked = spin_trylock_irqsave(&up->port.lock, flags); + locked = uart_port_trylock_irqsave(&up->port, &flags); else - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); uart_console_write(&up->port, s, n, sunsab_console_putchar); sunsab_tec_wait(up); if (locked) - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } static int sunsab_console_setup(struct console *con, char *options) @@ -914,7 +914,7 @@ static int sunsab_console_setup(struct console *con, char *options) */ sunsab_startup(&up->port); - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); /* * Finally, enable interrupts @@ -932,7 +932,7 @@ static int sunsab_console_setup(struct console *con, char *options) sunsab_convert_to_sab(up, con->cflag, 0, baud, quot); sunsab_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); return 0; } diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index 58a4342ad0f9..1e051cc2591c 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -212,9 +212,9 @@ static void enable_rsa(struct uart_sunsu_port *up) { if (up->port.type == PORT_RSA) { if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) { - spin_lock_irq(&up->port.lock); + uart_port_lock_irq(&up->port); __enable_rsa(up); - spin_unlock_irq(&up->port.lock); + uart_port_unlock_irq(&up->port); } if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) serial_outp(up, UART_RSA_FRR, 0); @@ -234,7 +234,7 @@ static void disable_rsa(struct uart_sunsu_port *up) if (up->port.type == PORT_RSA && up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) { - spin_lock_irq(&up->port.lock); + uart_port_lock_irq(&up->port); mode = serial_inp(up, UART_RSA_MSR); result = !(mode & UART_RSA_MSR_FIFO); @@ -247,7 +247,7 @@ static void disable_rsa(struct uart_sunsu_port *up) if (result) up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16; - spin_unlock_irq(&up->port.lock); + uart_port_unlock_irq(&up->port); } } #endif /* CONFIG_SERIAL_8250_RSA */ @@ -311,10 +311,10 @@ static void sunsu_enable_ms(struct uart_port *port) container_of(port, struct uart_sunsu_port, port); unsigned long flags; - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); up->ier |= UART_IER_MSI; serial_out(up, UART_IER, up->ier); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } static void @@ -456,7 +456,7 @@ static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id) unsigned long flags; unsigned char status; - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); do { status = serial_inp(up, UART_LSR); @@ -470,7 +470,7 @@ static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id) } while (!(serial_in(up, UART_IIR) & UART_IIR_NO_INT)); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); return IRQ_HANDLED; } @@ -545,9 +545,9 @@ static unsigned int sunsu_tx_empty(struct uart_port *port) unsigned long flags; unsigned int ret; - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); return ret; } @@ -599,13 +599,13 @@ static void sunsu_break_ctl(struct uart_port *port, int break_state) container_of(port, struct uart_sunsu_port, port); unsigned long flags; - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); if (break_state == -1) up->lcr |= UART_LCR_SBC; else up->lcr &= ~UART_LCR_SBC; serial_out(up, UART_LCR, up->lcr); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } static int sunsu_startup(struct uart_port *port) @@ -683,12 +683,12 @@ static int sunsu_startup(struct uart_port *port) */ serial_outp(up, UART_LCR, UART_LCR_WLEN8); - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); up->port.mctrl |= TIOCM_OUT2; sunsu_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); /* * Finally, enable interrupts. Note: Modem status interrupts @@ -731,7 +731,7 @@ static void sunsu_shutdown(struct uart_port *port) up->ier = 0; serial_outp(up, UART_IER, 0); - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); if (up->port.flags & UPF_FOURPORT) { /* reset interrupts on the AST Fourport board */ inb((up->port.iobase & 0xfe0) | 0x1f); @@ -740,7 +740,7 @@ static void sunsu_shutdown(struct uart_port *port) up->port.mctrl &= ~TIOCM_OUT2; sunsu_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); /* * Disable break condition and FIFOs @@ -826,7 +826,7 @@ sunsu_change_speed(struct uart_port *port, unsigned int cflag, * Ok, we're now changing the port state. Do it with * interrupts disabled. */ - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); /* * Update the per-port timeout. @@ -891,7 +891,7 @@ sunsu_change_speed(struct uart_port *port, unsigned int cflag, up->cflag = cflag; - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } static void @@ -1038,7 +1038,7 @@ static void sunsu_autoconfig(struct uart_sunsu_port *up) up->type_probed = PORT_UNKNOWN; up->port.iotype = UPIO_MEM; - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); if (!(up->port.flags & UPF_BUGGY_UART)) { /* @@ -1173,7 +1173,7 @@ static void sunsu_autoconfig(struct uart_sunsu_port *up) serial_outp(up, UART_IER, 0); out: - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } static struct uart_driver sunsu_reg = { @@ -1298,9 +1298,9 @@ static void sunsu_console_write(struct console *co, const char *s, int locked = 1; if (up->port.sysrq || oops_in_progress) - locked = spin_trylock_irqsave(&up->port.lock, flags); + locked = uart_port_trylock_irqsave(&up->port, &flags); else - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); /* * First save the UER then disable the interrupts @@ -1318,7 +1318,7 @@ static void sunsu_console_write(struct console *co, const char *s, serial_out(up, UART_IER, ier); if (locked) - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } /* diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c index c8c71c56264c..d3b5e864b727 100644 --- a/drivers/tty/serial/sunzilog.c +++ b/drivers/tty/serial/sunzilog.c @@ -531,7 +531,7 @@ static irqreturn_t sunzilog_interrupt(int irq, void *dev_id) struct tty_port *port; unsigned char r3; - spin_lock(&up->port.lock); + uart_port_lock(&up->port); r3 = read_zsreg(channel, R3); /* Channel A */ @@ -548,7 +548,7 @@ static irqreturn_t sunzilog_interrupt(int irq, void *dev_id) if (r3 & CHATxIP) sunzilog_transmit_chars(up, channel); } - spin_unlock(&up->port.lock); + uart_port_unlock(&up->port); if (port) tty_flip_buffer_push(port); @@ -557,7 +557,7 @@ static irqreturn_t sunzilog_interrupt(int irq, void *dev_id) up = up->next; channel = ZILOG_CHANNEL_FROM_PORT(&up->port); - spin_lock(&up->port.lock); + uart_port_lock(&up->port); port = NULL; if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { writeb(RES_H_IUS, &channel->control); @@ -571,7 +571,7 @@ static irqreturn_t sunzilog_interrupt(int irq, void *dev_id) if (r3 & CHBTxIP) sunzilog_transmit_chars(up, channel); } - spin_unlock(&up->port.lock); + uart_port_unlock(&up->port); if (port) tty_flip_buffer_push(port); @@ -604,11 +604,11 @@ static unsigned int sunzilog_tx_empty(struct uart_port *port) unsigned char status; unsigned int ret; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); status = sunzilog_read_channel_status(port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); if (status & Tx_BUF_EMP) ret = TIOCSER_TEMT; @@ -764,7 +764,7 @@ static void sunzilog_break_ctl(struct uart_port *port, int break_state) else clear_bits |= SND_BRK; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); new_reg = (up->curregs[R5] | set_bits) & ~clear_bits; if (new_reg != up->curregs[R5]) { @@ -774,7 +774,7 @@ static void sunzilog_break_ctl(struct uart_port *port, int break_state) write_zsreg(channel, R5, up->curregs[R5]); } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static void __sunzilog_startup(struct uart_sunzilog_port *up) @@ -800,9 +800,9 @@ static int sunzilog_startup(struct uart_port *port) if (ZS_IS_CONS(up)) return 0; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); __sunzilog_startup(up); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return 0; } @@ -840,7 +840,7 @@ static void sunzilog_shutdown(struct uart_port *port) if (ZS_IS_CONS(up)) return; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); channel = ZILOG_CHANNEL_FROM_PORT(port); @@ -853,7 +853,7 @@ static void sunzilog_shutdown(struct uart_port *port) up->curregs[R5] &= ~SND_BRK; sunzilog_maybe_update_regs(up, channel); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /* Shared by TTY driver and serial console setup. The port lock is held @@ -945,7 +945,7 @@ sunzilog_set_termios(struct uart_port *port, struct ktermios *termios, baud = uart_get_baud_rate(port, termios, old, 1200, 76800); - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); @@ -962,7 +962,7 @@ sunzilog_set_termios(struct uart_port *port, struct ktermios *termios, uart_update_timeout(port, termios->c_cflag, baud); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } static const char *sunzilog_type(struct uart_port *port) @@ -1201,15 +1201,15 @@ sunzilog_console_write(struct console *con, const char *s, unsigned int count) int locked = 1; if (up->port.sysrq || oops_in_progress) - locked = spin_trylock_irqsave(&up->port.lock, flags); + locked = uart_port_trylock_irqsave(&up->port, &flags); else - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); uart_console_write(&up->port, s, count, sunzilog_putchar); udelay(2); if (locked) - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); } static int __init sunzilog_console_setup(struct console *con, char *options) @@ -1244,7 +1244,7 @@ static int __init sunzilog_console_setup(struct console *con, char *options) brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); up->curregs[R15] |= BRKIE; sunzilog_convert_to_zs(up, con->cflag, 0, brg); @@ -1252,7 +1252,7 @@ static int __init sunzilog_console_setup(struct console *con, char *options) sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS); __sunzilog_startup(up); - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); return 0; } @@ -1333,7 +1333,7 @@ static void sunzilog_init_hw(struct uart_sunzilog_port *up) channel = ZILOG_CHANNEL_FROM_PORT(&up->port); - spin_lock_irqsave(&up->port.lock, flags); + uart_port_lock_irqsave(&up->port, &flags); if (ZS_IS_CHANNEL_A(up)) { write_zsreg(channel, R9, FHWRES); ZSDELAY_LONG(); @@ -1383,7 +1383,7 @@ static void sunzilog_init_hw(struct uart_sunzilog_port *up) write_zsreg(channel, R9, up->curregs[R9]); } - spin_unlock_irqrestore(&up->port.lock, flags); + uart_port_unlock_irqrestore(&up->port, flags); #ifdef CONFIG_SERIO if (up->flags & (SUNZILOG_FLAG_CONS_KEYB | diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c index 0859394a78cd..0cc6524f5e8b 100644 --- a/drivers/tty/serial/timbuart.c +++ b/drivers/tty/serial/timbuart.c @@ -174,7 +174,7 @@ static void timbuart_tasklet(struct tasklet_struct *t) struct timbuart_port *uart = from_tasklet(uart, t, tasklet); u32 isr, ier = 0; - spin_lock(&uart->port.lock); + uart_port_lock(&uart->port); isr = ioread32(uart->port.membase + TIMBUART_ISR); dev_dbg(uart->port.dev, "%s ISR: %x\n", __func__, isr); @@ -189,7 +189,7 @@ static void timbuart_tasklet(struct tasklet_struct *t) iowrite32(ier, uart->port.membase + TIMBUART_IER); - spin_unlock(&uart->port.lock); + uart_port_unlock(&uart->port); dev_dbg(uart->port.dev, "%s leaving\n", __func__); } @@ -295,10 +295,10 @@ static void timbuart_set_termios(struct uart_port *port, tty_termios_copy_hw(termios, old); tty_termios_encode_baud_rate(termios, baud, baud); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); iowrite8((u8)bindex, port->membase + TIMBUART_BAUDRATE); uart_update_timeout(port, termios->c_cflag, baud); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *timbuart_type(struct uart_port *port) diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index b225a78f6175..404c14acafa5 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -216,11 +216,11 @@ static irqreturn_t ulite_isr(int irq, void *dev_id) unsigned long flags; do { - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); stat = uart_in32(ULITE_STATUS, port); busy = ulite_receive(port, stat); busy |= ulite_transmit(port, stat); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); n++; } while (busy); @@ -238,9 +238,9 @@ static unsigned int ulite_tx_empty(struct uart_port *port) unsigned long flags; unsigned int ret; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); ret = uart_in32(ULITE_STATUS, port); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return ret & ULITE_STATUS_TXEMPTY ? TIOCSER_TEMT : 0; } @@ -323,7 +323,7 @@ static void ulite_set_termios(struct uart_port *port, termios->c_cflag |= pdata->cflags & (PARENB | PARODD | CSIZE); tty_termios_encode_baud_rate(termios, pdata->baud, pdata->baud); - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); port->read_status_mask = ULITE_STATUS_RXVALID | ULITE_STATUS_OVERRUN | ULITE_STATUS_TXFULL; @@ -346,7 +346,7 @@ static void ulite_set_termios(struct uart_port *port, /* update timeout */ uart_update_timeout(port, termios->c_cflag, pdata->baud); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *ulite_type(struct uart_port *port) @@ -495,9 +495,9 @@ static void ulite_console_write(struct console *co, const char *s, int locked = 1; if (oops_in_progress) { - locked = spin_trylock_irqsave(&port->lock, flags); + locked = uart_port_trylock_irqsave(port, &flags); } else - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* save and disable interrupt */ ier = uart_in32(ULITE_STATUS, port) & ULITE_STATUS_IE; @@ -512,7 +512,7 @@ static void ulite_console_write(struct console *co, const char *s, uart_out32(ULITE_CONTROL_IE, ULITE_CONTROL, port); if (locked) - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static int ulite_console_setup(struct console *co, char *options) diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c index b06661b80f41..ed7a6bb5596a 100644 --- a/drivers/tty/serial/ucc_uart.c +++ b/drivers/tty/serial/ucc_uart.c @@ -931,7 +931,7 @@ static void qe_uart_set_termios(struct uart_port *port, baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); /* Do we really need a spinlock here? */ - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Update the per-port timeout. */ uart_update_timeout(port, termios->c_cflag, baud); @@ -949,7 +949,7 @@ static void qe_uart_set_termios(struct uart_port *port, qe_setbrg(qe_port->us_info.tx_clock, baud, 16); } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /* diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index c5d5c2765119..78a1c1eea11b 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -227,7 +227,7 @@ static irqreturn_t vt8500_irq(int irq, void *dev_id) struct uart_port *port = dev_id; unsigned long isr; - spin_lock(&port->lock); + uart_port_lock(port); isr = vt8500_read(port, VT8500_URISR); /* Acknowledge active status bits */ @@ -240,7 +240,7 @@ static irqreturn_t vt8500_irq(int irq, void *dev_id) if (isr & TCTS) handle_delta_cts(port); - spin_unlock(&port->lock); + uart_port_unlock(port); return IRQ_HANDLED; } @@ -342,7 +342,7 @@ static void vt8500_set_termios(struct uart_port *port, unsigned int baud, lcr; unsigned int loops = 1000; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* calculate and set baud rate */ baud = uart_get_baud_rate(port, termios, old, 900, 921600); @@ -410,7 +410,7 @@ static void vt8500_set_termios(struct uart_port *port, vt8500_write(&vt8500_port->uart, 0x881, VT8500_URFCR); vt8500_write(&vt8500_port->uart, vt8500_port->ier, VT8500_URIER); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *vt8500_type(struct uart_port *port) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 2e5e86a00a77..9c13dac1d4d1 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -346,7 +346,7 @@ static irqreturn_t cdns_uart_isr(int irq, void *dev_id) struct uart_port *port = (struct uart_port *)dev_id; unsigned int isrstatus; - spin_lock(&port->lock); + uart_port_lock(port); /* Read the interrupt status register to determine which * interrupt(s) is/are active and clear them. @@ -369,7 +369,7 @@ static irqreturn_t cdns_uart_isr(int irq, void *dev_id) !(readl(port->membase + CDNS_UART_CR) & CDNS_UART_CR_RX_DIS)) cdns_uart_handle_rx(dev_id, isrstatus); - spin_unlock(&port->lock); + uart_port_unlock(port); return IRQ_HANDLED; } @@ -506,14 +506,14 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, return NOTIFY_BAD; } - spin_lock_irqsave(&cdns_uart->port->lock, flags); + uart_port_lock_irqsave(cdns_uart->port, &flags); /* Disable the TX and RX to set baud rate */ ctrl_reg = readl(port->membase + CDNS_UART_CR); ctrl_reg |= CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS; writel(ctrl_reg, port->membase + CDNS_UART_CR); - spin_unlock_irqrestore(&cdns_uart->port->lock, flags); + uart_port_unlock_irqrestore(cdns_uart->port, flags); return NOTIFY_OK; } @@ -523,7 +523,7 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, * frequency. */ - spin_lock_irqsave(&cdns_uart->port->lock, flags); + uart_port_lock_irqsave(cdns_uart->port, &flags); locked = 1; port->uartclk = ndata->new_rate; @@ -533,7 +533,7 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, fallthrough; case ABORT_RATE_CHANGE: if (!locked) - spin_lock_irqsave(&cdns_uart->port->lock, flags); + uart_port_lock_irqsave(cdns_uart->port, &flags); /* Set TX/RX Reset */ ctrl_reg = readl(port->membase + CDNS_UART_CR); @@ -555,7 +555,7 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN; writel(ctrl_reg, port->membase + CDNS_UART_CR); - spin_unlock_irqrestore(&cdns_uart->port->lock, flags); + uart_port_unlock_irqrestore(cdns_uart->port, flags); return NOTIFY_OK; default: @@ -652,7 +652,7 @@ static void cdns_uart_break_ctl(struct uart_port *port, int ctl) unsigned int status; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); status = readl(port->membase + CDNS_UART_CR); @@ -664,7 +664,7 @@ static void cdns_uart_break_ctl(struct uart_port *port, int ctl) writel(CDNS_UART_CR_STOPBRK | status, port->membase + CDNS_UART_CR); } - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /** @@ -683,7 +683,7 @@ static void cdns_uart_set_termios(struct uart_port *port, unsigned long flags; unsigned int ctrl_reg, mode_reg; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Disable the TX and RX to set baud rate */ ctrl_reg = readl(port->membase + CDNS_UART_CR); @@ -794,7 +794,7 @@ static void cdns_uart_set_termios(struct uart_port *port, cval &= ~CDNS_UART_MODEMCR_FCM; writel(cval, port->membase + CDNS_UART_MODEMCR); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /** @@ -813,7 +813,7 @@ static int cdns_uart_startup(struct uart_port *port) is_brk_support = cdns_uart->quirks & CDNS_UART_RXBS_SUPPORT; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Disable the TX and RX */ writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS, @@ -861,7 +861,7 @@ static int cdns_uart_startup(struct uart_port *port) writel(readl(port->membase + CDNS_UART_ISR), port->membase + CDNS_UART_ISR); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); ret = request_irq(port->irq, cdns_uart_isr, 0, CDNS_UART_NAME, port); if (ret) { @@ -889,7 +889,7 @@ static void cdns_uart_shutdown(struct uart_port *port) int status; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Disable interrupts */ status = readl(port->membase + CDNS_UART_IMR); @@ -900,7 +900,7 @@ static void cdns_uart_shutdown(struct uart_port *port) writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS, port->membase + CDNS_UART_CR); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); free_irq(port->irq, port); } @@ -1050,7 +1050,7 @@ static int cdns_uart_poll_get_char(struct uart_port *port) int c; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Check if FIFO is empty */ if (readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_RXEMPTY) @@ -1058,7 +1058,7 @@ static int cdns_uart_poll_get_char(struct uart_port *port) else /* Read a character */ c = (unsigned char) readl(port->membase + CDNS_UART_FIFO); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return c; } @@ -1067,7 +1067,7 @@ static void cdns_uart_poll_put_char(struct uart_port *port, unsigned char c) { unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Wait until FIFO is empty */ while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXEMPTY)) @@ -1080,7 +1080,7 @@ static void cdns_uart_poll_put_char(struct uart_port *port, unsigned char c) while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXEMPTY)) cpu_relax(); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } #endif @@ -1232,9 +1232,9 @@ static void cdns_uart_console_write(struct console *co, const char *s, if (port->sysrq) locked = 0; else if (oops_in_progress) - locked = spin_trylock_irqsave(&port->lock, flags); + locked = uart_port_trylock_irqsave(port, &flags); else - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* save and disable interrupt */ imr = readl(port->membase + CDNS_UART_IMR); @@ -1257,7 +1257,7 @@ static void cdns_uart_console_write(struct console *co, const char *s, writel(imr, port->membase + CDNS_UART_IER); if (locked) - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /** @@ -1325,7 +1325,7 @@ static int cdns_uart_suspend(struct device *device) if (console_suspend_enabled && uart_console(port) && may_wake) { unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Empty the receive FIFO 1st before making changes */ while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_RXEMPTY)) @@ -1334,7 +1334,7 @@ static int cdns_uart_suspend(struct device *device) writel(1, port->membase + CDNS_UART_RXWM); /* disable RX timeout interrups */ writel(CDNS_UART_IXR_TOUT, port->membase + CDNS_UART_IDR); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } /* @@ -1372,7 +1372,7 @@ static int cdns_uart_resume(struct device *device) return ret; } - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* Set TX/RX Reset */ ctrl_reg = readl(port->membase + CDNS_UART_CR); @@ -1392,14 +1392,14 @@ static int cdns_uart_resume(struct device *device) clk_disable(cdns_uart->uartclk); clk_disable(cdns_uart->pclk); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } else { - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); /* restore original rx trigger level */ writel(rx_trigger_level, port->membase + CDNS_UART_RXWM); /* enable RX timeout interrupt */ writel(CDNS_UART_IXR_TOUT, port->membase + CDNS_UART_IER); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } return uart_resume_port(cdns_uart->cdns_uart_driver, port); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 493fc4742895..afa52883c498 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -3543,8 +3543,15 @@ static ssize_t show_cons_active(struct device *dev, for_each_console(c) { if (!c->device) continue; - if (!c->write) - continue; + if (c->flags & CON_NBCON) { + if (!c->write_atomic && + !(c->write_thread && c->kthread)) { + continue; + } + } else { + if (!c->write) + continue; + } if ((c->flags & CON_ENABLED) == 0) continue; cs[i++] = c; diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index 106b47ab9f4f..d0b8375d10ec 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -145,6 +145,14 @@ config VIRTIO_INPUT If unsure, say M. +config VIRTIO_TRANS + tristate "VirtIO transfer test driver" + depends on VIRTIO + help + This driver is for VirtIO transfer performance test + between front-end and back-end driver. + If unsure, say N. + config VIRTIO_MMIO tristate "Platform bus driver for memory mapped virtio devices" depends on HAS_IOMEM && HAS_DMA diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index 7960f6493913..bdf34908610c 100644 --- a/drivers/virtio/Makefile +++ b/drivers/virtio/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_VIRTIO_INPUT) += virtio_input.o obj-$(CONFIG_VIRTIO_VDPA) += virtio_vdpa.o obj-$(CONFIG_VIRTIO_MEM) += virtio_mem.o obj-$(CONFIG_VIRTIO_DMA_SHARED_BUFFER) += virtio_dma_buf.o +obj-$(CONFIG_VIRTIO_TRANS) += virtio_trans.o diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index 59892a31cf76..f6b983303e31 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -3,6 +3,7 @@ * Virtio memory mapped device driver * * Copyright 2011-2014, ARM Ltd. + * Copyright 2022-2023 NXP * * This module allows virtio devices to be used over a virtual, memory mapped * platform device. @@ -60,8 +61,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -70,7 +73,7 @@ #include #include #include - +#include /* The alignment to use between consumer and producer parts of vring. @@ -82,6 +85,13 @@ #define to_virtio_mmio_device(_plat_dev) \ container_of(_plat_dev, struct virtio_mmio_device, vdev) +struct virtio_mmio_wr_op { + uint64_t phy_base; + uint32_t offset; + uint32_t value; + uint8_t len; +}; + struct virtio_mmio_device { struct virtio_device vdev; struct platform_device *pdev; @@ -92,6 +102,16 @@ struct virtio_mmio_device { /* a list of queues so we can dispatch IRQs */ spinlock_t lock; struct list_head virtqueues; + + /* Hypervisor_less virtio */ + bool is_hypervisor_less; + phys_addr_t phys; + struct virtio_mmio_wr_op *wr_ops; + dma_addr_t wr_ops_phys; + struct mbox_client mbox_wr_cl; + struct mbox_client mbox_irq_cl; + struct mbox_chan *mmio_wr_ch; + struct mbox_chan *mmio_irq_ch; }; struct virtio_mmio_vq_info { @@ -102,6 +122,72 @@ struct virtio_mmio_vq_info { struct list_head node; }; +static void vmd_write(struct virtio_mmio_device *vm_dev, u32 val, u32 off, u8 len) +{ + void __iomem *addr = vm_dev->base + off; + struct virtio_mmio_wr_op *wr_ops = vm_dev->wr_ops; + int timeout; + int ret; + + switch (len) { + case 1: + writeb(val, addr); + break; + case 2: + writew(val, addr); + break; + case 4: + writel(val, addr); + break; + default: + return; + } + + if (vm_dev->is_hypervisor_less) { + + timeout = 1000000; + writel(0, vm_dev->base + VIRTIO_MMIO_WD_STATUS); + + wr_ops->phy_base = (uint64_t)vm_dev->phys; + wr_ops->offset = off; + wr_ops->value = val; + wr_ops->len = len; + + ret = mbox_send_message(vm_dev->mmio_wr_ch, &vm_dev->wr_ops_phys); + if (ret < 0) + dev_err(&vm_dev->vdev.dev, "mmio write error: base: %llx offset: %x, val: %x, len: %d\n", + wr_ops->phy_base, wr_ops->offset, + wr_ops->value, wr_ops->len); + + /* Unnecessary to sync for virtqueue notification */ + if (off == VIRTIO_MMIO_QUEUE_NOTIFY) + return; + + /* poll backend write operation complete */ + while (timeout-- && !readl(vm_dev->base + VIRTIO_MMIO_WD_STATUS)) + udelay(1); + + if (timeout < 0) + dev_err(&vm_dev->vdev.dev, "mmio write timeout: base: %llx offset: %x, val: %x, len: %d\n", + wr_ops->phy_base, wr_ops->offset, + wr_ops->value, wr_ops->len); + } +} + +static void vmd_writel(struct virtio_mmio_device *vm_dev, u32 val, u32 off) +{ + vmd_write(vm_dev, val, off, 4); +} + +static void vmd_writew(struct virtio_mmio_device *vm_dev, u32 val, u32 off) +{ + vmd_write(vm_dev, val, off, 2); +} + +static void vmd_writeb(struct virtio_mmio_device *vm_dev, u32 val, u32 off) +{ + vmd_write(vm_dev, val, off, 1); +} /* Configuration interface */ @@ -111,11 +197,11 @@ static u64 vm_get_features(struct virtio_device *vdev) struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); u64 features; - writel(1, vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES_SEL); + vmd_writel(vm_dev, 1, VIRTIO_MMIO_DEVICE_FEATURES_SEL); features = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES); features <<= 32; - writel(0, vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES_SEL); + vmd_writel(vm_dev, 0, VIRTIO_MMIO_DEVICE_FEATURES_SEL); features |= readl(vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES); return features; @@ -135,13 +221,13 @@ static int vm_finalize_features(struct virtio_device *vdev) return -EINVAL; } - writel(1, vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES_SEL); - writel((u32)(vdev->features >> 32), - vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES); + vmd_writel(vm_dev, 1, VIRTIO_MMIO_DRIVER_FEATURES_SEL); + vmd_writel(vm_dev, (u32)(vdev->features >> 32), + VIRTIO_MMIO_DRIVER_FEATURES); - writel(0, vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES_SEL); - writel((u32)vdev->features, - vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES); + vmd_writel(vm_dev, 0, VIRTIO_MMIO_DRIVER_FEATURES_SEL); + vmd_writel(vm_dev, (u32)vdev->features, + VIRTIO_MMIO_DRIVER_FEATURES); return 0; } @@ -192,39 +278,34 @@ static void vm_set(struct virtio_device *vdev, unsigned int offset, const void *buf, unsigned int len) { struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); - void __iomem *base = vm_dev->base + VIRTIO_MMIO_CONFIG; + u32 base = VIRTIO_MMIO_CONFIG; u8 b; __le16 w; __le32 l; if (vm_dev->version == 1) { - const u8 *ptr = buf; - int i; - - for (i = 0; i < len; i++) - writeb(ptr[i], base + offset + i); - + vmd_writel(vm_dev, *(u32 *)buf, base + offset); return; } switch (len) { case 1: memcpy(&b, buf, sizeof b); - writeb(b, base + offset); + vmd_writeb(vm_dev, b, base + offset); break; case 2: memcpy(&w, buf, sizeof w); - writew(le16_to_cpu(w), base + offset); + vmd_writew(vm_dev, le16_to_cpu(w), base + offset); break; case 4: memcpy(&l, buf, sizeof l); - writel(le32_to_cpu(l), base + offset); + vmd_writel(vm_dev, le32_to_cpu(l), base + offset); break; case 8: memcpy(&l, buf, sizeof l); - writel(le32_to_cpu(l), base + offset); + vmd_writel(vm_dev, le32_to_cpu(l), base + offset); memcpy(&l, buf + sizeof l, sizeof l); - writel(le32_to_cpu(l), base + offset + sizeof l); + vmd_writel(vm_dev, le32_to_cpu(l), base + offset + sizeof l); break; default: BUG(); @@ -260,7 +341,7 @@ static void vm_set_status(struct virtio_device *vdev, u8 status) * that the cache coherent memory writes have completed * before writing to the MMIO region. */ - writel(status, vm_dev->base + VIRTIO_MMIO_STATUS); + vmd_writel(vm_dev, status, VIRTIO_MMIO_STATUS); } static void vm_reset(struct virtio_device *vdev) @@ -268,7 +349,7 @@ static void vm_reset(struct virtio_device *vdev) struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); /* 0 status means a reset. */ - writel(0, vm_dev->base + VIRTIO_MMIO_STATUS); + vmd_writel(vm_dev, 0, VIRTIO_MMIO_STATUS); } @@ -282,7 +363,7 @@ static bool vm_notify(struct virtqueue *vq) /* We write the queue's selector into the notification register to * signal the other end */ - writel(vq->index, vm_dev->base + VIRTIO_MMIO_QUEUE_NOTIFY); + vmd_writel(vm_dev, vq->index, VIRTIO_MMIO_QUEUE_NOTIFY); return true; } @@ -296,6 +377,36 @@ static bool vm_notify_with_data(struct virtqueue *vq) return true; } +/* Notify all virtqueues on an interrupt. */ +static void virtio_mmio_irq_callback(struct mbox_client *c, void *msg) +{ + struct virtio_mmio_device *vm_dev = container_of(c, + struct virtio_mmio_device, mbox_irq_cl); + struct virtio_mmio_vq_info *info; + unsigned long status; + unsigned long flags; + + /* Read and acknowledge interrupts */ + status = readl(vm_dev->base + VIRTIO_MMIO_INTERRUPT_STATUS); + + /* + * For Hypervisor-less virtio, write ACK without notification, since + * in mailbox RX channel callback function sometimes the timeout occur + * calling the vmd_writel(). + */ + writel(status, vm_dev->base + VIRTIO_MMIO_INTERRUPT_ACK); + + if (unlikely(status & VIRTIO_MMIO_INT_CONFIG)) + virtio_config_changed(&vm_dev->vdev); + + if (likely(status & VIRTIO_MMIO_INT_VRING)) { + spin_lock_irqsave(&vm_dev->lock, flags); + list_for_each_entry(info, &vm_dev->virtqueues, node) + vring_interrupt(0, info->vq); + spin_unlock_irqrestore(&vm_dev->lock, flags); + } +} + /* Notify all virtqueues on an interrupt. */ static irqreturn_t vm_interrupt(int irq, void *opaque) { @@ -307,7 +418,7 @@ static irqreturn_t vm_interrupt(int irq, void *opaque) /* Read and acknowledge interrupts */ status = readl(vm_dev->base + VIRTIO_MMIO_INTERRUPT_STATUS); - writel(status, vm_dev->base + VIRTIO_MMIO_INTERRUPT_ACK); + vmd_writel(vm_dev, status, VIRTIO_MMIO_INTERRUPT_ACK); if (unlikely(status & VIRTIO_MMIO_INT_CONFIG)) { virtio_config_changed(&vm_dev->vdev); @@ -338,11 +449,11 @@ static void vm_del_vq(struct virtqueue *vq) spin_unlock_irqrestore(&vm_dev->lock, flags); /* Select and deactivate the queue */ - writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL); + vmd_writel(vm_dev, index, VIRTIO_MMIO_QUEUE_SEL); if (vm_dev->version == 1) { - writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); + vmd_writel(vm_dev, 0, VIRTIO_MMIO_QUEUE_PFN); } else { - writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_READY); + vmd_writel(vm_dev, 0, VIRTIO_MMIO_QUEUE_READY); WARN_ON(readl(vm_dev->base + VIRTIO_MMIO_QUEUE_READY)); } @@ -359,7 +470,10 @@ static void vm_del_vqs(struct virtio_device *vdev) list_for_each_entry_safe(vq, n, &vdev->vqs, list) vm_del_vq(vq); - free_irq(platform_get_irq(vm_dev->pdev, 0), vm_dev); + if (vm_dev->is_hypervisor_less) + mbox_free_channel(vm_dev->mmio_irq_ch); + else + free_irq(platform_get_irq(vm_dev->pdev, 0), vm_dev); } static void vm_synchronize_cbs(struct virtio_device *vdev) @@ -390,7 +504,7 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned int in return NULL; /* Select the queue we're interested in */ - writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL); + vmd_writel(vm_dev, index, VIRTIO_MMIO_QUEUE_SEL); /* Queue shouldn't already be set up. */ if (readl(vm_dev->base + (vm_dev->version == 1 ? @@ -423,7 +537,7 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned int in vq->num_max = num; /* Activate the queue */ - writel(virtqueue_get_vring_size(vq), vm_dev->base + VIRTIO_MMIO_QUEUE_NUM); + vmd_writel(vm_dev, virtqueue_get_vring_size(vq), VIRTIO_MMIO_QUEUE_NUM); if (vm_dev->version == 1) { u64 q_pfn = virtqueue_get_desc_addr(vq) >> PAGE_SHIFT; @@ -440,27 +554,27 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned int in goto error_bad_pfn; } - writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_QUEUE_ALIGN); - writel(q_pfn, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); + vmd_writel(vm_dev, PAGE_SIZE, VIRTIO_MMIO_QUEUE_ALIGN); + vmd_writel(vm_dev, q_pfn, VIRTIO_MMIO_QUEUE_PFN); } else { u64 addr; addr = virtqueue_get_desc_addr(vq); - writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_DESC_LOW); - writel((u32)(addr >> 32), - vm_dev->base + VIRTIO_MMIO_QUEUE_DESC_HIGH); + vmd_writel(vm_dev, (u32)addr, VIRTIO_MMIO_QUEUE_DESC_LOW); + vmd_writel(vm_dev, (u32)(addr >> 32), + VIRTIO_MMIO_QUEUE_DESC_HIGH); addr = virtqueue_get_avail_addr(vq); - writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_AVAIL_LOW); - writel((u32)(addr >> 32), - vm_dev->base + VIRTIO_MMIO_QUEUE_AVAIL_HIGH); + vmd_writel(vm_dev, (u32)addr, VIRTIO_MMIO_QUEUE_AVAIL_LOW); + vmd_writel(vm_dev, (u32)(addr >> 32), + VIRTIO_MMIO_QUEUE_AVAIL_HIGH); addr = virtqueue_get_used_addr(vq); - writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_USED_LOW); - writel((u32)(addr >> 32), - vm_dev->base + VIRTIO_MMIO_QUEUE_USED_HIGH); + vmd_writel(vm_dev, (u32)addr, VIRTIO_MMIO_QUEUE_USED_LOW); + vmd_writel(vm_dev, (u32)(addr >> 32), + VIRTIO_MMIO_QUEUE_USED_HIGH); - writel(1, vm_dev->base + VIRTIO_MMIO_QUEUE_READY); + vmd_writel(vm_dev, 1, VIRTIO_MMIO_QUEUE_READY); } vq->priv = info; @@ -476,9 +590,9 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned int in vring_del_virtqueue(vq); error_new_virtqueue: if (vm_dev->version == 1) { - writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); + vmd_writel(vm_dev, 0, VIRTIO_MMIO_QUEUE_PFN); } else { - writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_READY); + vmd_writel(vm_dev, 0, VIRTIO_MMIO_QUEUE_READY); WARN_ON(readl(vm_dev->base + VIRTIO_MMIO_QUEUE_READY)); } kfree(info); @@ -487,6 +601,28 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned int in return ERR_PTR(err); } +static int virtio_mmio_irq_channel_init(struct virtio_mmio_device *vm_dev) +{ + struct platform_device *pdev = vm_dev->pdev; + struct device *dev = &pdev->dev; + struct mbox_client *cl; + int ret = 0; + + cl = &vm_dev->mbox_irq_cl; + cl->dev = dev; + cl->rx_callback = virtio_mmio_irq_callback; + + vm_dev->mmio_irq_ch = mbox_request_channel_byname(cl, "mmioirq"); + if (IS_ERR(vm_dev->mmio_irq_ch)) { + ret = PTR_ERR(vm_dev->mmio_irq_ch); + dev_err(cl->dev, "failed to request mbox irq chan, ret %d\n", + ret); + return ret; + } + + return ret; +} + static int vm_find_vqs(struct virtio_device *vdev, unsigned int nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], @@ -495,16 +631,23 @@ static int vm_find_vqs(struct virtio_device *vdev, unsigned int nvqs, struct irq_affinity *desc) { struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); - int irq = platform_get_irq(vm_dev->pdev, 0); + int irq; int i, err, queue_idx = 0; - if (irq < 0) - return irq; + if (vm_dev->is_hypervisor_less) { + err = virtio_mmio_irq_channel_init(vm_dev); + if (err) + return err; + } else { + irq = platform_get_irq(vm_dev->pdev, 0); + if (irq < 0) + return irq; - err = request_irq(irq, vm_interrupt, IRQF_SHARED, + err = request_irq(irq, vm_interrupt, IRQF_SHARED, dev_name(&vdev->dev), vm_dev); - if (err) - return err; + if (err) + return err; + } if (of_property_read_bool(vm_dev->pdev->dev.of_node, "wakeup-source")) enable_irq_wake(irq); @@ -540,7 +683,7 @@ static bool vm_get_shm_region(struct virtio_device *vdev, u64 len, addr; /* Select the region we're interested in */ - writel(id, vm_dev->base + VIRTIO_MMIO_SHM_SEL); + vmd_writel(vm_dev, id, VIRTIO_MMIO_SHM_SEL); /* Read the region size */ len = (u64) readl(vm_dev->base + VIRTIO_MMIO_SHM_LEN_LOW); @@ -611,6 +754,27 @@ static void virtio_mmio_release_dev(struct device *_d) kfree(vm_dev); } +static int virtio_mmio_mbox_channel_init(struct virtio_mmio_device *vm_dev) +{ + struct platform_device *pdev = vm_dev->pdev; + struct device *dev = &pdev->dev; + struct mbox_client *cl; + int ret = 0; + + cl = &vm_dev->mbox_wr_cl; + cl->dev = dev; + + vm_dev->mmio_wr_ch = mbox_request_channel_byname(cl, "mmiowr"); + if (IS_ERR(vm_dev->mmio_wr_ch)) { + ret = PTR_ERR(vm_dev->mmio_wr_ch); + dev_err(cl->dev, "failed to request mbox chan mmio, ret %d\n", + ret); + return ret; + } + + return ret; +} + /* Platform device */ static int virtio_mmio_probe(struct platform_device *pdev) @@ -618,11 +782,19 @@ static int virtio_mmio_probe(struct platform_device *pdev) struct virtio_mmio_device *vm_dev; unsigned long magic; int rc; + struct resource *res; vm_dev = kzalloc(sizeof(*vm_dev), GFP_KERNEL); if (!vm_dev) return -ENOMEM; + if (of_property_read_bool(pdev->dev.of_node, "hypervisor_less")) { + vm_dev->is_hypervisor_less = true; + rc = of_reserved_mem_device_init(&pdev->dev); + if (rc) + dev_info(&pdev->dev, "Device specific DMA pool is not available\n"); + } + vm_dev->vdev.dev.parent = &pdev->dev; vm_dev->vdev.dev.release = virtio_mmio_release_dev; vm_dev->vdev.config = &virtio_mmio_config_ops; @@ -630,7 +802,7 @@ static int virtio_mmio_probe(struct platform_device *pdev) INIT_LIST_HEAD(&vm_dev->virtqueues); spin_lock_init(&vm_dev->lock); - vm_dev->base = devm_platform_ioremap_resource(pdev, 0); + vm_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(vm_dev->base)) { rc = PTR_ERR(vm_dev->base); goto free_vm_dev; @@ -641,7 +813,7 @@ static int virtio_mmio_probe(struct platform_device *pdev) if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)) { dev_warn(&pdev->dev, "Wrong magic value 0x%08lx!\n", magic); rc = -ENODEV; - goto free_vm_dev; + goto err; } /* Check device version */ @@ -650,7 +822,7 @@ static int virtio_mmio_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Version %ld not supported!\n", vm_dev->version); rc = -ENXIO; - goto free_vm_dev; + goto err; } vm_dev->vdev.id.device = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_ID); @@ -660,12 +832,24 @@ static int virtio_mmio_probe(struct platform_device *pdev) * with no function. End probing now with no error reported. */ rc = -ENODEV; - goto free_vm_dev; + goto err; } vm_dev->vdev.id.vendor = readl(vm_dev->base + VIRTIO_MMIO_VENDOR_ID); + if (vm_dev->is_hypervisor_less) { + vm_dev->phys = res->start; + + vm_dev->wr_ops = vm_dev->base + VIRTIO_MMIO_RW_OPS_MEM_OFFSET; + vm_dev->wr_ops_phys = res->start + VIRTIO_MMIO_RW_OPS_MEM_OFFSET; + + /* Initialize mailbox mmio channel. */ + rc = virtio_mmio_mbox_channel_init(vm_dev); + if (rc) + goto err; + } + if (vm_dev->version == 1) { - writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE); + vmd_writel(vm_dev, PAGE_SIZE, VIRTIO_MMIO_GUEST_PAGE_SIZE); rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); /* @@ -691,14 +875,22 @@ static int virtio_mmio_probe(struct platform_device *pdev) return rc; +err: + if (vm_dev->is_hypervisor_less) + of_reserved_mem_device_release(&pdev->dev); free_vm_dev: kfree(vm_dev); + return rc; } static int virtio_mmio_remove(struct platform_device *pdev) { struct virtio_mmio_device *vm_dev = platform_get_drvdata(pdev); + + if (vm_dev->is_hypervisor_less) + mbox_free_channel(vm_dev->mmio_wr_ch); + unregister_virtio_device(&vm_dev->vdev); return 0; diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 80669e05bf0e..99ecfae18d9c 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -599,8 +599,6 @@ static inline int virtqueue_add_split(struct virtqueue *_vq, /* FIXME: for historical reasons, we force a notify here if * there are outgoing parts to the buffer. Presumably the * host should service the ring ASAP. */ - if (out_sgs) - vq->notify(&vq->vq); if (indirect) kfree(desc); END_USE(vq); diff --git a/drivers/virtio/virtio_trans.c b/drivers/virtio/virtio_trans.c new file mode 100644 index 000000000000..13c003e2ba45 --- /dev/null +++ b/drivers/virtio/virtio_trans.c @@ -0,0 +1,793 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2022-2023 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_TEST_LEN 2048 +#define VT_TIMES 1000 +#define INDIRECT_NUM 1 +#define WAIT_TO_MS 2 + +#define VT_TXRX_BIT (1 << 0) +#define VT_B_COPY_BIT (1 << 1) +#define VT_F_COPY_BIT (1 << 2) +#define VT_B_MODE_BIT (1 << 3) +#define VT_F_MODE_BIT (1 << 4) + +static u32 poll_delay = 10; + +struct virtio_trans { + /* The virtio device we're associated with */ + struct virtio_device *vdev; + struct device *dev; + wait_queue_head_t waitqueue; + struct work_struct config_work; + + struct virtqueue *tx_vq, *rx_vq; + void *tx_buf, *rx_buf; + spinlock_t txvq_lock; + spinlock_t rxvq_lock; + struct scatterlist *tx_sgl; + struct scatterlist *rx_sgl; + size_t tx_vring_size; + size_t rx_vring_size; + + bool vt_running; + u32 regression; + size_t pkt_size; + bool tx; + bool do_copy; + bool poll_mode; + bool back_poll_mode; + void *data_buf; +}; + +static void tx_intr(struct virtqueue *vq) +{ + struct virtio_trans *vt = vq->vdev->priv; + + if (vt->poll_mode) + return; + + if (!vt->vt_running) + return; + + if (!vt->tx) + return; + + wake_up_interruptible(&vt->waitqueue); +} + +static void virttrans_init_sg(struct scatterlist *sg, size_t sg_num, void *base, size_t len) +{ + int i; + + sg_init_table(sg, sg_num); + if (likely(is_vmalloc_addr(base))) + for (i = 0; i < sg_num; i++) + sg_set_page(&sg[i], vmalloc_to_page(base + i * len), len, + offset_in_page(base)); + else + for (i = 0; i < sg_num; i++) + sg_set_buf(&sg[i], base + i * len, len); +} + +/* call this func with lock */ +static void virttrans_queue_txbuf(struct virtio_trans *vt, + struct scatterlist *sg, + void *buf, size_t pkt_size) +{ + if (vt->do_copy) + memcpy(buf, vt->data_buf, pkt_size); + virtqueue_add_outbuf(vt->tx_vq, sg, INDIRECT_NUM, buf, GFP_ATOMIC); +} + +static void virttrans_reclaim_txvq(struct virtio_trans *vt) +{ + void *buf; + u32 len; + + spin_lock(&vt->txvq_lock); + while ((buf = virtqueue_get_buf(vt->tx_vq, &len)) != NULL) + ; + spin_unlock(&vt->txvq_lock); + +} + +static void virttrans_fill_txvq(struct virtio_trans *vt, size_t pkt_size) +{ + int i; + + spin_lock(&vt->txvq_lock); + for (i = 0; i < vt->tx_vring_size; i++) { + virttrans_init_sg(&vt->tx_sgl[i * INDIRECT_NUM], INDIRECT_NUM, + vt->tx_buf + i * MAX_TEST_LEN * INDIRECT_NUM, + pkt_size); + virttrans_queue_txbuf(vt, &vt->tx_sgl[i * INDIRECT_NUM], + vt->tx_buf + i * MAX_TEST_LEN * + INDIRECT_NUM, pkt_size); + } + spin_unlock(&vt->txvq_lock); +} + +static void *vt_get_tx_buf(struct virtio_trans *vt) +{ + void *buf; + int len; + + spin_lock(&vt->txvq_lock); + buf = virtqueue_get_buf(vt->tx_vq, &len); + spin_unlock(&vt->txvq_lock); + + return buf; +} +static int tx_pkts(struct virtio_trans *vt, size_t pkt_size, u32 times) +{ + + ktime_t start_time, stop_time; + u32 cnt = times; + u64 time_period; + u32 tx_count; + int err; + int i; + u64 idx = 0; + void *buf; + + if (pkt_size > MAX_TEST_LEN) { + dev_err(vt->dev, "pkt_size must be less than 0x%x\n", + MAX_TEST_LEN); + return -EINVAL; + } + + start_time = ktime_get(); + + while (vt->vt_running && cnt > 0) { + buf = vt_get_tx_buf(vt); + while (!buf) { + if (!vt->back_poll_mode) + virtqueue_kick(vt->tx_vq); + + if (vt->poll_mode) { + udelay(poll_delay); + buf = vt_get_tx_buf(vt); + continue; + } + + err = wait_event_interruptible_timeout(vt->waitqueue, + (buf = vt_get_tx_buf(vt)), + msecs_to_jiffies(WAIT_TO_MS)); + if (err == -ERESTARTSYS) { + dev_info(vt->dev, "%s: interrupt the waiting for tx buffer by signal\n", + __func__); + goto out; + } + } + + spin_lock(&vt->txvq_lock); + u64 tmp_idx = idx; + u32 tmp_rem; + div_u64_rem(tmp_idx, vt->tx_vring_size, &tmp_rem); + virttrans_queue_txbuf(vt, &vt->tx_sgl[tmp_rem], + buf, vt->pkt_size); + idx++; + spin_unlock(&vt->txvq_lock); + + cnt -= INDIRECT_NUM; + } + +out: + i = 100; + while (i--) { + virtqueue_kick(vt->tx_vq); + virtio_cread_le(vt->vdev, struct virtio_trans_config, + tx_count, &tx_count); + if (tx_count >= times - cnt) + break; + mdelay(10); + } + + if (i < 0) + dev_err(vt->dev, "Wait backend completion timeout\n"); + + stop_time = ktime_get(); + time_period = ktime_us_delta(stop_time, start_time); + + pr_info("tx_test: pkt_size (%zu B), pkt_cnt (%d), period (%llu us)\n", + pkt_size, times - cnt, time_period); + + return err; +} + +/* call this func with lock */ +static void virttrans_queue_rxbuf(struct virtio_trans *vt, + struct scatterlist *sg, + void *buf, size_t pkt_size) +{ + if (vt->do_copy) + memcpy(vt->data_buf, buf, pkt_size); + virtqueue_add_inbuf(vt->rx_vq, sg, INDIRECT_NUM, buf, GFP_ATOMIC); +} + +static void virttrans_reclaim_rxvq(struct virtio_trans *vt) +{ + void *buf; + u32 len; + + spin_lock(&vt->rxvq_lock); + while ((buf = virtqueue_get_buf(vt->rx_vq, &len)) != NULL) + ; + spin_unlock(&vt->rxvq_lock); + +} + +static void virttrans_fill_rxvq(struct virtio_trans *vt, size_t pkt_size) +{ + int i; + + spin_lock(&vt->rxvq_lock); + for (i = 0; i < vt->rx_vring_size; i++) { + virttrans_init_sg(&vt->rx_sgl[i * INDIRECT_NUM], INDIRECT_NUM, + vt->rx_buf + i * MAX_TEST_LEN * INDIRECT_NUM, + pkt_size); + virttrans_queue_rxbuf(vt, &vt->rx_sgl[i * INDIRECT_NUM], + vt->rx_buf + i * MAX_TEST_LEN * + INDIRECT_NUM, pkt_size); + } + spin_unlock(&vt->rxvq_lock); +} + +static void rx_intr(struct virtqueue *vq) +{ + struct virtio_trans *vt = vq->vdev->priv; + + if (vt->poll_mode) + return; + + if (!vt->vt_running) + return; + + if (vt->tx) + return; + + wake_up_interruptible(&vt->waitqueue); +} + +static void *vt_get_rx_buf(struct virtio_trans *vt) +{ + void *buf; + int len; + + spin_lock(&vt->rxvq_lock); + buf = virtqueue_get_buf(vt->rx_vq, &len); + spin_unlock(&vt->rxvq_lock); + + return buf; +} + +static int rx_pkts(struct virtio_trans *vt, size_t pkt_size, u32 times) +{ + + ktime_t start_time, stop_time; + u32 cnt = times; + u64 time_period; + int err; + void *buf; + u64 idx = 0; + + if (pkt_size > MAX_TEST_LEN) { + dev_err(vt->dev, "pkt_size must be less than 0x%x\n", + MAX_TEST_LEN); + return -EINVAL; + } + + start_time = ktime_get(); + while (vt->vt_running && cnt > 0) { + buf = vt_get_rx_buf(vt); + while (!buf) { + if (!vt->back_poll_mode) + virtqueue_kick(vt->rx_vq); + + if (vt->poll_mode) { + udelay(poll_delay); + buf = vt_get_rx_buf(vt); + continue; + } + + err = wait_event_interruptible_timeout(vt->waitqueue, + (buf = vt_get_rx_buf(vt)), + msecs_to_jiffies(WAIT_TO_MS)); + if (err == -ERESTARTSYS) { + dev_info(vt->dev, "%s: interrupt the waiting for rx buffer by signal\n", + __func__); + goto out; + } + } + + spin_lock(&vt->rxvq_lock); + u64 tmp_idx = idx; + u32 tmp_rem; + div_u64_rem(tmp_idx, vt->rx_vring_size, &tmp_rem); + virttrans_queue_rxbuf(vt, &vt->rx_sgl[tmp_rem], + buf, vt->pkt_size); + idx++; + spin_unlock(&vt->rxvq_lock); + + cnt -= INDIRECT_NUM; + } + +out: + stop_time = ktime_get(); + time_period = ktime_us_delta(stop_time, start_time); + + pr_info("rx_test: pkt_size (%zu B), pkt_cnt (%d), period (%llu us)\n", + pkt_size, times - cnt, time_period); + + return 0; +} + +static ssize_t vt_control_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct virtio_trans *vt = dev_get_drvdata(dev); + u32 control; + + virtio_cread_le(vt->vdev, struct virtio_trans_config, control, + &control); + + return sprintf(buf, "0: stop\n1: start\ncontrol = %u\n", control); +} + +static ssize_t vt_control_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t cnt) +{ + struct virtio_trans *vt = dev_get_drvdata(dev); + u32 control; + unsigned long val; + + if (kstrtoul(buf, 0, &val) < 0 || (val > 1)) { + dev_err(vt->dev, "Invalid param\n"); + pr_info("0: stop\n"); + pr_info("1: start\n"); + return -EINVAL; + } + + virtio_cread_le(vt->vdev, struct virtio_trans_config, control, + &control); + + if (!val) { + control &= ~VT_CTRL_START; + virtio_cwrite_le(vt->vdev, struct virtio_trans_config, control, + &control); + vt->vt_running = 0; + if (!vt->tx) + virttrans_reclaim_rxvq(vt); + else + virttrans_reclaim_txvq(vt); + + vt->regression = 0; + return cnt; + } + + if (vt->vt_running) { + dev_err(vt->dev, "Try again after this test completed\n"); + return -EAGAIN; + } + + vt->vt_running = 1; + control |= VT_CTRL_START; + + if (!vt->tx) { + virttrans_fill_rxvq(vt, vt->pkt_size); + virtio_cwrite_le(vt->vdev, struct virtio_trans_config, control, + &control); + rx_pkts(vt, vt->pkt_size, vt->regression); + } else { + virttrans_fill_txvq(vt, vt->pkt_size); + virtio_cwrite_le(vt->vdev, struct virtio_trans_config, control, + &control); + tx_pkts(vt, vt->pkt_size, vt->regression); + } + + control &= ~VT_CTRL_START; + virtio_cwrite_le(vt->vdev, struct virtio_trans_config, control, + &control); + + vt->vt_running = 0; + vt->regression = 0; + + return cnt; +} + +static ssize_t vt_config_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t cnt) +{ + struct virtio_trans *vt = dev_get_drvdata(dev); + u32 config; + unsigned long val; + + if (vt->vt_running) { + dev_err(dev, "Try again after this test completed\n"); + return -EAGAIN; + } + + if (kstrtoul(buf, 0, &val) < 0 || (val & ~0x1f)) { + dev_err(dev, "Invalid param\n"); + pr_info("bit[0]:\n"); + pr_info("\t0: TX\n"); + pr_info("\t1: RX\n"); + pr_info("bit[1]:\n"); + pr_info("\t0: Backend do NOT copy buffer\n"); + pr_info("\t1: Backend do copy buffer\n"); + pr_info("bit[2]:\n"); + pr_info("\t0: Frontend do NOT copy buffer\n"); + pr_info("\t1: Frontend do copy buffer\n"); + pr_info("bit[3]:\n"); + pr_info("\t0: Backend interrupt mode\n"); + pr_info("\t1: Backend polling mode\n"); + pr_info("bit[4]:\n"); + pr_info("\t0: Frontend interrupt mode\n"); + pr_info("\t1: Frontend polling mode\n"); + return -EINVAL; + } + + if (!vt->pkt_size) + vt->pkt_size = 64; + + if (!vt->regression) { + virtio_cwrite_le(vt->vdev, struct virtio_trans_config, + regression, &vt->regression); + vt->regression = VT_TIMES; + } + + virtio_cread_le(vt->vdev, struct virtio_trans_config, config, &config); + + config &= ~(VT_CFG_TX | VT_CFG_RX | VT_CFG_COPY | + VT_CFG_B_POLL | VT_CFG_F_POLL); + vt->do_copy = false; + vt->back_poll_mode = false; + vt->poll_mode = false; + + if (val & VT_TXRX_BIT) { + vt->tx = false; + config |= VT_CFG_RX; + } else { + vt->tx = true; + config |= VT_CFG_TX; + } + + if (val & VT_B_COPY_BIT) + config |= VT_CFG_COPY; + + if (val & VT_F_COPY_BIT) + vt->do_copy = true; + + if (val & VT_B_MODE_BIT) { + vt->back_poll_mode = true; + config |= VT_CFG_B_POLL; + } + + if (val & VT_F_MODE_BIT) { + vt->poll_mode = true; + config |= VT_CFG_F_POLL; + } + + pr_info("*********************************************************\n"); + pr_info("Front-end: %s mode\n", vt->poll_mode ? "poll" : "interrupt"); + pr_info("Back-end: %s mode\n", vt->back_poll_mode ? "poll" : "interrupt"); + pr_info("Front-end: do %scopy buffer\n", vt->do_copy ? "" : "NOT "); + pr_info("Back-end: do %scopy buffer\n", (config & VT_CFG_COPY) ? "" : "NOT"); + + pr_info("\tTest case: %s\n\tpkt_size: <%zu>\n\tregress times: <%d>\n\n", + vt->tx ? "TX" : "RX", vt->pkt_size, vt->regression); + + virtio_cwrite_le(vt->vdev, struct virtio_trans_config, config, &config); + + return cnt; +} +static ssize_t vt_config_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct virtio_trans *vt = dev_get_drvdata(dev); + u32 config; + + virtio_cread_le(vt->vdev, struct virtio_trans_config, config, &config); + + return sprintf(buf, "0x%x\n", config); +} + +static ssize_t vt_pkt_size_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t cnt) +{ + struct virtio_trans *vt = dev_get_drvdata(dev); + u32 val; + + if (vt->vt_running) { + dev_err(dev, "Try again after this test completed\n"); + return -EAGAIN; + } + + if (kstrtou32(buf, 0, &val) < 0 || (val > MAX_TEST_LEN)) { + dev_err(dev, "pkt_size must be less then 0x%x\n", MAX_TEST_LEN); + return -EINVAL; + } + + vt->pkt_size = val; + virtio_cwrite_le(vt->vdev, struct virtio_trans_config, pkt_size, &val); + + dev_dbg(dev, "Update pkt_size to %u\n", val); + + return cnt; +} +static ssize_t vt_pkt_size_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct virtio_trans *vt = dev_get_drvdata(dev); + + return sprintf(buf, "%ld\n", vt->pkt_size); +} + +static ssize_t vt_regression_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t cnt) +{ + struct virtio_trans *vt = dev_get_drvdata(dev); + u32 val; + + if (vt->vt_running) { + dev_err(dev, "Try again after this test completed\n"); + return -EAGAIN; + } + + if (kstrtou32(buf, 0, &val) < 0 || val <= 0) { + dev_err(dev, "Invalid regression\n"); + return -EINVAL; + } + + vt->regression = val; + + virtio_cwrite_le(vt->vdev, struct virtio_trans_config, regression, + &val); + dev_dbg(dev, "Update regression to %u\n", val); + + return cnt; +} +static ssize_t vt_regression_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct virtio_trans *vt = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", vt->regression); +} + +static DEVICE_ATTR_RW(vt_control); +static DEVICE_ATTR_RW(vt_config); +static DEVICE_ATTR_RW(vt_pkt_size); +static DEVICE_ATTR_RW(vt_regression); + +static struct attribute *sysfs_entries[] = { + &dev_attr_vt_config.attr, + &dev_attr_vt_control.attr, + &dev_attr_vt_pkt_size.attr, + &dev_attr_vt_regression.attr, + NULL +}; + +static const struct attribute_group vt_attribute_group = { + .name = NULL, /* put in device directory */ + .attrs = sysfs_entries, +}; + +static void config_intr(struct virtio_device *vdev) +{ + struct virtio_trans *vt = vdev->priv; + + schedule_work(&vt->config_work); +} + +static void config_work_handler(struct work_struct *work) +{ + /* handle the config change */ +} + +static int virttrans_init_vqs(struct virtio_trans *vt) +{ + struct virtqueue *vqs[2]; + vq_callback_t *cbs[] = { tx_intr, rx_intr}; + static const char * const names[] = { "tx", "rx" }; + int err; + + err = virtio_find_vqs(vt->vdev, 2, vqs, cbs, names, NULL); + if (err) { + dev_err(vt->dev, "Failed to find vqs\n"); + return err; + } + vt->tx_vq = vqs[0]; + vt->rx_vq = vqs[1]; + + return 0; +} + +static int virttrans_probe(struct virtio_device *vdev) +{ + struct virtio_trans *vt; + int err; + dma_addr_t tx_buf_dma; + + vt = kzalloc(sizeof(*vt), GFP_KERNEL); + if (!vt) + return -ENOMEM; + + vdev->priv = vt; + vt->vdev = vdev; + vt->dev = &vdev->dev; + vt->pkt_size = 64; + vt->regression = VT_TIMES; + + dev_set_drvdata(vt->dev, vt); + + err = virttrans_init_vqs(vt); + if (err) { + dev_err(vt->dev, "Failed to init virtqueues\n"); + goto err_init_vq; + } + + vt->tx_vring_size = virtqueue_get_vring_size(vt->tx_vq); + vt->rx_vring_size = virtqueue_get_vring_size(vt->rx_vq); + + vt->data_buf = kzalloc(MAX_TEST_LEN, GFP_KERNEL); + if (!vt->data_buf) { + err = -ENOMEM; + dev_err(vt->dev, "Failed to alloc data buffer\n"); + goto alloc_data_buf; + } + + memset(vt->data_buf, 0xa5, MAX_TEST_LEN); + + vt->tx_sgl = kzalloc(INDIRECT_NUM * (vt->rx_vring_size + + vt->tx_vring_size) * sizeof(struct scatterlist), + GFP_KERNEL); + + if (!vt->tx_sgl) { + err = -ENOMEM; + dev_err(vt->dev, "Failed to alloc SG descriptors\n"); + goto alloc_sgl; + } + + vt->rx_sgl = vt->tx_sgl + INDIRECT_NUM * vt->tx_vring_size; + + vt->tx_buf = dma_alloc_coherent(vdev->dev.parent, MAX_TEST_LEN * + INDIRECT_NUM * (vt->rx_vring_size + + vt->tx_vring_size), &tx_buf_dma, + GFP_KERNEL); + if (!vt->tx_buf) { + err = -ENOMEM; + dev_err(vt->dev, "Failed to alloc Vring buffers\n"); + goto alloc_buf; + } + + vt->rx_buf = vt->tx_buf + + MAX_TEST_LEN * INDIRECT_NUM * vt->tx_vring_size; + + spin_lock_init(&vt->txvq_lock); + spin_lock_init(&vt->rxvq_lock); + init_waitqueue_head(&vt->waitqueue); + INIT_WORK(&vt->config_work, &config_work_handler); + + err = sysfs_create_group(&vt->dev->kobj, &vt_attribute_group); + if (err) { + dev_err(vt->dev, "Failed to create sysfs device attributes\n"); + goto sysfs; + } else { + /* + * Generate a udev event so that appropriate + * symlinks can be created based on udev + * rules. + */ + kobject_uevent(&vt->dev->kobj, KOBJ_CHANGE); + } + + virtio_device_ready(vt->vdev); + + return 0; + +sysfs: + dma_free_coherent(vdev->dev.parent, MAX_TEST_LEN * INDIRECT_NUM * + (vt->rx_vring_size + vt->tx_vring_size), + vt->tx_buf, tx_buf_dma); +alloc_buf: + kfree(vt->tx_sgl); +alloc_sgl: + kfree(vt->data_buf); +alloc_data_buf: + /* TODO: vtrans_deinit_vq(); */ +err_init_vq: + kfree(vt); + + return err; +} + +static void virttrans_remove(struct virtio_device *vdev) +{ + struct virtio_trans *vt = vdev->priv; + void *buf; + + virtio_break_device(vdev); + flush_work(&vt->config_work); + vdev->config->reset(vdev); + cancel_work_sync(&vt->config_work); + + sysfs_remove_group(&vt->dev->kobj, &vt_attribute_group); + + while ((buf = virtqueue_detach_unused_buf(vt->tx_vq)) != NULL) + kfree(buf); + vdev->config->del_vqs(vdev); + + kfree(vt); +} + +static const struct virtio_device_id id_table[] = { + { VIRTIO_ID_TRANS, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static const unsigned int features[] = { + /* none */ +}; + + +static struct virtio_driver virtio_transfer = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = virttrans_probe, + .remove = virttrans_remove, + .config_changed = config_intr, +}; + +static int __init virtio_transfer_init(void) +{ + int err; + + err = register_virtio_driver(&virtio_transfer); + if (err < 0) { + pr_err("Failed to register virtio driver\n"); + return err; + } + return 0; +} + +static void __exit virtio_transfer_exit(void) +{ + unregister_virtio_driver(&virtio_transfer); +} + +module_init(virtio_transfer_init); +module_exit(virtio_transfer_exit); + +MODULE_DEVICE_TABLE(virtio, id_table); +MODULE_DESCRIPTION("VirtIO transfer test driver"); +MODULE_LICENSE("GPL"); diff --git a/fs/proc/consoles.c b/fs/proc/consoles.c index e0758fe7936d..2703676549f5 100644 --- a/fs/proc/consoles.c +++ b/fs/proc/consoles.c @@ -21,12 +21,14 @@ static int show_console_dev(struct seq_file *m, void *v) { CON_ENABLED, 'E' }, { CON_CONSDEV, 'C' }, { CON_BOOT, 'B' }, + { CON_NBCON, 'N' }, { CON_PRINTBUFFER, 'p' }, { CON_BRL, 'b' }, { CON_ANYTIME, 'a' }, }; char flags[ARRAY_SIZE(con_flags) + 1]; struct console *con = v; + char con_write = '-'; unsigned int a; dev_t dev = 0; @@ -57,9 +59,15 @@ static int show_console_dev(struct seq_file *m, void *v) seq_setwidth(m, 21 - 1); seq_printf(m, "%s%d", con->name, con->index); seq_pad(m, ' '); - seq_printf(m, "%c%c%c (%s)", con->read ? 'R' : '-', - con->write ? 'W' : '-', con->unblank ? 'U' : '-', - flags); + if (con->flags & CON_NBCON) { + if (con->write_atomic || con->write_thread) + con_write = 'W'; + } else { + if (con->write) + con_write = 'W'; + } + seq_printf(m, "%c%c%c (%s)", con->read ? 'R' : '-', con_write, + con->unblank ? 'U' : '-', flags); if (dev) seq_printf(m, " %4d:%d", MAJOR(dev), MINOR(dev)); diff --git a/include/dt-bindings/clock/imx8-clock.h b/include/dt-bindings/clock/imx8-clock.h index 940b408fb5ca..ef9bc8c8a80f 100644 --- a/include/dt-bindings/clock/imx8-clock.h +++ b/include/dt-bindings/clock/imx8-clock.h @@ -37,4 +37,14 @@ #define IMX_ADMA_ACM_CLK_END 25 +/* ACM GPT Event Mux Control Register Offset */ +#define IMX_ADMA_ACM_GPT0_CAPIN1_SEL 0x80004 +#define IMX_ADMA_ACM_GPT0_CAPIN2_SEL 0x80008 +#define IMX_ADMA_ACM_GPT1_CAPIN1_SEL 0x90004 +#define IMX_ADMA_ACM_GPT1_CAPIN2_SEL 0x90008 + +/* ACM GPT Event Mux Select Control */ +#define IMX_ADMA_ACM_GPT_EVENT_INPUT_ETH0 3 +#define IMX_ADMA_ACM_GPT_EVENT_INPUT_ETH1 4 + #endif /* __DT_BINDINGS_CLOCK_IMX_H */ diff --git a/include/dt-bindings/rpmsg/imx_srtm.h b/include/dt-bindings/rpmsg/imx_srtm.h new file mode 100644 index 000000000000..644ab9ceb407 --- /dev/null +++ b/include/dt-bindings/rpmsg/imx_srtm.h @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2023 NXP + * + */ + +#ifndef _DT_BINDINGS_RPMSG_IMX_SRTM_H +#define _DT_BINDINGS_RPMSG_IMX_SRTM_H + +/* Bit 0 as RPMSG Over UART flag */ +#define IMX_SRTM_RPMSG_OVER_UART_FLAG (1 << 0) +#define IMX_SRTM_UART_SUPPORT_MULTI_UART_MSG_FLAG (1 << 1) +/* [15:11]: port number, such as /dev/ttySRTM3, 3 is the port number */ +#define IMX_SRTM_UART_PORT_NUM_SHIFT (11U) +#define IMX_SRTM_UART_PORT_NUM_MASK (0x1F << 11U) +/* [10]: 0b1, specify port number; 0b0, not specify port number */ +#define IMX_SRTM_UART_SPECIFY_PORT_NUM_SHIFT (10U) +#define IMX_SRTM_UART_SPECIFY_PORT_NUM_MASK (1 << IMX_SRTM_UART_SPECIFY_PORT_NUM_SHIFT) + +#endif diff --git a/include/linux/bottom_half.h b/include/linux/bottom_half.h index fc53e0ad56d9..448bbef47456 100644 --- a/include/linux/bottom_half.h +++ b/include/linux/bottom_half.h @@ -35,8 +35,10 @@ static inline void local_bh_enable(void) #ifdef CONFIG_PREEMPT_RT extern bool local_bh_blocked(void); +extern void softirq_preempt(void); #else static inline bool local_bh_blocked(void) { return false; } +static inline void softirq_preempt(void) { } #endif #endif /* _LINUX_BH_H */ diff --git a/include/linux/clk/imx-pll.h b/include/linux/clk/imx-pll.h new file mode 100644 index 000000000000..9759ca15a6cc --- /dev/null +++ b/include/linux/clk/imx-pll.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 NXP + * + */ + +#ifndef __LINUX_CLK_IMX_PLL_H__ +#define __LINUX_CLK_IMX_PLL_H__ + +enum clk_imx_pll_error { + IMX_CLK_PLL_SUCCESS = 0, + IMX_CLK_PLL_INVALID_PARAM, + IMX_CLK_PLL_PREC_ERR, + IMX_CLK_PLL_LOCK_ERR, +}; + +struct clk_imx_pll; + +struct clk_imx_pll *clk_imx_pll_get_by_name(const char *name); +int clk_imx_pll_adjust(struct clk_imx_pll *pll, int *ppb); +unsigned long clk_imx_pll_get_rate(struct clk_imx_pll *pll, + unsigned long parent_rate); +int clk_imx_pll_set_rate(struct clk_imx_pll *pll, unsigned long rate, + unsigned long parent_rate); + +#endif /*__LINUX_CLK_IMX_PLL_H__*/ diff --git a/include/linux/console.h b/include/linux/console.h index 7de11c763eb3..1eb9580e9b18 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -16,7 +16,9 @@ #include #include +#include #include +#include #include struct vc_data; @@ -156,6 +158,8 @@ static inline int con_debug_leave(void) * /dev/kmesg which requires a larger output buffer. * @CON_SUSPENDED: Indicates if a console is suspended. If true, the * printing callbacks must not be called. + * @CON_NBCON: Console can operate outside of the legacy style console_lock + * constraints. */ enum cons_flags { CON_PRINTBUFFER = BIT(0), @@ -166,6 +170,111 @@ enum cons_flags { CON_BRL = BIT(5), CON_EXTENDED = BIT(6), CON_SUSPENDED = BIT(7), + CON_NBCON = BIT(8), +}; + +/** + * struct nbcon_state - console state for nbcon consoles + * @atom: Compound of the state fields for atomic operations + * + * @req_prio: The priority of a handover request + * @prio: The priority of the current owner + * @unsafe: Console is busy in a non takeover region + * @unsafe_takeover: A hostile takeover in an unsafe state happened in the + * past. The console cannot be safe until re-initialized. + * @cpu: The CPU on which the owner runs + * + * To be used for reading and preparing of the value stored in the nbcon + * state variable @console::nbcon_state. + * + * The @prio and @req_prio fields are particularly important to allow + * spin-waiting to timeout and give up without the risk of a waiter being + * assigned the lock after giving up. + */ +struct nbcon_state { + union { + unsigned int atom; + struct { + unsigned int prio : 2; + unsigned int req_prio : 2; + unsigned int unsafe : 1; + unsigned int unsafe_takeover : 1; + unsigned int cpu : 24; + }; + }; +}; + +/* + * The nbcon_state struct is used to easily create and interpret values that + * are stored in the @console::nbcon_state variable. Ensure this struct stays + * within the size boundaries of the atomic variable's underlying type in + * order to avoid any accidental truncation. + */ +static_assert(sizeof(struct nbcon_state) <= sizeof(int)); + +/** + * nbcon_prio - console owner priority for nbcon consoles + * @NBCON_PRIO_NONE: Unused + * @NBCON_PRIO_NORMAL: Normal (non-emergency) usage + * @NBCON_PRIO_EMERGENCY: Emergency output (WARN/OOPS...) + * @NBCON_PRIO_PANIC: Panic output + * @NBCON_PRIO_MAX: The number of priority levels + * + * A higher priority context can takeover the console when it is + * in the safe state. The final attempt to flush consoles in panic() + * can be allowed to do so even in an unsafe state (Hope and pray). + */ +enum nbcon_prio { + NBCON_PRIO_NONE = 0, + NBCON_PRIO_NORMAL, + NBCON_PRIO_EMERGENCY, + NBCON_PRIO_PANIC, + NBCON_PRIO_MAX, +}; + +struct console; +struct printk_buffers; + +/** + * struct nbcon_context - Context for console acquire/release + * @console: The associated console + * @spinwait_max_us: Limit for spin-wait acquire + * @prio: Priority of the context + * @allow_unsafe_takeover: Allow performing takeover even if unsafe. Can + * be used only with NBCON_PRIO_PANIC @prio. It + * might cause a system freeze when the console + * is used later. + * @backlog: Ringbuffer has pending records + * @pbufs: Pointer to the text buffer for this context + * @seq: The sequence number to print for this context + */ +struct nbcon_context { + /* members set by caller */ + struct console *console; + unsigned int spinwait_max_us; + enum nbcon_prio prio; + unsigned int allow_unsafe_takeover : 1; + + /* members set by emit */ + unsigned int backlog : 1; + + /* members set by acquire */ + struct printk_buffers *pbufs; + u64 seq; +}; + +/** + * struct nbcon_write_context - Context handed to the nbcon write callbacks + * @ctxt: The core console context + * @outbuf: Pointer to the text buffer for output + * @len: Length to write + * @unsafe_takeover: If a hostile takeover in an unsafe state has occurred + */ +struct nbcon_write_context { + struct nbcon_context __private ctxt; + char *outbuf; + unsigned int len; + bool unsafe_takeover; }; /** @@ -187,6 +296,17 @@ enum cons_flags { * @dropped: Number of unreported dropped ringbuffer records * @data: Driver private data * @node: hlist node for the console list + * + * @write_atomic: Write callback for atomic context + * @write_thread: Write callback for non-atomic context + * @driver_enter: Callback to begin synchronization with driver code + * @driver_exit: Callback to finish synchronization with driver code + * @nbcon_state: State for nbcon consoles + * @nbcon_seq: Sequence number of the next record for nbcon to print + * @pbufs: Pointer to nbcon private buffer + * @kthread: Printer kthread for this console + * @rcuwait: RCU-safe wait object for @kthread waking + * @irq_work: Defer @kthread waking to IRQ work context */ struct console { char name[16]; @@ -206,6 +326,20 @@ struct console { unsigned long dropped; void *data; struct hlist_node node; + + /* nbcon console specific members */ + bool (*write_atomic)(struct console *con, + struct nbcon_write_context *wctxt); + bool (*write_thread)(struct console *con, + struct nbcon_write_context *wctxt); + void (*driver_enter)(struct console *con, unsigned long *flags); + void (*driver_exit)(struct console *con, unsigned long flags); + atomic_t __private nbcon_state; + atomic_long_t __private nbcon_seq; + struct printk_buffers *pbufs; + struct task_struct *kthread; + struct rcuwait rcuwait; + struct irq_work irq_work; }; #ifdef CONFIG_LOCKDEP @@ -332,6 +466,22 @@ static inline bool console_is_registered(const struct console *con) lockdep_assert_console_list_lock_held(); \ hlist_for_each_entry(con, &console_list, node) +#ifdef CONFIG_PRINTK +extern void nbcon_cpu_emergency_enter(void); +extern void nbcon_cpu_emergency_exit(void); +extern bool nbcon_can_proceed(struct nbcon_write_context *wctxt); +extern bool nbcon_enter_unsafe(struct nbcon_write_context *wctxt); +extern bool nbcon_exit_unsafe(struct nbcon_write_context *wctxt); +extern void nbcon_reacquire(struct nbcon_write_context *wctxt); +#else +static inline void nbcon_cpu_emergency_enter(void) { } +static inline void nbcon_cpu_emergency_exit(void) { } +static inline bool nbcon_can_proceed(struct nbcon_write_context *wctxt) { return false; } +static inline bool nbcon_enter_unsafe(struct nbcon_write_context *wctxt) { return false; } +static inline bool nbcon_exit_unsafe(struct nbcon_write_context *wctxt) { return false; } +static inline void nbcon_reacquire(struct nbcon_write_context *wctxt) { } +#endif + extern int console_set_on_cmdline; extern struct console *early_console; diff --git a/include/linux/dsa/netc.h b/include/linux/dsa/netc.h new file mode 100644 index 000000000000..adafcf70ebf1 --- /dev/null +++ b/include/linux/dsa/netc.h @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2023 NXP + */ + +#ifndef _NET_DSA_NETC_H +#define _NET_DSA_NETC_H + +#include +#include +#include +#include + +#define ETH_P_NETC 0x88A8 +#define ETH_P_NETC_META 0xDADC +#define ETH_P_NETC_8021Q ETH_P_8021Q + +#define NETC_DEFAULT_VLAN 1 + +#define IFH_TAG_TYPE_C 0 +#define IFH_TAG_TYPE_S 1 + +/* IEEE 802.3 Annex 57A: Slow Protocols PDUs (01:80:C2:xx:xx:xx) */ +#define NETC_LINKLOCAL_FILTER_A 0x0180C2000000ull +#define NETC_LINKLOCAL_FILTER_A_MASK 0xFFFFFF000000ull +/* IEEE 1588 Annex F: Transport of PTP over Ethernet (01:1B:19:xx:xx:xx) */ +#define NETC_LINKLOCAL_FILTER_B 0x011B19000000ull +#define NETC_LINKLOCAL_FILTER_B_MASK 0xFFFFFF000000ull + +/* Source and Destination MAC of follow-up meta frames. + * Whereas the choice of SMAC only affects the unique identification of the + * switch as sender of meta frames, the DMAC must be an address that is present + * in the DSA master port's multicast MAC filter. + * 01-80-C2-00-00-0E is a good choice for this, as all profiles of IEEE 1588 + * over L2 use this address for some purpose already. + */ +#define NETC_META_SMAC 0x222222222222ull +#define NETC_META_DMAC 0x0180C200000Eull + +struct netc_deferred_xmit_work { + struct dsa_port *dp; + struct sk_buff *skb; + struct kthread_work work; +}; + +struct netc_skb_cb { + struct sk_buff *clone; + u64 tstamp; + u32 ts_id; +}; + +#define NETC_SKB_CB(skb) \ + ((struct netc_skb_cb *)((skb)->cb)) + +struct netc_tagger_data { + void (*meta_tstamp_handler)(struct dsa_switch *ds, int port, + u32 ts_id, u64 tstamp); + void (*meta_cmd_handler)(struct dsa_switch *ds, int port, + void *buf, size_t len); +}; + +static inline struct netc_tagger_data * +netc_tagger_data(struct dsa_switch *ds) +{ + WARN_ON_ONCE(ds->dst->tag_ops->proto != DSA_TAG_PROTO_NETC); + return ds->tagger_data; +} + +#endif /* _NET_DSA_NETC_H */ diff --git a/include/linux/dsa/sja1105.h b/include/linux/dsa/sja1105.h index c177322f793d..31779f9bc92b 100644 --- a/include/linux/dsa/sja1105.h +++ b/include/linux/dsa/sja1105.h @@ -35,6 +35,8 @@ #define SJA1105_META_SMAC 0x222222222222ull #define SJA1105_META_DMAC 0x0180C200000Eull +#define SJA1105_MAX_NUM_PCP 8 + enum sja1110_meta_tstamp { SJA1110_META_TSTAMP_TX = 0, SJA1110_META_TSTAMP_RX = 1, diff --git a/include/linux/entry-common.h b/include/linux/entry-common.h index d95ab85f96ba..8b3ab0cc1334 100644 --- a/include/linux/entry-common.h +++ b/include/linux/entry-common.h @@ -60,7 +60,7 @@ #define EXIT_TO_USER_MODE_WORK \ (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_UPROBE | \ _TIF_NEED_RESCHED | _TIF_PATCH_PENDING | _TIF_NOTIFY_SIGNAL | \ - ARCH_EXIT_TO_USER_MODE_WORK) + _TIF_NEED_RESCHED_LAZY | ARCH_EXIT_TO_USER_MODE_WORK) /** * arch_enter_from_user_mode - Architecture specific sanity check for user mode regs diff --git a/include/linux/entry-kvm.h b/include/linux/entry-kvm.h index 6813171afccb..674a622c91be 100644 --- a/include/linux/entry-kvm.h +++ b/include/linux/entry-kvm.h @@ -18,7 +18,7 @@ #define XFER_TO_GUEST_MODE_WORK \ (_TIF_NEED_RESCHED | _TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL | \ - _TIF_NOTIFY_RESUME | ARCH_XFER_TO_GUEST_MODE_WORK) + _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED_LAZY | ARCH_XFER_TO_GUEST_MODE_WORK) struct kvm_vcpu; diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 8d497be66da8..8543e5ffea39 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -752,6 +752,8 @@ struct ethtool_mm_stats { * @get_ethtool_phy_stats: Return extended statistics about the PHY device. * This is only useful if the device maintains PHY statistics and * cannot use the standard PHY library helpers. + * @get_preempt: Get the network device Frame Preemption parameters. + * @set_preempt: Set the network device Frame Preemption parameters. * @get_phy_tunable: Read the value of a PHY tunable. * @set_phy_tunable: Set the value of a PHY tunable. * @get_module_eeprom_by_page: Get a region of plug-in module EEPROM data from @@ -884,6 +886,11 @@ struct ethtool_ops { struct ethtool_fecparam *); int (*set_fecparam)(struct net_device *, struct ethtool_fecparam *); + int (*get_preempt)(struct net_device *, + struct ethtool_fp *); + int (*set_preempt)(struct net_device *, + struct ethtool_fp *); + int (*reset_preempt)(struct net_device *, bool enable); void (*get_ethtool_phy_stats)(struct net_device *, struct ethtool_stats *, u64 *); int (*get_phy_tunable)(struct net_device *, diff --git a/include/linux/fec.h b/include/linux/fec.h index 9aaf53f07269..11f1d8bd6290 100644 --- a/include/linux/fec.h +++ b/include/linux/fec.h @@ -19,4 +19,124 @@ struct fec_platform_data { void (*sleep_mode_enable)(int enabled); }; +/* The number of Tx and Rx buffers. These are allocated from the page + * pool. The code may assume these are power of two, so it it best + * to keep them that size. + * We don't need to allocate pages for the transmitter. We just use + * the skbuffer directly. + */ + +#define FEC_ENET_XDP_HEADROOM (XDP_PACKET_HEADROOM) +#define FEC_ENET_RX_PAGES 256 +#define FEC_ENET_RX_FRSIZE (PAGE_SIZE - FEC_ENET_XDP_HEADROOM \ + - SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) +#define FEC_ENET_RX_FRPPG (PAGE_SIZE / FEC_ENET_RX_FRSIZE) +#ifdef CONFIG_AVB_SUPPORT +#define FEC_RX_RING_SIZE 256 +#define FEC_ENET_AVB_RX_FRSIZE 1522 +#else +#define FEC_RX_RING_SIZE (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES) +#endif +#define FEC_ENET_TX_FRSIZE 2048 +#define FEC_ENET_TX_FRPPG (PAGE_SIZE / FEC_ENET_TX_FRSIZE) + +#ifdef CONFIG_AVB_SUPPORT +#define FEC_TX_RING_SIZE 256 /* Must be power of two */ +#else +#define FEC_TX_RING_SIZE 1024 /* Must be power of two */ +#endif + +#ifdef CONFIG_AVB_SUPPORT +struct avb_desc { + u16 offset; + u16 len; + u32 ts; + u32 flags:28; + u32 pool_type:4; + u32 private; /* Will be used for saving userspace private value on TX and esc hw descriptor value on RX */ +}; + +struct avb_tx_desc { + struct avb_desc common; + + unsigned long dma_addr; + void *data; + u32 esc; + unsigned short queue_id; + unsigned short sc; + unsigned long bufaddr; + unsigned short datlen; +}; + +struct avb_rx_desc { + struct avb_desc common; + + /* end of common rx fields */ + unsigned long dma_addr; + unsigned short sc; /* Control and status info */ + unsigned short queue_id; +}; + +#define AVB_WAKE_THREAD (1 << 0) +#define AVB_WAKE_NAPI (1 << 1) + +#define AVB_TX_FLAG_SKB (1 << 0) +#define AVB_TX_FLAG_HW_TS (1 << 1) +#define AVB_TX_FLAG_HW_CSUM (1 << 2) +#define AVB_TX_FLAG_TS (1 << 3) + +struct avb_ops { + void (*open)(void *, void *, int); + void (*close)(void *); + + void * (*alloc)(void *); + void (*free)(void *, struct avb_desc *); + + int (*rx)(void *, struct avb_rx_desc *); + void * (*dequeue)(void *); + + int (*tx)(void *, struct avb_tx_desc *); + int (*tx_full)(void *); + + int (*tx_cleanup)(void *, struct avb_tx_desc *); + int (*tx_cleanup_ready)(void *); + void * (*tx_cleanup_dequeue)(void *); + + int (*tx_ts)(void *, struct avb_desc *); + + struct module *owner; +}; + +#define TX_QUEUE_FLAGS_STRICT_PRIORITY BIT(0) +#define TX_QUEUE_FLAGS_CREDIT_SHAPER BIT(1) + +#define TX_QUEUE_PROP_MAX 8 + +struct tx_queue_property { + unsigned int priority; + unsigned int flags; +}; + +struct tx_queue_properties { + int num_queues; + struct tx_queue_property queue[TX_QUEUE_PROP_MAX]; +}; + +int fec_enet_get_tx_queue_properties(int ifindex, struct tx_queue_properties *prop); +int fec_enet_set_idle_slope(void *data, unsigned int queue_id, u32 idle_slope); +int fec_enet_avb_register(const char *ifname, const struct avb_ops *avb, void *data); +struct device *fec_enet_avb_get_device(const char *ifname); +int fec_enet_avb_unregister(int ifindex, const struct avb_ops *avb); +int fec_enet_rx_poll_avb(void *data); +int fec_enet_start_xmit_avb(void *data, struct avb_tx_desc *desc); +void fec_enet_finish_xmit_avb(void *data, unsigned int queue_id); +int fec_enet_tx_avb(void *data); + +int fec_ptp_read_cnt(void *data, u32 *cnt); +int fec_ptp_tc_start(void *data, u8 id, u32 ts_0, u32 ts_1, u32 tcsr_val); +void fec_ptp_tc_stop(void *data, u8 id); +int fec_ptp_tc_reload(void *data, u8 id, u32 ts); + +#endif + #endif diff --git a/include/linux/fsl_qman.h b/include/linux/fsl_qman.h index fd6b8a255969..ee2afdfa6f8c 100644 --- a/include/linux/fsl_qman.h +++ b/include/linux/fsl_qman.h @@ -1,4 +1,5 @@ /* Copyright 2008-2012 Freescale Semiconductor, Inc. + * Copyright 2019-2023 NXP * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -2125,6 +2126,9 @@ const cpumask_t *qman_affine_cpus(void); * member of the mask returned from qman_affine_cpus(). */ u16 qman_affine_channel(int cpu); +#ifdef CONFIG_FSL_DPAA_ETHERCAT +u16 qman_affine_channel_ethercat(int cpu); +#endif /** * qman_get_affine_portal - return the portal pointer affine to cpu @@ -2132,6 +2136,10 @@ u16 qman_affine_channel(int cpu); * */ void *qman_get_affine_portal(int cpu); +#ifdef CONFIG_FSL_DPAA_ETHERCAT +void *qman_get_affine_portal_ethercat(int cpu); +u32 qman_get_affine_last_cpu(void); +#endif /** * qman_poll_dqrr - process DQRR (fast-path) entries diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 4a1dc88ddbff..a5091ac97fc6 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -609,6 +609,35 @@ extern void __raise_softirq_irqoff(unsigned int nr); extern void raise_softirq_irqoff(unsigned int nr); extern void raise_softirq(unsigned int nr); +#ifdef CONFIG_PREEMPT_RT +DECLARE_PER_CPU(struct task_struct *, timersd); +DECLARE_PER_CPU(unsigned long, pending_timer_softirq); + +extern void raise_timer_softirq(void); +extern void raise_hrtimer_softirq(void); + +static inline unsigned int local_pending_timers(void) +{ + return __this_cpu_read(pending_timer_softirq); +} + +#else +static inline void raise_timer_softirq(void) +{ + raise_softirq(TIMER_SOFTIRQ); +} + +static inline void raise_hrtimer_softirq(void) +{ + raise_softirq_irqoff(HRTIMER_SOFTIRQ); +} + +static inline unsigned int local_pending_timers(void) +{ + return local_softirq_pending(); +} +#endif + DECLARE_PER_CPU(struct task_struct *, ksoftirqd); static inline struct task_struct *this_cpu_ksoftirqd(void) diff --git a/include/linux/ipi_baremetal.h b/include/linux/ipi_baremetal.h new file mode 100644 index 000000000000..5f124be40dcc --- /dev/null +++ b/include/linux/ipi_baremetal.h @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * include/linux/ipi_baremetal.h + * + * Copyright 2018-2023 NXP + * + */ + +#ifndef __LINUX_IPI_BAREMETAL_H +#define __LINUX_IPI_BAREMETAL_H + +#include + +#if defined(CONFIG_LS1021A_BAREMETAL) || \ + defined(CONFIG_LS1028A_BAREMETAL) || \ + defined(CONFIG_IMX93_BAREMETAL) +#define CONFIG_MAX_CPUS 2 +#elif defined(CONFIG_IMX8M_BAREMETAL) +#define CONFIG_MAX_CPUS 4 +#elif defined(CONFIG_LX2160A_BAREMETAL) +#define CONFIG_MAX_CPUS 16 +#else +#define CONFIG_MAX_CPUS 4 +#endif + +int ipi_baremetal_handle(u32 irqnr, u32 irqsrc); +#endif /* !__LINUX_IPI_BAREMETAL_H */ diff --git a/include/linux/net.h b/include/linux/net.h index c9b4a63791a4..57bcc0e9bf65 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -126,6 +126,8 @@ struct socket { const struct proto_ops *ops; /* Might change with IPV6_ADDRFORM or MPTCP. */ struct socket_wq wq; + + struct net_device *ndev; }; /* diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index b8e60a20416b..2b45c8fdafb9 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -346,6 +346,8 @@ struct gro_list { */ #define GRO_HASH_BUCKETS 8 + +#define NAPINAMSIZ 8 /* * Structure for NAPI scheduling similar to tasklet but with weighting */ @@ -380,6 +382,7 @@ struct napi_struct { /* control-path-only fields follow */ struct list_head dev_list; struct hlist_node napi_hash_node; + char name[NAPINAMSIZ]; }; enum { @@ -1414,6 +1417,9 @@ struct net_device_ops { int (*ndo_stop)(struct net_device *dev); netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb, struct net_device *dev); + struct sk_buff* (*ndo_start_recv)(struct net_device *dev, int *err); + int (*ndo_fast_recv)(struct net_device *ndev, void __user *buff, size_t len, struct sockaddr __user *addr, int __user *addr_len); + int (*ndo_fast_xmit)(struct net_device *ndev, void __user *buff, size_t len); netdev_features_t (*ndo_features_check)(struct sk_buff *skb, struct net_device *dev, netdev_features_t features); @@ -2076,6 +2082,8 @@ struct net_device { unsigned long mem_start; unsigned long base_addr; + unsigned int fast_raw_device; + /* * Some hardware also needs these fields (state,dev_list, * napi_list,unreg_list,close_list) but they are not @@ -2599,6 +2607,21 @@ void dev_net_set(struct net_device *dev, struct net *net) write_pnet(&dev->nd_net, net); } +/** + * netif_napi_add_named - initialize a NAPI context + * @dev: network device + * @napi: NAPI context + * @poll: polling function + * @weight: default weight + * @name: napi instance name + * + * netif_napi_add_named() must be used to initialize a NAPI context prior to calling + * *any* of the other NAPI-related functions. + */ +void netif_napi_add_named(struct net_device *dev, struct napi_struct *napi, + int (*poll)(struct napi_struct *, int), int weight, + const char *name); + /** * netdev_priv - access network device private data * @dev: network device @@ -2672,6 +2695,27 @@ static inline void netif_napi_add_tx(struct net_device *dev, netif_napi_add_tx_weight(dev, napi, poll, NAPI_POLL_WEIGHT); } +/** + * netif_napi_add_tx_named - initialize a NAPI context + * @dev: network device + * @napi: NAPI context + * @poll: polling function + * @weight: default weight + * @name: napi instance name + * + * This variant of netif_napi_add_named() should be used from drivers using NAPI + * to exclusively poll a TX queue. + * This will avoid we add it into napi_hash[], thus polluting this hash table. + */ +static inline void netif_napi_add_tx_named(struct net_device *dev, + struct napi_struct *napi, + int (*poll)(struct napi_struct *, int), + int weight, const char *name) +{ + set_bit(NAPI_STATE_NO_BUSY_POLL, &napi->state); + netif_napi_add_named(dev, napi, poll, weight, name); +} + /** * __netif_napi_del - remove a NAPI context * @napi: NAPI context @@ -3258,7 +3302,11 @@ struct softnet_data { int defer_count; int defer_ipi_scheduled; struct sk_buff *defer_list; +#ifndef CONFIG_PREEMPT_RT call_single_data_t defer_csd; +#else + struct work_struct defer_work; +#endif }; static inline void input_queue_head_incr(struct softnet_data *sd) diff --git a/include/linux/printk.h b/include/linux/printk.h index e4878bb58f66..ebebc32e78de 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -9,6 +9,8 @@ #include #include +struct uart_port; + extern const char linux_banner[]; extern const char linux_proc_banner[]; @@ -159,13 +161,16 @@ __printf(1, 2) __cold int _printk_deferred(const char *fmt, ...); extern void __printk_safe_enter(void); extern void __printk_safe_exit(void); +extern void __printk_deferred_enter(void); +extern void __printk_deferred_exit(void); + /* * The printk_deferred_enter/exit macros are available only as a hack for * some code paths that need to defer all printk console printing. Interrupts * must be disabled for the deferred duration. */ -#define printk_deferred_enter __printk_safe_enter -#define printk_deferred_exit __printk_safe_exit +#define printk_deferred_enter() __printk_deferred_enter() +#define printk_deferred_exit() __printk_deferred_exit() /* * Please don't use printk_ratelimit(), because it shares ratelimiting state @@ -192,6 +197,10 @@ void show_regs_print_info(const char *log_lvl); extern asmlinkage void dump_stack_lvl(const char *log_lvl) __cold; extern asmlinkage void dump_stack(void) __cold; void printk_trigger_flush(void); +void printk_legacy_allow_panic_sync(void); +extern void nbcon_acquire(struct uart_port *up); +extern void nbcon_release(struct uart_port *up); +void nbcon_atomic_flush_unsafe(void); #else static inline __printf(1, 0) int vprintk(const char *s, va_list args) @@ -271,6 +280,23 @@ static inline void dump_stack(void) static inline void printk_trigger_flush(void) { } + +static inline void printk_legacy_allow_panic_sync(void) +{ +} + +static inline void nbcon_acquire(struct uart_port *up) +{ +} + +static inline void nbcon_release(struct uart_port *up) +{ +} + +static inline void nbcon_atomic_flush_unsafe(void) +{ +} + #endif #ifdef CONFIG_SMP diff --git a/include/linux/rpmsg/imx_srtm.h b/include/linux/rpmsg/imx_srtm.h new file mode 100644 index 000000000000..7c0c53916cce --- /dev/null +++ b/include/linux/rpmsg/imx_srtm.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2022-2023 NXP + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * @file linux/rpmsg/imx_srtm.h + * + * @brief Global header file for iMX SRTM (Simplified Real Time Message Application Protocol, base on rpmsg) + * + * @ingroup SRTM + */ +#ifndef __LINUX_RPMSG_IMX_SRTM_H__ +#define __LINUX_RPMSG_IMX_SRTM_H__ + +/* Category define */ +#define IMX_SRTM_CATEGORY_LIFECYCLE (0x1) +#define IMX_SRTM_CATEGORY_PMIC (0x2) +#define IMX_SRTM_CATEGORY_AUDIO (0x3) +#define IMX_SRTM_CATEGORY_KEY (0x4) +#define IMX_SRTM_CATEGORY_GPIO (0x5) +#define IMX_SRTM_CATEGORY_RTC (0x6) +#define IMX_SRTM_CATEGORY_SENSOR (0x7) +#define IMX_SRTM_CATEGORY_AUTO (0x8) +#define IMX_SRTM_CATEGORY_I2C (0x9) +#define IMX_SRTM_CATEGORY_PWM (0xA) +#define IMX_SRTM_CATEGORY_UART (0xB) + +/* srtm version */ +#define IMX_SRTM_VER_UART (0x0001) + +/* type */ +#define IMX_SRTM_TYPE_REQUEST (0) +#define IMX_SRTM_TYPE_RESPONSE (1) +#define IMX_SRTM_TYPE_NOTIFY (2) + +/* command */ +#define IMX_SRTM_UART_COMMAND_SEND (1) +#define IMX_SRTM_UART_COMMAND_HELLO (2) + +/* priority */ +#define IMX_SRTM_UART_PRIORITY (0x01) + +/* flags */ +#define IMX_SRTM_RPMSG_OVER_UART_FLAG (1 << 0) +#define IMX_SRTM_UART_SUPPORT_MULTI_UART_MSG_FLAG (1 << 1) +#define IMX_SRTM_UART_PORT_NUM_SHIFT (11U) +#define IMX_SRTM_UART_PORT_NUM_MASK (0x1F << 11U) +#define IMX_SRTM_UART_SPECIFY_PORT_NUM_SHIFT (10U) +#define IMX_SRTM_UART_SPECIFY_PORT_NUM_MASK (1 << IMX_SRTM_UART_SPECIFY_PORT_NUM_SHIFT) + +struct imx_srtm_head { + u8 cate; + u8 major; + u8 minor; + u8 type; + u8 cmd; + u8 reserved[5]; +} __packed; + +#endif /* __LINUX_RPMSG_IMX_SRTM_H__ */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 77f01ac385f7..c02fd12b49dc 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -911,6 +911,9 @@ struct task_struct { * ->sched_remote_wakeup gets used, so it can be in this word. */ unsigned sched_remote_wakeup:1; +#ifdef CONFIG_RT_MUTEXES + unsigned sched_rt_mutex:1; +#endif /* Bit to tell LSMs we're in execve(): */ unsigned in_execve:1; @@ -1902,6 +1905,7 @@ static inline int dl_task_check_affinity(struct task_struct *p, const struct cpu } #endif +extern bool task_is_pi_boosted(const struct task_struct *p); extern int yield_to(struct task_struct *p, bool preempt); extern void set_user_nice(struct task_struct *p, long nice); extern int task_prio(const struct task_struct *p); @@ -2046,17 +2050,17 @@ static inline void update_tsk_thread_flag(struct task_struct *tsk, int flag, update_ti_thread_flag(task_thread_info(tsk), flag, value); } -static inline int test_and_set_tsk_thread_flag(struct task_struct *tsk, int flag) +static inline bool test_and_set_tsk_thread_flag(struct task_struct *tsk, int flag) { return test_and_set_ti_thread_flag(task_thread_info(tsk), flag); } -static inline int test_and_clear_tsk_thread_flag(struct task_struct *tsk, int flag) +static inline bool test_and_clear_tsk_thread_flag(struct task_struct *tsk, int flag) { return test_and_clear_ti_thread_flag(task_thread_info(tsk), flag); } -static inline int test_tsk_thread_flag(struct task_struct *tsk, int flag) +static inline bool test_tsk_thread_flag(struct task_struct *tsk, int flag) { return test_ti_thread_flag(task_thread_info(tsk), flag); } @@ -2069,9 +2073,11 @@ static inline void set_tsk_need_resched(struct task_struct *tsk) static inline void clear_tsk_need_resched(struct task_struct *tsk) { clear_tsk_thread_flag(tsk,TIF_NEED_RESCHED); + if (IS_ENABLED(CONFIG_PREEMPT_BUILD_AUTO)) + clear_tsk_thread_flag(tsk, TIF_NEED_RESCHED_LAZY); } -static inline int test_tsk_need_resched(struct task_struct *tsk) +static inline bool test_tsk_need_resched(struct task_struct *tsk) { return unlikely(test_tsk_thread_flag(tsk,TIF_NEED_RESCHED)); } @@ -2252,7 +2258,7 @@ static inline int rwlock_needbreak(rwlock_t *lock) static __always_inline bool need_resched(void) { - return unlikely(tif_need_resched()); + return unlikely(tif_need_resched_lazy() || tif_need_resched()); } /* diff --git a/include/linux/sched/idle.h b/include/linux/sched/idle.h index 478084f9105e..719416fe8ddc 100644 --- a/include/linux/sched/idle.h +++ b/include/linux/sched/idle.h @@ -63,7 +63,7 @@ static __always_inline bool __must_check current_set_polling_and_test(void) */ smp_mb__after_atomic(); - return unlikely(tif_need_resched()); + return unlikely(need_resched()); } static __always_inline bool __must_check current_clr_polling_and_test(void) @@ -76,7 +76,7 @@ static __always_inline bool __must_check current_clr_polling_and_test(void) */ smp_mb__after_atomic(); - return unlikely(tif_need_resched()); + return unlikely(need_resched()); } #else @@ -85,11 +85,11 @@ static inline void __current_clr_polling(void) { } static inline bool __must_check current_set_polling_and_test(void) { - return unlikely(tif_need_resched()); + return unlikely(need_resched()); } static inline bool __must_check current_clr_polling_and_test(void) { - return unlikely(tif_need_resched()); + return unlikely(need_resched()); } #endif diff --git a/include/linux/sched/rt.h b/include/linux/sched/rt.h index 994c25640e15..b2b9e6eb9683 100644 --- a/include/linux/sched/rt.h +++ b/include/linux/sched/rt.h @@ -30,6 +30,10 @@ static inline bool task_is_realtime(struct task_struct *tsk) } #ifdef CONFIG_RT_MUTEXES +extern void rt_mutex_pre_schedule(void); +extern void rt_mutex_schedule(void); +extern void rt_mutex_post_schedule(void); + /* * Must hold either p->pi_lock or task_rq(p)->lock. */ diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index be65de65fe61..ec46e3b49ee9 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -153,6 +153,8 @@ struct uart_8250_port { #define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA unsigned char msr_saved_flags; + bool console_newline_needed; + struct uart_8250_dma *dma; const struct uart_8250_ops *ops; @@ -204,6 +206,10 @@ void serial8250_init_port(struct uart_8250_port *up); void serial8250_set_defaults(struct uart_8250_port *up); void serial8250_console_write(struct uart_8250_port *up, const char *s, unsigned int count); +bool serial8250_console_write_atomic(struct uart_8250_port *up, + struct nbcon_write_context *wctxt); +bool serial8250_console_write_thread(struct uart_8250_port *up, + struct nbcon_write_context *wctxt); int serial8250_console_setup(struct uart_port *port, char *options, bool probe); int serial8250_console_exit(struct uart_port *port); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 052df85dfd59..6de3d7aab17d 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -489,6 +489,7 @@ struct uart_port { struct uart_icount icount; /* statistics */ struct console *cons; /* struct console, if any */ + bool nbcon_locked_port; /* True, if the port is locked by nbcon */ /* flags must be updated while holding port mutex */ upf_t flags; @@ -596,6 +597,7 @@ struct uart_port { static inline void uart_port_lock(struct uart_port *up) { spin_lock(&up->lock); + nbcon_acquire(up); } /** @@ -605,6 +607,7 @@ static inline void uart_port_lock(struct uart_port *up) static inline void uart_port_lock_irq(struct uart_port *up) { spin_lock_irq(&up->lock); + nbcon_acquire(up); } /** @@ -615,6 +618,7 @@ static inline void uart_port_lock_irq(struct uart_port *up) static inline void uart_port_lock_irqsave(struct uart_port *up, unsigned long *flags) { spin_lock_irqsave(&up->lock, *flags); + nbcon_acquire(up); } /** @@ -625,7 +629,11 @@ static inline void uart_port_lock_irqsave(struct uart_port *up, unsigned long *f */ static inline bool uart_port_trylock(struct uart_port *up) { - return spin_trylock(&up->lock); + if (!spin_trylock(&up->lock)) + return false; + + nbcon_acquire(up); + return true; } /** @@ -637,7 +645,11 @@ static inline bool uart_port_trylock(struct uart_port *up) */ static inline bool uart_port_trylock_irqsave(struct uart_port *up, unsigned long *flags) { - return spin_trylock_irqsave(&up->lock, *flags); + if (!spin_trylock_irqsave(&up->lock, *flags)) + return false; + + nbcon_acquire(up); + return true; } /** @@ -646,6 +658,7 @@ static inline bool uart_port_trylock_irqsave(struct uart_port *up, unsigned long */ static inline void uart_port_unlock(struct uart_port *up) { + nbcon_release(up); spin_unlock(&up->lock); } @@ -655,6 +668,7 @@ static inline void uart_port_unlock(struct uart_port *up) */ static inline void uart_port_unlock_irq(struct uart_port *up) { + nbcon_release(up); spin_unlock_irq(&up->lock); } @@ -664,6 +678,19 @@ static inline void uart_port_unlock_irq(struct uart_port *up) * @flags: The saved interrupt flags for restore */ static inline void uart_port_unlock_irqrestore(struct uart_port *up, unsigned long flags) +{ + nbcon_release(up); + spin_unlock_irqrestore(&up->lock, flags); +} + +/* Only for use in the console->driver_enter() callback. */ +static inline void __uart_port_lock_irqsave(struct uart_port *up, unsigned long *flags) +{ + spin_lock_irqsave(&up->lock, *flags); +} + +/* Only for use in the console->driver_exit() callback. */ +static inline void __uart_port_unlock_irqrestore(struct uart_port *up, unsigned long flags) { spin_unlock_irqrestore(&up->lock, flags); } @@ -1078,14 +1105,14 @@ static inline void uart_unlock_and_check_sysrq(struct uart_port *port) u8 sysrq_ch; if (!port->has_sysrq) { - spin_unlock(&port->lock); + uart_port_unlock(port); return; } sysrq_ch = port->sysrq_ch; port->sysrq_ch = 0; - spin_unlock(&port->lock); + uart_port_unlock(port); if (sysrq_ch) handle_sysrq(sysrq_ch); @@ -1097,14 +1124,14 @@ static inline void uart_unlock_and_check_sysrq_irqrestore(struct uart_port *port u8 sysrq_ch; if (!port->has_sysrq) { - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); return; } sysrq_ch = port->sysrq_ch; port->sysrq_ch = 0; - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); if (sysrq_ch) handle_sysrq(sysrq_ch); @@ -1120,12 +1147,12 @@ static inline int uart_prepare_sysrq_char(struct uart_port *port, u8 ch) } static inline void uart_unlock_and_check_sysrq(struct uart_port *port) { - spin_unlock(&port->lock); + uart_port_unlock(port); } static inline void uart_unlock_and_check_sysrq_irqrestore(struct uart_port *port, unsigned long flags) { - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } #endif /* CONFIG_MAGIC_SYSRQ_SERIAL */ diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h index 9ea0b28068f4..5ded1450ac1a 100644 --- a/include/linux/thread_info.h +++ b/include/linux/thread_info.h @@ -59,6 +59,16 @@ enum syscall_work_bit { #include +#ifdef CONFIG_PREEMPT_BUILD_AUTO +# define TIF_NEED_RESCHED_LAZY TIF_ARCH_RESCHED_LAZY +# define _TIF_NEED_RESCHED_LAZY _TIF_ARCH_RESCHED_LAZY +# define TIF_NEED_RESCHED_LAZY_OFFSET (TIF_NEED_RESCHED_LAZY - TIF_NEED_RESCHED) +#else +# define TIF_NEED_RESCHED_LAZY TIF_NEED_RESCHED +# define _TIF_NEED_RESCHED_LAZY _TIF_NEED_RESCHED +# define TIF_NEED_RESCHED_LAZY_OFFSET 0 +#endif + #ifdef __KERNEL__ #ifndef arch_set_restart_data @@ -185,6 +195,13 @@ static __always_inline bool tif_need_resched(void) (unsigned long *)(¤t_thread_info()->flags)); } +static __always_inline bool tif_need_resched_lazy(void) +{ + return IS_ENABLED(CONFIG_PREEMPT_BUILD_AUTO) && + arch_test_bit(TIF_NEED_RESCHED_LAZY, + (unsigned long *)(¤t_thread_info()->flags)); +} + #else static __always_inline bool tif_need_resched(void) @@ -193,6 +210,13 @@ static __always_inline bool tif_need_resched(void) (unsigned long *)(¤t_thread_info()->flags)); } +static __always_inline bool tif_need_resched_lazy(void) +{ + return IS_ENABLED(CONFIG_PREEMPT_BUILD_AUTO) && + test_bit(TIF_NEED_RESCHED_LAZY, + (unsigned long *)(¤t_thread_info()->flags)); +} + #endif /* _ASM_GENERIC_BITOPS_INSTRUMENTED_NON_ATOMIC_H */ #ifndef CONFIG_HAVE_ARCH_WITHIN_STACK_FRAMES diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index cb8bd759e800..ce1fefc37cb5 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h @@ -178,8 +178,8 @@ unsigned int tracing_gen_ctx_irq_test(unsigned int irqs_status); enum trace_flag_type { TRACE_FLAG_IRQS_OFF = 0x01, - TRACE_FLAG_IRQS_NOSUPPORT = 0x02, - TRACE_FLAG_NEED_RESCHED = 0x04, + TRACE_FLAG_NEED_RESCHED = 0x02, + TRACE_FLAG_NEED_RESCHED_LAZY = 0x04, TRACE_FLAG_HARDIRQ = 0x08, TRACE_FLAG_SOFTIRQ = 0x10, TRACE_FLAG_PREEMPT_RESCHED = 0x20, @@ -205,11 +205,11 @@ static inline unsigned int tracing_gen_ctx(void) static inline unsigned int tracing_gen_ctx_flags(unsigned long irqflags) { - return tracing_gen_ctx_irq_test(TRACE_FLAG_IRQS_NOSUPPORT); + return tracing_gen_ctx_irq_test(0); } static inline unsigned int tracing_gen_ctx(void) { - return tracing_gen_ctx_irq_test(TRACE_FLAG_IRQS_NOSUPPORT); + return tracing_gen_ctx_irq_test(0); } #endif diff --git a/include/net/dsa.h b/include/net/dsa.h index 0b9c6aa27047..d6c3e3457401 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -56,6 +56,7 @@ struct phylink_link_state; #define DSA_TAG_PROTO_RTL8_4T_VALUE 25 #define DSA_TAG_PROTO_RZN1_A5PSW_VALUE 26 #define DSA_TAG_PROTO_LAN937X_VALUE 27 +#define DSA_TAG_PROTO_NETC_VALUE 28 enum dsa_tag_protocol { DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, @@ -86,6 +87,7 @@ enum dsa_tag_protocol { DSA_TAG_PROTO_RTL8_4T = DSA_TAG_PROTO_RTL8_4T_VALUE, DSA_TAG_PROTO_RZN1_A5PSW = DSA_TAG_PROTO_RZN1_A5PSW_VALUE, DSA_TAG_PROTO_LAN937X = DSA_TAG_PROTO_LAN937X_VALUE, + DSA_TAG_PROTO_NETC = DSA_TAG_PROTO_NETC_VALUE, }; struct dsa_switch; @@ -956,6 +958,22 @@ struct dsa_switch_ops { int (*port_del_dscp_prio)(struct dsa_switch *ds, int port, u8 dscp, u8 prio); + /* + * ethtool --set-frame-preemption + */ + int (*set_preempt)(struct dsa_switch *ds, int port, + struct ethtool_fp *fpcmd); + + /* + * ethtool --show-frame-preemption + */ + int (*get_preempt)(struct dsa_switch *ds, int port, + struct ethtool_fp *fpcmd); + + /* + * ethtool --reset-frame-preemption + */ + int (*reset_preempt)(struct dsa_switch *ds, int port, bool enable); /* * Suspend and resume */ diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h index 9efa9a59e81f..e8c472ce2bfb 100644 --- a/include/net/flow_offload.h +++ b/include/net/flow_offload.h @@ -184,6 +184,7 @@ enum flow_action_id { FLOW_ACTION_VLAN_PUSH_ETH, FLOW_ACTION_VLAN_POP_ETH, FLOW_ACTION_CONTINUE, + FLOW_ACTION_FRER, NUM_FLOW_ACTIONS, }; @@ -327,6 +328,14 @@ struct flow_action_entry { struct { /* FLOW_ACTION_PPPOE_PUSH */ u16 sid; } pppoe; + struct { + u8 tag_type; + u8 tag_action; + u8 recover; + u8 rcvy_alg; + u8 rcvy_history_len; + u16 rcvy_reset_msec; + } frer; }; struct flow_action_cookie *user_cookie; /* user defined action cookie */ }; diff --git a/include/net/switchdev.h b/include/net/switchdev.h index 8346b0d29542..990b1a430b81 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -97,6 +97,7 @@ struct switchdev_obj_port_vlan { struct switchdev_obj obj; u16 flags; u16 vid; + u16 proto; /* If set, the notifier signifies a change of one of the following * flags for a VLAN that already exists: * - BRIDGE_VLAN_INFO_PVID diff --git a/include/net/tc_act/tc_frer.h b/include/net/tc_act/tc_frer.h new file mode 100644 index 000000000000..b2ad2b2a3fe1 --- /dev/null +++ b/include/net/tc_act/tc_frer.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright 2021 NXP */ + +#ifndef __NET_TC_FRER_H +#define __NET_TC_FRER_H + +#include +#include + +struct tcf_frer; + +struct tcf_frer_proto_ops { + int (*encode)(struct sk_buff *skb, struct tcf_frer *frer_act); + int (*decode)(struct sk_buff *skb); + void (*tag_pop)(struct sk_buff *skb, struct tcf_frer *frer_act); +}; + +struct tcf_frer { + struct tc_action common; + u8 tag_type; + u8 tag_action; + u8 recover; + u8 rcvy_alg; + u8 rcvy_history_len; + u64 rcvy_reset_msec; + u32 gen_seq_num; + u32 rcvy_seq_num; + u64 seq_space; + u32 seq_history; + bool take_any; + bool rcvy_take_noseq; + u32 cps_seq_rcvy_lost_pkts; + u32 cps_seq_rcvy_tagless_pkts; + u32 cps_seq_rcvy_out_of_order_pkts; + u32 cps_seq_rcvy_rogue_pkts; + u32 cps_seq_rcvy_resets; + struct hrtimer hrtimer; + const struct tcf_frer_proto_ops *proto_ops; +}; + +#define to_frer(a) ((struct tcf_frer *)a) + +static inline bool is_tcf_frer(const struct tc_action *a) +{ +#ifdef CONFIG_NET_CLS_ACT + if (a->ops && a->ops->id == TCA_ID_FRER) + return true; +#endif + return false; +} + +#endif diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index 4d41baa6e0e1..87613b5746f5 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -11,6 +11,9 @@ #include #include +#define OCELOT_MAX_PTP_ID 63 +#define OCELOT_PTP_FIFO_SIZE 128 + struct phy; struct tc_mqprio_qopt_offload; @@ -788,7 +791,6 @@ struct ocelot_port { phy_interface_t phy_mode; struct phy *serdes; - unsigned int ptp_skbs_in_flight; struct sk_buff_head tx_skbs; unsigned int trap_proto; @@ -796,12 +798,12 @@ struct ocelot_port { u16 mrp_ring_id; u8 ptp_cmd; - u8 ts_id; u8 index; u8 stp_state; bool vlan_aware; + bool qinq_mode; bool is_dsa_8021q_cpu; bool learn_ena; @@ -812,8 +814,11 @@ struct ocelot_port { bool force_forward; u8 cut_thru; u8 cut_thru_selected_by_user; + bool fp_enabled_admin; int speed; + DECLARE_BITMAP(ts_id_in_flight, OCELOT_MAX_PTP_ID); + unsigned long ptp_tx_time[OCELOT_MAX_PTP_ID]; }; struct ocelot { @@ -902,6 +907,9 @@ struct ocelot { struct ocelot_mm_state *mm; + bool qinq_enable; + struct kref qinq_refcount; + struct ocelot_fdma *fdma; }; diff --git a/include/soc/mscc/ocelot_ptp.h b/include/soc/mscc/ocelot_ptp.h index f085884b1fa2..ded497d72bdb 100644 --- a/include/soc/mscc/ocelot_ptp.h +++ b/include/soc/mscc/ocelot_ptp.h @@ -13,9 +13,6 @@ #include #include -#define OCELOT_MAX_PTP_ID 63 -#define OCELOT_PTP_FIFO_SIZE 128 - #define PTP_PIN_CFG_RSZ 0x20 #define PTP_PIN_TOD_SEC_MSB_RSZ PTP_PIN_CFG_RSZ #define PTP_PIN_TOD_SEC_LSB_RSZ PTP_PIN_CFG_RSZ diff --git a/include/soc/mscc/ocelot_vcap.h b/include/soc/mscc/ocelot_vcap.h index c601a4598b0d..f8cde2408890 100644 --- a/include/soc/mscc/ocelot_vcap.h +++ b/include/soc/mscc/ocelot_vcap.h @@ -704,6 +704,7 @@ struct ocelot_vcap_filter { enum ocelot_vcap_bit dmac_mc; enum ocelot_vcap_bit dmac_bc; struct ocelot_vcap_key_vlan vlan; + struct ocelot_vcap_key_vlan cvlan; enum ocelot_vcap_key_type key_type; union { diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index 421eb57fb6e9..b7a7aa00f499 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -379,6 +379,32 @@ struct ethtool_eee { __u32 reserved[2]; }; +/** + * struct ethtool_fp - Frame Preemption information + * @cmd: ETHTOOL_{G,S}FP + * @disabled: Disable hardware preemption supports. + * @fp_supported: If frame preemption is supported. + * @fp_enabled: If frame preemption should be advertised to the link partner + * as enabled. + * @supported_queues_mask: Bitmask indicating which queues support being + * configured as preemptible (bit 0 -> queue 0, bit N -> queue N). + * @preemptible_queues_mask: Bitmask indicating which queues are + * configured as preemptible (bit 0 -> queue 0, bit N -> queue N). + * @min_frag_size: Minimum size for all non-final fragment size. + */ +struct ethtool_fp { + __u32 cmd; + __u8 disabled; + __u8 fp_supported; + __u8 fp_enabled; + __u8 fp_status; + __u8 fp_active; + __u32 supported_queues_mask; + __u32 preemptible_queues_mask; + __u32 min_frag_size; + __u32 reserved[2]; +}; + /** * struct ethtool_modinfo - plugin module eeprom information * @cmd: %ETHTOOL_GMODULEINFO @@ -1671,6 +1697,10 @@ enum ethtool_fec_config_bits { #define ETHTOOL_GFECPARAM 0x00000050 /* Get FEC settings */ #define ETHTOOL_SFECPARAM 0x00000051 /* Set FEC settings */ +#define ETHTOOL_GFP 0x00000052 /* Get Frame Preemption settings */ +#define ETHTOOL_SFP 0x00000053 /* Set Frame Preemption settings */ +#define ETHTOOL_RFP 0x00000054 /* Reset Frame Preemption settings */ + /* compatibility with older code */ #define SPARC_ETH_GSET ETHTOOL_GSET #define SPARC_ETH_SSET ETHTOOL_SSET diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index 73e2c10dc2cc..45010bfc11ca 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -41,6 +41,8 @@ enum { ETHTOOL_MSG_TSINFO_GET, ETHTOOL_MSG_CABLE_TEST_ACT, ETHTOOL_MSG_CABLE_TEST_TDR_ACT, + ETHTOOL_MSG_PREEMPT_GET, + ETHTOOL_MSG_PREEMPT_SET, ETHTOOL_MSG_TUNNEL_INFO_GET, ETHTOOL_MSG_FEC_GET, ETHTOOL_MSG_FEC_SET, @@ -94,6 +96,8 @@ enum { ETHTOOL_MSG_TSINFO_GET_REPLY, ETHTOOL_MSG_CABLE_TEST_NTF, ETHTOOL_MSG_CABLE_TEST_TDR_NTF, + ETHTOOL_MSG_PREEMPT_GET_REPLY, + ETHTOOL_MSG_PREEMPT_NTF, ETHTOOL_MSG_TUNNEL_INFO_GET_REPLY, ETHTOOL_MSG_FEC_GET_REPLY, ETHTOOL_MSG_FEC_NTF, @@ -975,6 +979,23 @@ enum { ETHTOOL_A_MM_MAX = (__ETHTOOL_A_MM_CNT - 1) }; +/* FRAME PREEMPTION */ +enum { + ETHTOOL_A_PREEMPT_UNSPEC, + ETHTOOL_A_PREEMPT_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_PREEMPT_DISABLED, /* bool */ + ETHTOOL_A_PREEMPT_SUPPORTED, /* u8 */ + ETHTOOL_A_PREEMPT_STATUS, /* u8 */ + ETHTOOL_A_PREEMPT_ACTIVE, /* u8 */ + ETHTOOL_A_PREEMPT_MIN_FRAG_SIZE, /* u32 */ + ETHTOOL_A_PREEMPT_QUEUES_SUPPORTED, /* u32 */ + ETHTOOL_A_PREEMPT_QUEUES_PREEMPTIBLE, /* u32 */ + + /* add new constants above here */ + __ETHTOOL_A_PREEMPT_CNT, + ETHTOOL_A_PREEMPT_MAX = (__ETHTOOL_A_PREEMPT_CNT - 1) +}; + /* generic netlink info */ #define ETHTOOL_GENL_NAME "ethtool" #define ETHTOOL_GENL_VERSION 1 diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h index 69e0457eb200..9bdcecde2be8 100644 --- a/include/uapi/linux/if_ether.h +++ b/include/uapi/linux/if_ether.h @@ -119,6 +119,7 @@ #define ETH_P_DSA_A5PSW 0xE001 /* A5PSW Tag Value [ NOT AN OFFICIALLY REGISTERED ID ] */ #define ETH_P_IFE 0xED3E /* ForCES inter-FE LFB type */ #define ETH_P_AF_IUCV 0xFBFB /* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_RTAG 0xF1C1 /* Redundancy Tag(IEEE 802.1CB) */ #define ETH_P_802_3_MIN 0x0600 /* If the value in the ethernet type is more than this value * then the frame is Ethernet II. Else it is 802.3 */ diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h index c7082cc60d21..930eb82f3e20 100644 --- a/include/uapi/linux/pkt_cls.h +++ b/include/uapi/linux/pkt_cls.h @@ -139,6 +139,7 @@ enum tca_id { TCA_ID_MPLS, TCA_ID_CT, TCA_ID_GATE, + TCA_ID_FRER, /* other actions go here */ __TCA_ID_MAX = 255 }; diff --git a/include/uapi/linux/ptp_clock.h b/include/uapi/linux/ptp_clock.h index 05cc35fc94ac..e9f63daa4d06 100644 --- a/include/uapi/linux/ptp_clock.h +++ b/include/uapi/linux/ptp_clock.h @@ -199,6 +199,17 @@ struct ptp_pin_desc { unsigned int rsv[5]; }; +#define PTP_MAX_CONVERT_TS_NUM 16 /* Maximum allowed number of timestamps to convert. */ + +struct ptp_convert_timestamps { + /* src timestamp to convert in the ptp device clock domain. */ + struct ptp_clock_time src_ts[PTP_MAX_CONVERT_TS_NUM]; + /* converted timestamp in the destination ptp device clock domain. */ + struct ptp_clock_time dst_ts[PTP_MAX_CONVERT_TS_NUM]; + unsigned int dst_phc_index; /* destination ptp clock domain. */ + unsigned int n_ts; /* Number of timestamps in src_ts array. */ +}; + #define PTP_CLK_MAGIC '=' #define PTP_CLOCK_GETCAPS _IOR(PTP_CLK_MAGIC, 1, struct ptp_clock_caps) @@ -224,6 +235,7 @@ struct ptp_pin_desc { _IOWR(PTP_CLK_MAGIC, 17, struct ptp_sys_offset_precise) #define PTP_SYS_OFFSET_EXTENDED2 \ _IOWR(PTP_CLK_MAGIC, 18, struct ptp_sys_offset_extended) +#define PTP_CONVERT_TIMESTAMPS _IOW(PTP_CLK_MAGIC, 21, struct ptp_convert_timestamps) struct ptp_extts_event { struct ptp_clock_time t; /* Time event occured. */ diff --git a/include/uapi/linux/tc_act/tc_frer.h b/include/uapi/linux/tc_act/tc_frer.h new file mode 100644 index 000000000000..fe2420337633 --- /dev/null +++ b/include/uapi/linux/tc_act/tc_frer.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* Copyright 2021 NXP */ + +#ifndef __LINUX_TC_FRER_H +#define __LINUX_TC_FRER_H + +#include + +struct tc_frer { + tc_gen; +}; + +enum { + TCA_FRER_UNSPEC, + TCA_FRER_TM, + TCA_FRER_PARMS, + TCA_FRER_PAD, + TCA_FRER_TAG_TYPE, + TCA_FRER_TAG_ACTION, + TCA_FRER_RECOVER, + TCA_FRER_RECOVER_ALG, + TCA_FRER_RECOVER_HISTORY_LEN, + TCA_FRER_RECOVER_RESET_TM, + TCA_FRER_RECOVER_TAGLESS_PKTS, + TCA_FRER_RECOVER_OUT_OF_ORDER_PKTS, + TCA_FRER_RECOVER_ROGUE_PKTS, + TCA_FRER_RECOVER_LOST_PKTS, + TCA_FRER_RECOVER_RESETS, + __TCA_FRER_MAX, +}; +#define TCA_FRER_MAX (__TCA_FRER_MAX - 1) + +enum tc_frer_tag_action { + TCA_FRER_TAG_NULL, + TCA_FRER_TAG_PUSH, + TCA_FRER_TAG_POP, +}; + +enum tc_frer_tag_type { + TCA_FRER_TAG_RTAG = 1, + TCA_FRER_TAG_HSR, + TCA_FRER_TAG_PRP, +}; + +enum tc_frer_rcvy_alg { + TCA_FRER_RCVY_VECTOR_ALG, + TCA_FRER_RCVY_MATCH_ALG, +}; + +#endif diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h index 7aa2eb766205..67f7fe400730 100644 --- a/include/uapi/linux/virtio_ids.h +++ b/include/uapi/linux/virtio_ids.h @@ -68,6 +68,7 @@ #define VIRTIO_ID_AUDIO_POLICY 39 /* virtio audio policy */ #define VIRTIO_ID_BT 40 /* virtio bluetooth */ #define VIRTIO_ID_GPIO 41 /* virtio gpio */ +#define VIRTIO_ID_TRANS 42 /* virtio transfer test */ /* * Virtio Transitional IDs diff --git a/include/uapi/linux/virtio_mmio.h b/include/uapi/linux/virtio_mmio.h index 0650f91bea6c..ee672e1b4a6e 100644 --- a/include/uapi/linux/virtio_mmio.h +++ b/include/uapi/linux/virtio_mmio.h @@ -110,6 +110,9 @@ /* Device status register - Read Write */ #define VIRTIO_MMIO_STATUS 0x070 +/* Hypervisor-less virtio: Device write operation done status register */ +#define VIRTIO_MMIO_WD_STATUS 0x074 + /* Selected queue's Descriptor Table address, 64 bits in two halves */ #define VIRTIO_MMIO_QUEUE_DESC_LOW 0x080 #define VIRTIO_MMIO_QUEUE_DESC_HIGH 0x084 @@ -140,7 +143,9 @@ * the per-driver configuration space - Read Write */ #define VIRTIO_MMIO_CONFIG 0x100 - +/* Hypervisor-less virtio: write operation space shared with remote */ +#define VIRTIO_MMIO_RW_OPS_MEM_OFFSET 0x400 +#define VIRTIO_MMIO_RW_OPS_MEM_SIZE 64 /* * Interrupt flags (re: interrupt status & acknowledge registers) diff --git a/include/uapi/linux/virtio_trans.h b/include/uapi/linux/virtio_trans.h new file mode 100644 index 000000000000..24a10efb370e --- /dev/null +++ b/include/uapi/linux/virtio_trans.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright 2022 NXP + */ + +#ifndef _LINUX_VIRTIO_TRANS_H +#define _LINUX_VIRTIO_TRANS_H + +#include + +/* + * status: + * bit0: TX ready + * bit1: RX ready + * config: + * bit0: TX + * bit1: RX + * bit2: 0: Backend do NOT copy buffer + * 1: Backend do copy buffer + * bit3: 0: Backend interrupt mode + * 1: Backend polling mode + * bit4: 0: Frontend interrupt mode + * 1: Frontend polling mode + * control: + * bit0: start transfer + * bit1: reset + * pkt_size: packet size in Byte + * tx_count: Completed count for TX packet, update by Backend + * rx_count: Completed count for RX packet, update by Backend + * regression: Packets to test, set by Frontend + */ + +#define VT_STATUS 0x0 +#define VT_CONFIG 0x4 +#define VT_CONTROL 0x8 +#define VT_PKT_SIZE 0xc +#define VT_TX_COUNT 0x10 +#define VT_RX_COUNT 0x14 +#define VT_REGRESSION 0x18 + +#define VT_CFG_TX BIT(0) +#define VT_CFG_RX BIT(1) +#define VT_CFG_COPY BIT(2) +#define VT_CFG_B_POLL BIT(3) +#define VT_CFG_F_POLL BIT(4) + +#define VT_CTRL_START BIT(0) +#define VT_CTRL_RESET BIT(1) + +struct virtio_trans_config { + __le32 status; + __le32 config; + __le32 control; + __le32 pkt_size; + __le32 tx_count; + __le32 rx_count; + __le32 regression; +}; + +#endif /* _LINUX_VIRTIO_TRANS_H */ diff --git a/kernel/Kconfig.preempt b/kernel/Kconfig.preempt index c2f1fd95a821..0f3d4c2a41cb 100644 --- a/kernel/Kconfig.preempt +++ b/kernel/Kconfig.preempt @@ -11,6 +11,13 @@ config PREEMPT_BUILD select PREEMPTION select UNINLINE_SPIN_UNLOCK if !ARCH_INLINE_SPIN_UNLOCK +config PREEMPT_BUILD_AUTO + bool + select PREEMPT_BUILD + +config HAVE_PREEMPT_AUTO + bool + choice prompt "Preemption Model" default PREEMPT_NONE @@ -67,9 +74,17 @@ config PREEMPT embedded system with latency requirements in the milliseconds range. +config PREEMPT_AUTO + bool "Automagic preemption mode with runtime tweaking support" + depends on HAVE_PREEMPT_AUTO + select PREEMPT_BUILD_AUTO + help + Add some sensible blurb here + config PREEMPT_RT bool "Fully Preemptible Kernel (Real-Time)" depends on EXPERT && ARCH_SUPPORTS_RT + select PREEMPT_BUILD_AUTO if HAVE_PREEMPT_AUTO select PREEMPTION help This option turns the kernel into a real-time kernel by replacing @@ -95,7 +110,7 @@ config PREEMPTION config PREEMPT_DYNAMIC bool "Preemption behaviour defined on boot" - depends on HAVE_PREEMPT_DYNAMIC && !PREEMPT_RT + depends on HAVE_PREEMPT_DYNAMIC && !PREEMPT_RT && !PREEMPT_AUTO select JUMP_LABEL if HAVE_PREEMPT_DYNAMIC_KEY select PREEMPT_BUILD default y if HAVE_PREEMPT_DYNAMIC_CALL diff --git a/kernel/dma/coherent.c b/kernel/dma/coherent.c index ff5683a57f77..3c659063b73a 100644 --- a/kernel/dma/coherent.c +++ b/kernel/dma/coherent.c @@ -36,7 +36,8 @@ static inline dma_addr_t dma_get_device_base(struct device *dev, } static struct dma_coherent_mem *dma_init_coherent_memory(phys_addr_t phys_addr, - dma_addr_t device_addr, size_t size, bool use_dma_pfn_offset) + dma_addr_t device_addr, size_t size, bool use_dma_pfn_offset, + bool cacheable) { struct dma_coherent_mem *dma_mem; int pages = size >> PAGE_SHIFT; @@ -45,7 +46,8 @@ static struct dma_coherent_mem *dma_init_coherent_memory(phys_addr_t phys_addr, if (!size) return ERR_PTR(-EINVAL); - mem_base = memremap(phys_addr, size, MEMREMAP_WC); + mem_base = memremap(phys_addr, size, cacheable ? MEMREMAP_WB : + MEMREMAP_WC); if (!mem_base) return ERR_PTR(-EINVAL); @@ -119,8 +121,10 @@ int dma_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr, { struct dma_coherent_mem *mem; int ret; + bool cacheable = dev_is_dma_coherent(dev); - mem = dma_init_coherent_memory(phys_addr, device_addr, size, false); + mem = dma_init_coherent_memory(phys_addr, device_addr, size, false, + cacheable); if (IS_ERR(mem)) return PTR_ERR(mem); @@ -312,7 +316,7 @@ int dma_init_global_coherent(phys_addr_t phys_addr, size_t size) { struct dma_coherent_mem *mem; - mem = dma_init_coherent_memory(phys_addr, phys_addr, size, true); + mem = dma_init_coherent_memory(phys_addr, phys_addr, size, true, false); if (IS_ERR(mem)) return PTR_ERR(mem); dma_coherent_default_memory = mem; @@ -337,9 +341,10 @@ static int rmem_dma_device_init(struct reserved_mem *rmem, struct device *dev) { if (!rmem->priv) { struct dma_coherent_mem *mem; + bool cacheable = dev_is_dma_coherent(dev); mem = dma_init_coherent_memory(rmem->base, rmem->base, - rmem->size, true); + rmem->size, true, cacheable); if (IS_ERR(mem)) return PTR_ERR(mem); rmem->priv = mem; diff --git a/kernel/entry/common.c b/kernel/entry/common.c index ccf2b1e1b40b..f70d2a164de1 100644 --- a/kernel/entry/common.c +++ b/kernel/entry/common.c @@ -161,7 +161,7 @@ static unsigned long exit_to_user_mode_loop(struct pt_regs *regs, local_irq_enable_exit_to_user(ti_work); - if (ti_work & _TIF_NEED_RESCHED) + if (ti_work & (_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY)) schedule(); if (ti_work & _TIF_UPROBE) @@ -392,7 +392,7 @@ void raw_irqentry_exit_cond_resched(void) rcu_irq_exit_check_preempt(); if (IS_ENABLED(CONFIG_DEBUG_ENTRY)) WARN_ON_ONCE(!on_thread_stack()); - if (need_resched()) + if (test_tsk_need_resched(current)) preempt_schedule_irq(); } } diff --git a/kernel/entry/kvm.c b/kernel/entry/kvm.c index 2e0f75bcb7fd..d952fa5ee880 100644 --- a/kernel/entry/kvm.c +++ b/kernel/entry/kvm.c @@ -13,7 +13,7 @@ static int xfer_to_guest_mode_work(struct kvm_vcpu *vcpu, unsigned long ti_work) return -EINTR; } - if (ti_work & _TIF_NEED_RESCHED) + if (ti_work & (_TIF_NEED_RESCHED | TIF_NEED_RESCHED_LAZY)) schedule(); if (ti_work & _TIF_NOTIFY_RESUME) diff --git a/kernel/futex/pi.c b/kernel/futex/pi.c index ce2889f12375..d636a1bbd7d0 100644 --- a/kernel/futex/pi.c +++ b/kernel/futex/pi.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include "futex.h" @@ -610,29 +611,16 @@ int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb, /* * Caller must hold a reference on @pi_state. */ -static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_pi_state *pi_state) +static int wake_futex_pi(u32 __user *uaddr, u32 uval, + struct futex_pi_state *pi_state, + struct rt_mutex_waiter *top_waiter) { - struct rt_mutex_waiter *top_waiter; struct task_struct *new_owner; bool postunlock = false; DEFINE_RT_WAKE_Q(wqh); u32 curval, newval; int ret = 0; - top_waiter = rt_mutex_top_waiter(&pi_state->pi_mutex); - if (WARN_ON_ONCE(!top_waiter)) { - /* - * As per the comment in futex_unlock_pi() this should not happen. - * - * When this happens, give up our locks and try again, giving - * the futex_lock_pi() instance time to complete, either by - * waiting on the rtmutex or removing itself from the futex - * queue. - */ - ret = -EAGAIN; - goto out_unlock; - } - new_owner = top_waiter->task; /* @@ -1002,6 +990,12 @@ int futex_lock_pi(u32 __user *uaddr, unsigned int flags, ktime_t *time, int tryl goto no_block; } + /* + * Must be done before we enqueue the waiter, here is unfortunately + * under the hb lock, but that *should* work because it does nothing. + */ + rt_mutex_pre_schedule(); + rt_mutex_init_waiter(&rt_waiter); /* @@ -1039,19 +1033,37 @@ int futex_lock_pi(u32 __user *uaddr, unsigned int flags, ktime_t *time, int tryl ret = rt_mutex_wait_proxy_lock(&q.pi_state->pi_mutex, to, &rt_waiter); cleanup: - spin_lock(q.lock_ptr); /* * If we failed to acquire the lock (deadlock/signal/timeout), we must - * first acquire the hb->lock before removing the lock from the - * rt_mutex waitqueue, such that we can keep the hb and rt_mutex wait - * lists consistent. + * must unwind the above, however we canont lock hb->lock because + * rt_mutex already has a waiter enqueued and hb->lock can itself try + * and enqueue an rt_waiter through rtlock. + * + * Doing the cleanup without holding hb->lock can cause inconsistent + * state between hb and pi_state, but only in the direction of not + * seeing a waiter that is leaving. + * + * See futex_unlock_pi(), it deals with this inconsistency. + * + * There be dragons here, since we must deal with the inconsistency on + * the way out (here), it is impossible to detect/warn about the race + * the other way around (missing an incoming waiter). * - * In particular; it is important that futex_unlock_pi() can not - * observe this inconsistency. + * What could possibly go wrong... */ if (ret && !rt_mutex_cleanup_proxy_lock(&q.pi_state->pi_mutex, &rt_waiter)) ret = 0; + /* + * Now that the rt_waiter has been dequeued, it is safe to use + * spinlock/rtlock (which might enqueue its own rt_waiter) and fix up + * the + */ + spin_lock(q.lock_ptr); + /* + * Waiter is unqueued. + */ + rt_mutex_post_schedule(); no_block: /* * Fixup the pi_state owner and possibly acquire the lock if we @@ -1132,6 +1144,7 @@ int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) top_waiter = futex_top_waiter(hb, &key); if (top_waiter) { struct futex_pi_state *pi_state = top_waiter->pi_state; + struct rt_mutex_waiter *rt_waiter; ret = -EINVAL; if (!pi_state) @@ -1144,22 +1157,39 @@ int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) if (pi_state->owner != current) goto out_unlock; - get_pi_state(pi_state); /* * By taking wait_lock while still holding hb->lock, we ensure - * there is no point where we hold neither; and therefore - * wake_futex_p() must observe a state consistent with what we - * observed. + * there is no point where we hold neither; and thereby + * wake_futex_pi() must observe any new waiters. + * + * Since the cleanup: case in futex_lock_pi() removes the + * rt_waiter without holding hb->lock, it is possible for + * wake_futex_pi() to not find a waiter while the above does, + * in this case the waiter is on the way out and it can be + * ignored. * * In particular; this forces __rt_mutex_start_proxy() to * complete such that we're guaranteed to observe the - * rt_waiter. Also see the WARN in wake_futex_pi(). + * rt_waiter. */ raw_spin_lock_irq(&pi_state->pi_mutex.wait_lock); + + /* + * Futex vs rt_mutex waiter state -- if there are no rt_mutex + * waiters even though futex thinks there are, then the waiter + * is leaving and the uncontended path is safe to take. + */ + rt_waiter = rt_mutex_top_waiter(&pi_state->pi_mutex); + if (!rt_waiter) { + raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock); + goto do_uncontended; + } + + get_pi_state(pi_state); spin_unlock(&hb->lock); /* drops pi_state->pi_mutex.wait_lock */ - ret = wake_futex_pi(uaddr, uval, pi_state); + ret = wake_futex_pi(uaddr, uval, pi_state, rt_waiter); put_pi_state(pi_state); @@ -1187,6 +1217,7 @@ int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) return ret; } +do_uncontended: /* * We have no kernel internal state, i.e. no waiters in the * kernel. Waiters which are about to queue themselves are stuck diff --git a/kernel/futex/requeue.c b/kernel/futex/requeue.c index cba8b1a6a4cc..4c73e0b81acc 100644 --- a/kernel/futex/requeue.c +++ b/kernel/futex/requeue.c @@ -850,11 +850,13 @@ int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags, pi_mutex = &q.pi_state->pi_mutex; ret = rt_mutex_wait_proxy_lock(pi_mutex, to, &rt_waiter); - /* Current is not longer pi_blocked_on */ - spin_lock(q.lock_ptr); + /* + * See futex_unlock_pi()'s cleanup: comment. + */ if (ret && !rt_mutex_cleanup_proxy_lock(pi_mutex, &rt_waiter)) ret = 0; + spin_lock(q.lock_ptr); debug_rt_mutex_free_waiter(&rt_waiter); /* * Fixup the pi_state owner and possibly acquire the lock if we diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c index 1d4bc493b2f4..486c68c11bbe 100644 --- a/kernel/ksysfs.c +++ b/kernel/ksysfs.c @@ -179,6 +179,15 @@ KERNEL_ATTR_RO(crash_elfcorehdr_size); #endif /* CONFIG_CRASH_CORE */ +#if defined(CONFIG_PREEMPT_RT) +static ssize_t realtime_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", 1); +} +KERNEL_ATTR_RO(realtime); +#endif + /* whether file capabilities are enabled */ static ssize_t fscaps_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) @@ -274,6 +283,9 @@ static struct attribute * kernel_attrs[] = { #ifndef CONFIG_TINY_RCU &rcu_expedited_attr.attr, &rcu_normal_attr.attr, +#endif +#ifdef CONFIG_PREEMPT_RT + &realtime_attr.attr, #endif NULL }; diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index 151bd3de5936..5c21ba41e308 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -56,6 +56,7 @@ #include #include #include +#include #include @@ -3971,6 +3972,8 @@ print_usage_bug(struct task_struct *curr, struct held_lock *this, if (!debug_locks_off() || debug_locks_silent) return; + nbcon_cpu_emergency_enter(); + pr_warn("\n"); pr_warn("================================\n"); pr_warn("WARNING: inconsistent lock state\n"); @@ -3999,6 +4002,8 @@ print_usage_bug(struct task_struct *curr, struct held_lock *this, pr_warn("\nstack backtrace:\n"); dump_stack(); + + nbcon_cpu_emergency_exit(); } /* diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c index bf3a28ee7d8f..99129c89120a 100644 --- a/kernel/locking/rtmutex.c +++ b/kernel/locking/rtmutex.c @@ -218,6 +218,11 @@ static __always_inline bool rt_mutex_cmpxchg_acquire(struct rt_mutex_base *lock, return try_cmpxchg_acquire(&lock->owner, &old, new); } +static __always_inline bool rt_mutex_try_acquire(struct rt_mutex_base *lock) +{ + return rt_mutex_cmpxchg_acquire(lock, NULL, current); +} + static __always_inline bool rt_mutex_cmpxchg_release(struct rt_mutex_base *lock, struct task_struct *old, struct task_struct *new) @@ -297,6 +302,20 @@ static __always_inline bool rt_mutex_cmpxchg_acquire(struct rt_mutex_base *lock, } +static int __sched rt_mutex_slowtrylock(struct rt_mutex_base *lock); + +static __always_inline bool rt_mutex_try_acquire(struct rt_mutex_base *lock) +{ + /* + * With debug enabled rt_mutex_cmpxchg trylock() will always fail. + * + * Avoid unconditionally taking the slow path by using + * rt_mutex_slow_trylock() which is covered by the debug code and can + * acquire a non-contended rtmutex. + */ + return rt_mutex_slowtrylock(lock); +} + static __always_inline bool rt_mutex_cmpxchg_release(struct rt_mutex_base *lock, struct task_struct *old, struct task_struct *new) @@ -1613,7 +1632,7 @@ static int __sched rt_mutex_slowlock_block(struct rt_mutex_base *lock, raw_spin_unlock_irq(&lock->wait_lock); if (!owner || !rtmutex_spin_on_owner(lock, waiter, owner)) - schedule(); + rt_mutex_schedule(); raw_spin_lock_irq(&lock->wait_lock); set_current_state(state); @@ -1643,7 +1662,7 @@ static void __sched rt_mutex_handle_deadlock(int res, int detect_deadlock, while (1) { set_current_state(TASK_INTERRUPTIBLE); - schedule(); + rt_mutex_schedule(); } } @@ -1738,6 +1757,15 @@ static int __sched rt_mutex_slowlock(struct rt_mutex_base *lock, unsigned long flags; int ret; + /* + * Do all pre-schedule work here, before we queue a waiter and invoke + * PI -- any such work that trips on rtlock (PREEMPT_RT spinlock) would + * otherwise recurse back into task_blocks_on_rt_mutex() through + * rtlock_slowlock() and will then enqueue a second waiter for this + * same task and things get really confusing real fast. + */ + rt_mutex_pre_schedule(); + /* * Technically we could use raw_spin_[un]lock_irq() here, but this can * be called in early boot if the cmpxchg() fast path is disabled @@ -1749,6 +1777,7 @@ static int __sched rt_mutex_slowlock(struct rt_mutex_base *lock, raw_spin_lock_irqsave(&lock->wait_lock, flags); ret = __rt_mutex_slowlock_locked(lock, ww_ctx, state); raw_spin_unlock_irqrestore(&lock->wait_lock, flags); + rt_mutex_post_schedule(); return ret; } @@ -1756,7 +1785,9 @@ static int __sched rt_mutex_slowlock(struct rt_mutex_base *lock, static __always_inline int __rt_mutex_lock(struct rt_mutex_base *lock, unsigned int state) { - if (likely(rt_mutex_cmpxchg_acquire(lock, NULL, current))) + lockdep_assert(!current->pi_blocked_on); + + if (likely(rt_mutex_try_acquire(lock))) return 0; return rt_mutex_slowlock(lock, NULL, state); diff --git a/kernel/locking/rwbase_rt.c b/kernel/locking/rwbase_rt.c index 25ec0239477c..2eafd73a76eb 100644 --- a/kernel/locking/rwbase_rt.c +++ b/kernel/locking/rwbase_rt.c @@ -71,8 +71,20 @@ static int __sched __rwbase_read_lock(struct rwbase_rt *rwb, struct rt_mutex_base *rtm = &rwb->rtmutex; int ret; + rwbase_pre_schedule(); raw_spin_lock_irq(&rtm->wait_lock); + /* + * Allow readers, as long as the writer has not completely + * acquired the semaphore for write. + */ + if (atomic_read(&rwb->readers) != WRITER_BIAS) { + atomic_inc(&rwb->readers); + raw_spin_unlock_irq(&rtm->wait_lock); + rwbase_post_schedule(); + return 0; + } + /* * Call into the slow lock path with the rtmutex->wait_lock * held, so this can't result in the following race: @@ -125,12 +137,15 @@ static int __sched __rwbase_read_lock(struct rwbase_rt *rwb, rwbase_rtmutex_unlock(rtm); trace_contention_end(rwb, ret); + rwbase_post_schedule(); return ret; } static __always_inline int rwbase_read_lock(struct rwbase_rt *rwb, unsigned int state) { + lockdep_assert(!current->pi_blocked_on); + if (rwbase_read_trylock(rwb)) return 0; @@ -237,6 +252,8 @@ static int __sched rwbase_write_lock(struct rwbase_rt *rwb, /* Force readers into slow path */ atomic_sub(READER_BIAS, &rwb->readers); + rwbase_pre_schedule(); + raw_spin_lock_irqsave(&rtm->wait_lock, flags); if (__rwbase_write_trylock(rwb)) goto out_unlock; @@ -248,6 +265,7 @@ static int __sched rwbase_write_lock(struct rwbase_rt *rwb, if (rwbase_signal_pending_state(state, current)) { rwbase_restore_current_state(); __rwbase_write_unlock(rwb, 0, flags); + rwbase_post_schedule(); trace_contention_end(rwb, -EINTR); return -EINTR; } @@ -266,6 +284,7 @@ static int __sched rwbase_write_lock(struct rwbase_rt *rwb, out_unlock: raw_spin_unlock_irqrestore(&rtm->wait_lock, flags); + rwbase_post_schedule(); return 0; } diff --git a/kernel/locking/rwsem.c b/kernel/locking/rwsem.c index 11ed7ce6579e..62eac9fd809a 100644 --- a/kernel/locking/rwsem.c +++ b/kernel/locking/rwsem.c @@ -1427,8 +1427,14 @@ static inline void __downgrade_write(struct rw_semaphore *sem) #define rwbase_signal_pending_state(state, current) \ signal_pending_state(state, current) +#define rwbase_pre_schedule() \ + rt_mutex_pre_schedule() + #define rwbase_schedule() \ - schedule() + rt_mutex_schedule() + +#define rwbase_post_schedule() \ + rt_mutex_post_schedule() #include "rwbase_rt.c" diff --git a/kernel/locking/spinlock_rt.c b/kernel/locking/spinlock_rt.c index 48a19ed8486d..38e292454fcc 100644 --- a/kernel/locking/spinlock_rt.c +++ b/kernel/locking/spinlock_rt.c @@ -37,6 +37,8 @@ static __always_inline void rtlock_lock(struct rt_mutex_base *rtm) { + lockdep_assert(!current->pi_blocked_on); + if (unlikely(!rt_mutex_cmpxchg_acquire(rtm, NULL, current))) rtlock_slowlock(rtm); } @@ -184,9 +186,13 @@ static __always_inline int rwbase_rtmutex_trylock(struct rt_mutex_base *rtm) #define rwbase_signal_pending_state(state, current) (0) +#define rwbase_pre_schedule() + #define rwbase_schedule() \ schedule_rtlock() +#define rwbase_post_schedule() + #include "rwbase_rt.c" /* * The common functions which get wrapped into the rwlock API. diff --git a/kernel/locking/ww_rt_mutex.c b/kernel/locking/ww_rt_mutex.c index d1473c624105..c7196de838ed 100644 --- a/kernel/locking/ww_rt_mutex.c +++ b/kernel/locking/ww_rt_mutex.c @@ -62,7 +62,7 @@ __ww_rt_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ww_ctx, } mutex_acquire_nest(&rtm->dep_map, 0, 0, nest_lock, ip); - if (likely(rt_mutex_cmpxchg_acquire(&rtm->rtmutex, NULL, current))) { + if (likely(rt_mutex_try_acquire(&rtm->rtmutex))) { if (ww_ctx) ww_mutex_set_context_fastpath(lock, ww_ctx); return 0; diff --git a/kernel/panic.c b/kernel/panic.c index ef9f9a4e928d..9215df21d8c2 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -366,6 +366,8 @@ void panic(const char *fmt, ...) */ atomic_notifier_call_chain(&panic_notifier_list, 0, buf); + printk_legacy_allow_panic_sync(); + panic_print_sys_info(false); kmsg_dump(KMSG_DUMP_PANIC); @@ -449,6 +451,7 @@ void panic(const char *fmt, ...) * Explicitly flush the kernel log buffer one last time. */ console_flush_on_panic(CONSOLE_FLUSH_PENDING); + nbcon_atomic_flush_unsafe(); local_irq_enable(); for (i = 0; ; i += PANIC_TIMER_STEP) { @@ -627,6 +630,7 @@ bool oops_may_print(void) */ void oops_enter(void) { + nbcon_cpu_emergency_enter(); tracing_off(); /* can't trust the integrity of the kernel anymore: */ debug_locks_off(); @@ -649,6 +653,7 @@ void oops_exit(void) { do_oops_enter_exit(); print_oops_end_marker(); + nbcon_cpu_emergency_exit(); kmsg_dump(KMSG_DUMP_OOPS); } @@ -660,6 +665,8 @@ struct warn_args { void __warn(const char *file, int line, void *caller, unsigned taint, struct pt_regs *regs, struct warn_args *args) { + nbcon_cpu_emergency_enter(); + disable_trace_on_warning(); if (file) @@ -690,6 +697,8 @@ void __warn(const char *file, int line, void *caller, unsigned taint, /* Just a warning, don't kill lockdep. */ add_taint(taint, LOCKDEP_STILL_OK); + + nbcon_cpu_emergency_exit(); } #ifdef CONFIG_BUG diff --git a/kernel/printk/Makefile b/kernel/printk/Makefile index f5b388e810b9..39a2b61c7232 100644 --- a/kernel/printk/Makefile +++ b/kernel/printk/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only obj-y = printk.o -obj-$(CONFIG_PRINTK) += printk_safe.o +obj-$(CONFIG_PRINTK) += printk_safe.o nbcon.o obj-$(CONFIG_A11Y_BRAILLE_CONSOLE) += braille.o obj-$(CONFIG_PRINTK_INDEX) += index.o diff --git a/kernel/printk/internal.h b/kernel/printk/internal.h index 7d4979d5c3ce..7db6992c54f3 100644 --- a/kernel/printk/internal.h +++ b/kernel/printk/internal.h @@ -3,6 +3,8 @@ * internal.h - printk internal definitions */ #include +#include +#include "printk_ringbuffer.h" #if defined(CONFIG_PRINTK) && defined(CONFIG_SYSCTL) void __init printk_sysctl_init(void); @@ -12,6 +14,12 @@ int devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write, #define printk_sysctl_init() do { } while (0) #endif +#define con_printk(lvl, con, fmt, ...) \ + printk(lvl pr_fmt("%s%sconsole [%s%d] " fmt), \ + (con->flags & CON_NBCON) ? "" : "legacy ", \ + (con->flags & CON_BOOT) ? "boot" : "", \ + con->name, con->index, ##__VA_ARGS__) + #ifdef CONFIG_PRINTK #ifdef CONFIG_PRINTK_CALLER @@ -35,6 +43,19 @@ enum printk_info_flags { LOG_CONT = 8, /* text is a fragment of a continuation line */ }; +extern struct printk_ringbuffer *prb; +extern bool printk_threads_enabled; +extern bool have_legacy_console; +extern bool have_boot_console; + +/* + * Specifies if the console lock/unlock dance is needed for console + * printing. If @have_boot_console is true, the nbcon consoles will + * be printed serially along with the legacy consoles because nbcon + * consoles cannot print simultaneously with boot consoles. + */ +#define printing_via_unlock (have_legacy_console || have_boot_console) + __printf(4, 0) int vprintk_store(int facility, int level, const struct dev_printk_info *dev_info, @@ -61,12 +82,90 @@ void defer_console_output(void); u16 printk_parse_prefix(const char *text, int *level, enum printk_info_flags *flags); +void console_lock_spinning_enable(void); +int console_lock_spinning_disable_and_check(int cookie); + +u64 nbcon_seq_read(struct console *con); +void nbcon_seq_force(struct console *con, u64 seq); +bool nbcon_alloc(struct console *con); +void nbcon_init(struct console *con); +void nbcon_free(struct console *con); +enum nbcon_prio nbcon_get_default_prio(void); +void nbcon_atomic_flush_all(void); +bool nbcon_atomic_emit_next_record(struct console *con, bool *handover, int cookie); +void nbcon_kthread_create(struct console *con); +void nbcon_wake_threads(void); +void nbcon_legacy_kthread_create(void); + +/* + * Check if the given console is currently capable and allowed to print + * records. Note that this function does not consider the current context, + * which can also play a role in deciding if @con can be used to print + * records. + */ +static inline bool console_is_usable(struct console *con, short flags, bool use_atomic) +{ + if (!(flags & CON_ENABLED)) + return false; + + if ((flags & CON_SUSPENDED)) + return false; + + if (flags & CON_NBCON) { + if (use_atomic) { + if (!con->write_atomic) + return false; + } else { + if (!con->write_thread || !con->kthread) + return false; + } + } else { + if (!con->write) + return false; + } + + /* + * Console drivers may assume that per-cpu resources have been + * allocated. So unless they're explicitly marked as being able to + * cope (CON_ANYTIME) don't call them until this CPU is officially up. + */ + if (!cpu_online(raw_smp_processor_id()) && !(flags & CON_ANYTIME)) + return false; + + return true; +} + +/** + * nbcon_kthread_wake - Wake up a printk thread + * @con: Console to operate on + */ +static inline void nbcon_kthread_wake(struct console *con) +{ + /* + * Guarantee any new records can be seen by tasks preparing to wait + * before this context checks if the rcuwait is empty. + * + * The full memory barrier in rcuwait_wake_up() pairs with the full + * memory barrier within set_current_state() of + * ___rcuwait_wait_event(), which is called after prepare_to_rcuwait() + * adds the waiter but before it has checked the wait condition. + * + * This pairs with nbcon_kthread_func:A. + */ + rcuwait_wake_up(&con->rcuwait); /* LMM(nbcon_kthread_wake:A) */ +} + #else #define PRINTK_PREFIX_MAX 0 #define PRINTK_MESSAGE_MAX 0 #define PRINTKRB_RECORD_MAX 0 +static inline void nbcon_kthread_wake(struct console *con) { } +static inline void nbcon_kthread_create(struct console *con) { } +#define printk_threads_enabled (false) +#define printing_via_unlock (false) + /* * In !PRINTK builds we still export console_sem * semaphore and some of console functions (console_unlock()/etc.), so @@ -76,8 +175,23 @@ u16 printk_parse_prefix(const char *text, int *level, #define printk_safe_exit_irqrestore(flags) local_irq_restore(flags) static inline bool printk_percpu_data_ready(void) { return false; } +static inline u64 nbcon_seq_read(struct console *con) { return 0; } +static inline void nbcon_seq_force(struct console *con, u64 seq) { } +static inline bool nbcon_alloc(struct console *con) { return false; } +static inline void nbcon_init(struct console *con) { } +static inline void nbcon_free(struct console *con) { } +static inline enum nbcon_prio nbcon_get_default_prio(void) { return NBCON_PRIO_NONE; } +static inline void nbcon_atomic_flush_all(void) { } +static inline bool nbcon_atomic_emit_next_record(struct console *con, bool *handover, + int cookie) { return false; } + +static inline bool console_is_usable(struct console *con, short flags, + bool use_atomic) { return false; } + #endif /* CONFIG_PRINTK */ +extern struct printk_buffers printk_shared_pbufs; + /** * struct printk_buffers - Buffers to read/format/output printk messages. * @outbuf: After formatting, contains text to output. @@ -105,3 +219,10 @@ struct printk_message { }; bool other_cpu_in_panic(void); +bool this_cpu_in_panic(void); +bool printk_get_next_message(struct printk_message *pmsg, u64 seq, + bool is_extended, bool may_supress); + +#ifdef CONFIG_PRINTK +void console_prepend_dropped(struct printk_message *pmsg, unsigned long dropped); +#endif diff --git a/kernel/printk/nbcon.c b/kernel/printk/nbcon.c new file mode 100644 index 000000000000..b941039ee7d2 --- /dev/null +++ b/kernel/printk/nbcon.c @@ -0,0 +1,1665 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2022 Linutronix GmbH, John Ogness +// Copyright (C) 2022 Intel, Thomas Gleixner + +#include +#include +#include +#include +#include +#include +#include +#include "printk_ringbuffer.h" +#include "internal.h" +/* + * Printk console printing implementation for consoles which does not depend + * on the legacy style console_lock mechanism. + * + * The state of the console is maintained in the "nbcon_state" atomic + * variable. + * + * The console is locked when: + * + * - The 'prio' field contains the priority of the context that owns the + * console. Only higher priority contexts are allowed to take over the + * lock. A value of 0 (NBCON_PRIO_NONE) means the console is not locked. + * + * - The 'cpu' field denotes on which CPU the console is locked. It is used + * to prevent busy waiting on the same CPU. Also it informs the lock owner + * that it has lost the lock in a more complex scenario when the lock was + * taken over by a higher priority context, released, and taken on another + * CPU with the same priority as the interrupted owner. + * + * The acquire mechanism uses a few more fields: + * + * - The 'req_prio' field is used by the handover approach to make the + * current owner aware that there is a context with a higher priority + * waiting for the friendly handover. + * + * - The 'unsafe' field allows to take over the console in a safe way in the + * middle of emitting a message. The field is set only when accessing some + * shared resources or when the console device is manipulated. It can be + * cleared, for example, after emitting one character when the console + * device is in a consistent state. + * + * - The 'unsafe_takeover' field is set when a hostile takeover took the + * console in an unsafe state. The console will stay in the unsafe state + * until re-initialized. + * + * The acquire mechanism uses three approaches: + * + * 1) Direct acquire when the console is not owned or is owned by a lower + * priority context and is in a safe state. + * + * 2) Friendly handover mechanism uses a request/grant handshake. It is used + * when the current owner has lower priority and the console is in an + * unsafe state. + * + * The requesting context: + * + * a) Sets its priority into the 'req_prio' field. + * + * b) Waits (with a timeout) for the owning context to unlock the + * console. + * + * c) Takes the lock and clears the 'req_prio' field. + * + * The owning context: + * + * a) Observes the 'req_prio' field set on exit from the unsafe + * console state. + * + * b) Gives up console ownership by clearing the 'prio' field. + * + * 3) Unsafe hostile takeover allows to take over the lock even when the + * console is an unsafe state. It is used only in panic() by the final + * attempt to flush consoles in a try and hope mode. + * + * Note that separate record buffers are used in panic(). As a result, + * the messages can be read and formatted without any risk even after + * using the hostile takeover in unsafe state. + * + * The release function simply clears the 'prio' field. + * + * All operations on @console::nbcon_state are atomic cmpxchg based to + * handle concurrency. + * + * The acquire/release functions implement only minimal policies: + * + * - Preference for higher priority contexts. + * - Protection of the panic CPU. + * + * All other policy decisions must be made at the call sites: + * + * - What is marked as an unsafe section. + * - Whether to spin-wait if there is already an owner and the console is + * in an unsafe state. + * - Whether to attempt an unsafe hostile takeover. + * + * The design allows to implement the well known: + * + * acquire() + * output_one_printk_record() + * release() + * + * The output of one printk record might be interrupted with a higher priority + * context. The new owner is supposed to reprint the entire interrupted record + * from scratch. + */ + +/** + * nbcon_state_set - Helper function to set the console state + * @con: Console to update + * @new: The new state to write + * + * Only to be used when the console is not yet or no longer visible in the + * system. Otherwise use nbcon_state_try_cmpxchg(). + */ +static inline void nbcon_state_set(struct console *con, struct nbcon_state *new) +{ + atomic_set(&ACCESS_PRIVATE(con, nbcon_state), new->atom); +} + +/** + * nbcon_state_read - Helper function to read the console state + * @con: Console to read + * @state: The state to store the result + */ +static inline void nbcon_state_read(struct console *con, struct nbcon_state *state) +{ + state->atom = atomic_read(&ACCESS_PRIVATE(con, nbcon_state)); +} + +/** + * nbcon_state_try_cmpxchg() - Helper function for atomic_try_cmpxchg() on console state + * @con: Console to update + * @cur: Old/expected state + * @new: New state + * + * Return: True on success. False on fail and @cur is updated. + */ +static inline bool nbcon_state_try_cmpxchg(struct console *con, struct nbcon_state *cur, + struct nbcon_state *new) +{ + return atomic_try_cmpxchg(&ACCESS_PRIVATE(con, nbcon_state), &cur->atom, new->atom); +} + +/** + * nbcon_seq_read - Read the current console sequence + * @con: Console to read the sequence of + * + * Return: Sequence number of the next record to print on @con. + */ +u64 nbcon_seq_read(struct console *con) +{ + unsigned long nbcon_seq = atomic_long_read(&ACCESS_PRIVATE(con, nbcon_seq)); + + return __ulseq_to_u64seq(prb, nbcon_seq); +} + +/** + * nbcon_seq_force - Force console sequence to a specific value + * @con: Console to work on + * @seq: Sequence number value to set + * + * Only to be used during init (before registration) or in extreme situations + * (such as panic with CONSOLE_REPLAY_ALL). + */ +void nbcon_seq_force(struct console *con, u64 seq) +{ + /* + * If the specified record no longer exists, the oldest available record + * is chosen. This is especially important on 32bit systems because only + * the lower 32 bits of the sequence number are stored. The upper 32 bits + * are derived from the sequence numbers available in the ringbuffer. + */ + u64 valid_seq = max_t(u64, seq, prb_first_valid_seq(prb)); + + atomic_long_set(&ACCESS_PRIVATE(con, nbcon_seq), __u64seq_to_ulseq(valid_seq)); + + /* Clear con->seq since nbcon consoles use con->nbcon_seq instead. */ + con->seq = 0; +} + +/** + * nbcon_seq_try_update - Try to update the console sequence number + * @ctxt: Pointer to an acquire context that contains + * all information about the acquire mode + * @new_seq: The new sequence number to set + * + * @ctxt->seq is updated to the new value of @con::nbcon_seq (expanded to + * the 64bit value). This could be a different value than @new_seq if + * nbcon_seq_force() was used or the current context no longer owns the + * console. In the later case, it will stop printing anyway. + */ +static void nbcon_seq_try_update(struct nbcon_context *ctxt, u64 new_seq) +{ + unsigned long nbcon_seq = __u64seq_to_ulseq(ctxt->seq); + struct console *con = ctxt->console; + + if (atomic_long_try_cmpxchg(&ACCESS_PRIVATE(con, nbcon_seq), &nbcon_seq, + __u64seq_to_ulseq(new_seq))) { + ctxt->seq = new_seq; + } else { + ctxt->seq = nbcon_seq_read(con); + } +} + +bool printk_threads_enabled __ro_after_init; + +/** + * nbcon_context_try_acquire_direct - Try to acquire directly + * @ctxt: The context of the caller + * @cur: The current console state + * + * Acquire the console when it is released. Also acquire the console when + * the current owner has a lower priority and the console is in a safe state. + * + * Return: 0 on success. Otherwise, an error code on failure. Also @cur + * is updated to the latest state when failed to modify it. + * + * Errors: + * + * -EPERM: A panic is in progress and this is not the panic CPU. + * Or the current owner or waiter has the same or higher + * priority. No acquire method can be successful in + * this case. + * + * -EBUSY: The current owner has a lower priority but the console + * in an unsafe state. The caller should try using + * the handover acquire method. + */ +static int nbcon_context_try_acquire_direct(struct nbcon_context *ctxt, + struct nbcon_state *cur) +{ + unsigned int cpu = smp_processor_id(); + struct console *con = ctxt->console; + struct nbcon_state new; + + do { + if (other_cpu_in_panic()) + return -EPERM; + + if (ctxt->prio <= cur->prio || ctxt->prio <= cur->req_prio) + return -EPERM; + + if (cur->unsafe) + return -EBUSY; + + /* + * The console should never be safe for a direct acquire + * if an unsafe hostile takeover has ever happened. + */ + WARN_ON_ONCE(cur->unsafe_takeover); + + new.atom = cur->atom; + new.prio = ctxt->prio; + new.req_prio = NBCON_PRIO_NONE; + new.unsafe = cur->unsafe_takeover; + new.cpu = cpu; + + } while (!nbcon_state_try_cmpxchg(con, cur, &new)); + + return 0; +} + +static bool nbcon_waiter_matches(struct nbcon_state *cur, int expected_prio) +{ + /* + * The request context is well defined by the @req_prio because: + * + * - Only a context with a higher priority can take over the request. + * - There are only three priorities. + * - Only one CPU is allowed to request PANIC priority. + * - Lower priorities are ignored during panic() until reboot. + * + * As a result, the following scenario is *not* possible: + * + * 1. Another context with a higher priority directly takes ownership. + * 2. The higher priority context releases the ownership. + * 3. A lower priority context takes the ownership. + * 4. Another context with the same priority as this context + * creates a request and starts waiting. + */ + + return (cur->req_prio == expected_prio); +} + +/** + * nbcon_context_try_acquire_requested - Try to acquire after having + * requested a handover + * @ctxt: The context of the caller + * @cur: The current console state + * + * This is a helper function for nbcon_context_try_acquire_handover(). + * It is called when the console is in an unsafe state. The current + * owner will release the console on exit from the unsafe region. + * + * Return: 0 on success and @cur is updated to the new console state. + * Otherwise an error code on failure. + * + * Errors: + * + * -EPERM: A panic is in progress and this is not the panic CPU + * or this context is no longer the waiter. + * + * -EBUSY: The console is still locked. The caller should + * continue waiting. + * + * Note: The caller must still remove the request when an error has occurred + * except when this context is no longer the waiter. + */ +static int nbcon_context_try_acquire_requested(struct nbcon_context *ctxt, + struct nbcon_state *cur) +{ + unsigned int cpu = smp_processor_id(); + struct console *con = ctxt->console; + struct nbcon_state new; + + /* Note that the caller must still remove the request! */ + if (other_cpu_in_panic()) + return -EPERM; + + /* + * Note that the waiter will also change if there was an unsafe + * hostile takeover. + */ + if (!nbcon_waiter_matches(cur, ctxt->prio)) + return -EPERM; + + /* If still locked, caller should continue waiting. */ + if (cur->prio != NBCON_PRIO_NONE) + return -EBUSY; + + /* + * The previous owner should have never released ownership + * in an unsafe region. + */ + WARN_ON_ONCE(cur->unsafe); + + new.atom = cur->atom; + new.prio = ctxt->prio; + new.req_prio = NBCON_PRIO_NONE; + new.unsafe = cur->unsafe_takeover; + new.cpu = cpu; + + if (!nbcon_state_try_cmpxchg(con, cur, &new)) { + /* + * The acquire could fail only when it has been taken + * over by a higher priority context. + */ + WARN_ON_ONCE(nbcon_waiter_matches(cur, ctxt->prio)); + return -EPERM; + } + + /* Handover success. This context now owns the console. */ + return 0; +} + +/** + * nbcon_context_try_acquire_handover - Try to acquire via handover + * @ctxt: The context of the caller + * @cur: The current console state + * + * The function must be called only when the context has higher priority + * than the current owner and the console is in an unsafe state. + * It is the case when nbcon_context_try_acquire_direct() returns -EBUSY. + * + * The function sets "req_prio" field to make the current owner aware of + * the request. Then it waits until the current owner releases the console, + * or an even higher context takes over the request, or timeout expires. + * + * The current owner checks the "req_prio" field on exit from the unsafe + * region and releases the console. It does not touch the "req_prio" field + * so that the console stays reserved for the waiter. + * + * Return: 0 on success. Otherwise, an error code on failure. Also @cur + * is updated to the latest state when failed to modify it. + * + * Errors: + * + * -EPERM: A panic is in progress and this is not the panic CPU. + * Or a higher priority context has taken over the + * console or the handover request. + * + * -EBUSY: The current owner is on the same CPU so that the hand + * shake could not work. Or the current owner is not + * willing to wait (zero timeout). Or the console does + * not enter the safe state before timeout passed. The + * caller might still use the unsafe hostile takeover + * when allowed. + * + * -EAGAIN: @cur has changed when creating the handover request. + * The caller should retry with direct acquire. + */ +static int nbcon_context_try_acquire_handover(struct nbcon_context *ctxt, + struct nbcon_state *cur) +{ + unsigned int cpu = smp_processor_id(); + struct console *con = ctxt->console; + struct nbcon_state new; + int timeout; + int request_err = -EBUSY; + + /* + * Check that the handover is called when the direct acquire failed + * with -EBUSY. + */ + WARN_ON_ONCE(ctxt->prio <= cur->prio || ctxt->prio <= cur->req_prio); + WARN_ON_ONCE(!cur->unsafe); + + /* Handover is not possible on the same CPU. */ + if (cur->cpu == cpu) + return -EBUSY; + + /* + * Console stays unsafe after an unsafe takeover until re-initialized. + * Waiting is not going to help in this case. + */ + if (cur->unsafe_takeover) + return -EBUSY; + + /* Is the caller willing to wait? */ + if (ctxt->spinwait_max_us == 0) + return -EBUSY; + + /* + * Setup a request for the handover. The caller should try to acquire + * the console directly when the current state has been modified. + */ + new.atom = cur->atom; + new.req_prio = ctxt->prio; + if (!nbcon_state_try_cmpxchg(con, cur, &new)) + return -EAGAIN; + + cur->atom = new.atom; + + /* Wait until there is no owner and then acquire the console. */ + for (timeout = ctxt->spinwait_max_us; timeout >= 0; timeout--) { + /* On successful acquire, this request is cleared. */ + request_err = nbcon_context_try_acquire_requested(ctxt, cur); + if (!request_err) + return 0; + + /* + * If the acquire should be aborted, it must be ensured + * that the request is removed before returning to caller. + */ + if (request_err == -EPERM) + break; + + udelay(1); + + /* Re-read the state because some time has passed. */ + nbcon_state_read(con, cur); + } + + /* Timed out or aborted. Carefully remove handover request. */ + do { + /* + * No need to remove request if there is a new waiter. This + * can only happen if a higher priority context has taken over + * the console or the handover request. + */ + if (!nbcon_waiter_matches(cur, ctxt->prio)) + return -EPERM; + + /* Unset request for handover. */ + new.atom = cur->atom; + new.req_prio = NBCON_PRIO_NONE; + if (nbcon_state_try_cmpxchg(con, cur, &new)) { + /* + * Request successfully unset. Report failure of + * acquiring via handover. + */ + cur->atom = new.atom; + return request_err; + } + + /* + * Unable to remove request. Try to acquire in case + * the owner has released the lock. + */ + } while (nbcon_context_try_acquire_requested(ctxt, cur)); + + /* Lucky timing. The acquire succeeded while removing the request. */ + return 0; +} + +/** + * nbcon_context_try_acquire_hostile - Acquire via unsafe hostile takeover + * @ctxt: The context of the caller + * @cur: The current console state + * + * Acquire the console even in the unsafe state. + * + * It can be permitted by setting the 'allow_unsafe_takeover' field only + * by the final attempt to flush messages in panic(). + * + * Return: 0 on success. -EPERM when not allowed by the context. + */ +static int nbcon_context_try_acquire_hostile(struct nbcon_context *ctxt, + struct nbcon_state *cur) +{ + unsigned int cpu = smp_processor_id(); + struct console *con = ctxt->console; + struct nbcon_state new; + + if (!ctxt->allow_unsafe_takeover) + return -EPERM; + + /* Ensure caller is allowed to perform unsafe hostile takeovers. */ + if (WARN_ON_ONCE(ctxt->prio != NBCON_PRIO_PANIC)) + return -EPERM; + + /* + * Check that try_acquire_direct() and try_acquire_handover() returned + * -EBUSY in the right situation. + */ + WARN_ON_ONCE(ctxt->prio <= cur->prio || ctxt->prio <= cur->req_prio); + WARN_ON_ONCE(cur->unsafe != true); + + do { + new.atom = cur->atom; + new.cpu = cpu; + new.prio = ctxt->prio; + new.unsafe |= cur->unsafe_takeover; + new.unsafe_takeover |= cur->unsafe; + + } while (!nbcon_state_try_cmpxchg(con, cur, &new)); + + return 0; +} + +static struct printk_buffers panic_nbcon_pbufs; + +/** + * nbcon_context_try_acquire - Try to acquire nbcon console + * @ctxt: The context of the caller + * + * Context: Any context which could not be migrated to another CPU. + * Return: True if the console was acquired. False otherwise. + * + * If the caller allowed an unsafe hostile takeover, on success the + * caller should check the current console state to see if it is + * in an unsafe state. Otherwise, on success the caller may assume + * the console is not in an unsafe state. + */ +static bool nbcon_context_try_acquire(struct nbcon_context *ctxt) +{ + unsigned int cpu = smp_processor_id(); + struct console *con = ctxt->console; + struct nbcon_state cur; + int err; + + nbcon_state_read(con, &cur); +try_again: + err = nbcon_context_try_acquire_direct(ctxt, &cur); + if (err != -EBUSY) + goto out; + + err = nbcon_context_try_acquire_handover(ctxt, &cur); + if (err == -EAGAIN) + goto try_again; + if (err != -EBUSY) + goto out; + + err = nbcon_context_try_acquire_hostile(ctxt, &cur); +out: + if (err) + return false; + + /* Acquire succeeded. */ + + /* Assign the appropriate buffer for this context. */ + if (atomic_read(&panic_cpu) == cpu) + ctxt->pbufs = &panic_nbcon_pbufs; + else + ctxt->pbufs = con->pbufs; + + /* Set the record sequence for this context to print. */ + ctxt->seq = nbcon_seq_read(ctxt->console); + + return true; +} + +static bool nbcon_owner_matches(struct nbcon_state *cur, int expected_cpu, + int expected_prio) +{ + /* + * Since consoles can only be acquired by higher priorities, + * owning contexts are uniquely identified by @prio. However, + * since contexts can unexpectedly lose ownership, it is + * possible that later another owner appears with the same + * priority. For this reason @cpu is also needed. + */ + + if (cur->prio != expected_prio) + return false; + + if (cur->cpu != expected_cpu) + return false; + + return true; +} + +/** + * nbcon_context_release - Release the console + * @ctxt: The nbcon context from nbcon_context_try_acquire() + */ +static void nbcon_context_release(struct nbcon_context *ctxt) +{ + unsigned int cpu = smp_processor_id(); + struct console *con = ctxt->console; + struct nbcon_state cur; + struct nbcon_state new; + + nbcon_state_read(con, &cur); + + do { + if (!nbcon_owner_matches(&cur, cpu, ctxt->prio)) + break; + + new.atom = cur.atom; + new.prio = NBCON_PRIO_NONE; + + /* + * If @unsafe_takeover is set, it is kept set so that + * the state remains permanently unsafe. + */ + new.unsafe |= cur.unsafe_takeover; + + } while (!nbcon_state_try_cmpxchg(con, &cur, &new)); + + ctxt->pbufs = NULL; +} + +/** + * nbcon_context_can_proceed - Check whether ownership can proceed + * @ctxt: The nbcon context from nbcon_context_try_acquire() + * @cur: The current console state + * + * Return: True if this context still owns the console. False if + * ownership was handed over or taken. + * + * Must be invoked when entering the unsafe state to make sure that it still + * owns the lock. Also must be invoked when exiting the unsafe context + * to eventually free the lock for a higher priority context which asked + * for the friendly handover. + * + * It can be called inside an unsafe section when the console is just + * temporary in safe state instead of exiting and entering the unsafe + * state. + * + * Also it can be called in the safe context before doing an expensive + * safe operation. It does not make sense to do the operation when + * a higher priority context took the lock. + * + * When this function returns false then the calling context no longer owns + * the console and is no longer allowed to go forward. In this case it must + * back out immediately and carefully. The buffer content is also no longer + * trusted since it no longer belongs to the calling context. + */ +static bool nbcon_context_can_proceed(struct nbcon_context *ctxt, struct nbcon_state *cur) +{ + unsigned int cpu = smp_processor_id(); + + /* Make sure this context still owns the console. */ + if (!nbcon_owner_matches(cur, cpu, ctxt->prio)) + return false; + + /* The console owner can proceed if there is no waiter. */ + if (cur->req_prio == NBCON_PRIO_NONE) + return true; + + /* + * A console owner within an unsafe region is always allowed to + * proceed, even if there are waiters. It can perform a handover + * when exiting the unsafe region. Otherwise the waiter will + * need to perform an unsafe hostile takeover. + */ + if (cur->unsafe) + return true; + + /* Waiters always have higher priorities than owners. */ + WARN_ON_ONCE(cur->req_prio <= cur->prio); + + /* + * Having a safe point for take over and eventually a few + * duplicated characters or a full line is way better than a + * hostile takeover. Post processing can take care of the garbage. + * Release and hand over. + */ + nbcon_context_release(ctxt); + + /* + * It is not clear whether the waiter really took over ownership. The + * outermost callsite must make the final decision whether console + * ownership is needed for it to proceed. If yes, it must reacquire + * ownership (possibly hostile) before carefully proceeding. + * + * The calling context no longer owns the console so go back all the + * way instead of trying to implement reacquire heuristics in tons of + * places. + */ + return false; +} + +/** + * nbcon_can_proceed - Check whether ownership can proceed + * @wctxt: The write context that was handed to the write function + * + * Return: True if this context still owns the console. False if + * ownership was handed over or taken. + * + * It is used in nbcon_enter_unsafe() to make sure that it still owns the + * lock. Also it is used in nbcon_exit_unsafe() to eventually free the lock + * for a higher priority context which asked for the friendly handover. + * + * It can be called inside an unsafe section when the console is just + * temporary in safe state instead of exiting and entering the unsafe state. + * + * Also it can be called in the safe context before doing an expensive safe + * operation. It does not make sense to do the operation when a higher + * priority context took the lock. + * + * When this function returns false then the calling context no longer owns + * the console and is no longer allowed to go forward. In this case it must + * back out immediately and carefully. The buffer content is also no longer + * trusted since it no longer belongs to the calling context. + */ +bool nbcon_can_proceed(struct nbcon_write_context *wctxt) +{ + struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt); + struct console *con = ctxt->console; + struct nbcon_state cur; + + nbcon_state_read(con, &cur); + + return nbcon_context_can_proceed(ctxt, &cur); +} +EXPORT_SYMBOL_GPL(nbcon_can_proceed); + +#define nbcon_context_enter_unsafe(c) __nbcon_context_update_unsafe(c, true) +#define nbcon_context_exit_unsafe(c) __nbcon_context_update_unsafe(c, false) + +/** + * __nbcon_context_update_unsafe - Update the unsafe bit in @con->nbcon_state + * @ctxt: The nbcon context from nbcon_context_try_acquire() + * @unsafe: The new value for the unsafe bit + * + * Return: True if the unsafe state was updated and this context still + * owns the console. Otherwise false if ownership was handed + * over or taken. + * + * This function allows console owners to modify the unsafe status of the + * console. + * + * When this function returns false then the calling context no longer owns + * the console and is no longer allowed to go forward. In this case it must + * back out immediately and carefully. The buffer content is also no longer + * trusted since it no longer belongs to the calling context. + * + * Internal helper to avoid duplicated code. + */ +static bool __nbcon_context_update_unsafe(struct nbcon_context *ctxt, bool unsafe) +{ + struct console *con = ctxt->console; + struct nbcon_state cur; + struct nbcon_state new; + + nbcon_state_read(con, &cur); + + do { + /* + * The unsafe bit must not be cleared if an + * unsafe hostile takeover has occurred. + */ + if (!unsafe && cur.unsafe_takeover) + goto out; + + if (!nbcon_context_can_proceed(ctxt, &cur)) + return false; + + new.atom = cur.atom; + new.unsafe = unsafe; + } while (!nbcon_state_try_cmpxchg(con, &cur, &new)); + + cur.atom = new.atom; +out: + return nbcon_context_can_proceed(ctxt, &cur); +} + +/** + * nbcon_enter_unsafe - Enter an unsafe region in the driver + * @wctxt: The write context that was handed to the write function + * + * Return: True if this context still owns the console. False if + * ownership was handed over or taken. + * + * When this function returns false then the calling context no longer owns + * the console and is no longer allowed to go forward. In this case it must + * back out immediately and carefully. The buffer content is also no longer + * trusted since it no longer belongs to the calling context. + */ +bool nbcon_enter_unsafe(struct nbcon_write_context *wctxt) +{ + struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt); + + return nbcon_context_enter_unsafe(ctxt); +} +EXPORT_SYMBOL_GPL(nbcon_enter_unsafe); + +/** + * nbcon_exit_unsafe - Exit an unsafe region in the driver + * @wctxt: The write context that was handed to the write function + * + * Return: True if this context still owns the console. False if + * ownership was handed over or taken. + * + * When this function returns false then the calling context no longer owns + * the console and is no longer allowed to go forward. In this case it must + * back out immediately and carefully. The buffer content is also no longer + * trusted since it no longer belongs to the calling context. + */ +bool nbcon_exit_unsafe(struct nbcon_write_context *wctxt) +{ + struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt); + + return nbcon_context_exit_unsafe(ctxt); +} +EXPORT_SYMBOL_GPL(nbcon_exit_unsafe); + +/** + * nbcon_reacquire - Reacquire a console after losing ownership + * @wctxt: The write context that was handed to the write function + * + * Since ownership can be lost at any time due to handover or takeover, a + * printing context _should_ be prepared to back out immediately and + * carefully. However, there are many scenarios where the context _must_ + * reacquire ownership in order to finalize or revert hardware changes. + * + * This function allows a context to reacquire ownership using the same + * priority as its previous ownership. + * + * Note that for printing contexts, after a successful reacquire the + * context will have no output buffer because that has been lost. This + * function cannot be used to resume printing. + */ +void nbcon_reacquire(struct nbcon_write_context *wctxt) +{ + struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt); + struct console *con = ctxt->console; + struct nbcon_state cur; + + while (!nbcon_context_try_acquire(ctxt)) + cpu_relax(); + + wctxt->outbuf = NULL; + wctxt->len = 0; + nbcon_state_read(con, &cur); + wctxt->unsafe_takeover = cur.unsafe_takeover; +} +EXPORT_SYMBOL_GPL(nbcon_reacquire); + +/** + * nbcon_emit_next_record - Emit a record in the acquired context + * @wctxt: The write context that will be handed to the write function + * @use_atomic: True if the write_atomic callback is to be used + * + * Return: True if this context still owns the console. False if + * ownership was handed over or taken. + * + * When this function returns false then the calling context no longer owns + * the console and is no longer allowed to go forward. In this case it must + * back out immediately and carefully. The buffer content is also no longer + * trusted since it no longer belongs to the calling context. If the caller + * wants to do more it must reacquire the console first. + * + * When true is returned, @wctxt->ctxt.backlog indicates whether there are + * still records pending in the ringbuffer, + */ +static bool nbcon_emit_next_record(struct nbcon_write_context *wctxt, bool use_atomic) +{ + struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt); + struct console *con = ctxt->console; + bool is_extended = console_srcu_read_flags(con) & CON_EXTENDED; + struct printk_message pmsg = { + .pbufs = ctxt->pbufs, + }; + unsigned long con_dropped; + struct nbcon_state cur; + unsigned long dropped; + bool done = false; + + /* + * The printk buffers are filled within an unsafe section. This + * prevents NBCON_PRIO_NORMAL and NBCON_PRIO_EMERGENCY from + * clobbering each other. + */ + + if (!nbcon_context_enter_unsafe(ctxt)) + return false; + + ctxt->backlog = printk_get_next_message(&pmsg, ctxt->seq, is_extended, true); + if (!ctxt->backlog) + return nbcon_context_exit_unsafe(ctxt); + + /* + * @con->dropped is not protected in case of an unsafe hostile + * takeover. In that situation the update can be racy so + * annotate it accordingly. + */ + con_dropped = data_race(READ_ONCE(con->dropped)); + + dropped = con_dropped + pmsg.dropped; + if (dropped && !is_extended) + console_prepend_dropped(&pmsg, dropped); + + if (!nbcon_context_exit_unsafe(ctxt)) + return false; + + /* For skipped records just update seq/dropped in @con. */ + if (pmsg.outbuf_len == 0) + goto update_con; + + /* Initialize the write context for driver callbacks. */ + wctxt->outbuf = &pmsg.pbufs->outbuf[0]; + wctxt->len = pmsg.outbuf_len; + nbcon_state_read(con, &cur); + wctxt->unsafe_takeover = cur.unsafe_takeover; + + if (use_atomic && + con->write_atomic) { + done = con->write_atomic(con, wctxt); + + } else if (!use_atomic && + con->write_thread && + con->kthread) { + WARN_ON_ONCE(con->kthread != current); + done = con->write_thread(con, wctxt); + } + + if (!done) { + /* + * The emit was aborted, probably due to a loss of ownership. + * Ensure ownership was lost or released before reporting the + * loss. + */ + nbcon_context_release(ctxt); + return false; + } + + /* + * Since any dropped message was successfully output, reset the + * dropped count for the console. + */ + dropped = 0; +update_con: + /* + * The dropped count and the sequence number are updated within an + * unsafe section. This limits update races to the panic context and + * allows the panic context to win. + */ + + if (!nbcon_context_enter_unsafe(ctxt)) + return false; + + if (dropped != con_dropped) { + /* Counterpart to the READ_ONCE() above. */ + WRITE_ONCE(con->dropped, dropped); + } + + nbcon_seq_try_update(ctxt, pmsg.seq + 1); + + return nbcon_context_exit_unsafe(ctxt); +} + +/** + * nbcon_kthread_should_wakeup - Check whether a printer thread should wakeup + * @con: Console to operate on + * @ctxt: The acquire context that contains the state + * at console_acquire() + * + * Return: True if the thread should shutdown or if the console is + * allowed to print and a record is available. False otherwise. + * + * After the thread wakes up, it must first check if it should shutdown before + * attempting any printing. + */ +static bool nbcon_kthread_should_wakeup(struct console *con, struct nbcon_context *ctxt) +{ + bool is_usable; + short flags; + int cookie; + + if (kthread_should_stop()) + return true; + + cookie = console_srcu_read_lock(); + flags = console_srcu_read_flags(con); + is_usable = console_is_usable(con, flags, false); + console_srcu_read_unlock(cookie); + + if (!is_usable) + return false; + + /* Bring the sequence in @ctxt up to date */ + ctxt->seq = nbcon_seq_read(con); + + return prb_read_valid(prb, ctxt->seq, NULL); +} + +/** + * nbcon_kthread_func - The printer thread function + * @__console: Console to operate on + */ +static int nbcon_kthread_func(void *__console) +{ + struct console *con = __console; + struct nbcon_write_context wctxt = { + .ctxt.console = con, + .ctxt.prio = NBCON_PRIO_NORMAL, + }; + struct nbcon_context *ctxt = &ACCESS_PRIVATE(&wctxt, ctxt); + unsigned long flags; + short con_flags; + bool backlog; + int cookie; + int ret; + +wait_for_event: + /* + * Guarantee this task is visible on the rcuwait before + * checking the wake condition. + * + * The full memory barrier within set_current_state() of + * ___rcuwait_wait_event() pairs with the full memory + * barrier within rcuwait_has_sleeper(). + * + * This pairs with rcuwait_has_sleeper:A and nbcon_kthread_wake:A. + */ + ret = rcuwait_wait_event(&con->rcuwait, + nbcon_kthread_should_wakeup(con, ctxt), + TASK_INTERRUPTIBLE); /* LMM(nbcon_kthread_func:A) */ + + if (kthread_should_stop()) + return 0; + + /* Wait was interrupted by a spurious signal, go back to sleep. */ + if (ret) + goto wait_for_event; + + do { + backlog = false; + + cookie = console_srcu_read_lock(); + + con_flags = console_srcu_read_flags(con); + + if (console_is_usable(con, con_flags, false)) { + con->driver_enter(con, &flags); + + /* + * Ensure this stays on the CPU to make handover and + * takeover possible. + */ + cant_migrate(); + + if (nbcon_context_try_acquire(ctxt)) { + /* + * If the emit fails, this context is no + * longer the owner. + */ + if (nbcon_emit_next_record(&wctxt, false)) { + nbcon_context_release(ctxt); + backlog = ctxt->backlog; + } + } + + con->driver_exit(con, flags); + } + + console_srcu_read_unlock(cookie); + cond_resched(); + + } while (backlog); + + goto wait_for_event; +} + +/** + * nbcon_irq_work - irq work to wake printk thread + * @irq_work: The irq work to operate on + */ +static void nbcon_irq_work(struct irq_work *irq_work) +{ + struct console *con = container_of(irq_work, struct console, irq_work); + + nbcon_kthread_wake(con); +} + +static inline bool rcuwait_has_sleeper(struct rcuwait *w) +{ + bool has_sleeper; + + rcu_read_lock(); + /* + * Guarantee any new records can be seen by tasks preparing to wait + * before this context checks if the rcuwait is empty. + * + * This full memory barrier pairs with the full memory barrier within + * set_current_state() of ___rcuwait_wait_event(), which is called + * after prepare_to_rcuwait() adds the waiter but before it has + * checked the wait condition. + * + * This pairs with nbcon_kthread_func:A. + */ + smp_mb(); /* LMM(rcuwait_has_sleeper:A) */ + has_sleeper = !!rcu_dereference(w->task); + rcu_read_unlock(); + + return has_sleeper; +} + +/** + * nbcon_wake_threads - Wake up printing threads using irq_work + */ +void nbcon_wake_threads(void) +{ + struct console *con; + int cookie; + + cookie = console_srcu_read_lock(); + for_each_console_srcu(con) { + /* + * Only schedule irq_work if the printing thread is + * actively waiting. If not waiting, the thread will + * notice by itself that it has work to do. + */ + if (con->kthread && rcuwait_has_sleeper(&con->rcuwait)) + irq_work_queue(&con->irq_work); + } + console_srcu_read_unlock(cookie); +} + +/* Track the nbcon emergency nesting per CPU. */ +static DEFINE_PER_CPU(unsigned int, nbcon_pcpu_emergency_nesting); +static unsigned int early_nbcon_pcpu_emergency_nesting __initdata; + +/** + * nbcon_get_cpu_emergency_nesting - Get the per CPU emergency nesting pointer + * + * Return: Either a pointer to the per CPU emergency nesting counter of + * the current CPU or to the init data during early boot. + */ +static __ref unsigned int *nbcon_get_cpu_emergency_nesting(void) +{ + /* + * The value of __printk_percpu_data_ready gets set in normal + * context and before SMP initialization. As a result it could + * never change while inside an nbcon emergency section. + */ + if (!printk_percpu_data_ready()) + return &early_nbcon_pcpu_emergency_nesting; + + return this_cpu_ptr(&nbcon_pcpu_emergency_nesting); +} + +/** + * nbcon_atomic_emit_one - Print one record for an nbcon console using the + * write_atomic() callback + * @wctxt: An initialized write context struct to use + * for this context + * + * Return: False if the given console could not print a record or there + * are no more records to print, otherwise true. + * + * This is an internal helper to handle the locking of the console before + * calling nbcon_emit_next_record(). + */ +static bool nbcon_atomic_emit_one(struct nbcon_write_context *wctxt) +{ + struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt); + + if (!nbcon_context_try_acquire(ctxt)) + return false; + + /* + * nbcon_emit_next_record() returns false when the console was + * handed over or taken over. In both cases the context is no + * longer valid. + */ + if (!nbcon_emit_next_record(wctxt, true)) + return false; + + nbcon_context_release(ctxt); + + return ctxt->backlog; +} + +/** + * nbcon_get_default_prio - The appropriate nbcon priority to use for nbcon + * printing on the current CPU + * + * Context: Any context which could not be migrated to another CPU. + * Return: The nbcon_prio to use for acquiring an nbcon console in this + * context for printing. + */ +enum nbcon_prio nbcon_get_default_prio(void) +{ + unsigned int *cpu_emergency_nesting; + + if (this_cpu_in_panic()) + return NBCON_PRIO_PANIC; + + cpu_emergency_nesting = nbcon_get_cpu_emergency_nesting(); + if (*cpu_emergency_nesting) + return NBCON_PRIO_EMERGENCY; + + return NBCON_PRIO_NORMAL; +} + +/** + * nbcon_atomic_emit_next_record - Print one record for an nbcon console + * using the write_atomic() callback + * @con: The console to print on + * @handover: Will be set to true if a printk waiter has taken over the + * console_lock, in which case the caller is no longer holding + * both the console_lock and the SRCU read lock. Otherwise it + * is set to false. + * @cookie: The cookie from the SRCU read lock. + * + * Context: Any context which could not be migrated to another CPU. + * Return: True if a record could be printed, otherwise false. + * + * This function is meant to be called by console_flush_all() to print records + * on nbcon consoles using the write_atomic() callback. Essentially it is the + * nbcon version of console_emit_next_record(). + */ +bool nbcon_atomic_emit_next_record(struct console *con, bool *handover, int cookie) +{ + struct nbcon_write_context wctxt = { }; + struct nbcon_context *ctxt = &ACCESS_PRIVATE(&wctxt, ctxt); + unsigned long driver_flags; + bool progress = false; + unsigned long flags; + + *handover = false; + + /* Use the same locking order as console_emit_next_record(). */ + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) { + printk_safe_enter_irqsave(flags); + console_lock_spinning_enable(); + stop_critical_timings(); + } + + con->driver_enter(con, &driver_flags); + cant_migrate(); + + ctxt->console = con; + ctxt->prio = nbcon_get_default_prio(); + + progress = nbcon_atomic_emit_one(&wctxt); + + con->driver_exit(con, driver_flags); + + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) { + start_critical_timings(); + *handover = console_lock_spinning_disable_and_check(cookie); + printk_safe_exit_irqrestore(flags); + } + + return progress; +} + +/** + * __nbcon_atomic_flush_all - Flush all nbcon consoles using their + * write_atomic() callback + * @stop_seq: Flush up until this record + * @allow_unsafe_takeover: True, to allow unsafe hostile takeovers + */ +static void __nbcon_atomic_flush_all(u64 stop_seq, bool allow_unsafe_takeover) +{ + struct nbcon_write_context wctxt = { }; + struct nbcon_context *ctxt = &ACCESS_PRIVATE(&wctxt, ctxt); + struct console *con; + bool any_progress; + int cookie; + + do { + any_progress = false; + + cookie = console_srcu_read_lock(); + for_each_console_srcu(con) { + short flags = console_srcu_read_flags(con); + unsigned long irq_flags; + + if (!(flags & CON_NBCON)) + continue; + + if (!console_is_usable(con, flags, true)) + continue; + + if (nbcon_seq_read(con) >= stop_seq) + continue; + + memset(ctxt, 0, sizeof(*ctxt)); + ctxt->console = con; + ctxt->spinwait_max_us = 2000; + ctxt->allow_unsafe_takeover = allow_unsafe_takeover; + + /* + * Atomic flushing does not use console driver + * synchronization (i.e. it does not hold the port + * lock for uart consoles). Therefore IRQs must be + * disabled to avoid being interrupted and then + * calling into a driver that will deadlock trying + * acquire console ownership. + * + * This also disables migration in order to get the + * current CPU priority. + */ + local_irq_save(irq_flags); + + ctxt->prio = nbcon_get_default_prio(); + + any_progress |= nbcon_atomic_emit_one(&wctxt); + + local_irq_restore(irq_flags); + } + console_srcu_read_unlock(cookie); + } while (any_progress); +} + +/** + * nbcon_atomic_flush_all - Flush all nbcon consoles using their + * write_atomic() callback + * + * Flush the backlog up through the currently newest record. Any new + * records added while flushing will not be flushed. This is to avoid + * one CPU printing unbounded because other CPUs continue to add records. + */ +void nbcon_atomic_flush_all(void) +{ + __nbcon_atomic_flush_all(prb_next_reserve_seq(prb), false); +} + +/** + * nbcon_atomic_flush_unsafe - Flush all nbcon consoles using their + * write_atomic() callback and allowing unsafe hostile takeovers + * + * Flush the backlog up through the currently newest record. Unsafe hostile + * takeovers will be performed, if necessary. + */ +void nbcon_atomic_flush_unsafe(void) +{ + __nbcon_atomic_flush_all(prb_next_reserve_seq(prb), true); +} + +/** + * nbcon_cpu_emergency_enter - Enter an emergency section where printk() + * messages for that CPU are only stored + * + * Upon exiting the emergency section, all stored messages are flushed. + * + * Context: Any context. Disables preemption. + * + * When within an emergency section, no printing occurs on that CPU. This + * is to allow all emergency messages to be dumped into the ringbuffer before + * flushing the ringbuffer. The actual printing occurs when exiting the + * outermost emergency section. + */ +void nbcon_cpu_emergency_enter(void) +{ + unsigned int *cpu_emergency_nesting; + + preempt_disable(); + + cpu_emergency_nesting = nbcon_get_cpu_emergency_nesting(); + (*cpu_emergency_nesting)++; +} + +/** + * nbcon_cpu_emergency_exit - Exit an emergency section and flush the + * stored messages + * + * Flushing only occurs when exiting all nesting for the CPU. + * + * Context: Any context. Enables preemption. + */ +void nbcon_cpu_emergency_exit(void) +{ + unsigned int *cpu_emergency_nesting; + bool do_trigger_flush = false; + + cpu_emergency_nesting = nbcon_get_cpu_emergency_nesting(); + + WARN_ON_ONCE(*cpu_emergency_nesting == 0); + + if (*cpu_emergency_nesting == 1) + do_trigger_flush = true; + + /* Undo the nesting count of nbcon_cpu_emergency_enter(). */ + (*cpu_emergency_nesting)--; + + preempt_enable(); + + if (do_trigger_flush) + printk_trigger_flush(); +} + +/** + * nbcon_kthread_stop - Stop a printer thread + * @con: Console to operate on + */ +static void nbcon_kthread_stop(struct console *con) +{ + lockdep_assert_console_list_lock_held(); + + if (!con->kthread) + return; + + kthread_stop(con->kthread); + con->kthread = NULL; +} + +/** + * nbcon_kthread_create - Create a printer thread + * @con: Console to operate on + * + * If it fails, let the console proceed. The atomic part might + * be usable and useful. + */ +void nbcon_kthread_create(struct console *con) +{ + struct task_struct *kt; + + lockdep_assert_console_list_lock_held(); + + if (!(con->flags & CON_NBCON) || !con->write_thread) + return; + + if (!printk_threads_enabled || con->kthread) + return; + + /* + * Printer threads cannot be started as long as any boot console is + * registered because there is no way to synchronize the hardware + * registers between boot console code and regular console code. + */ + if (have_boot_console) + return; + + kt = kthread_run(nbcon_kthread_func, con, "pr/%s%d", con->name, con->index); + if (IS_ERR(kt)) { + con_printk(KERN_ERR, con, "failed to start printing thread\n"); + return; + } + + con->kthread = kt; + + /* + * It is important that console printing threads are scheduled + * shortly after a printk call and with generous runtime budgets. + */ + sched_set_normal(con->kthread, -20); +} + +static int __init printk_setup_threads(void) +{ + struct console *con; + + console_list_lock(); + printk_threads_enabled = true; + for_each_console(con) + nbcon_kthread_create(con); + if (IS_ENABLED(CONFIG_PREEMPT_RT) && printing_via_unlock) + nbcon_legacy_kthread_create(); + console_list_unlock(); + return 0; +} +early_initcall(printk_setup_threads); + +/** + * nbcon_alloc - Allocate buffers needed by the nbcon console + * @con: Console to allocate buffers for + * + * Return: True on success. False otherwise and the console cannot + * be used. + * + * This is not part of nbcon_init() because buffer allocation must + * be performed earlier in the console registration process. + */ +bool nbcon_alloc(struct console *con) +{ + if (con->flags & CON_BOOT) { + /* + * Boot console printing is synchronized with legacy console + * printing, so boot consoles can share the same global printk + * buffers. + */ + con->pbufs = &printk_shared_pbufs; + } else { + con->pbufs = kmalloc(sizeof(*con->pbufs), GFP_KERNEL); + if (!con->pbufs) { + con_printk(KERN_ERR, con, "failed to allocate printing buffer\n"); + return false; + } + } + + return true; +} + +/** + * nbcon_init - Initialize the nbcon console specific data + * @con: Console to initialize + * + * nbcon_alloc() *must* be called and succeed before this function + * is called. + * + * This function expects that the legacy @con->seq has been set. + */ +void nbcon_init(struct console *con) +{ + struct nbcon_state state = { }; + + /* nbcon_alloc() must have been called and successful! */ + BUG_ON(!con->pbufs); + + rcuwait_init(&con->rcuwait); + init_irq_work(&con->irq_work, nbcon_irq_work); + nbcon_seq_force(con, con->seq); + nbcon_state_set(con, &state); + nbcon_kthread_create(con); +} + +/** + * nbcon_free - Free and cleanup the nbcon console specific data + * @con: Console to free/cleanup nbcon data + */ +void nbcon_free(struct console *con) +{ + struct nbcon_state state = { }; + + nbcon_kthread_stop(con); + nbcon_state_set(con, &state); + + /* Boot consoles share global printk buffers. */ + if (!(con->flags & CON_BOOT)) + kfree(con->pbufs); + + con->pbufs = NULL; +} + +static inline bool uart_is_nbcon(struct uart_port *up) +{ + int cookie; + bool ret; + + if (!uart_console(up)) + return false; + + cookie = console_srcu_read_lock(); + ret = (console_srcu_read_flags(up->cons) & CON_NBCON); + console_srcu_read_unlock(cookie); + return ret; +} + +/** + * nbcon_acquire - The second half of the port locking wrapper + * @up: The uart port whose @lock was locked + * + * The uart_port_lock() wrappers will first lock the spin_lock @up->lock. + * Then this function is called to implement nbcon-specific processing. + * + * If @up is an nbcon console, this console will be acquired and marked as + * unsafe. Otherwise this function does nothing. + * + * nbcon consoles acquired via the port lock wrapper always use priority + * NBCON_PRIO_NORMAL. + */ +void nbcon_acquire(struct uart_port *up) +{ + struct console *con = up->cons; + struct nbcon_context ctxt; + + if (!uart_is_nbcon(up)) + return; + + WARN_ON_ONCE(up->nbcon_locked_port); + + do { + do { + memset(&ctxt, 0, sizeof(ctxt)); + ctxt.console = con; + ctxt.prio = NBCON_PRIO_NORMAL; + } while (!nbcon_context_try_acquire(&ctxt)); + + } while (!nbcon_context_enter_unsafe(&ctxt)); + + up->nbcon_locked_port = true; +} +EXPORT_SYMBOL_GPL(nbcon_acquire); + +/** + * nbcon_release - The first half of the port unlocking wrapper + * @up: The uart port whose @lock is about to be unlocked + * + * The uart_port_unlock() wrappers will first call this function to implement + * nbcon-specific processing. Then afterwards the uart_port_unlock() wrappers + * will unlock the spin_lock @up->lock. + * + * If @up is an nbcon console, the console will be marked as safe and + * released. Otherwise this function does nothing. + * + * nbcon consoles acquired via the port lock wrapper always use priority + * NBCON_PRIO_NORMAL. + */ +void nbcon_release(struct uart_port *up) +{ + struct console *con = up->cons; + struct nbcon_context ctxt = { + .console = con, + .prio = NBCON_PRIO_NORMAL, + }; + + if (!up->nbcon_locked_port) + return; + + if (nbcon_context_exit_unsafe(&ctxt)) + nbcon_context_release(&ctxt); + + up->nbcon_locked_port = false; +} +EXPORT_SYMBOL_GPL(nbcon_release); + +/** + * printk_kthread_shutdown - shutdown all threaded printers + * + * On system shutdown all threaded printers are stopped. This allows printk + * to transition back to atomic printing, thus providing a robust mechanism + * for the final shutdown/reboot messages to be output. + */ +static void printk_kthread_shutdown(void) +{ + struct console *con; + + console_list_lock(); + for_each_console(con) { + if (con->flags & CON_NBCON) + nbcon_kthread_stop(con); + } + console_list_unlock(); +} + +static struct syscore_ops printk_syscore_ops = { + .shutdown = printk_kthread_shutdown, +}; + +static int __init printk_init_ops(void) +{ + register_syscore_ops(&printk_syscore_ops); + return 0; +} +device_initcall(printk_init_ops); diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 0fca282c0a25..249576a8c351 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -102,12 +102,6 @@ DEFINE_STATIC_SRCU(console_srcu); */ int __read_mostly suppress_printk; -/* - * During panic, heavy printk by other CPUs can delay the - * panic and risk deadlock on console resources. - */ -static int __read_mostly suppress_panic_printk; - #ifdef CONFIG_LOCKDEP static struct lockdep_map console_lock_dep_map = { .name = "console_lock" @@ -288,6 +282,7 @@ EXPORT_SYMBOL(console_list_unlock); * Return: A cookie to pass to console_srcu_read_unlock(). */ int console_srcu_read_lock(void) + __acquires(&console_srcu) { return srcu_read_lock_nmisafe(&console_srcu); } @@ -301,6 +296,7 @@ EXPORT_SYMBOL(console_srcu_read_lock); * Counterpart to console_srcu_read_lock() */ void console_srcu_read_unlock(int cookie) + __releases(&console_srcu) { srcu_read_unlock_nmisafe(&console_srcu, cookie); } @@ -353,6 +349,29 @@ static bool panic_in_progress(void) return unlikely(atomic_read(&panic_cpu) != PANIC_CPU_INVALID); } +/* Return true if a panic is in progress on the current CPU. */ +bool this_cpu_in_panic(void) +{ + /* + * We can use raw_smp_processor_id() here because it is impossible for + * the task to be migrated to the panic_cpu, or away from it. If + * panic_cpu has already been set, and we're not currently executing on + * that CPU, then we never will be. + */ + return unlikely(atomic_read(&panic_cpu) == raw_smp_processor_id()); +} + +/* + * Return true if a panic is in progress on a remote CPU. + * + * On true, the local CPU should immediately release any printing resources + * that may be needed by the panic CPU. + */ +bool other_cpu_in_panic(void) +{ + return (panic_in_progress() && !this_cpu_in_panic()); +} + /* * This is used for debugging the mess that is the VT code by * keeping track if we have the console semaphore held. It's @@ -444,8 +463,33 @@ static int console_msg_format = MSG_FORMAT_DEFAULT; /* syslog_lock protects syslog_* variables and write access to clear_seq. */ static DEFINE_MUTEX(syslog_lock); +/* + * Specifies if a legacy console is registered. If legacy consoles are + * present, it is necessary to perform the console_lock/console_unlock dance + * whenever console flushing should occur. + */ +bool have_legacy_console; + +/* + * Specifies if an nbcon console is registered. If nbcon consoles are present, + * synchronous printing of legacy consoles will not occur during panic until + * the backtrace has been stored to the ringbuffer. + */ +bool have_nbcon_console; + +/* + * Specifies if a boot console is registered. If boot consoles are present, + * nbcon consoles cannot print simultaneously and must be synchronized by + * the console lock. This is because boot consoles and nbcon consoles may + * have mapped the same hardware. + */ +bool have_boot_console; + #ifdef CONFIG_PRINTK DECLARE_WAIT_QUEUE_HEAD(log_wait); + +static DECLARE_WAIT_QUEUE_HEAD(legacy_wait); + /* All 3 protected by @syslog_lock. */ /* the next printk record to read by syslog(READ) or /proc/kmsg */ static u64 syslog_seq; @@ -494,7 +538,7 @@ _DEFINE_PRINTKRB(printk_rb_static, CONFIG_LOG_BUF_SHIFT - PRB_AVGBITS, static struct printk_ringbuffer printk_rb_dynamic; -static struct printk_ringbuffer *prb = &printk_rb_static; +struct printk_ringbuffer *prb = &printk_rb_static; /* * We cannot access per-CPU data (e.g. per-CPU flush irq_work) before @@ -698,9 +742,6 @@ static ssize_t msg_print_ext_body(char *buf, size_t size, return len; } -static bool printk_get_next_message(struct printk_message *pmsg, u64 seq, - bool is_extended, bool may_supress); - /* /dev/kmsg - userspace message inject/listen interface */ struct devkmsg_user { atomic64_t seq; @@ -1848,7 +1889,7 @@ static bool console_waiter; * there may be a waiter spinning (like a spinlock). Also it must be * ready to hand over the lock at the end of the section. */ -static void console_lock_spinning_enable(void) +void console_lock_spinning_enable(void) { /* * Do not use spinning in panic(). The panic CPU wants to keep the lock. @@ -1887,7 +1928,7 @@ static void console_lock_spinning_enable(void) * * Return: 1 if the lock rights were passed, 0 otherwise. */ -static int console_lock_spinning_disable_and_check(int cookie) +int console_lock_spinning_disable_and_check(int cookie) { int waiter; @@ -2298,54 +2339,123 @@ int vprintk_store(int facility, int level, return ret; } +static bool legacy_allow_panic_sync; + +/* + * This acts as a one-way switch to allow legacy consoles to print from + * the printk() caller context on a panic CPU. + */ +void printk_legacy_allow_panic_sync(void) +{ + legacy_allow_panic_sync = true; +} + asmlinkage int vprintk_emit(int facility, int level, const struct dev_printk_info *dev_info, const char *fmt, va_list args) { + bool do_trylock_unlock = printing_via_unlock && + !IS_ENABLED(CONFIG_PREEMPT_RT); int printed_len; - bool in_sched = false; /* Suppress unimportant messages after panic happens */ if (unlikely(suppress_printk)) return 0; - if (unlikely(suppress_panic_printk) && other_cpu_in_panic()) + /* + * The messages on the panic CPU are the most important. If + * non-panic CPUs are generating any messages, they will be + * silently dropped. + */ + if (other_cpu_in_panic()) return 0; if (level == LOGLEVEL_SCHED) { level = LOGLEVEL_DEFAULT; - in_sched = true; + /* If called from the scheduler, we can not call up(). */ + do_trylock_unlock = false; } printk_delay(level); printed_len = vprintk_store(facility, level, dev_info, fmt, args); - /* If called from the scheduler, we can not call up(). */ - if (!in_sched) { + if (!have_boot_console && have_nbcon_console) { + bool is_panic_context = this_cpu_in_panic(); + + /* + * In panic, the legacy consoles are not allowed to print from + * the printk calling context unless explicitly allowed. This + * gives the safe nbcon consoles a chance to print out all the + * panic messages first. This restriction only applies if + * there are nbcon consoles registered. + */ + if (is_panic_context) + do_trylock_unlock &= legacy_allow_panic_sync; + + /* + * There are situations where nbcon atomic printing should + * happen in the printk() caller context: + * + * - When this CPU is in panic. + * + * - When booting, before the printing threads have been + * started. + * + * - During shutdown, since the printing threads may not get + * a chance to print the final messages. + * + * Note that if boot consoles are registered, the + * console_lock/console_unlock dance must be relied upon + * instead because nbcon consoles cannot print simultaneously + * with boot consoles. + */ + if (is_panic_context || + !printk_threads_enabled || + (system_state > SYSTEM_RUNNING)) { + nbcon_atomic_flush_all(); + } + } + + nbcon_wake_threads(); + + if (do_trylock_unlock) { /* * The caller may be holding system-critical or * timing-sensitive locks. Disable preemption during * printing of all remaining records to all consoles so that * this context can return as soon as possible. Hopefully * another printk() caller will take over the printing. + * + * Also, nbcon_get_default_prio() requires migration disabled. */ preempt_disable(); + /* - * Try to acquire and then immediately release the console - * semaphore. The release will print out buffers. With the - * spinning variant, this context tries to take over the - * printing from another printing context. + * Do not emit for EMERGENCY priority. The console will be + * explicitly flushed when exiting the emergency section. */ - if (console_trylock_spinning()) - console_unlock(); + if (nbcon_get_default_prio() == NBCON_PRIO_EMERGENCY) { + do_trylock_unlock = false; + } else { + /* + * Try to acquire and then immediately release the + * console semaphore. The release will print out + * buffers. With the spinning variant, this context + * tries to take over the printing from another + * printing context. + */ + if (console_trylock_spinning()) + console_unlock(); + } + preempt_enable(); } - if (in_sched) - defer_console_output(); - else + if (do_trylock_unlock) wake_up_klogd(); + else + defer_console_output(); return printed_len; } @@ -2373,6 +2483,14 @@ EXPORT_SYMBOL(_printk); static bool pr_flush(int timeout_ms, bool reset_on_progress); static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progress); +static struct task_struct *nbcon_legacy_kthread; + +static inline void wake_up_legacy_kthread(void) +{ + if (nbcon_legacy_kthread) + wake_up_interruptible(&legacy_wait); +} + #else /* CONFIG_PRINTK */ #define printk_time false @@ -2383,25 +2501,11 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre static u64 syslog_seq; -static size_t record_print_text(const struct printk_record *r, - bool syslog, bool time) -{ - return 0; -} -static ssize_t info_print_ext_header(char *buf, size_t size, - struct printk_info *info) -{ - return 0; -} -static ssize_t msg_print_ext_body(char *buf, size_t size, - char *text, size_t text_len, - struct dev_printk_info *dev_info) { return 0; } -static void console_lock_spinning_enable(void) { } -static int console_lock_spinning_disable_and_check(int cookie) { return 0; } -static bool suppress_message_printing(int level) { return false; } static bool pr_flush(int timeout_ms, bool reset_on_progress) { return true; } static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progress) { return true; } +static inline void nbcon_legacy_kthread_create(void) { } +static inline void wake_up_legacy_kthread(void) { } #endif /* CONFIG_PRINTK */ #ifdef CONFIG_EARLY_PRINTK @@ -2609,6 +2713,8 @@ void suspend_console(void) void resume_console(void) { struct console *con; + short flags; + int cookie; if (!console_suspend_enabled) return; @@ -2625,6 +2731,20 @@ void resume_console(void) */ synchronize_srcu(&console_srcu); + /* + * Since this runs in task context, wake the threaded printers + * directly rather than scheduling irq_work to do it. + */ + cookie = console_srcu_read_lock(); + for_each_console_srcu(con) { + flags = console_srcu_read_flags(con); + if (flags & CON_NBCON) + nbcon_kthread_wake(con); + } + console_srcu_read_unlock(cookie); + + wake_up_legacy_kthread(); + pr_flush(1000, true); } @@ -2639,7 +2759,8 @@ void resume_console(void) */ static int console_cpu_notify(unsigned int cpu) { - if (!cpuhp_tasks_frozen) { + if (!cpuhp_tasks_frozen && printing_via_unlock && + !IS_ENABLED(CONFIG_PREEMPT_RT)) { /* If trylock fails, someone else is doing the printing */ if (console_trylock()) console_unlock(); @@ -2647,26 +2768,6 @@ static int console_cpu_notify(unsigned int cpu) return 0; } -/* - * Return true if a panic is in progress on a remote CPU. - * - * On true, the local CPU should immediately release any printing resources - * that may be needed by the panic CPU. - */ -bool other_cpu_in_panic(void) -{ - if (!panic_in_progress()) - return false; - - /* - * We can use raw_smp_processor_id() here because it is impossible for - * the task to be migrated to the panic_cpu, or away from it. If - * panic_cpu has already been set, and we're not currently executing on - * that CPU, then we never will be. - */ - return atomic_read(&panic_cpu) != raw_smp_processor_id(); -} - /** * console_lock - block the console subsystem from printing * @@ -2716,42 +2817,16 @@ int is_console_locked(void) } EXPORT_SYMBOL(is_console_locked); -/* - * Check if the given console is currently capable and allowed to print - * records. - * - * Requires the console_srcu_read_lock. - */ -static inline bool console_is_usable(struct console *con) -{ - short flags = console_srcu_read_flags(con); - - if (!(flags & CON_ENABLED)) - return false; - - if ((flags & CON_SUSPENDED)) - return false; - - if (!con->write) - return false; - - /* - * Console drivers may assume that per-cpu resources have been - * allocated. So unless they're explicitly marked as being able to - * cope (CON_ANYTIME) don't call them until this CPU is officially up. - */ - if (!cpu_online(raw_smp_processor_id()) && !(flags & CON_ANYTIME)) - return false; - - return true; -} - static void __console_unlock(void) { console_locked = 0; up_console_sem(); } +static DEFINE_WAIT_OVERRIDE_MAP(printk_legacy_map, LD_WAIT_SLEEP); + +#ifdef CONFIG_PRINTK + /* * Prepend the message in @pmsg->pbufs->outbuf with a "dropped message". This * is achieved by shifting the existing message over and inserting the dropped @@ -2766,8 +2841,7 @@ static void __console_unlock(void) * * If @pmsg->pbufs->outbuf is modified, @pmsg->outbuf_len is updated. */ -#ifdef CONFIG_PRINTK -static void console_prepend_dropped(struct printk_message *pmsg, unsigned long dropped) +void console_prepend_dropped(struct printk_message *pmsg, unsigned long dropped) { struct printk_buffers *pbufs = pmsg->pbufs; const size_t scratchbuf_sz = sizeof(pbufs->scratchbuf); @@ -2798,9 +2872,6 @@ static void console_prepend_dropped(struct printk_message *pmsg, unsigned long d memcpy(outbuf, scratchbuf, len); pmsg->outbuf_len += len; } -#else -#define console_prepend_dropped(pmsg, dropped) -#endif /* CONFIG_PRINTK */ /* * Read and format the specified record (or a later record if the specified @@ -2821,11 +2892,9 @@ static void console_prepend_dropped(struct printk_message *pmsg, unsigned long d * of @pmsg are valid. (See the documentation of struct printk_message * for information about the @pmsg fields.) */ -static bool printk_get_next_message(struct printk_message *pmsg, u64 seq, - bool is_extended, bool may_suppress) +bool printk_get_next_message(struct printk_message *pmsg, u64 seq, + bool is_extended, bool may_suppress) { - static int panic_console_dropped; - struct printk_buffers *pbufs = pmsg->pbufs; const size_t scratchbuf_sz = sizeof(pbufs->scratchbuf); const size_t outbuf_sz = sizeof(pbufs->outbuf); @@ -2853,17 +2922,6 @@ static bool printk_get_next_message(struct printk_message *pmsg, u64 seq, pmsg->seq = r.info->seq; pmsg->dropped = r.info->seq - seq; - /* - * Check for dropped messages in panic here so that printk - * suppression can occur as early as possible if necessary. - */ - if (pmsg->dropped && - panic_in_progress() && - panic_console_dropped++ > 10) { - suppress_panic_printk = 1; - pr_warn_once("Too many dropped messages. Suppress messages on non-panic CPUs to prevent livelock.\n"); - } - /* Skip record that has level above the console loglevel. */ if (may_suppress && suppress_message_printing(r.info->level)) goto out; @@ -2880,6 +2938,13 @@ static bool printk_get_next_message(struct printk_message *pmsg, u64 seq, return true; } +/* + * Used as the printk buffers for non-panic, serialized console printing. + * This is for legacy (!CON_NBCON) as well as all boot (CON_BOOT) consoles. + * Its usage requires the console_lock held. + */ +struct printk_buffers printk_shared_pbufs; + /* * Print one record for the given console. The record printed is whatever * record is the next available record for the given console. @@ -2897,12 +2962,10 @@ static bool printk_get_next_message(struct printk_message *pmsg, u64 seq, */ static bool console_emit_next_record(struct console *con, bool *handover, int cookie) { - static struct printk_buffers pbufs; - bool is_extended = console_srcu_read_flags(con) & CON_EXTENDED; - char *outbuf = &pbufs.outbuf[0]; + char *outbuf = &printk_shared_pbufs.outbuf[0]; struct printk_message pmsg = { - .pbufs = &pbufs, + .pbufs = &printk_shared_pbufs, }; unsigned long flags; @@ -2924,35 +2987,59 @@ static bool console_emit_next_record(struct console *con, bool *handover, int co con->dropped = 0; } - /* - * While actively printing out messages, if another printk() - * were to occur on another CPU, it may wait for this one to - * finish. This task can not be preempted if there is a - * waiter waiting to take over. - * - * Interrupts are disabled because the hand over to a waiter - * must not be interrupted until the hand over is completed - * (@console_waiter is cleared). - */ - printk_safe_enter_irqsave(flags); - console_lock_spinning_enable(); + /* Write everything out to the hardware. */ - /* Do not trace print latency. */ - stop_critical_timings(); + if (IS_ENABLED(CONFIG_PREEMPT_RT)) { + /* + * On PREEMPT_RT this function is either in a thread or + * panic context. So there is no need for concern about + * printk reentrance, handovers, or lockdep complaints. + */ - /* Write everything out to the hardware. */ - con->write(con, outbuf, pmsg.outbuf_len); + con->write(con, outbuf, pmsg.outbuf_len); + con->seq = pmsg.seq + 1; + } else { + /* + * While actively printing out messages, if another printk() + * were to occur on another CPU, it may wait for this one to + * finish. This task can not be preempted if there is a + * waiter waiting to take over. + * + * Interrupts are disabled because the hand over to a waiter + * must not be interrupted until the hand over is completed + * (@console_waiter is cleared). + */ + printk_safe_enter_irqsave(flags); + console_lock_spinning_enable(); - start_critical_timings(); + /* Do not trace print latency. */ + stop_critical_timings(); - con->seq = pmsg.seq + 1; + lock_map_acquire_try(&printk_legacy_map); + con->write(con, outbuf, pmsg.outbuf_len); + lock_map_release(&printk_legacy_map); - *handover = console_lock_spinning_disable_and_check(cookie); - printk_safe_exit_irqrestore(flags); + start_critical_timings(); + + con->seq = pmsg.seq + 1; + + *handover = console_lock_spinning_disable_and_check(cookie); + printk_safe_exit_irqrestore(flags); + } skip: return true; } +#else + +static bool console_emit_next_record(struct console *con, bool *handover, int cookie) +{ + *handover = false; + return false; +} + +#endif /* CONFIG_PRINTK */ + /* * Print out all remaining records to all consoles. * @@ -2991,13 +3078,33 @@ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handove cookie = console_srcu_read_lock(); for_each_console_srcu(con) { + short flags = console_srcu_read_flags(con); + u64 printk_seq; bool progress; - if (!console_is_usable(con)) + /* + * console_flush_all() is only for legacy consoles, + * unless the nbcon console has no kthread printer. + */ + if ((flags & CON_NBCON) && con->kthread) + continue; + + if (!console_is_usable(con, flags, true)) continue; any_usable = true; - progress = console_emit_next_record(con, handover, cookie); + if (flags & CON_NBCON) { + + lock_map_acquire_try(&printk_legacy_map); + progress = nbcon_atomic_emit_next_record(con, handover, cookie); + lock_map_release(&printk_legacy_map); + + printk_seq = nbcon_seq_read(con); + } else { + progress = console_emit_next_record(con, handover, cookie); + + printk_seq = con->seq; + } /* * If a handover has occurred, the SRCU read lock @@ -3007,8 +3114,8 @@ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handove return false; /* Track the next of the highest seq flushed. */ - if (con->seq > *next_seq) - *next_seq = con->seq; + if (printk_seq > *next_seq) + *next_seq = printk_seq; if (!progress) continue; @@ -3031,19 +3138,7 @@ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handove return false; } -/** - * console_unlock - unblock the console subsystem from printing - * - * Releases the console_lock which the caller holds to block printing of - * the console subsystem. - * - * While the console_lock was held, console output may have been buffered - * by printk(). If this is the case, console_unlock(); emits - * the output prior to releasing the lock. - * - * console_unlock(); may be called from any context. - */ -void console_unlock(void) +static void console_flush_and_unlock(void) { bool do_cond_resched; bool handover; @@ -3087,6 +3182,32 @@ void console_unlock(void) */ } while (prb_read_valid(prb, next_seq, NULL) && console_trylock()); } + +/** + * console_unlock - unblock the console subsystem from printing + * + * Releases the console_lock which the caller holds to block printing of + * the console subsystem. + * + * While the console_lock was held, console output may have been buffered + * by printk(). If this is the case, console_unlock(); emits + * the output prior to releasing the lock. + * + * console_unlock(); may be called from any context. + */ +void console_unlock(void) +{ + /* + * PREEMPT_RT relies on kthread and atomic consoles for printing. + * It never attempts to print from console_unlock(). + */ + if (IS_ENABLED(CONFIG_PREEMPT_RT)) { + __console_unlock(); + return; + } + + console_flush_and_unlock(); +} EXPORT_SYMBOL(console_unlock); /** @@ -3197,6 +3318,7 @@ void console_flush_on_panic(enum con_flush_mode mode) if (mode == CONSOLE_REPLAY_ALL) { struct console *c; + short flags; int cookie; u64 seq; @@ -3204,16 +3326,25 @@ void console_flush_on_panic(enum con_flush_mode mode) cookie = console_srcu_read_lock(); for_each_console_srcu(c) { - /* - * This is an unsynchronized assignment, but the - * kernel is in "hope and pray" mode anyway. - */ - c->seq = seq; + flags = console_srcu_read_flags(c); + + if (flags & CON_NBCON) { + nbcon_seq_force(c, seq); + } else { + /* + * This is an unsynchronized assignment. On + * panic legacy consoles are only best effort. + */ + c->seq = seq; + } } console_srcu_read_unlock(cookie); } - console_flush_all(false, &next_seq, &handover); + nbcon_atomic_flush_all(); + + if (printing_via_unlock) + console_flush_all(false, &next_seq, &handover); } /* @@ -3270,13 +3401,122 @@ EXPORT_SYMBOL(console_stop); void console_start(struct console *console) { + short flags; + console_list_lock(); console_srcu_write_flags(console, console->flags | CON_ENABLED); + flags = console->flags; console_list_unlock(); + + /* + * Ensure that all SRCU list walks have completed. The related + * printing context must be able to see it is enabled so that + * it is guaranteed to wake up and resume printing. + */ + synchronize_srcu(&console_srcu); + + if (flags & CON_NBCON) + nbcon_kthread_wake(console); + else + wake_up_legacy_kthread(); + __pr_flush(console, 1000, true); } EXPORT_SYMBOL(console_start); +#ifdef CONFIG_PRINTK +static bool printer_should_wake(void) +{ + bool available = false; + struct console *con; + int cookie; + + if (kthread_should_stop()) + return true; + + cookie = console_srcu_read_lock(); + for_each_console_srcu(con) { + short flags = console_srcu_read_flags(con); + u64 printk_seq; + + /* + * The legacy printer thread is only for legacy consoles, + * unless the nbcon console has no kthread printer. + */ + if ((flags & CON_NBCON) && con->kthread) + continue; + + if (!console_is_usable(con, flags, true)) + continue; + + if (flags & CON_NBCON) { + printk_seq = nbcon_seq_read(con); + } else { + /* + * It is safe to read @seq because only this + * thread context updates @seq. + */ + printk_seq = con->seq; + } + + if (prb_read_valid(prb, printk_seq, NULL)) { + available = true; + break; + } + } + console_srcu_read_unlock(cookie); + + return available; +} + +static int nbcon_legacy_kthread_func(void *unused) +{ + int error; + + for (;;) { + error = wait_event_interruptible(legacy_wait, printer_should_wake()); + + if (kthread_should_stop()) + break; + + if (error) + continue; + + console_lock(); + console_flush_and_unlock(); + } + + return 0; +} + +void nbcon_legacy_kthread_create(void) +{ + struct task_struct *kt; + + lockdep_assert_held(&console_mutex); + + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) + return; + + if (!printk_threads_enabled || nbcon_legacy_kthread) + return; + + kt = kthread_run(nbcon_legacy_kthread_func, NULL, "pr/legacy"); + if (IS_ERR(kt)) { + pr_err("unable to start legacy printing thread\n"); + return; + } + + nbcon_legacy_kthread = kt; + + /* + * It is important that console printing threads are scheduled + * shortly after a printk call and with generous runtime budgets. + */ + sched_set_normal(nbcon_legacy_kthread, -20); +} +#endif /* CONFIG_PRINTK */ + static int __read_mostly keep_bootcon; static int __init keep_bootcon_setup(char *str) @@ -3375,11 +3615,6 @@ static void try_enable_default_console(struct console *newcon) newcon->flags |= CON_CONSDEV; } -#define con_printk(lvl, con, fmt, ...) \ - printk(lvl pr_fmt("%sconsole [%s%d] " fmt), \ - (con->flags & CON_BOOT) ? "boot" : "", \ - con->name, con->index, ##__VA_ARGS__) - static void console_init_seq(struct console *newcon, bool bootcon_registered) { struct console *con; @@ -3428,11 +3663,20 @@ static void console_init_seq(struct console *newcon, bool bootcon_registered) newcon->seq = prb_next_seq(prb); for_each_console(con) { - if ((con->flags & CON_BOOT) && - (con->flags & CON_ENABLED) && - con->seq < newcon->seq) { - newcon->seq = con->seq; + u64 seq; + + if (!((con->flags & CON_BOOT) && + (con->flags & CON_ENABLED))) { + continue; } + + if (con->flags & CON_NBCON) + seq = nbcon_seq_read(con); + else + seq = con->seq; + + if (seq < newcon->seq) + newcon->seq = seq; } } @@ -3493,6 +3737,15 @@ void register_console(struct console *newcon) goto unlock; } + if (newcon->flags & CON_NBCON) { + /* + * Ensure the nbcon console buffers can be allocated + * before modifying any global data. + */ + if (!nbcon_alloc(newcon)) + goto unlock; + } + /* * See if we want to enable this console driver by default. * @@ -3520,8 +3773,11 @@ void register_console(struct console *newcon) err = try_enable_preferred_console(newcon, false); /* printk() messages are not printed to the Braille console. */ - if (err || newcon->flags & CON_BRL) + if (err || newcon->flags & CON_BRL) { + if (newcon->flags & CON_NBCON) + nbcon_free(newcon); goto unlock; + } /* * If we have a bootconsole, and are switching to a real console, @@ -3537,6 +3793,17 @@ void register_console(struct console *newcon) newcon->dropped = 0; console_init_seq(newcon, bootcon_registered); + if (newcon->flags & CON_NBCON) { + have_nbcon_console = true; + nbcon_init(newcon); + } else { + have_legacy_console = true; + nbcon_legacy_kthread_create(); + } + + if (newcon->flags & CON_BOOT) + have_boot_console = true; + /* * Put this console in the list - keep the * preferred driver at the head of the list. @@ -3589,6 +3856,11 @@ EXPORT_SYMBOL(register_console); /* Must be called under console_list_lock(). */ static int unregister_console_locked(struct console *console) { + bool is_boot_con = (console->flags & CON_BOOT); + bool found_legacy_con = false; + bool found_nbcon_con = false; + bool found_boot_con = false; + struct console *c; int res; lockdep_assert_console_list_lock_held(); @@ -3628,11 +3900,50 @@ static int unregister_console_locked(struct console *console) */ synchronize_srcu(&console_srcu); + if (console->flags & CON_NBCON) + nbcon_free(console); + console_sysfs_notify(); if (console->exit) res = console->exit(console); + /* + * With this console gone, the global flags tracking registered + * console types may have changed. Update them. + */ + for_each_console(c) { + if (c->flags & CON_BOOT) + found_boot_con = true; + + if (c->flags & CON_NBCON) + found_nbcon_con = true; + else + found_legacy_con = true; + } + if (!found_boot_con) + have_boot_console = false; + if (!found_legacy_con) + have_legacy_console = false; + if (!found_nbcon_con) + have_nbcon_console = false; + + /* + * When the last boot console unregisters, start up the + * printing threads. + */ + if (is_boot_con && !have_boot_console) { + for_each_console(c) + nbcon_kthread_create(c); + } + +#ifdef CONFIG_PRINTK + if (!printing_via_unlock && nbcon_legacy_kthread) { + kthread_stop(nbcon_legacy_kthread); + nbcon_legacy_kthread = NULL; + } +#endif + return res; } @@ -3777,69 +4088,94 @@ late_initcall(printk_late_init); /* If @con is specified, only wait for that console. Otherwise wait for all. */ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progress) { - int remaining = timeout_ms; + unsigned long timeout_jiffies = msecs_to_jiffies(timeout_ms); + unsigned long remaining_jiffies = timeout_jiffies; struct console *c; u64 last_diff = 0; u64 printk_seq; + short flags; + bool locked; int cookie; u64 diff; u64 seq; might_sleep(); - seq = prb_next_seq(prb); + seq = prb_next_reserve_seq(prb); - /* Flush the consoles so that records up to @seq are printed. */ - console_lock(); - console_unlock(); + /* + * Flush the consoles so that records up to @seq are printed. + * Otherwise this function will just wait for the threaded printers + * to print up to @seq. + */ + if (printing_via_unlock && !IS_ENABLED(CONFIG_PREEMPT_RT)) { + console_lock(); + console_unlock(); + } for (;;) { + unsigned long begin_jiffies; + unsigned long slept_jiffies; + + locked = false; diff = 0; - /* - * Hold the console_lock to guarantee safe access to - * console->seq. Releasing console_lock flushes more - * records in case @seq is still not printed on all - * usable consoles. - */ - console_lock(); + if (printing_via_unlock) { + /* + * Hold the console_lock to guarantee safe access to + * console->seq. Releasing console_lock flushes more + * records in case @seq is still not printed on all + * usable consoles. + */ + console_lock(); + locked = true; + } cookie = console_srcu_read_lock(); for_each_console_srcu(c) { if (con && con != c) continue; + + flags = console_srcu_read_flags(c); + /* * If consoles are not usable, it cannot be expected * that they make forward progress, so only increment * @diff for usable consoles. */ - if (!console_is_usable(c)) + if (!console_is_usable(c, flags, true) && + !console_is_usable(c, flags, false)) { continue; - printk_seq = c->seq; + } + + if (flags & CON_NBCON) { + printk_seq = nbcon_seq_read(c); + } else { + WARN_ON_ONCE(!locked); + printk_seq = c->seq; + } + if (printk_seq < seq) diff += seq - printk_seq; } console_srcu_read_unlock(cookie); if (diff != last_diff && reset_on_progress) - remaining = timeout_ms; + remaining_jiffies = timeout_jiffies; - console_unlock(); + if (locked) + console_unlock(); /* Note: @diff is 0 if there are no usable consoles. */ - if (diff == 0 || remaining == 0) + if (diff == 0 || remaining_jiffies == 0) break; - if (remaining < 0) { - /* no timeout limit */ - msleep(100); - } else if (remaining < 100) { - msleep(remaining); - remaining = 0; - } else { - msleep(100); - remaining -= 100; - } + /* msleep(1) might sleep much longer. Check time by jiffies. */ + begin_jiffies = jiffies; + msleep(1); + slept_jiffies = jiffies - begin_jiffies; + + remaining_jiffies -= min(slept_jiffies, remaining_jiffies); last_diff = diff; } @@ -3880,9 +4216,16 @@ static void wake_up_klogd_work_func(struct irq_work *irq_work) int pending = this_cpu_xchg(printk_pending, 0); if (pending & PRINTK_PENDING_OUTPUT) { - /* If trylock fails, someone else is doing the printing */ - if (console_trylock()) - console_unlock(); + if (IS_ENABLED(CONFIG_PREEMPT_RT)) { + wake_up_interruptible(&legacy_wait); + } else { + /* + * If trylock fails, some other context + * will do the printing. + */ + if (console_trylock()) + console_unlock(); + } } if (pending & PRINTK_PENDING_WAKEUP) @@ -3950,11 +4293,16 @@ void defer_console_output(void) * New messages may have been added directly to the ringbuffer * using vprintk_store(), so wake any waiters as well. */ - __wake_up_klogd(PRINTK_PENDING_WAKEUP | PRINTK_PENDING_OUTPUT); + int val = PRINTK_PENDING_WAKEUP; + + if (printing_via_unlock) + val |= PRINTK_PENDING_OUTPUT; + __wake_up_klogd(val); } void printk_trigger_flush(void) { + nbcon_wake_threads(); defer_console_output(); } diff --git a/kernel/printk/printk_ringbuffer.c b/kernel/printk/printk_ringbuffer.c index fde338606ce8..e7b808b829a0 100644 --- a/kernel/printk/printk_ringbuffer.c +++ b/kernel/printk/printk_ringbuffer.c @@ -6,6 +6,7 @@ #include #include #include "printk_ringbuffer.h" +#include "internal.h" /** * DOC: printk_ringbuffer overview @@ -303,6 +304,9 @@ * * desc_push_tail:B / desc_reserve:D * set descriptor reusable (state), then push descriptor tail (id) + * + * desc_update_last_finalized:A / desc_last_finalized_seq:A + * store finalized record, then set new highest finalized sequence number */ #define DATA_SIZE(data_ring) _DATA_SIZE((data_ring)->size_bits) @@ -1030,9 +1034,13 @@ static char *data_alloc(struct printk_ringbuffer *rb, unsigned int size, unsigned long next_lpos; if (size == 0) { - /* Specify a data-less block. */ - blk_lpos->begin = NO_LPOS; - blk_lpos->next = NO_LPOS; + /* + * Data blocks are not created for empty lines. Instead, the + * reader will recognize these special lpos values and handle + * it appropriately. + */ + blk_lpos->begin = EMPTY_LINE_LPOS; + blk_lpos->next = EMPTY_LINE_LPOS; return NULL; } @@ -1210,10 +1218,18 @@ static const char *get_data(struct prb_data_ring *data_ring, /* Data-less data block description. */ if (BLK_DATALESS(blk_lpos)) { - if (blk_lpos->begin == NO_LPOS && blk_lpos->next == NO_LPOS) { + /* + * Records that are just empty lines are also valid, even + * though they do not have a data block. For such records + * explicitly return empty string data to signify success. + */ + if (blk_lpos->begin == EMPTY_LINE_LPOS && + blk_lpos->next == EMPTY_LINE_LPOS) { *data_size = 0; return ""; } + + /* Data lost, invalid, or otherwise unavailable. */ return NULL; } @@ -1441,20 +1457,118 @@ bool prb_reserve_in_last(struct prb_reserved_entry *e, struct printk_ringbuffer return false; } +/* + * @last_finalized_seq value guarantees that all records up to and including + * this sequence number are finalized and can be read. The only exception are + * too old records which have already been overwritten. + * + * It is also guaranteed that @last_finalized_seq only increases. + * + * Be aware that finalized records following non-finalized records are not + * reported because they are not yet available to the reader. For example, + * a new record stored via printk() will not be available to a printer if + * it follows a record that has not been finalized yet. However, once that + * non-finalized record becomes finalized, @last_finalized_seq will be + * appropriately updated and the full set of finalized records will be + * available to the printer. And since each printk() caller will either + * directly print or trigger deferred printing of all available unprinted + * records, all printk() messages will get printed. + */ +static u64 desc_last_finalized_seq(struct printk_ringbuffer *rb) +{ + struct prb_desc_ring *desc_ring = &rb->desc_ring; + unsigned long ulseq; + + /* + * Guarantee the sequence number is loaded before loading the + * associated record in order to guarantee that the record can be + * seen by this CPU. This pairs with desc_update_last_finalized:A. + */ + ulseq = atomic_long_read_acquire(&desc_ring->last_finalized_seq + ); /* LMM(desc_last_finalized_seq:A) */ + + return __ulseq_to_u64seq(rb, ulseq); +} + +static bool _prb_read_valid(struct printk_ringbuffer *rb, u64 *seq, + struct printk_record *r, unsigned int *line_count); + +/* + * Check if there are records directly following @last_finalized_seq that are + * finalized. If so, update @last_finalized_seq to the latest of these + * records. It is not allowed to skip over records that are not yet finalized. + */ +static void desc_update_last_finalized(struct printk_ringbuffer *rb) +{ + struct prb_desc_ring *desc_ring = &rb->desc_ring; + u64 old_seq = desc_last_finalized_seq(rb); + unsigned long oldval; + unsigned long newval; + u64 finalized_seq; + u64 try_seq; + +try_again: + finalized_seq = old_seq; + try_seq = finalized_seq + 1; + + /* Try to find later finalized records. */ + while (_prb_read_valid(rb, &try_seq, NULL, NULL)) { + finalized_seq = try_seq; + try_seq++; + } + + /* No update needed if no later finalized record was found. */ + if (finalized_seq == old_seq) + return; + + oldval = __u64seq_to_ulseq(old_seq); + newval = __u64seq_to_ulseq(finalized_seq); + + /* + * Set the sequence number of a later finalized record that has been + * seen. + * + * Guarantee the record data is visible to other CPUs before storing + * its sequence number. This pairs with desc_last_finalized_seq:A. + * + * Memory barrier involvement: + * + * If desc_last_finalized_seq:A reads from + * desc_update_last_finalized:A, then desc_read:A reads from + * _prb_commit:B. + * + * Relies on: + * + * RELEASE from _prb_commit:B to desc_update_last_finalized:A + * matching + * ACQUIRE from desc_last_finalized_seq:A to desc_read:A + * + * Note: _prb_commit:B and desc_update_last_finalized:A can be + * different CPUs. However, the desc_update_last_finalized:A + * CPU (which performs the release) must have previously seen + * _prb_commit:B. + */ + if (!atomic_long_try_cmpxchg_release(&desc_ring->last_finalized_seq, + &oldval, newval)) { /* LMM(desc_update_last_finalized:A) */ + old_seq = __ulseq_to_u64seq(rb, oldval); + goto try_again; + } +} + /* * Attempt to finalize a specified descriptor. If this fails, the descriptor * is either already final or it will finalize itself when the writer commits. */ -static void desc_make_final(struct prb_desc_ring *desc_ring, unsigned long id) +static void desc_make_final(struct printk_ringbuffer *rb, unsigned long id) { + struct prb_desc_ring *desc_ring = &rb->desc_ring; unsigned long prev_state_val = DESC_SV(id, desc_committed); struct prb_desc *d = to_desc(desc_ring, id); - atomic_long_cmpxchg_relaxed(&d->state_var, prev_state_val, - DESC_SV(id, desc_finalized)); /* LMM(desc_make_final:A) */ - - /* Best effort to remember the last finalized @id. */ - atomic_long_set(&desc_ring->last_finalized_id, id); + if (atomic_long_try_cmpxchg_relaxed(&d->state_var, &prev_state_val, + DESC_SV(id, desc_finalized))) { /* LMM(desc_make_final:A) */ + desc_update_last_finalized(rb); + } } /** @@ -1550,7 +1664,7 @@ bool prb_reserve(struct prb_reserved_entry *e, struct printk_ringbuffer *rb, * readers. (For seq==0 there is no previous descriptor.) */ if (info->seq > 0) - desc_make_final(desc_ring, DESC_ID(id - 1)); + desc_make_final(rb, DESC_ID(id - 1)); r->text_buf = data_alloc(rb, r->text_buf_size, &d->text_blk_lpos, id); /* If text data allocation fails, a data-less record is committed. */ @@ -1643,7 +1757,7 @@ void prb_commit(struct prb_reserved_entry *e) */ head_id = atomic_long_read(&desc_ring->head_id); /* LMM(prb_commit:A) */ if (head_id != e->id) - desc_make_final(desc_ring, e->id); + desc_make_final(e->rb, e->id); } /** @@ -1663,12 +1777,9 @@ void prb_commit(struct prb_reserved_entry *e) */ void prb_final_commit(struct prb_reserved_entry *e) { - struct prb_desc_ring *desc_ring = &e->rb->desc_ring; - _prb_commit(e, desc_finalized); - /* Best effort to remember the last finalized @id. */ - atomic_long_set(&desc_ring->last_finalized_id, e->id); + desc_update_last_finalized(e->rb); } /* @@ -1746,6 +1857,8 @@ static bool copy_data(struct prb_data_ring *data_ring, * descriptor. However, it also verifies that the record is finalized and has * the sequence number @seq. On success, 0 is returned. * + * For the panic CPU, committed descriptors are also considered finalized. + * * Error return values: * -EINVAL: A finalized record with sequence number @seq does not exist. * -ENOENT: A finalized record with sequence number @seq exists, but its data @@ -1764,16 +1877,25 @@ static int desc_read_finalized_seq(struct prb_desc_ring *desc_ring, /* * An unexpected @id (desc_miss) or @seq mismatch means the record - * does not exist. A descriptor in the reserved or committed state - * means the record does not yet exist for the reader. + * does not exist. A descriptor in the reserved state means the + * record does not yet exist for the reader. */ if (d_state == desc_miss || d_state == desc_reserved || - d_state == desc_committed || s != seq) { return -EINVAL; } + /* + * A descriptor in the committed state means the record does not yet + * exist for the reader. However, for the panic CPU, committed + * records are also handled as finalized records since they contain + * message data in a consistent state and may contain additional + * hints as to the cause of the panic. + */ + if (d_state == desc_committed && !this_cpu_in_panic()) + return -EINVAL; + /* * A descriptor in the reusable state may no longer have its data * available; report it as existing but with lost data. Or the record @@ -1832,7 +1954,7 @@ static int prb_read(struct printk_ringbuffer *rb, u64 seq, } /* Get the sequence number of the tail descriptor. */ -static u64 prb_first_seq(struct printk_ringbuffer *rb) +u64 prb_first_seq(struct printk_ringbuffer *rb) { struct prb_desc_ring *desc_ring = &rb->desc_ring; enum desc_state d_state; @@ -1875,12 +1997,131 @@ static u64 prb_first_seq(struct printk_ringbuffer *rb) return seq; } +/** + * prb_next_reserve_seq() - Get the sequence number after the most recently + * reserved record. + * + * @rb: The ringbuffer to get the sequence number from. + * + * This is the public function available to readers to see what sequence + * number will be assigned to the next reserved record. + * + * Note that depending on the situation, this value can be equal to or + * higher than the sequence number returned by prb_next_seq(). + * + * Context: Any context. + * Return: The sequence number that will be assigned to the next record + * reserved. + */ +u64 prb_next_reserve_seq(struct printk_ringbuffer *rb) +{ + struct prb_desc_ring *desc_ring = &rb->desc_ring; + unsigned long last_finalized_id; + atomic_long_t *state_var; + u64 last_finalized_seq; + unsigned long head_id; + struct prb_desc desc; + unsigned long diff; + struct prb_desc *d; + int err; + + /* + * It may not be possible to read a sequence number for @head_id. + * So the ID of @last_finailzed_seq is used to calculate what the + * sequence number of @head_id will be. + */ + +try_again: + last_finalized_seq = desc_last_finalized_seq(rb); + + /* + * @head_id is loaded after @last_finalized_seq to ensure that it is + * at or beyond @last_finalized_seq. + * + * Memory barrier involvement: + * + * If desc_last_finalized_seq:A reads from + * desc_update_last_finalized:A, then + * prb_next_reserve_seq:A reads from desc_reserve:D. + * + * Relies on: + * + * RELEASE from desc_reserve:D to desc_update_last_finalized:A + * matching + * ACQUIRE from desc_last_finalized_seq:A to prb_next_reserve_seq:A + * + * Note: desc_reserve:D and desc_update_last_finalized:A can be + * different CPUs. However, the desc_update_last_finalized:A CPU + * (which performs the release) must have previously seen + * desc_read:C, which implies desc_reserve:D can be seen. + */ + head_id = atomic_long_read(&desc_ring->head_id); /* LMM(prb_next_reserve_seq:A) */ + + d = to_desc(desc_ring, last_finalized_seq); + state_var = &d->state_var; + + /* Extract the ID, used to specify the descriptor to read. */ + last_finalized_id = DESC_ID(atomic_long_read(state_var)); + + /* Ensure @last_finalized_id is correct. */ + err = desc_read_finalized_seq(desc_ring, last_finalized_id, last_finalized_seq, &desc); + + if (err == -EINVAL) { + if (last_finalized_seq == 0) { + /* + * @last_finalized_seq still contains its initial + * value. Probably no record has been finalized yet. + * This means the ringbuffer is not yet full and the + * @head_id value can be used directly (subtracting + * off the id value corresponding to seq=0). + */ + + /* + * Because of hack#2 of the bootstrapping phase, the + * @head_id initial value must be handled separately. + */ + if (head_id == DESC0_ID(desc_ring->count_bits)) + return 0; + + /* + * The @head_id is initialized such that the first + * increment will yield the first record (seq=0). + * Therefore use the initial value +1 as the base to + * subtract from @head_id. + */ + last_finalized_id = DESC0_ID(desc_ring->count_bits) + 1; + } else { + /* Record must have been overwritten. Try again. */ + goto try_again; + } + } + + /* + * @diff is the number of records beyond the last record available + * to readers. + */ + diff = head_id - last_finalized_id; + + /* + * @head_id points to the most recently reserved record, but this + * function returns the sequence number that will be assigned to the + * next (not yet reserved) record. Thus +1 is needed. + */ + return (last_finalized_seq + diff + 1); +} + /* - * Non-blocking read of a record. Updates @seq to the last finalized record - * (which may have no data available). + * Non-blocking read of a record. * - * See the description of prb_read_valid() and prb_read_valid_info() - * for details. + * On success @seq is updated to the record that was read and (if provided) + * @r and @line_count will contain the read/calculated data. + * + * On failure @seq is updated to a record that is not yet available to the + * reader, but it will be the next record available to the reader. + * + * Note: When the current CPU is in panic, this function will skip over any + * non-existent/non-finalized records in order to allow the panic CPU + * to print any and all records that have been finalized. */ static bool _prb_read_valid(struct printk_ringbuffer *rb, u64 *seq, struct printk_record *r, unsigned int *line_count) @@ -1899,12 +2140,32 @@ static bool _prb_read_valid(struct printk_ringbuffer *rb, u64 *seq, *seq = tail_seq; } else if (err == -ENOENT) { - /* Record exists, but no data available. Skip. */ + /* Record exists, but the data was lost. Skip. */ (*seq)++; } else { - /* Non-existent/non-finalized record. Must stop. */ - return false; + /* + * Non-existent/non-finalized record. Must stop. + * + * For panic situations it cannot be expected that + * non-finalized records will become finalized. But + * there may be other finalized records beyond that + * need to be printed for a panic situation. If this + * is the panic CPU, skip this + * non-existent/non-finalized record unless it is + * at or beyond the head, in which case it is not + * possible to continue. + * + * Note that new messages printed on panic CPU are + * finalized when we are here. The only exception + * might be the last message without trailing newline. + * But it would have the sequence number returned + * by "prb_next_reserve_seq() - 1". + */ + if (this_cpu_in_panic() && ((*seq + 1) < prb_next_reserve_seq(rb))) + (*seq)++; + else + return false; } } @@ -1932,7 +2193,7 @@ static bool _prb_read_valid(struct printk_ringbuffer *rb, u64 *seq, * On success, the reader must check r->info.seq to see which record was * actually read. This allows the reader to detect dropped records. * - * Failure means @seq refers to a not yet written record. + * Failure means @seq refers to a record not yet available to the reader. */ bool prb_read_valid(struct printk_ringbuffer *rb, u64 seq, struct printk_record *r) @@ -1962,7 +2223,7 @@ bool prb_read_valid(struct printk_ringbuffer *rb, u64 seq, * On success, the reader must check info->seq to see which record meta data * was actually read. This allows the reader to detect dropped records. * - * Failure means @seq refers to a not yet written record. + * Failure means @seq refers to a record not yet available to the reader. */ bool prb_read_valid_info(struct printk_ringbuffer *rb, u64 seq, struct printk_info *info, unsigned int *line_count) @@ -2008,7 +2269,9 @@ u64 prb_first_valid_seq(struct printk_ringbuffer *rb) * newest sequence number available to readers will be. * * This provides readers a sequence number to jump to if all currently - * available records should be skipped. + * available records should be skipped. It is guaranteed that all records + * previous to the returned value have been finalized and are (or were) + * available to the reader. * * Context: Any context. * Return: The sequence number of the next newest (not yet available) record @@ -2016,34 +2279,19 @@ u64 prb_first_valid_seq(struct printk_ringbuffer *rb) */ u64 prb_next_seq(struct printk_ringbuffer *rb) { - struct prb_desc_ring *desc_ring = &rb->desc_ring; - enum desc_state d_state; - unsigned long id; u64 seq; - /* Check if the cached @id still points to a valid @seq. */ - id = atomic_long_read(&desc_ring->last_finalized_id); - d_state = desc_read(desc_ring, id, NULL, &seq, NULL); + seq = desc_last_finalized_seq(rb); - if (d_state == desc_finalized || d_state == desc_reusable) { - /* - * Begin searching after the last finalized record. - * - * On 0, the search must begin at 0 because of hack#2 - * of the bootstrapping phase it is not known if a - * record at index 0 exists. - */ - if (seq != 0) - seq++; - } else { - /* - * The information about the last finalized sequence number - * has gone. It should happen only when there is a flood of - * new messages and the ringbuffer is rapidly recycled. - * Give up and start from the beginning. - */ - seq = 0; - } + /* + * Begin searching after the last finalized record. + * + * On 0, the search must begin at 0 because of hack#2 + * of the bootstrapping phase it is not known if a + * record at index 0 exists. + */ + if (seq != 0) + seq++; /* * The information about the last finalized @seq might be inaccurate. @@ -2085,7 +2333,7 @@ void prb_init(struct printk_ringbuffer *rb, rb->desc_ring.infos = infos; atomic_long_set(&rb->desc_ring.head_id, DESC0_ID(descbits)); atomic_long_set(&rb->desc_ring.tail_id, DESC0_ID(descbits)); - atomic_long_set(&rb->desc_ring.last_finalized_id, DESC0_ID(descbits)); + atomic_long_set(&rb->desc_ring.last_finalized_seq, 0); rb->text_data_ring.size_bits = textbits; rb->text_data_ring.data = text_buf; diff --git a/kernel/printk/printk_ringbuffer.h b/kernel/printk/printk_ringbuffer.h index 18cd25e489b8..52626d0f1fa3 100644 --- a/kernel/printk/printk_ringbuffer.h +++ b/kernel/printk/printk_ringbuffer.h @@ -75,7 +75,7 @@ struct prb_desc_ring { struct printk_info *infos; atomic_long_t head_id; atomic_long_t tail_id; - atomic_long_t last_finalized_id; + atomic_long_t last_finalized_seq; }; /* @@ -127,8 +127,22 @@ enum desc_state { #define DESC_SV(id, state) (((unsigned long)state << DESC_FLAGS_SHIFT) | id) #define DESC_ID_MASK (~DESC_FLAGS_MASK) #define DESC_ID(sv) ((sv) & DESC_ID_MASK) + +/* + * Special data block logical position values (for fields of + * @prb_desc.text_blk_lpos). + * + * - Bit0 is used to identify if the record has no data block. (Implemented in + * the LPOS_DATALESS() macro.) + * + * - Bit1 specifies the reason for not having a data block. + * + * These special values could never be real lpos values because of the + * meta data and alignment padding of data blocks. (See to_blk_size() for + * details.) + */ #define FAILED_LPOS 0x1 -#define NO_LPOS 0x3 +#define EMPTY_LINE_LPOS 0x3 #define FAILED_BLK_LPOS \ { \ @@ -259,7 +273,7 @@ static struct printk_ringbuffer name = { \ .infos = &_##name##_infos[0], \ .head_id = ATOMIC_INIT(DESC0_ID(descbits)), \ .tail_id = ATOMIC_INIT(DESC0_ID(descbits)), \ - .last_finalized_id = ATOMIC_INIT(DESC0_ID(descbits)), \ + .last_finalized_seq = ATOMIC_INIT(0), \ }, \ .text_data_ring = { \ .size_bits = (avgtextbits) + (descbits), \ @@ -378,7 +392,41 @@ bool prb_read_valid(struct printk_ringbuffer *rb, u64 seq, bool prb_read_valid_info(struct printk_ringbuffer *rb, u64 seq, struct printk_info *info, unsigned int *line_count); +u64 prb_first_seq(struct printk_ringbuffer *rb); u64 prb_first_valid_seq(struct printk_ringbuffer *rb); u64 prb_next_seq(struct printk_ringbuffer *rb); +u64 prb_next_reserve_seq(struct printk_ringbuffer *rb); + +#ifdef CONFIG_64BIT + +#define __u64seq_to_ulseq(u64seq) (u64seq) +#define __ulseq_to_u64seq(rb, ulseq) (ulseq) + +#else /* CONFIG_64BIT */ + +#define __u64seq_to_ulseq(u64seq) ((u32)u64seq) + +static inline u64 __ulseq_to_u64seq(struct printk_ringbuffer *rb, u32 ulseq) +{ + u64 rb_first_seq = prb_first_seq(rb); + u64 seq; + + /* + * The provided sequence is only the lower 32 bits of the ringbuffer + * sequence. It needs to be expanded to 64bit. Get the first sequence + * number from the ringbuffer and fold it. + * + * Having a 32bit representation in the console is sufficient. + * If a console ever gets more than 2^31 records behind + * the ringbuffer then this is the least of the problems. + * + * Also the access to the ring buffer is always safe. + */ + seq = rb_first_seq - (s32)((u32)rb_first_seq - ulseq); + + return seq; +} + +#endif /* CONFIG_64BIT */ #endif /* _KERNEL_PRINTK_RINGBUFFER_H */ diff --git a/kernel/printk/printk_safe.c b/kernel/printk/printk_safe.c index 6d10927a07d8..8d9408d653de 100644 --- a/kernel/printk/printk_safe.c +++ b/kernel/printk/printk_safe.c @@ -26,6 +26,18 @@ void __printk_safe_exit(void) this_cpu_dec(printk_context); } +void __printk_deferred_enter(void) +{ + cant_migrate(); + this_cpu_inc(printk_context); +} + +void __printk_deferred_exit(void) +{ + cant_migrate(); + this_cpu_dec(printk_context); +} + asmlinkage int vprintk(const char *fmt, va_list args) { #ifdef CONFIG_KGDB_KDB diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 46612fb15fc6..f893f4cfdd38 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -2409,6 +2409,12 @@ static int rcutorture_booster_init(unsigned int cpu) WARN_ON_ONCE(!t); sp.sched_priority = 2; sched_setscheduler_nocheck(t, SCHED_FIFO, &sp); +#ifdef CONFIG_PREEMPT_RT + t = per_cpu(timersd, cpu); + WARN_ON_ONCE(!t); + sp.sched_priority = 2; + sched_setscheduler_nocheck(t, SCHED_FIFO, &sp); +#endif } /* Don't allow time recalculation while creating a new task. */ diff --git a/kernel/rcu/tree_stall.h b/kernel/rcu/tree_stall.h index 11a1fac3a589..6f085a159fca 100644 --- a/kernel/rcu/tree_stall.h +++ b/kernel/rcu/tree_stall.h @@ -8,6 +8,7 @@ */ #include +#include ////////////////////////////////////////////////////////////////////////////// // @@ -604,6 +605,8 @@ static void print_other_cpu_stall(unsigned long gp_seq, unsigned long gps) if (rcu_stall_is_suppressed()) return; + nbcon_cpu_emergency_enter(); + /* * OK, time to rat on our buddy... * See Documentation/RCU/stallwarn.rst for info on how to debug @@ -658,6 +661,8 @@ static void print_other_cpu_stall(unsigned long gp_seq, unsigned long gps) panic_on_rcu_stall(); rcu_force_quiescent_state(); /* Kick them all. */ + + nbcon_cpu_emergency_exit(); } static void print_cpu_stall(unsigned long gps) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 9b406d988654..5e65826dd984 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -897,14 +897,15 @@ static inline void hrtick_rq_init(struct rq *rq) #if defined(CONFIG_SMP) && defined(TIF_POLLING_NRFLAG) /* - * Atomically set TIF_NEED_RESCHED and test for TIF_POLLING_NRFLAG, + * Atomically set TIF_NEED_RESCHED[_LAZY] and test for TIF_POLLING_NRFLAG, * this avoids any races wrt polling state changes and thereby avoids * spurious IPIs. */ -static inline bool set_nr_and_not_polling(struct task_struct *p) +static inline bool set_nr_and_not_polling(struct task_struct *p, int tif_bit) { struct thread_info *ti = task_thread_info(p); - return !(fetch_or(&ti->flags, _TIF_NEED_RESCHED) & _TIF_POLLING_NRFLAG); + + return !(fetch_or(&ti->flags, 1 << tif_bit) & _TIF_POLLING_NRFLAG); } /* @@ -921,7 +922,7 @@ static bool set_nr_if_polling(struct task_struct *p) for (;;) { if (!(val & _TIF_POLLING_NRFLAG)) return false; - if (val & _TIF_NEED_RESCHED) + if (val & (_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY)) return true; if (try_cmpxchg(&ti->flags, &val, val | _TIF_NEED_RESCHED)) break; @@ -930,9 +931,9 @@ static bool set_nr_if_polling(struct task_struct *p) } #else -static inline bool set_nr_and_not_polling(struct task_struct *p) +static inline bool set_nr_and_not_polling(struct task_struct *p, int tif_bit) { - set_tsk_need_resched(p); + set_tsk_thread_flag(p, tif_bit); return true; } @@ -1037,28 +1038,47 @@ void wake_up_q(struct wake_q_head *head) * might also involve a cross-CPU call to trigger the scheduler on * the target CPU. */ -void resched_curr(struct rq *rq) +static void __resched_curr(struct rq *rq, int lazy) { + int cpu, tif_bit = TIF_NEED_RESCHED + lazy; struct task_struct *curr = rq->curr; - int cpu; lockdep_assert_rq_held(rq); - if (test_tsk_need_resched(curr)) + if (unlikely(test_tsk_thread_flag(curr, tif_bit))) return; cpu = cpu_of(rq); if (cpu == smp_processor_id()) { - set_tsk_need_resched(curr); - set_preempt_need_resched(); + set_tsk_thread_flag(curr, tif_bit); + if (!lazy) + set_preempt_need_resched(); return; } - if (set_nr_and_not_polling(curr)) - smp_send_reschedule(cpu); - else + if (set_nr_and_not_polling(curr, tif_bit)) { + if (!lazy) + smp_send_reschedule(cpu); + } else { trace_sched_wake_idle_without_ipi(cpu); + } +} + +void resched_curr(struct rq *rq) +{ + __resched_curr(rq, 0); +} + +void resched_curr_lazy(struct rq *rq) +{ + int lazy = IS_ENABLED(CONFIG_PREEMPT_BUILD_AUTO) && !sched_feat(FORCE_NEED_RESCHED) ? + TIF_NEED_RESCHED_LAZY_OFFSET : 0; + + if (lazy && unlikely(test_tsk_thread_flag(rq->curr, TIF_NEED_RESCHED))) + return; + + __resched_curr(rq, lazy); } void resched_cpu(int cpu) @@ -1131,7 +1151,7 @@ static void wake_up_idle_cpu(int cpu) if (cpu == smp_processor_id()) return; - if (set_nr_and_not_polling(rq->idle)) + if (set_nr_and_not_polling(rq->idle, TIF_NEED_RESCHED)) smp_send_reschedule(cpu); else trace_sched_wake_idle_without_ipi(cpu); @@ -6722,10 +6742,14 @@ void __noreturn do_task_dead(void) static inline void sched_submit_work(struct task_struct *tsk) { + static DEFINE_WAIT_OVERRIDE_MAP(sched_map, LD_WAIT_CONFIG); unsigned int task_flags; - if (task_is_running(tsk)) - return; + /* + * Establish LD_WAIT_CONFIG context to ensure none of the code called + * will use a blocking primitive -- which would lead to recursion. + */ + lock_map_acquire_try(&sched_map); task_flags = tsk->flags; /* @@ -6751,6 +6775,8 @@ static inline void sched_submit_work(struct task_struct *tsk) * make sure to submit it to avoid deadlocks. */ blk_flush_plug(tsk->plug, true); + + lock_map_release(&sched_map); } static void sched_update_worker(struct task_struct *tsk) @@ -6763,16 +6789,26 @@ static void sched_update_worker(struct task_struct *tsk) } } -asmlinkage __visible void __sched schedule(void) +static __always_inline void __schedule_loop(unsigned int sched_mode) { - struct task_struct *tsk = current; - - sched_submit_work(tsk); do { preempt_disable(); - __schedule(SM_NONE); + __schedule(sched_mode); sched_preempt_enable_no_resched(); } while (need_resched()); +} + +asmlinkage __visible void __sched schedule(void) +{ + struct task_struct *tsk = current; + +#ifdef CONFIG_RT_MUTEXES + lockdep_assert(!tsk->sched_rt_mutex); +#endif + + if (!task_is_running(tsk)) + sched_submit_work(tsk); + __schedule_loop(SM_NONE); sched_update_worker(tsk); } EXPORT_SYMBOL(schedule); @@ -6836,11 +6872,7 @@ void __sched schedule_preempt_disabled(void) #ifdef CONFIG_PREEMPT_RT void __sched notrace schedule_rtlock(void) { - do { - preempt_disable(); - __schedule(SM_RTLOCK_WAIT); - sched_preempt_enable_no_resched(); - } while (need_resched()); + __schedule_loop(SM_RTLOCK_WAIT); } NOKPROBE_SYMBOL(schedule_rtlock); #endif @@ -7036,6 +7068,32 @@ static void __setscheduler_prio(struct task_struct *p, int prio) #ifdef CONFIG_RT_MUTEXES +/* + * Would be more useful with typeof()/auto_type but they don't mix with + * bit-fields. Since it's a local thing, use int. Keep the generic sounding + * name such that if someone were to implement this function we get to compare + * notes. + */ +#define fetch_and_set(x, v) ({ int _x = (x); (x) = (v); _x; }) + +void rt_mutex_pre_schedule(void) +{ + lockdep_assert(!fetch_and_set(current->sched_rt_mutex, 1)); + sched_submit_work(current); +} + +void rt_mutex_schedule(void) +{ + lockdep_assert(current->sched_rt_mutex); + __schedule_loop(SM_NONE); +} + +void rt_mutex_post_schedule(void) +{ + sched_update_worker(current); + lockdep_assert(fetch_and_set(current->sched_rt_mutex, 0)); +} + static inline int __rt_effective_prio(struct task_struct *pi_task, int prio) { if (pi_task) @@ -8886,6 +8944,21 @@ static inline void preempt_dynamic_init(void) { } #endif /* #ifdef CONFIG_PREEMPT_DYNAMIC */ +/* + * task_is_pi_boosted - Check if task has been PI boosted. + * @p: Task to check. + * + * Return true if task is subject to priority inheritance. + */ +bool task_is_pi_boosted(const struct task_struct *p) +{ + int prio = p->prio; + + if (!rt_prio(prio)) + return false; + return prio != p->normal_prio; +} + /** * yield - yield the current processor to other threads. * diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c index 4c3d0d9f3db6..63e19b89c8c3 100644 --- a/kernel/sched/debug.c +++ b/kernel/sched/debug.c @@ -333,6 +333,23 @@ static const struct file_operations sched_debug_fops = { .release = seq_release, }; +static ssize_t sched_hog_write(struct file *filp, const char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + unsigned long end = jiffies + 60 * HZ; + + for (; time_before(jiffies, end) && !signal_pending(current);) + cpu_relax(); + + return cnt; +} + +static const struct file_operations sched_hog_fops = { + .write = sched_hog_write, + .open = simple_open, + .llseek = default_llseek, +}; + static struct dentry *debugfs_sched; static __init int sched_init_debug(void) @@ -374,6 +391,8 @@ static __init int sched_init_debug(void) debugfs_create_file("debug", 0444, debugfs_sched, NULL, &sched_debug_fops); + debugfs_create_file("hog", 0200, debugfs_sched, NULL, &sched_hog_fops); + return 0; } late_initcall(sched_init_debug); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index b2e1009e5706..e7e5569fd505 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1022,8 +1022,10 @@ static void clear_buddies(struct cfs_rq *cfs_rq, struct sched_entity *se); * XXX: strictly: vd_i += N*r_i/w_i such that: vd_i > ve_i * this is probably good enough. */ -static void update_deadline(struct cfs_rq *cfs_rq, struct sched_entity *se) +static void update_deadline(struct cfs_rq *cfs_rq, struct sched_entity *se, bool tick) { + struct rq *rq = rq_of(cfs_rq); + if ((s64)(se->vruntime - se->deadline) < 0) return; @@ -1042,10 +1044,19 @@ static void update_deadline(struct cfs_rq *cfs_rq, struct sched_entity *se) /* * The task has consumed its request, reschedule. */ - if (cfs_rq->nr_running > 1) { - resched_curr(rq_of(cfs_rq)); - clear_buddies(cfs_rq, se); + if (cfs_rq->nr_running < 2) + return; + + if (!IS_ENABLED(CONFIG_PREEMPT_BUILD_AUTO) || sched_feat(FORCE_NEED_RESCHED)) { + resched_curr(rq); + } else { + /* Did the task ignore the lazy reschedule request? */ + if (tick && test_tsk_thread_flag(rq->curr, TIF_NEED_RESCHED_LAZY)) + resched_curr(rq); + else + resched_curr_lazy(rq); } + clear_buddies(cfs_rq, se); } #include "pelt.h" @@ -1153,7 +1164,7 @@ static void update_tg_load_avg(struct cfs_rq *cfs_rq) /* * Update the current task's runtime statistics. */ -static void update_curr(struct cfs_rq *cfs_rq) +static void __update_curr(struct cfs_rq *cfs_rq, bool tick) { struct sched_entity *curr = cfs_rq->curr; u64 now = rq_clock_task(rq_of(cfs_rq)); @@ -1180,7 +1191,7 @@ static void update_curr(struct cfs_rq *cfs_rq) schedstat_add(cfs_rq->exec_clock, delta_exec); curr->vruntime += calc_delta_fair(delta_exec, curr); - update_deadline(cfs_rq, curr); + update_deadline(cfs_rq, curr, tick); update_min_vruntime(cfs_rq); if (entity_is_task(curr)) { @@ -1194,6 +1205,11 @@ static void update_curr(struct cfs_rq *cfs_rq) account_cfs_rq_runtime(cfs_rq, delta_exec); } +static inline void update_curr(struct cfs_rq *cfs_rq) +{ + __update_curr(cfs_rq, false); +} + static void update_curr_fair(struct rq *rq) { update_curr(cfs_rq_of(&rq->curr->se)); @@ -5403,7 +5419,7 @@ entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued) /* * Update run-time statistics of the 'current'. */ - update_curr(cfs_rq); + __update_curr(cfs_rq, true); /* * Ensure that runnable average is periodically updated. @@ -5417,7 +5433,7 @@ entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued) * validating it and just reschedule. */ if (queued) { - resched_curr(rq_of(cfs_rq)); + resched_curr_lazy(rq_of(cfs_rq)); return; } /* @@ -5563,7 +5579,7 @@ static void __account_cfs_rq_runtime(struct cfs_rq *cfs_rq, u64 delta_exec) * hierarchy can be throttled */ if (!assign_cfs_rq_runtime(cfs_rq) && likely(cfs_rq->curr)) - resched_curr(rq_of(cfs_rq)); + resched_curr_lazy(rq_of(cfs_rq)); } static __always_inline @@ -5823,7 +5839,7 @@ void unthrottle_cfs_rq(struct cfs_rq *cfs_rq) /* Determine whether we need to wake up potentially idle CPU: */ if (rq->curr == rq->idle && rq->cfs.nr_running) - resched_curr(rq); + resched_curr_lazy(rq); } #ifdef CONFIG_SMP @@ -6528,7 +6544,7 @@ static void hrtick_start_fair(struct rq *rq, struct task_struct *p) if (delta < 0) { if (task_current(rq, p)) - resched_curr(rq); + resched_curr_lazy(rq); return; } hrtick_start(rq, delta); @@ -8206,7 +8222,7 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_ * prevents us from potentially nominating it as a false LAST_BUDDY * below. */ - if (test_tsk_need_resched(curr)) + if (need_resched()) return; /* Idle tasks are by definition preempted by non-idle tasks. */ @@ -8248,7 +8264,7 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_ return; preempt: - resched_curr(rq); + resched_curr_lazy(rq); } #ifdef CONFIG_SMP @@ -12394,7 +12410,7 @@ static inline void task_tick_core(struct rq *rq, struct task_struct *curr) */ if (rq->core->core_forceidle_count && rq->cfs.nr_running == 1 && __entity_slice_used(&curr->se, MIN_NR_TASKS_DURING_FORCEIDLE)) - resched_curr(rq); + resched_curr_lazy(rq); } /* @@ -12559,7 +12575,7 @@ prio_changed_fair(struct rq *rq, struct task_struct *p, int oldprio) */ if (task_current(rq, p)) { if (p->prio > oldprio) - resched_curr(rq); + resched_curr_lazy(rq); } else check_preempt_curr(rq, p, 0); } diff --git a/kernel/sched/features.h b/kernel/sched/features.h index f770168230ae..dd8b35f67fed 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -89,3 +89,5 @@ SCHED_FEAT(UTIL_EST_FASTUP, true) SCHED_FEAT(LATENCY_WARN, false) SCHED_FEAT(HZ_BW, true) + +SCHED_FEAT(FORCE_NEED_RESCHED, false) diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c index 5007b25c5bc6..95e1b3df1400 100644 --- a/kernel/sched/idle.c +++ b/kernel/sched/idle.c @@ -57,8 +57,7 @@ static noinline int __cpuidle cpu_idle_poll(void) ct_cpuidle_enter(); raw_local_irq_enable(); - while (!tif_need_resched() && - (cpu_idle_force_poll || tick_check_broadcast_expired())) + while (!need_resched() && (cpu_idle_force_poll || tick_check_broadcast_expired())) cpu_relax(); raw_local_irq_disable(); diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index 4ac36eb4cdee..acd1510e8d47 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -2253,8 +2253,11 @@ static int rto_next_cpu(struct root_domain *rd) rd->rto_cpu = cpu; - if (cpu < nr_cpu_ids) + if (cpu < nr_cpu_ids) { + if (!has_pushable_tasks(cpu_rq(cpu))) + continue; return cpu; + } rd->rto_cpu = -1; diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 8cbbbea7fdbb..00cdf0db8732 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -2438,6 +2438,7 @@ extern void init_sched_fair_class(void); extern void reweight_task(struct task_struct *p, const struct load_weight *lw); extern void resched_curr(struct rq *rq); +extern void resched_curr_lazy(struct rq *rq); extern void resched_cpu(int cpu); extern struct rt_bandwidth def_rt_bandwidth; diff --git a/kernel/signal.c b/kernel/signal.c index 21903f524ef8..9f240d9cb240 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2329,15 +2329,35 @@ static int ptrace_stop(int exit_code, int why, unsigned long message, do_notify_parent_cldstop(current, false, why); /* - * Don't want to allow preemption here, because - * sys_ptrace() needs this task to be inactive. + * The previous do_notify_parent_cldstop() invocation woke ptracer. + * One a PREEMPTION kernel this can result in preemption requirement + * which will be fulfilled after read_unlock() and the ptracer will be + * put on the CPU. + * The ptracer is in wait_task_inactive(, __TASK_TRACED) waiting for + * this task wait in schedule(). If this task gets preempted then it + * remains enqueued on the runqueue. The ptracer will observe this and + * then sleep for a delay of one HZ tick. In the meantime this task + * gets scheduled, enters schedule() and will wait for the ptracer. * - * XXX: implement read_unlock_no_resched(). + * This preemption point is not bad from correctness point of view but + * extends the runtime by one HZ tick time due to the ptracer's sleep. + * The preempt-disable section ensures that there will be no preemption + * between unlock and schedule() and so improving the performance since + * the ptracer has no reason to sleep. + * + * On PREEMPT_RT locking tasklist_lock does not disable preemption. + * Therefore the task can be preempted (after + * do_notify_parent_cldstop()) before unlocking tasklist_lock so there + * is no benefit in doing this. The optimisation is harmful on + * PEEMPT_RT because the spinlock_t (in cgroup_enter_frozen()) must not + * be acquired with disabled preemption. */ - preempt_disable(); + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) + preempt_disable(); read_unlock(&tasklist_lock); cgroup_enter_frozen(); - preempt_enable_no_resched(); + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) + preempt_enable_no_resched(); schedule(); cgroup_leave_frozen(true); diff --git a/kernel/softirq.c b/kernel/softirq.c index bd9716d7bb63..2fde8af88e48 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -247,6 +247,19 @@ void __local_bh_enable_ip(unsigned long ip, unsigned int cnt) } EXPORT_SYMBOL(__local_bh_enable_ip); +void softirq_preempt(void) +{ + if (WARN_ON_ONCE(!preemptible())) + return; + + if (WARN_ON_ONCE(__this_cpu_read(softirq_ctrl.cnt) != SOFTIRQ_OFFSET)) + return; + + __local_bh_enable(SOFTIRQ_OFFSET, true); + /* preemption point */ + __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET); +} + /* * Invoked from ksoftirqd_run() outside of the interrupt disabled section * to acquire the per CPU local lock for reentrancy protection. @@ -623,6 +636,24 @@ static inline void tick_irq_exit(void) #endif } +#ifdef CONFIG_PREEMPT_RT +DEFINE_PER_CPU(struct task_struct *, timersd); +DEFINE_PER_CPU(unsigned long, pending_timer_softirq); + +static void wake_timersd(void) +{ + struct task_struct *tsk = __this_cpu_read(timersd); + + if (tsk) + wake_up_process(tsk); +} + +#else + +static inline void wake_timersd(void) { } + +#endif + static inline void __irq_exit_rcu(void) { #ifndef __ARCH_IRQ_EXIT_IRQS_DISABLED @@ -635,6 +666,10 @@ static inline void __irq_exit_rcu(void) if (!in_interrupt() && local_softirq_pending()) invoke_softirq(); + if (IS_ENABLED(CONFIG_PREEMPT_RT) && local_pending_timers() && + !(in_nmi() | in_hardirq())) + wake_timersd(); + tick_irq_exit(); } @@ -967,12 +1002,70 @@ static struct smp_hotplug_thread softirq_threads = { .thread_comm = "ksoftirqd/%u", }; +#ifdef CONFIG_PREEMPT_RT +static void timersd_setup(unsigned int cpu) +{ + sched_set_fifo_low(current); +} + +static int timersd_should_run(unsigned int cpu) +{ + return local_pending_timers(); +} + +static void run_timersd(unsigned int cpu) +{ + unsigned int timer_si; + + ksoftirqd_run_begin(); + + timer_si = local_pending_timers(); + __this_cpu_write(pending_timer_softirq, 0); + or_softirq_pending(timer_si); + + __do_softirq(); + + ksoftirqd_run_end(); +} + +static void raise_ktimers_thread(unsigned int nr) +{ + trace_softirq_raise(nr); + __this_cpu_or(pending_timer_softirq, 1 << nr); +} + +void raise_hrtimer_softirq(void) +{ + raise_ktimers_thread(HRTIMER_SOFTIRQ); +} + +void raise_timer_softirq(void) +{ + unsigned long flags; + + local_irq_save(flags); + raise_ktimers_thread(TIMER_SOFTIRQ); + wake_timersd(); + local_irq_restore(flags); +} + +static struct smp_hotplug_thread timer_threads = { + .store = &timersd, + .setup = timersd_setup, + .thread_should_run = timersd_should_run, + .thread_fn = run_timersd, + .thread_comm = "ktimers/%u", +}; +#endif + static __init int spawn_ksoftirqd(void) { cpuhp_setup_state_nocalls(CPUHP_SOFTIRQ_DEAD, "softirq:dead", NULL, takeover_tasklets); BUG_ON(smpboot_register_percpu_thread(&softirq_threads)); - +#ifdef CONFIG_PREEMPT_RT + BUG_ON(smpboot_register_percpu_thread(&timer_threads)); +#endif return 0; } early_initcall(spawn_ksoftirqd); diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index 57e5cb36f1bc..c4ae45701fab 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -1812,7 +1812,7 @@ void hrtimer_interrupt(struct clock_event_device *dev) if (!ktime_before(now, cpu_base->softirq_expires_next)) { cpu_base->softirq_expires_next = KTIME_MAX; cpu_base->softirq_activated = 1; - raise_softirq_irqoff(HRTIMER_SOFTIRQ); + raise_hrtimer_softirq(); } __hrtimer_run_queues(cpu_base, now, flags, HRTIMER_ACTIVE_HARD); @@ -1925,7 +1925,7 @@ void hrtimer_run_queues(void) if (!ktime_before(now, cpu_base->softirq_expires_next)) { cpu_base->softirq_expires_next = KTIME_MAX; cpu_base->softirq_activated = 1; - raise_softirq_irqoff(HRTIMER_SOFTIRQ); + raise_hrtimer_softirq(); } __hrtimer_run_queues(cpu_base, now, flags, HRTIMER_ACTIVE_HARD); diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 55cbc49f70d1..1a0ed106b192 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -795,7 +795,7 @@ static void tick_nohz_restart(struct tick_sched *ts, ktime_t now) static inline bool local_timer_softirq_pending(void) { - return local_softirq_pending() & BIT(TIMER_SOFTIRQ); + return local_pending_timers() & BIT(TIMER_SOFTIRQ); } static ktime_t tick_nohz_next_event(struct tick_sched *ts, int cpu) diff --git a/kernel/time/timer.c b/kernel/time/timer.c index 63a8ce7177dd..b3fbe97d1e34 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -1470,9 +1470,16 @@ static inline void timer_base_unlock_expiry(struct timer_base *base) */ static void timer_sync_wait_running(struct timer_base *base) { - if (atomic_read(&base->timer_waiters)) { + bool need_preempt; + + need_preempt = task_is_pi_boosted(current); + if (need_preempt || atomic_read(&base->timer_waiters)) { raw_spin_unlock_irq(&base->lock); spin_unlock(&base->expiry_lock); + + if (need_preempt) + softirq_preempt(); + spin_lock(&base->expiry_lock); raw_spin_lock_irq(&base->lock); } @@ -2054,7 +2061,7 @@ static void run_local_timers(void) if (time_before(jiffies, base->next_expiry)) return; } - raise_softirq(TIMER_SOFTIRQ); + raise_timer_softirq(); } /* diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index be878005e344..fe755b1c2fe6 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -2708,6 +2708,8 @@ unsigned int tracing_gen_ctx_irq_test(unsigned int irqs_status) if (tif_need_resched()) trace_flags |= TRACE_FLAG_NEED_RESCHED; + if (tif_need_resched_lazy()) + trace_flags |= TRACE_FLAG_NEED_RESCHED_LAZY; if (test_preempt_need_resched()) trace_flags |= TRACE_FLAG_PREEMPT_RESCHED; return (trace_flags << 16) | (min_t(unsigned int, pc & 0xff, 0xf)) | diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index 3b7d3e9eb6ea..5a4fefbc0856 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -460,17 +460,29 @@ int trace_print_lat_fmt(struct trace_seq *s, struct trace_entry *entry) (entry->flags & TRACE_FLAG_IRQS_OFF && bh_off) ? 'D' : (entry->flags & TRACE_FLAG_IRQS_OFF) ? 'd' : bh_off ? 'b' : - (entry->flags & TRACE_FLAG_IRQS_NOSUPPORT) ? 'X' : + !IS_ENABLED(CONFIG_TRACE_IRQFLAGS_SUPPORT) ? 'X' : '.'; - switch (entry->flags & (TRACE_FLAG_NEED_RESCHED | + switch (entry->flags & (TRACE_FLAG_NEED_RESCHED | TRACE_FLAG_NEED_RESCHED_LAZY | TRACE_FLAG_PREEMPT_RESCHED)) { + case TRACE_FLAG_NEED_RESCHED | TRACE_FLAG_NEED_RESCHED_LAZY | TRACE_FLAG_PREEMPT_RESCHED: + need_resched = 'B'; + break; case TRACE_FLAG_NEED_RESCHED | TRACE_FLAG_PREEMPT_RESCHED: need_resched = 'N'; break; + case TRACE_FLAG_NEED_RESCHED_LAZY | TRACE_FLAG_PREEMPT_RESCHED: + need_resched = 'L'; + break; + case TRACE_FLAG_NEED_RESCHED | TRACE_FLAG_NEED_RESCHED_LAZY: + need_resched = 'b'; + break; case TRACE_FLAG_NEED_RESCHED: need_resched = 'n'; break; + case TRACE_FLAG_NEED_RESCHED_LAZY: + need_resched = 'l'; + break; case TRACE_FLAG_PREEMPT_RESCHED: need_resched = 'p'; break; diff --git a/localversion-rt b/localversion-rt new file mode 100644 index 000000000000..366440d74b77 --- /dev/null +++ b/localversion-rt @@ -0,0 +1 @@ +-rt35 diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c index 7b41ee8740cb..294cb699bff2 100644 --- a/net/bridge/br_switchdev.c +++ b/net/bridge/br_switchdev.c @@ -173,6 +173,26 @@ br_switchdev_fdb_notify(struct net_bridge *br, } } +static u16 br_switchdev_get_bridge_vlan_proto(const struct net_device *dev) +{ + const struct net_device *br = NULL; + u16 vlan_proto = ETH_P_8021Q; + struct net_bridge_port *p; + + if (netif_is_bridge_master(dev)) { + br = dev; + } else { + p = br_port_get_rtnl_rcu(dev); + if (p) + br = p->br->dev; + } + + if (br) + br_vlan_get_proto(br, &vlan_proto); + + return vlan_proto; +} + int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags, bool changed, struct netlink_ext_ack *extack) { @@ -184,6 +204,8 @@ int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags, .changed = changed, }; + v.proto = br_switchdev_get_bridge_vlan_proto(dev); + return switchdev_port_obj_add(dev, &v.obj, extack); } @@ -195,6 +217,8 @@ int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid) .vid = vid, }; + v.proto = br_switchdev_get_bridge_vlan_proto(dev); + return switchdev_port_obj_del(dev, &v.obj); } diff --git a/net/core/dev.c b/net/core/dev.c index 5a5bd339f11e..c57b4918b2b4 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1428,8 +1428,8 @@ static int napi_kthread_create(struct napi_struct *n) * TASK_INTERRUPTIBLE mode to avoid the blocked task * warning and work with loadavg. */ - n->thread = kthread_run(napi_threaded_poll, n, "napi/%s-%d", - n->dev->name, n->napi_id); + n->thread = kthread_run(napi_threaded_poll, n, "napi/%s-%s", + n->dev->name, n->name); if (IS_ERR(n->thread)) { err = PTR_ERR(n->thread); pr_err("kthread_run failed with err %d\n", err); @@ -4705,15 +4705,6 @@ static void rps_trigger_softirq(void *data) #endif /* CONFIG_RPS */ -/* Called from hardirq (IPI) context */ -static void trigger_rx_softirq(void *data) -{ - struct softnet_data *sd = data; - - __raise_softirq_irqoff(NET_RX_SOFTIRQ); - smp_store_release(&sd->defer_ipi_scheduled, 0); -} - /* * After we queued a packet into sd->input_pkt_queue, * we need to make sure this queue is serviced soon. @@ -6426,8 +6417,9 @@ int dev_set_threaded(struct net_device *dev, bool threaded) } EXPORT_SYMBOL(dev_set_threaded); -void netif_napi_add_weight(struct net_device *dev, struct napi_struct *napi, - int (*poll)(struct napi_struct *, int), int weight) +void netif_napi_add_named(struct net_device *dev, struct napi_struct *napi, + int (*poll)(struct napi_struct *, int), int weight, + const char *name) { if (WARN_ON(test_and_set_bit(NAPI_STATE_LISTED, &napi->state))) return; @@ -6455,6 +6447,12 @@ void netif_napi_add_weight(struct net_device *dev, struct napi_struct *napi, list_add_rcu(&napi->dev_list, &dev->napi_list); napi_hash_add(napi); napi_get_frags_check(napi); + + if (name) + strncpy(napi->name, name, NAPINAMSIZ); + else + snprintf(napi->name, NAPINAMSIZ, "%d", napi->napi_id); + /* Create kthread for this napi if dev->threaded is set. * Clear dev->threaded if kthread creation failed so that * threaded mode will not be enabled in napi_enable(). @@ -6462,6 +6460,13 @@ void netif_napi_add_weight(struct net_device *dev, struct napi_struct *napi, if (dev->threaded && napi_kthread_create(napi)) dev->threaded = 0; } +EXPORT_SYMBOL(netif_napi_add_named); + +void netif_napi_add_weight(struct net_device *dev, struct napi_struct *napi, + int (*poll)(struct napi_struct *, int), int weight) +{ + netif_napi_add_named(dev, napi, poll, weight, NULL); +} EXPORT_SYMBOL(netif_napi_add_weight); void napi_disable(struct napi_struct *n) @@ -6682,6 +6687,32 @@ static void skb_defer_free_flush(struct softnet_data *sd) } } +#ifndef CONFIG_PREEMPT_RT + +/* Called from hardirq (IPI) context */ +static void trigger_rx_softirq(void *data) +{ + struct softnet_data *sd = data; + + __raise_softirq_irqoff(NET_RX_SOFTIRQ); + smp_store_release(&sd->defer_ipi_scheduled, 0); +} + +#else + +static void trigger_rx_softirq(struct work_struct *defer_work) +{ + struct softnet_data *sd; + + sd = container_of(defer_work, struct softnet_data, defer_work); + smp_store_release(&sd->defer_ipi_scheduled, 0); + local_bh_disable(); + skb_defer_free_flush(sd); + local_bh_enable(); +} + +#endif + static int napi_threaded_poll(void *data) { struct napi_struct *napi = data; @@ -10792,6 +10823,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, dev_mc_init(dev); dev_uc_init(dev); + dev->fast_raw_device = 0; dev_net_set(dev, &init_net); dev->gso_max_size = GSO_LEGACY_MAX_SIZE; @@ -11619,7 +11651,11 @@ static int __init net_dev_init(void) INIT_CSD(&sd->csd, rps_trigger_softirq, sd); sd->cpu = i; #endif +#ifndef CONFIG_PREEMPT_RT INIT_CSD(&sd->defer_csd, trigger_rx_softirq, sd); +#else + INIT_WORK(&sd->defer_work, trigger_rx_softirq); +#endif spin_lock_init(&sd->defer_lock); init_gro_hash(&sd->backlog); diff --git a/net/core/skbuff.c b/net/core/skbuff.c index cf3c7a0062b8..c82011571e07 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -6889,8 +6889,13 @@ nodefer: __kfree_skb(skb); /* Make sure to trigger NET_RX_SOFTIRQ on the remote CPU * if we are unlucky enough (this seems very unlikely). */ - if (unlikely(kick) && !cmpxchg(&sd->defer_ipi_scheduled, 0, 1)) + if (unlikely(kick) && !cmpxchg(&sd->defer_ipi_scheduled, 0, 1)) { +#ifndef CONFIG_PREEMPT_RT smp_call_function_single_async(cpu, &sd->defer_csd); +#else + schedule_work_on(cpu, &sd->defer_work); +#endif + } } static void skb_splice_csum_page(struct sk_buff *skb, struct page *page, diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index 8e698bea99a3..93bd38d95236 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -172,4 +172,10 @@ config NET_DSA_TAG_XRS700X Say Y or M if you want to enable support for tagging frames for Arrow SpeedChips XRS700x switches that use a single byte tag trailer. +config NET_DSA_TAG_NETC + tristate "Tag driver for NETC family of switches, using VLAN" + help + Say Y or M if you want to enable support for tagging frames with a + NXP NETC switch family. The custom 802.1Q VLAN header is available. + endif diff --git a/net/dsa/Makefile b/net/dsa/Makefile index 12e305824a96..b9d63a7c3a77 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_NET_DSA_TAG_RZN1_A5PSW) += tag_rzn1_a5psw.o obj-$(CONFIG_NET_DSA_TAG_SJA1105) += tag_sja1105.o obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o obj-$(CONFIG_NET_DSA_TAG_XRS700X) += tag_xrs700x.o +obj-$(CONFIG_NET_DSA_TAG_NETC) += tag_netc.o # for tracing framework to find trace.h CFLAGS_trace.o := -I$(src) diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 48db91b33390..75024f6fd3ef 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -1753,6 +1753,41 @@ static int dsa_slave_get_ts_info(struct net_device *dev, return ds->ops->get_ts_info(ds, p->dp->index, ts); } +static int dsa_slave_reset_preempt(struct net_device *dev, bool enable) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->dp->ds; + + if (!ds->ops->reset_preempt) + return -EOPNOTSUPP; + + return ds->ops->reset_preempt(ds, p->dp->index, enable); +} + +static int dsa_slave_set_preempt(struct net_device *dev, + struct ethtool_fp *fpcmd) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->dp->ds; + + if (!ds->ops->set_preempt) + return -EOPNOTSUPP; + + return ds->ops->set_preempt(ds, p->dp->index, fpcmd); +} + +static int dsa_slave_get_preempt(struct net_device *dev, + struct ethtool_fp *fpcmd) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->dp->ds; + + if (!ds->ops->get_preempt) + return -EOPNOTSUPP; + + return ds->ops->get_preempt(ds, p->dp->index, fpcmd); +} + static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid) { @@ -1762,6 +1797,7 @@ static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto, .vid = vid, /* This API only allows programming tagged, non-PVID VIDs */ .flags = 0, + .proto = ntohs(proto), }; struct netlink_ext_ack extack = {0}; struct dsa_switch *ds = dp->ds; @@ -1778,6 +1814,7 @@ static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto, } /* And CPU port... */ + vlan.proto = ETH_P_8021Q; ret = dsa_port_host_vlan_add(dp, &vlan, &extack); if (ret) { if (extack._msg) @@ -1836,6 +1873,7 @@ static int dsa_slave_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, .vid = vid, /* This API only allows programming tagged, non-PVID VIDs */ .flags = 0, + .proto = ntohs(proto), }; struct dsa_switch *ds = dp->ds; struct netdev_hw_addr *ha; @@ -2387,6 +2425,9 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = { .get_mm = dsa_slave_get_mm, .set_mm = dsa_slave_set_mm, .get_mm_stats = dsa_slave_get_mm_stats, + .set_preempt = dsa_slave_set_preempt, + .get_preempt = dsa_slave_get_preempt, + .reset_preempt = dsa_slave_reset_preempt, }; static const struct dcbnl_rtnl_ops __maybe_unused dsa_slave_dcbnl_ops = { @@ -2999,7 +3040,9 @@ dsa_slave_check_8021q_upper(struct net_device *dev, struct net_device *br = dsa_port_bridge_dev_get(dp); struct bridge_vlan_info br_info; struct netlink_ext_ack *extack; + bool update_proto = false; int err = NOTIFY_DONE; + u16 br_proto, proto; u16 vid; if (!br || !br_vlan_enabled(br)) @@ -3007,13 +3050,17 @@ dsa_slave_check_8021q_upper(struct net_device *dev, extack = netdev_notifier_info_to_extack(&info->info); vid = vlan_dev_vlan_id(info->upper_dev); + proto = vlan_dev_vlan_proto(info->upper_dev); + err = br_vlan_get_proto(br, &br_proto); + if (err == 0 && br_proto != proto) + update_proto = true; /* br_vlan_get_info() returns -EINVAL or -ENOENT if the * device, respectively the VID is not found, returning * 0 means success, which is a failure for us here. */ err = br_vlan_get_info(br, vid, &br_info); - if (err == 0) { + if (err == 0 && !update_proto) { NL_SET_ERR_MSG_MOD(extack, "This VLAN is already configured by the bridge"); return notifier_from_errno(-EBUSY); diff --git a/net/dsa/tag_netc.c b/net/dsa/tag_netc.c new file mode 100644 index 000000000000..b32ae484af9c --- /dev/null +++ b/net/dsa/tag_netc.c @@ -0,0 +1,489 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2023 NXP + */ + +#include +#include +#include +#include "tag.h" +#include "tag_8021q.h" + +#define NETC_8021Q_NAME "netc-8021q" + +/* + * NETC HEADRER after Source MAC + * + * | 2B | 2B | 0 / 4B / 8B / 12B / 16B | + * +------------ +-------------+---------------------------+ + * | 0xDADC | HEADRER | DATA | + * +------------ +------------ +---------------------------+ + */ + +#define NETC_HEADER_LEN 4 +#define NETC_HEADER_DATA_TS_ID_LEN 4 +#define NETC_HEADER_DATA_TIMESTAP_LEN 8 +#define NETC_HEADER_DATA_CMD_LEN 16 + +#define NETC_HEADER_HOST_TO_SWITCH BIT(15) + +/* Binary structure of the NETC Header ETH_P_NETC_META: + * + * | 15 | 14 | 13 | 12 | 11 | 10 - 9 | 7 - 4 | 3 - 0 | + * +-----------+------+-----------+-------+-------+--------+-----------+---------+ + * | TO HOST 0 | META | HOST Only | RX TS | TX TS | | Switch ID | Port ID | + * +-----------+------+-----------+-------+-------+--------+-----------+---------+ + */ +#define NETC_RX_HEADER_IS_METADATA BIT(14) +#define NETC_RX_HEADER_HOST_ONLY BIT(13) +#define NETC_RX_HEADER_RX_TIMESTAP BIT(12) +#define NETC_RX_HEADER_TX_TIMESTAP BIT(11) + +#define NETC_HEADER_PORT_MASK 0x0F +#define NETC_HEADER_PORT_OFFSET 0 +#define NETC_HEADER_SWITCH_MASK 0xF0 +#define NETC_HEADER_SWITCH_OFFSET 4 +#define NETC_RX_HEADER_PORT_ID(x) ((x) & NETC_HEADER_PORT_MASK) +#define NETC_RX_HEADER_SWITCH_ID(x) (((x) & NETC_HEADER_SWITCH_MASK) >> NETC_HEADER_SWITCH_OFFSET) + +/* + * RX RX_Timestamp: + * + * | 64 - 0 | + * +------------ + + * | TimeStamp | + * +------------ + + */ +#define NETC_HEADER_TIMESTAMP_LEN 8 + +/* + * RX TX_Timestamp: + * + * | 64 - 0 | 32 - 0 | + * +------------ +------------ + + * | TimeStamp | TS_ID | + * +------------ +------------ + + */ +#define NETC_RX_HEADER_TS_ID_LEN 4 + +/* TX header */ + +/* + * Binary structure of the NETC Header ETH_P_NETC_META: + * + * | 15 | 14 | 13 | 12 | 11 | 10 - 9 | 7 - 4 | 3 - 0 | + * +-----------+------+--------+-------+---------+--------+-----------+---------+ + * | To SW 1 | META | | | TAKE TS | | SWITCH ID | PORT ID | + * +-----------+------+--------+-------+------ -+--------+-----------+---------+ + */ + +#define NETC_TX_HEADER_IS_METADATA BIT(14) +#define NETC_TX_HEADER_TAKE_TS BIT(11) + +#define NETC_TX_HEADER_TSTAMP_ID(x) (x) +#define NETC_TX_HEADER_SWITCHID(x) (((x) << NETC_HEADER_SWITCH_OFFSET) & NETC_HEADER_SWITCH_MASK) +#define NETC_TX_HEADER_DESTPORTID(x) ((x) & NETC_HEADER_PORT_MASK) + +/* + * TX Take TS: + * + * | 32 - 0 | + * +------------ + + * | TS_ID | + * +------------ + + */ +#define NETC_TX_HEADER_TS_ID_LEN 4 + +void print_skb_data(struct sk_buff *skb) +{ + u8 *buf = skb->data - ETH_HLEN; + int len = skb->len; + int i = 0; + + if (!skb) { + printk("Bad skb parameter"); + return; + } + printk("Packet length = 0x%x", len); + + for (i = 0; i < len; i += 8) { + printk("0x%04x: %02x %02x %02x %02x %02x %02x %02x %02x\n", i, + buf[i + 0], buf[i + 1], buf[i + 2], buf[i + 3], + buf[i + 4], buf[i + 5], buf[i + 6], buf[i + 7]); + } + printk("\n"); +} + +/* Similar to is_link_local_ether_addr(hdr->h_dest) but also covers PTP */ +static inline bool netc_is_link_local(const struct sk_buff *skb) +{ + const struct ethhdr *hdr = eth_hdr(skb); + u64 dmac = ether_addr_to_u64(hdr->h_dest); + + if (ntohs(hdr->h_proto) == ETH_P_NETC) + return false; + + if ((dmac & NETC_LINKLOCAL_FILTER_A_MASK) == + NETC_LINKLOCAL_FILTER_A) + return true; + + if ((dmac & NETC_LINKLOCAL_FILTER_B_MASK) == + NETC_LINKLOCAL_FILTER_B) + return true; + + return false; +} + +/* Send VLAN tags with a TPID that blends in with whatever VLAN protocol a + * bridge spanning ports of this switch might have. + */ +static u16 netc_xmit_tpid(struct dsa_port *dp) +{ + struct dsa_switch *ds = dp->ds; + struct dsa_port *other_dp; + u16 proto; + + if (!dsa_port_is_vlan_filtering(dp)) + return ETH_P_NETC_8021Q; + + /* Port is VLAN-aware, so there is a bridge somewhere (a single one, + * we're sure about that). It may not be on this port though, so we + * need to find it. + */ + dsa_switch_for_each_port(other_dp, ds) { + struct net_device *br = dsa_port_bridge_dev_get(other_dp); + + if (!br) + continue; + + /* Error is returned only if CONFIG_BRIDGE_VLAN_FILTERING, + * which seems pointless to handle, as our port cannot become + * VLAN-aware in that case. + */ + br_vlan_get_proto(br, &proto); + + return proto; + } + + WARN_ONCE(1, "Port is VLAN-aware but cannot find associated bridge!\n"); + + return ETH_P_NETC_8021Q; +} + +static struct sk_buff *netc_imprecise_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct dsa_port *dp = dsa_slave_to_port(netdev); + unsigned int bridge_num = dsa_port_bridge_num_get(dp); + struct net_device *br = dsa_port_bridge_dev_get(dp); + u16 tx_vid; + + /* If the port is under a VLAN-aware bridge, just slide the + * VLAN-tagged packet into the FDB and hope for the best. + * This works because we support a single VLAN-aware bridge + * across the entire dst, and its VLANs cannot be shared with + * any standalone port. + */ + if (br_vlan_enabled(br)) + return skb; + + /* If the port is under a VLAN-unaware bridge, use an imprecise + * TX VLAN that targets the bridge's entire broadcast domain, + * instead of just the specific port. + */ + tx_vid = dsa_tag_8021q_bridge_vid(bridge_num); + + if (unlikely(skb_vlan_tag_present(skb))) { + skb = __vlan_hwaccel_push_inside(skb); + if (!skb) { + WARN_ONCE(1, "Failed to push VLAN tag to payload!\n"); + return NULL; + } + } + + return dsa_8021q_xmit(skb, netdev, netc_xmit_tpid(dp), tx_vid); +} + +static struct sk_buff *netc_meta_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct sk_buff *clone = NETC_SKB_CB(skb)->clone; + struct dsa_port *dp = dsa_slave_to_port(netdev); + int len = NETC_HEADER_LEN; + __be16 *tx_header; + __be32 *p_ts_id; + + if (clone) + len = len + NETC_TX_HEADER_TS_ID_LEN; + + skb_push(skb, len); + + dsa_alloc_etype_header(skb, len); + + tx_header = dsa_etype_header_pos_tx(skb); + + tx_header[0] = htons(ETH_P_NETC_META); + tx_header[1] = htons(NETC_HEADER_HOST_TO_SWITCH | + NETC_TX_HEADER_SWITCHID(dp->ds->index) | + NETC_TX_HEADER_DESTPORTID(dp->index)); + if(clone) { + tx_header[1] |= htons(NETC_TX_HEADER_TAKE_TS); + p_ts_id = dsa_etype_header_pos_tx(skb) + NETC_HEADER_LEN; + p_ts_id[0] = cpu_to_be32(NETC_SKB_CB(clone)->ts_id); + } + + return skb; +} + +static struct sk_buff *netc_8021q_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct dsa_port *dp = dsa_slave_to_port(netdev); + u16 queue_mapping = skb_get_queue_mapping(skb); + u8 pcp = netdev_txq_to_tc(netdev, queue_mapping); + u16 tx_vid = dsa_tag_8021q_standalone_vid(dp); + + return dsa_8021q_xmit(skb, netdev, netc_xmit_tpid(dp), + ((pcp << VLAN_PRIO_SHIFT) | tx_vid)); +} + +static struct sk_buff *netc_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + if (skb->offload_fwd_mark) + return netc_imprecise_xmit(skb, netdev); + + if (unlikely(netc_is_link_local(skb))) + return netc_meta_xmit(skb, netdev); + + return netc_8021q_xmit(skb, netdev); +} + +static bool netc_skb_has_tag_8021q(const struct sk_buff *skb) +{ + u16 tpid = ntohs(eth_hdr(skb)->h_proto); + + return tpid == ETH_P_NETC || tpid == ETH_P_8021Q || + skb_vlan_tag_present(skb); +} + +static bool netc_skb_has_inband_control_extension(const struct sk_buff *skb) +{ + return ntohs(eth_hdr(skb)->h_proto) == ETH_P_NETC_META; +} + +static struct sk_buff *netc_rcv_meta_cmd(struct sk_buff *skb, u16 rx_header) +{ + u8 *buf = dsa_etype_header_pos_rx(skb) + NETC_HEADER_LEN; + int switch_id = NETC_RX_HEADER_SWITCH_ID(rx_header); + int source_port = NETC_RX_HEADER_PORT_ID(rx_header); + struct netc_tagger_data *tagger_data; + struct net_device *master = skb->dev; + struct dsa_port *cpu_dp; + struct dsa_switch *ds; + + cpu_dp = master->dsa_ptr; + ds = dsa_switch_find(cpu_dp->dst->index, switch_id); + if (!ds) { + net_err_ratelimited("%s: cannot find switch id %d\n", + master->name, switch_id); + return NULL; + } + + tagger_data = netc_tagger_data(ds); + if (!tagger_data->meta_cmd_handler) + return NULL; + + if (skb_is_nonlinear(skb)) + if(skb_linearize(skb)) + return NULL; + + tagger_data->meta_cmd_handler(ds, source_port, buf, + skb->len - NETC_HEADER_LEN - 2 * ETH_ALEN); + + /* Discard the meta frame */ + return NULL; +} + +static struct sk_buff *netc_rcv_tx_timestap(struct sk_buff *skb, u16 rx_header) +{ + u8 *buf = dsa_etype_header_pos_rx(skb) + NETC_HEADER_LEN; + int switch_id = NETC_RX_HEADER_SWITCH_ID(rx_header); + int source_port = NETC_RX_HEADER_PORT_ID(rx_header); + struct netc_tagger_data *tagger_data; + struct net_device *master = skb->dev; + struct dsa_port *cpu_dp; + struct dsa_switch *ds; + u32 ts_id; + u64 tstamp; + + cpu_dp = master->dsa_ptr; + + ds = dsa_switch_find(cpu_dp->dst->index, switch_id); + if (!ds) { + net_err_ratelimited("%s: cannot find switch id %d\n", + master->name, switch_id); + return NULL; + } + + tagger_data = netc_tagger_data(ds); + if (!tagger_data->meta_tstamp_handler) + return NULL; + + + tstamp = be64_to_cpu(*(__be64 *)buf); + ts_id = be32_to_cpu(*(__be32 *)(buf + NETC_HEADER_TIMESTAMP_LEN)); + + tagger_data->meta_tstamp_handler(ds, source_port, ts_id, tstamp); + + /* Discard the meta frame, we've consumed the timestamps it contained */ + return NULL; +} + +static struct sk_buff *netc_rcv_inband_control_extension(struct sk_buff *skb, + int *source_port, + int *switch_id, + bool *host_only) +{ + u16 rx_header; + int len = 0; + + if (unlikely(!pskb_may_pull(skb, + NETC_HEADER_LEN + + NETC_HEADER_TIMESTAMP_LEN + + NETC_RX_HEADER_TS_ID_LEN))) + return NULL; + + rx_header = ntohs(*(__be16 *)skb->data); + + if (rx_header & NETC_RX_HEADER_HOST_ONLY) + *host_only = true; + + if (rx_header & NETC_RX_HEADER_IS_METADATA) + return netc_rcv_meta_cmd(skb, rx_header); + + if (rx_header & NETC_RX_HEADER_TX_TIMESTAP) + return netc_rcv_tx_timestap(skb, rx_header); + + /* RX Timestamp frame */ + if (rx_header & NETC_RX_HEADER_RX_TIMESTAP) { + u64 *tstamp = &NETC_SKB_CB(skb)->tstamp; + u8 *buf = dsa_etype_header_pos_rx(skb) + NETC_HEADER_LEN; + + *tstamp = be64_to_cpu(*(__be64 *)buf); + + len += NETC_HEADER_TIMESTAMP_LEN; + } + + *source_port = NETC_RX_HEADER_PORT_ID(rx_header); + *switch_id = NETC_RX_HEADER_SWITCH_ID(rx_header); + + len += NETC_HEADER_LEN; + + /* Advance skb->data past the DSA header */ + skb_pull_rcsum(skb, len); + + dsa_strip_etype_header(skb, len); + + /* With skb->data in its final place, update the MAC header + * so that eth_hdr() continues to works properly. + */ + skb_set_mac_header(skb, -ETH_HLEN); + + return skb; +} + +/* If the VLAN in the packet is a tag_8021q one, set @source_port and + * @switch_id and strip the header. Otherwise set @vid and keep it in the + * packet. + */ +static void netc_vlan_rcv(struct sk_buff *skb, int *source_port, + int *switch_id, int *vbid, u16 *vid) +{ + struct vlan_ethhdr *hdr = vlan_eth_hdr(skb); + u16 vlan_tci; + + if (skb_vlan_tag_present(skb)) + vlan_tci = skb_vlan_tag_get(skb); + else + vlan_tci = ntohs(hdr->h_vlan_TCI); + + if (vid_is_dsa_8021q(vlan_tci & VLAN_VID_MASK)) + return dsa_8021q_rcv(skb, source_port, switch_id, vbid); + + /* Try our best with imprecise RX */ + *vid = vlan_tci & VLAN_VID_MASK; +} + +static struct sk_buff *netc_rcv(struct sk_buff *skb, + struct net_device *netdev) +{ + int src_port = -1, switch_id = -1, vbid = -1; + bool host_only = false; + u16 vid = 0; + + if (netc_skb_has_inband_control_extension(skb)) { + skb = netc_rcv_inband_control_extension(skb, &src_port, + &switch_id, + &host_only); + if (!skb) + return NULL; + } + + /* Packets with in-band control extensions might still have RX VLANs */ + if (likely(netc_skb_has_tag_8021q(skb))) + netc_vlan_rcv(skb, &src_port, &switch_id, &vbid, &vid); + + if (vbid >= 1) + skb->dev = dsa_tag_8021q_find_port_by_vbid(netdev, vbid); + else if (src_port == -1 || switch_id == -1) + skb->dev = dsa_find_designated_bridge_port_by_vid(netdev, vid); + else + skb->dev = dsa_master_find_slave(netdev, switch_id, src_port); + if (!skb->dev) { + /* netdev_warn(netdev, "Couldn't decode source port\n"); */ + return NULL; + } + + if (!host_only) + dsa_default_offload_fwd_mark(skb); + + return skb; +} + +static void netc_disconnect(struct dsa_switch *ds) +{ + struct netc_tagger_data *tagger_data = ds->tagger_data; + + kfree(tagger_data); + ds->tagger_data = NULL; +} + +static int netc_connect(struct dsa_switch *ds) +{ + struct netc_tagger_data *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + ds->tagger_data = data; + + return 0; +} + +static const struct dsa_device_ops netc_netdev_ops = { + .name = NETC_8021Q_NAME, + .proto = DSA_TAG_PROTO_NETC, + .xmit = netc_xmit, + .rcv = netc_rcv, + .connect = netc_connect, + .disconnect = netc_disconnect, + .needed_headroom = VLAN_HLEN, + .promisc_on_master = true, +}; + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_NETC, NETC_8021Q_NAME); + +module_dsa_tag_driver(netc_netdev_ops); diff --git a/net/dsa/tag_sja1105.c b/net/dsa/tag_sja1105.c index ade3eeb2f3e6..1f70d9859862 100644 --- a/net/dsa/tag_sja1105.c +++ b/net/dsa/tag_sja1105.c @@ -267,9 +267,8 @@ static struct sk_buff *sja1105_xmit(struct sk_buff *skb, struct net_device *netdev) { struct dsa_port *dp = dsa_slave_to_port(netdev); - u16 queue_mapping = skb_get_queue_mapping(skb); - u8 pcp = netdev_txq_to_tc(netdev, queue_mapping); u16 tx_vid = dsa_tag_8021q_standalone_vid(dp); + u8 pcp = skb->priority; if (skb->offload_fwd_mark) return sja1105_imprecise_xmit(skb, netdev); diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile index 504f954a1b28..348993bf8eab 100644 --- a/net/ethtool/Makefile +++ b/net/ethtool/Makefile @@ -8,4 +8,4 @@ ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o rss.o \ linkstate.o debug.o wol.o features.o privflags.o rings.o \ channels.o coalesce.o pause.o eee.o tsinfo.o cabletest.o \ tunnels.o fec.o eeprom.o stats.o phc_vclocks.o mm.o \ - module.o pse-pd.o plca.o mm.o + module.o pse-pd.o plca.o mm.o preempt.o diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 4486cbe2faf0..07b9b939e294 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -2765,6 +2765,49 @@ static int ethtool_set_fecparam(struct net_device *dev, void __user *useraddr) return dev->ethtool_ops->set_fecparam(dev, &fecparam); } +static int ethtool_get_preempt(struct net_device *dev, void __user *useraddr) +{ + struct ethtool_fp fpparam = { .cmd = ETHTOOL_GFP }; + int rc; + + if (!dev->ethtool_ops->get_preempt) + return -EOPNOTSUPP; + + rc = dev->ethtool_ops->get_preempt(dev, &fpparam); + if (rc) + return rc; + + if (copy_to_user(useraddr, &fpparam, sizeof(fpparam))) + return -EFAULT; + return 0; +} + +static int ethtool_set_preempt(struct net_device *dev, void __user *useraddr) +{ + struct ethtool_fp fpparam; + + if (!dev->ethtool_ops->set_preempt) + return -EOPNOTSUPP; + + if (copy_from_user(&fpparam, useraddr, sizeof(fpparam))) + return -EFAULT; + + return dev->ethtool_ops->set_preempt(dev, &fpparam); +} + +static int ethtool_reset_preempt(struct net_device *dev, void __user *useraddr) +{ + struct ethtool_fp fpparam; + + if (!dev->ethtool_ops->reset_preempt) + return -EOPNOTSUPP; + + if (copy_from_user(&fpparam, useraddr, sizeof(fpparam))) + return -EFAULT; + + return dev->ethtool_ops->reset_preempt(dev, fpparam.fp_enabled); +} + /* The main entry point in this file. Called from net/core/dev_ioctl.c */ static int @@ -2824,6 +2867,9 @@ __dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr, case ETHTOOL_PHY_GTUNABLE: case ETHTOOL_GLINKSETTINGS: case ETHTOOL_GFECPARAM: + case ETHTOOL_GFP: + case ETHTOOL_SFP: + case ETHTOOL_RFP: break; default: if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) @@ -3051,6 +3097,15 @@ __dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr, case ETHTOOL_SFECPARAM: rc = ethtool_set_fecparam(dev, useraddr); break; + case ETHTOOL_GFP: + rc = ethtool_get_preempt(dev, useraddr); + break; + case ETHTOOL_SFP: + rc = ethtool_set_preempt(dev, useraddr); + break; + case ETHTOOL_RFP: + rc = ethtool_reset_preempt(dev, useraddr); + break; default: rc = -EOPNOTSUPP; } diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index fe3553f60bf3..4734689e173f 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -306,6 +306,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = { [ETHTOOL_MSG_PLCA_GET_STATUS] = ðnl_plca_status_request_ops, [ETHTOOL_MSG_MM_GET] = ðnl_mm_request_ops, [ETHTOOL_MSG_MM_SET] = ðnl_mm_request_ops, + [ETHTOOL_MSG_PREEMPT_GET] = ðnl_preempt_request_ops, }; static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) @@ -639,6 +640,7 @@ ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = { [ETHTOOL_MSG_MODULE_NTF] = ðnl_module_request_ops, [ETHTOOL_MSG_PLCA_NTF] = ðnl_plca_cfg_request_ops, [ETHTOOL_MSG_MM_NTF] = ðnl_mm_request_ops, + [ETHTOOL_MSG_PREEMPT_NTF] = ðnl_preempt_request_ops, }; /* default notification handler */ @@ -737,6 +739,7 @@ static const ethnl_notify_handler_t ethnl_notify_handlers[] = { [ETHTOOL_MSG_MODULE_NTF] = ethnl_default_notify, [ETHTOOL_MSG_PLCA_NTF] = ethnl_default_notify, [ETHTOOL_MSG_MM_NTF] = ethnl_default_notify, + [ETHTOOL_MSG_PREEMPT_NTF] = ethnl_default_notify, }; void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data) @@ -1129,6 +1132,20 @@ static const struct genl_ops ethtool_genl_ops[] = { .policy = ethnl_mm_set_policy, .maxattr = ARRAY_SIZE(ethnl_mm_set_policy) - 1, }, + { + .cmd = ETHTOOL_MSG_PREEMPT_GET, + .doit = ethnl_default_doit, + .start = ethnl_default_start, + .dumpit = ethnl_default_dumpit, + .done = ethnl_default_done, + .policy = preempt_get_policy, + .maxattr = ETHTOOL_A_PREEMPT_MAX, + }, + { + .cmd = ETHTOOL_MSG_PREEMPT_SET, + .flags = GENL_UNS_ADMIN_PERM, + .doit = ethnl_set_preempt, + }, }; static const struct genl_multicast_group ethtool_nl_mcgrps[] = { diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 9a333a8d04c1..ee2dddc44224 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -395,6 +395,7 @@ extern const struct ethnl_request_ops ethnl_rss_request_ops; extern const struct ethnl_request_ops ethnl_plca_cfg_request_ops; extern const struct ethnl_request_ops ethnl_plca_status_request_ops; extern const struct ethnl_request_ops ethnl_mm_request_ops; +extern const struct ethnl_request_ops ethnl_preempt_request_ops; extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_FLAGS + 1]; extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_FLAGS + 1]; @@ -441,6 +442,7 @@ extern const struct nla_policy ethnl_plca_set_cfg_policy[ETHTOOL_A_PLCA_MAX + 1] extern const struct nla_policy ethnl_plca_get_status_policy[ETHTOOL_A_PLCA_HEADER + 1]; extern const struct nla_policy ethnl_mm_get_policy[ETHTOOL_A_MM_HEADER + 1]; extern const struct nla_policy ethnl_mm_set_policy[ETHTOOL_A_MM_MAX + 1]; +extern const struct nla_policy preempt_get_policy[ETHTOOL_A_PREEMPT_MAX + 1]; int ethnl_set_features(struct sk_buff *skb, struct genl_info *info); int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info); @@ -448,6 +450,7 @@ int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info); int ethnl_tunnel_info_doit(struct sk_buff *skb, struct genl_info *info); int ethnl_tunnel_info_start(struct netlink_callback *cb); int ethnl_tunnel_info_dumpit(struct sk_buff *skb, struct netlink_callback *cb); +int ethnl_set_preempt(struct sk_buff *skb, struct genl_info *info); extern const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN]; extern const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN]; diff --git a/net/ethtool/preempt.c b/net/ethtool/preempt.c new file mode 100644 index 000000000000..06f47fc75c58 --- /dev/null +++ b/net/ethtool/preempt.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "netlink.h" +#include "common.h" + +struct preempt_req_info { + struct ethnl_req_info base; +}; + +struct preempt_reply_data { + struct ethnl_reply_data base; + struct ethtool_fp fp; +}; + +#define PREEMPT_REPDATA(__reply_base) \ + container_of(__reply_base, struct preempt_reply_data, base) + +const struct nla_policy preempt_get_policy[ETHTOOL_A_PREEMPT_MAX + 1] = { + [ETHTOOL_A_PREEMPT_UNSPEC] = { .type = NLA_REJECT }, + [ETHTOOL_A_PREEMPT_HEADER] = { .type = NLA_NESTED }, + [ETHTOOL_A_PREEMPT_SUPPORTED] = { .type = NLA_REJECT }, + [ETHTOOL_A_PREEMPT_STATUS] = { .type = NLA_REJECT }, + [ETHTOOL_A_PREEMPT_ACTIVE] = { .type = NLA_REJECT }, + [ETHTOOL_A_PREEMPT_MIN_FRAG_SIZE] = { .type = NLA_REJECT }, + [ETHTOOL_A_PREEMPT_QUEUES_SUPPORTED] = { .type = NLA_REJECT }, + [ETHTOOL_A_PREEMPT_QUEUES_PREEMPTIBLE] = { .type = NLA_REJECT }, +}; + +static int preempt_prepare_data(const struct ethnl_req_info *req_base, + struct ethnl_reply_data *reply_base, + const struct genl_info *info) +{ + struct preempt_reply_data *data = PREEMPT_REPDATA(reply_base); + struct net_device *dev = reply_base->dev; + int ret; + + if (!dev->ethtool_ops->get_preempt) + return -EOPNOTSUPP; + + ret = ethnl_ops_begin(dev); + if (ret < 0) + return ret; + + ret = dev->ethtool_ops->get_preempt(dev, &data->fp); + ethnl_ops_complete(dev); + + return ret; +} + +static int preempt_reply_size(const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + int len = 0; + + len += nla_total_size(sizeof(u8)); /* _PREEMPT_SUPPORTED */ + len += nla_total_size(sizeof(u8)); /* _PREEMPT_STATUS */ + len += nla_total_size(sizeof(u8)); /* _PREEMPT_ACTIVE */ + len += nla_total_size(sizeof(u32)); /* _PREEMPT_QUEUES_SUPPORTED */ + len += nla_total_size(sizeof(u32)); /* _PREEMPT_QUEUES_PREEMPTIBLE */ + len += nla_total_size(sizeof(u32)); /* _PREEMPT_MIN_FRAG_SIZE */ + + return len; +} + +static int preempt_fill_reply(struct sk_buff *skb, + const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + const struct preempt_reply_data *data = PREEMPT_REPDATA(reply_base); + const struct ethtool_fp *preempt = &data->fp; + + if (nla_put_u32(skb, ETHTOOL_A_PREEMPT_QUEUES_SUPPORTED, + preempt->supported_queues_mask)) + return -EMSGSIZE; + + if (nla_put_u32(skb, ETHTOOL_A_PREEMPT_QUEUES_PREEMPTIBLE, + preempt->preemptible_queues_mask)) + return -EMSGSIZE; + + if (nla_put_u8(skb, ETHTOOL_A_PREEMPT_STATUS, preempt->fp_status)) + return -EMSGSIZE; + + if (nla_put_u8(skb, ETHTOOL_A_PREEMPT_ACTIVE, preempt->fp_active)) + return -EMSGSIZE; + + if (nla_put_u8(skb, ETHTOOL_A_PREEMPT_SUPPORTED, + preempt->fp_supported)) + return -EMSGSIZE; + + if (nla_put_u32(skb, ETHTOOL_A_PREEMPT_MIN_FRAG_SIZE, + preempt->min_frag_size)) + return -EMSGSIZE; + + return 0; +} + +const struct ethnl_request_ops ethnl_preempt_request_ops = { + .request_cmd = ETHTOOL_MSG_PREEMPT_GET, + .reply_cmd = ETHTOOL_MSG_PREEMPT_GET_REPLY, + .hdr_attr = ETHTOOL_A_PREEMPT_HEADER, + .req_info_size = sizeof(struct preempt_req_info), + .reply_data_size = sizeof(struct preempt_reply_data), + + .prepare_data = preempt_prepare_data, + .reply_size = preempt_reply_size, + .fill_reply = preempt_fill_reply, +}; + +static const struct nla_policy +preempt_set_policy[ETHTOOL_A_PREEMPT_MAX + 1] = { + [ETHTOOL_A_PREEMPT_UNSPEC] = { .type = NLA_REJECT }, + [ETHTOOL_A_PREEMPT_HEADER] = { .type = NLA_NESTED }, + [ETHTOOL_A_PREEMPT_DISABLED] = { .type = NLA_FLAG }, + [ETHTOOL_A_PREEMPT_SUPPORTED] = { .type = NLA_REJECT }, + [ETHTOOL_A_PREEMPT_STATUS] = { .type = NLA_REJECT }, + [ETHTOOL_A_PREEMPT_ACTIVE] = { .type = NLA_U8 }, + [ETHTOOL_A_PREEMPT_MIN_FRAG_SIZE] = { .type = NLA_U32 }, + [ETHTOOL_A_PREEMPT_QUEUES_SUPPORTED] = { .type = NLA_REJECT }, + [ETHTOOL_A_PREEMPT_QUEUES_PREEMPTIBLE] = { .type = NLA_U32 }, +}; + +int ethnl_set_preempt(struct sk_buff *skb, struct genl_info *info) +{ + struct nlattr *tb[ARRAY_SIZE(preempt_set_policy)]; + struct ethtool_fp preempt = {}; + struct ethnl_req_info req_info = {}; + struct net_device *dev; + bool mod = false; + int ret; + + ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, + ETHTOOL_A_PREEMPT_MAX, preempt_set_policy, + info->extack); + if (ret < 0) + return ret; + + ret = ethnl_parse_header_dev_get(&req_info, + tb[ETHTOOL_A_PREEMPT_HEADER], + genl_info_net(info), info->extack, + true); + if (ret < 0) + return ret; + dev = req_info.dev; + ret = -EOPNOTSUPP; + if (!dev->ethtool_ops->get_preempt || + !dev->ethtool_ops->set_preempt) + goto out_dev; + + rtnl_lock(); + ret = ethnl_ops_begin(dev); + if (ret < 0) + goto out_rtnl; + + if (nla_get_flag(tb[ETHTOOL_A_PREEMPT_DISABLED])) { + preempt.disabled = 1; + mod = true; + } + if (tb[ETHTOOL_A_PREEMPT_ACTIVE]) { + ethnl_update_u8(&preempt.fp_enabled, + tb[ETHTOOL_A_PREEMPT_ACTIVE], &mod); + mod = true; + } + if (tb[ETHTOOL_A_PREEMPT_MIN_FRAG_SIZE]) + ethnl_update_u32(&preempt.min_frag_size, + tb[ETHTOOL_A_PREEMPT_MIN_FRAG_SIZE], &mod); + else + preempt.min_frag_size = 60; + if (tb[ETHTOOL_A_PREEMPT_QUEUES_PREEMPTIBLE]) { + ethnl_update_u32(&preempt.preemptible_queues_mask, + tb[ETHTOOL_A_PREEMPT_QUEUES_PREEMPTIBLE], &mod); + mod = true; + } + + ret = 0; + if (!mod) + goto out_ops; + + ret = dev->ethtool_ops->set_preempt(dev, &preempt); + if (ret < 0) + GENL_SET_ERR_MSG(info, "frame preemption settings update failed"); + else + ethtool_notify(dev, ETHTOOL_MSG_PREEMPT_NTF, NULL); + +out_ops: + ethnl_ops_complete(dev); +out_rtnl: + rtnl_unlock(); +out_dev: + dev_put(dev); + return ret; +} diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 3e5703537e4e..9a4bae08b82e 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -3261,9 +3261,10 @@ static int packet_release(struct socket *sock) * Attach a packet hook. */ -static int packet_do_bind(struct sock *sk, const char *name, int ifindex, +static int packet_do_bind(struct socket *sock, const char *name, int ifindex, __be16 proto) { + struct sock *sk = sock->sk; struct packet_sock *po = pkt_sk(sk); struct net_device *dev = NULL; bool unlisted = false; @@ -3329,6 +3330,7 @@ static int packet_do_bind(struct sock *sk, const char *name, int ifindex, po->prot_hook.dev = dev; WRITE_ONCE(po->ifindex, dev ? dev->ifindex : 0); packet_cached_dev_assign(po, dev); + sock->ndev = dev; } dev_put(dev); } @@ -3358,7 +3360,6 @@ static int packet_do_bind(struct sock *sk, const char *name, int ifindex, static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr, int addr_len) { - struct sock *sk = sock->sk; char name[sizeof(uaddr->sa_data_min) + 1]; /* @@ -3373,13 +3374,13 @@ static int packet_bind_spkt(struct socket *sock, struct sockaddr *uaddr, memcpy(name, uaddr->sa_data, sizeof(uaddr->sa_data_min)); name[sizeof(uaddr->sa_data_min)] = 0; - return packet_do_bind(sk, name, 0, 0); + + return packet_do_bind(sock, name, 0, 0); } static int packet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sockaddr_ll *sll = (struct sockaddr_ll *)uaddr; - struct sock *sk = sock->sk; /* * Check legality @@ -3390,7 +3391,7 @@ static int packet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len if (sll->sll_family != AF_PACKET) return -EINVAL; - return packet_do_bind(sk, NULL, sll->sll_ifindex, sll->sll_protocol); + return packet_do_bind(sock, NULL, sll->sll_ifindex, sll->sll_protocol); } static struct proto packet_proto = { diff --git a/net/sched/Kconfig b/net/sched/Kconfig index 470c70deffe2..1f64c0ee84d1 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -925,6 +925,19 @@ config NET_ACT_GATE To compile this code as a module, choose M here: the module will be called act_gate. +config NET_ACT_FRER + tristate "Frame frer tc action" + depends on NET_CLS_ACT + help + Say Y here to support frame replication and elimination for + reliability, which is defined by IEEE 802.1CB. + This action allow to add a frer tag. It also allow to remove + the frer tag and drop repeat frames. + + If unsure, say N. + To compile this code as a module, choose M here: the + module will be called act_frer. + config NET_IFE_SKBMARK tristate "Support to encoding decoding skb mark on IFE action" depends on NET_ACT_IFE diff --git a/net/sched/Makefile b/net/sched/Makefile index b5fd49641d91..6ff9bc76febc 100644 --- a/net/sched/Makefile +++ b/net/sched/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_NET_IFE_SKBTCINDEX) += act_meta_skbtcindex.o obj-$(CONFIG_NET_ACT_TUNNEL_KEY)+= act_tunnel_key.o obj-$(CONFIG_NET_ACT_CT) += act_ct.o obj-$(CONFIG_NET_ACT_GATE) += act_gate.o +obj-$(CONFIG_NET_ACT_FRER) += act_frer.o obj-$(CONFIG_NET_SCH_FIFO) += sch_fifo.o obj-$(CONFIG_NET_SCH_HTB) += sch_htb.o obj-$(CONFIG_NET_SCH_HFSC) += sch_hfsc.o diff --git a/net/sched/act_frer.c b/net/sched/act_frer.c new file mode 100644 index 000000000000..af455ef08813 --- /dev/null +++ b/net/sched/act_frer.c @@ -0,0 +1,731 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Copyright 2021 NXP */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FRER_SEQ_SPACE 16 +#define FRER_RCVY_RESET_MSEC 100 +#define FRER_RCVY_INVALID_SEQ 0x100 +#define FRER_RCVY_PASSED 0 +#define FRER_RCVY_DISCARDED -1 + +static unsigned int frer_net_id; +static struct tc_action_ops act_frer_ops; + +struct r_tag { + __be16 reserved; + __be16 sequence_nr; + __be16 encap_proto; +} __packed; + +struct rtag_ethhdr { + struct ethhdr ethhdr; + struct r_tag h_rtag; +} __packed; + +struct rtag_vlan_ethhdr { + struct vlan_ethhdr vlanhdr; + struct r_tag h_rtag; +} __packed; + +static const struct nla_policy frer_policy[TCA_FRER_MAX + 1] = { + [TCA_FRER_PARMS] = + NLA_POLICY_EXACT_LEN(sizeof(struct tc_frer)), + [TCA_FRER_TAG_TYPE] = { .type = NLA_U8 }, + [TCA_FRER_TAG_ACTION] = { .type = NLA_U8 }, + [TCA_FRER_RECOVER] = { .type = NLA_U8 }, + [TCA_FRER_RECOVER_ALG] = { .type = NLA_U8 }, + [TCA_FRER_RECOVER_HISTORY_LEN] = { .type = NLA_U8 }, + [TCA_FRER_RECOVER_RESET_TM] = { .type = NLA_U64 }, +}; + +static void frer_seq_recovery_reset(struct tcf_frer *frer_act); + +static enum hrtimer_restart frer_hrtimer_func(struct hrtimer *timer) +{ + struct tcf_frer *frer_act = container_of(timer, struct tcf_frer, + hrtimer); + ktime_t remaining_tm; + + frer_seq_recovery_reset(frer_act); + + remaining_tm = (ktime_t)(frer_act->rcvy_reset_msec * 1000000); + + hrtimer_forward(timer, timer->base->get_time(), remaining_tm); + + return HRTIMER_RESTART; +} + +static int frer_rtag_decode(struct sk_buff *skb) +{ + struct rtag_vlan_ethhdr *rtag_vlan_hdr; + struct rtag_ethhdr *rtag_hdr; + struct vlan_ethhdr *vlanhdr; + struct ethhdr *ethhdr; + struct r_tag *rtag; + bool is_vlan; + u16 sequence; + u16 proto; + + ethhdr = (struct ethhdr *)skb_mac_header(skb); + proto = ethhdr->h_proto; + is_vlan = false; + + if (proto == htons(ETH_P_8021Q)) { + vlanhdr = (struct vlan_ethhdr *)ethhdr; + proto = vlanhdr->h_vlan_encapsulated_proto; + is_vlan = true; + } + + if (proto != htons(ETH_P_RTAG)) + return FRER_RCVY_INVALID_SEQ; + + if (is_vlan) { + rtag_vlan_hdr = (struct rtag_vlan_ethhdr *)ethhdr; + rtag = &rtag_vlan_hdr->h_rtag; + } else { + rtag_hdr = (struct rtag_ethhdr *)ethhdr; + rtag = &rtag_hdr->h_rtag; + } + + sequence = ntohs(rtag->sequence_nr); + + return sequence; +} + +static int frer_seq_generation_alg(struct tcf_frer *frer_act) +{ + u32 gen_seq_max = frer_act->seq_space - 1; + u32 gen_seq_num = frer_act->gen_seq_num; + int sequence_number; + + sequence_number = gen_seq_num; + + if (gen_seq_num >= gen_seq_max) + gen_seq_num = 0; + else + gen_seq_num++; + + frer_act->gen_seq_num = gen_seq_num; + + return sequence_number; +} + +static int frer_rtag_encode(struct sk_buff *skb, struct tcf_frer *frer_act) +{ + struct vlan_ethhdr *vlanhdr; + struct ethhdr *ethhdr; + struct r_tag *rtag; + int rtag_len, head_len; + unsigned char *dst, *src, *p; + __be16 *proto, proto_val; + + ethhdr = (struct ethhdr *)skb_mac_header(skb); + if (ethhdr->h_proto == htons(ETH_P_8021Q)) { + vlanhdr = (struct vlan_ethhdr *)ethhdr; + p = (unsigned char *)(vlanhdr + 1); + proto = &vlanhdr->h_vlan_encapsulated_proto; + } else { + p = (unsigned char *)(ethhdr + 1); + proto = ðhdr->h_proto; + } + + proto_val = *proto; + *proto = htons(ETH_P_RTAG); + + src = skb_mac_header(skb); + head_len = p - src; + + rtag_len = sizeof(struct r_tag); + if (skb_cow_head(skb, rtag_len) < 0) + return -ENOMEM; + + skb_push(skb, rtag_len); + skb_reset_network_header(skb); + skb->mac_header -= rtag_len; + + dst = skb_mac_header(skb); + memmove(dst, src, head_len); + + rtag = (struct r_tag *)(dst + head_len); + rtag->encap_proto = proto_val; + rtag->sequence_nr = htons(frer_act->gen_seq_num); + rtag->reserved = 0; + + return 0; +} + +static void frer_rtag_pop(struct sk_buff *skb, struct tcf_frer *frer_act) +{ + struct vlan_ethhdr *vlanhdr; + struct ethhdr *ethhdr; + struct r_tag *rtag; + int rtag_len, head_len; + unsigned char *dst, *src, *p; + __be16 *proto; + + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + if (ethhdr->h_proto == htons(ETH_P_8021Q)) { + vlanhdr = (struct vlan_ethhdr *)ethhdr; + p = (unsigned char *)(vlanhdr + 1); + proto = &vlanhdr->h_vlan_encapsulated_proto; + } else { + p = (unsigned char *)(ethhdr + 1); + proto = ðhdr->h_proto; + } + + if (*proto != htons(ETH_P_RTAG)) + return; + + rtag = (struct r_tag *)p; + rtag_len = sizeof(struct r_tag); + *proto = rtag->encap_proto; + + src = skb_mac_header(skb); + head_len = p - src; + + skb->data = skb_mac_header(skb); + skb_pull(skb, rtag_len); + + skb_reset_mac_header(skb); + + if (skb->ip_summed == CHECKSUM_PARTIAL) + skb->csum_start += rtag_len; + + dst = skb_mac_header(skb); + memmove(dst, src, head_len); +} + +static const struct tcf_frer_proto_ops rtag_ops = { + .encode = frer_rtag_encode, + .decode = frer_rtag_decode, + .tag_pop = frer_rtag_pop, +}; + +static int tcf_frer_init(struct net *net, struct nlattr *nla, + struct nlattr *est, struct tc_action **a, + struct tcf_proto *tp, u32 flags, + struct netlink_ext_ack *extack) +{ + struct tc_action_net *tn = net_generic(net, frer_net_id); + bool bind = flags & TCA_ACT_FLAGS_BIND; + struct nlattr *tb[TCA_FRER_MAX + 1]; + struct tcf_chain *goto_ch = NULL; + struct tcf_frer *frer_act; + struct tc_frer *parm; + bool exists = false; + int ret = 0, err, index; + ktime_t remaining_tm; + + if (!nla) { + NL_SET_ERR_MSG_MOD(extack, "FRER requires attributes to be passed"); + return -EINVAL; + } + + err = nla_parse_nested_deprecated(tb, TCA_FRER_MAX, nla, frer_policy, extack); + if (err < 0) + return err; + + if (!tb[TCA_FRER_PARMS]) { + NL_SET_ERR_MSG_MOD(extack, "Missing required FRER parameters"); + return -EINVAL; + } + + parm = nla_data(tb[TCA_FRER_PARMS]); + index = parm->index; + + err = tcf_idr_check_alloc(tn, &index, a, bind); + if (err < 0) + return err; + exists = err; + + if (exists && bind) + return 0; + + if (!exists) { + ret = tcf_idr_create_from_flags(tn, index, est, a, + &act_frer_ops, bind, flags); + + if (ret) { + tcf_idr_cleanup(tn, index); + return ret; + } + ret = ACT_P_CREATED; + } else if (!(flags & TCA_ACT_FLAGS_REPLACE)) { + tcf_idr_release(*a, bind); + return -EEXIST; + } + + err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); + if (err < 0) + goto release_idr; + + frer_act = to_frer(*a); + + spin_lock_bh(&frer_act->tcf_lock); + goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch); + + frer_act->tag_type = nla_get_u8(tb[TCA_FRER_TAG_TYPE]); + frer_act->tag_action = nla_get_u8(tb[TCA_FRER_TAG_ACTION]); + frer_act->recover = nla_get_u8(tb[TCA_FRER_RECOVER]); + frer_act->rcvy_alg = nla_get_u8(tb[TCA_FRER_RECOVER_ALG]); + frer_act->rcvy_history_len = nla_get_u8(tb[TCA_FRER_RECOVER_HISTORY_LEN]); + frer_act->rcvy_reset_msec = nla_get_u64(tb[TCA_FRER_RECOVER_RESET_TM]); + + frer_act->gen_seq_num = 0; + frer_act->seq_space = 1 << FRER_SEQ_SPACE; + frer_act->rcvy_seq_num = 0; + frer_act->seq_history = 0xFFFFFFFF; + frer_act->rcvy_take_noseq = true; + + switch (frer_act->tag_type) { + case TCA_FRER_TAG_RTAG: + frer_act->proto_ops = &rtag_ops; + break; + case TCA_FRER_TAG_HSR: + case TCA_FRER_TAG_PRP: + default: + spin_unlock_bh(&frer_act->tcf_lock); + return -EOPNOTSUPP; + } + + if (frer_act->recover && frer_act->rcvy_reset_msec) { + hrtimer_init(&frer_act->hrtimer, CLOCK_TAI, + HRTIMER_MODE_REL_SOFT); + frer_act->hrtimer.function = frer_hrtimer_func; + + remaining_tm = (ktime_t)(frer_act->rcvy_reset_msec * 1000000); + hrtimer_start(&frer_act->hrtimer, remaining_tm, + HRTIMER_MODE_REL_SOFT); + } + + spin_unlock_bh(&frer_act->tcf_lock); + + if (goto_ch) + tcf_chain_put_by_act(goto_ch); + + return ret; + +release_idr: + tcf_idr_release(*a, bind); + return err; +} + +static void frer_seq_recovery_reset(struct tcf_frer *frer_act) +{ + spin_lock(&frer_act->tcf_lock); + if (frer_act->rcvy_alg == TCA_FRER_RCVY_VECTOR_ALG) { + frer_act->rcvy_seq_num = frer_act->seq_space - 1; + frer_act->seq_history = 0; + } + frer_act->cps_seq_rcvy_resets++; + frer_act->take_any = true; + spin_unlock(&frer_act->tcf_lock); +} + +static void frer_shift_seq_history(int value, struct tcf_frer *frer_act) +{ + int history_len = frer_act->rcvy_history_len; + + if ((frer_act->seq_history & BIT(history_len - 1)) == 0) + frer_act->cps_seq_rcvy_lost_pkts++; + + frer_act->seq_history <<= 1; + + if (value) + frer_act->seq_history |= BIT(0); +} + +static int frer_vector_rcvy_alg(struct tcf_frer *frer_act, int sequence, + bool individual) +{ + struct hrtimer *timer = &frer_act->hrtimer; + bool reset_timer = false; + ktime_t remaining_tm; + int delta, ret; + + if (sequence == FRER_RCVY_INVALID_SEQ) { + frer_act->cps_seq_rcvy_tagless_pkts++; + if (frer_act->rcvy_take_noseq) { + reset_timer = true; + ret = FRER_RCVY_PASSED; + goto out; + } else { + return FRER_RCVY_DISCARDED; + } + } + + delta = (sequence - frer_act->rcvy_seq_num) & (frer_act->seq_space - 1); + /* -(RecovSeqSpace/2) <= delta <= ((RecovSeqSpace/2)-1) */ + if (delta & (frer_act->seq_space / 2)) + delta -= frer_act->seq_space; + + if (frer_act->take_any) { + frer_act->take_any = false; + frer_act->seq_history |= BIT(0); + frer_act->rcvy_seq_num = sequence; + + reset_timer = true; + ret = FRER_RCVY_PASSED; + goto out; + } + + if (delta >= frer_act->rcvy_history_len || + delta <= -frer_act->rcvy_history_len) { + /* Packet is out-of-range. */ + frer_act->cps_seq_rcvy_rogue_pkts++; + + if (individual) + reset_timer = true; + + ret = FRER_RCVY_DISCARDED; + goto out; + } else if (delta <= 0) { + /* Packet is old and in SequenceHistory. */ + if (frer_act->seq_history & BIT(-delta)) { + if (individual) + reset_timer = true; + + /* Packet has been seen. */ + ret = FRER_RCVY_DISCARDED; + goto out; + } else { + /* Packet has not been seen. */ + frer_act->seq_history |= BIT(-delta); + frer_act->cps_seq_rcvy_out_of_order_pkts++; + + reset_timer = true; + ret = FRER_RCVY_PASSED; + goto out; + } + } else { + /* Packet is not too far ahead of the one we want. */ + if (delta != 1) + frer_act->cps_seq_rcvy_out_of_order_pkts++; + + while (--delta) + frer_shift_seq_history(0, frer_act); + frer_shift_seq_history(1, frer_act); + frer_act->rcvy_seq_num = sequence; + + reset_timer = true; + ret = FRER_RCVY_PASSED; + goto out; + } +out: + if (reset_timer && frer_act->rcvy_reset_msec) { + remaining_tm = + (ktime_t)(frer_act->rcvy_reset_msec * 1000000); + hrtimer_start(timer, remaining_tm, HRTIMER_MODE_REL_SOFT); + } + + return ret; +} + +static int frer_match_rcvy_alg(struct tcf_frer *frer_act, int sequence, + bool individual) +{ + struct hrtimer *timer = &frer_act->hrtimer; + bool reset_timer = false; + ktime_t remaining_tm; + int delta, ret; + + if (sequence == FRER_RCVY_INVALID_SEQ) { + frer_act->cps_seq_rcvy_tagless_pkts++; + + return FRER_RCVY_PASSED; + } + + if (frer_act->take_any) { + frer_act->take_any = false; + frer_act->rcvy_seq_num = sequence; + + reset_timer = true; + ret = FRER_RCVY_PASSED; + goto out; + } + + delta = sequence - frer_act->rcvy_seq_num; + if (delta) { + /* Packet has not been seen, accept it. */ + if (delta != 1) + frer_act->cps_seq_rcvy_out_of_order_pkts++; + + frer_act->rcvy_seq_num = sequence; + + reset_timer = true; + ret = FRER_RCVY_PASSED; + goto out; + } else { + if (individual) + reset_timer = true; + + /* Packet has been seen. Do not forward. */ + ret = FRER_RCVY_DISCARDED; + goto out; + } + +out: + if (reset_timer && frer_act->rcvy_reset_msec) { + remaining_tm = (ktime_t)(frer_act->rcvy_reset_msec * 1000000); + hrtimer_start(timer, remaining_tm, HRTIMER_MODE_REL_SOFT); + } + + return ret; +} + +static int tcf_frer_act(struct sk_buff *skb, const struct tc_action *a, + struct tcf_result *res) +{ + struct tcf_frer *frer_act = to_frer(a); + bool ingress, individual; + int ret, retval; + int sequence; + + tcf_lastuse_update(&frer_act->tcf_tm); + tcf_action_update_bstats(&frer_act->common, skb); + + retval = READ_ONCE(frer_act->tcf_action); + + sequence = frer_act->proto_ops->decode(skb); + + ingress = skb_at_tc_ingress(skb); + individual = ingress; + + if (frer_act->recover) { + spin_lock(&frer_act->tcf_lock); + + if (frer_act->rcvy_alg == TCA_FRER_RCVY_VECTOR_ALG) + ret = frer_vector_rcvy_alg(frer_act, sequence, + individual); + else + ret = frer_match_rcvy_alg(frer_act, sequence, + individual); + if (ret) { + frer_act->tcf_qstats.drops++; + retval = TC_ACT_SHOT; + } + + if (frer_act->tag_action == TCA_FRER_TAG_POP) + frer_act->proto_ops->tag_pop(skb, frer_act); + + spin_unlock(&frer_act->tcf_lock); + + return retval; + } + + if (frer_act->tag_action == TCA_FRER_TAG_PUSH && + sequence == FRER_RCVY_INVALID_SEQ) { + spin_lock(&frer_act->tcf_lock); + + frer_seq_generation_alg(frer_act); + + frer_act->proto_ops->encode(skb, frer_act); + + spin_unlock(&frer_act->tcf_lock); + } + + return retval; +} + +static int tcf_frer_dump(struct sk_buff *skb, struct tc_action *a, + int bind, int ref) +{ + unsigned char *b = skb_tail_pointer(skb); + struct tcf_frer *frer_act = to_frer(a); + struct tc_frer opt = { + .index = frer_act->tcf_index, + .refcnt = refcount_read(&frer_act->tcf_refcnt) - ref, + .bindcnt = atomic_read(&frer_act->tcf_bindcnt) - bind, + }; + struct tcf_t t; + + spin_lock_bh(&frer_act->tcf_lock); + opt.action = frer_act->tcf_action; + + if (nla_put(skb, TCA_FRER_PARMS, sizeof(opt), &opt)) + goto nla_put_failure; + + if (nla_put_u8(skb, TCA_FRER_TAG_TYPE, frer_act->tag_type)) + goto nla_put_failure; + + if (nla_put_u8(skb, TCA_FRER_TAG_ACTION, frer_act->tag_action)) + goto nla_put_failure; + + if (nla_put_u8(skb, TCA_FRER_RECOVER, frer_act->recover)) + goto nla_put_failure; + + if (nla_put_u8(skb, TCA_FRER_RECOVER_ALG, frer_act->rcvy_alg)) + goto nla_put_failure; + + if (nla_put_u8(skb, TCA_FRER_RECOVER_HISTORY_LEN, + frer_act->rcvy_history_len)) + goto nla_put_failure; + + if (nla_put_u64_64bit(skb, TCA_FRER_RECOVER_RESET_TM, + frer_act->rcvy_reset_msec, TCA_FRER_PAD)) + goto nla_put_failure; + + if (nla_put_u32(skb, TCA_FRER_RECOVER_TAGLESS_PKTS, + frer_act->cps_seq_rcvy_tagless_pkts)) + goto nla_put_failure; + + if (nla_put_u32(skb, TCA_FRER_RECOVER_OUT_OF_ORDER_PKTS, + frer_act->cps_seq_rcvy_out_of_order_pkts)) + goto nla_put_failure; + + if (nla_put_u32(skb, TCA_FRER_RECOVER_ROGUE_PKTS, + frer_act->cps_seq_rcvy_rogue_pkts)) + goto nla_put_failure; + + if (nla_put_u32(skb, TCA_FRER_RECOVER_LOST_PKTS, + frer_act->cps_seq_rcvy_lost_pkts)) + goto nla_put_failure; + + if (nla_put_u32(skb, TCA_FRER_RECOVER_RESETS, + frer_act->cps_seq_rcvy_resets)) + goto nla_put_failure; + + tcf_tm_dump(&t, &frer_act->tcf_tm); + if (nla_put_64bit(skb, TCA_FRER_TM, sizeof(t), + &t, TCA_FRER_PAD)) + goto nla_put_failure; + spin_unlock_bh(&frer_act->tcf_lock); + + return skb->len; + +nla_put_failure: + spin_unlock_bh(&frer_act->tcf_lock); + nlmsg_trim(skb, b); + + return -1; +} + +static int tcf_frer_walker(struct net *net, struct sk_buff *skb, + struct netlink_callback *cb, int type, + const struct tc_action_ops *ops, + struct netlink_ext_ack *extack) +{ + struct tc_action_net *tn = net_generic(net, frer_net_id); + + return tcf_generic_walker(tn, skb, cb, type, ops, extack); +} + +static int tcf_frer_search(struct net *net, struct tc_action **a, u32 index) +{ + struct tc_action_net *tn = net_generic(net, frer_net_id); + + return tcf_idr_search(tn, a, index); +} + +static void tcf_frer_stats_update(struct tc_action *a, u64 bytes, u64 packets, + u64 drops, u64 lastuse, bool hw) +{ + struct tcf_frer *frer_act = to_frer(a); + struct tcf_t *tm = &frer_act->tcf_tm; + + tcf_action_update_stats(a, bytes, packets, drops, hw); + tm->lastuse = max_t(u64, tm->lastuse, lastuse); +} + +static void tcf_frer_cleanup(struct tc_action *a) +{ + struct tcf_frer *frer_act = to_frer(a); + + if (frer_act->rcvy_reset_msec) + hrtimer_cancel(&frer_act->hrtimer); +} + +static size_t tcf_frer_get_fill_size(const struct tc_action *act) +{ + return nla_total_size(sizeof(struct tc_frer)); +} + +static int tcf_frer_offload_act_setup(struct tc_action *act, void *entry_data, + u32 *index_inc, bool bind, + struct netlink_ext_ack *extack) +{ + if (bind) { + struct flow_action_entry *entry = entry_data; + + entry->id = FLOW_ACTION_FRER; + entry->frer.tag_type = to_frer(act)->tag_type; + entry->frer.tag_action = to_frer(act)->tag_action; + entry->frer.recover = to_frer(act)->recover; + entry->frer.rcvy_alg = to_frer(act)->rcvy_alg; + entry->frer.rcvy_history_len = + to_frer(act)->rcvy_history_len; + entry->frer.rcvy_reset_msec = + to_frer(act)->rcvy_reset_msec; + + *index_inc = 1; + } else { + struct flow_offload_action *fl_action = entry_data; + + fl_action->id = FLOW_ACTION_FRER; + } + + return 0; +} + +static struct tc_action_ops act_frer_ops = { + .kind = "frer", + .id = TCA_ID_FRER, + .owner = THIS_MODULE, + .act = tcf_frer_act, + .init = tcf_frer_init, + .cleanup = tcf_frer_cleanup, + .dump = tcf_frer_dump, + .walk = tcf_frer_walker, + .stats_update = tcf_frer_stats_update, + .get_fill_size = tcf_frer_get_fill_size, + .offload_act_setup = tcf_frer_offload_act_setup, + .lookup = tcf_frer_search, + .size = sizeof(struct tcf_frer), +}; + +static __net_init int frer_init_net(struct net *net) +{ + struct tc_action_net *tn = net_generic(net, frer_net_id); + + return tc_action_net_init(net, tn, &act_frer_ops); +} + +static void __net_exit frer_exit_net(struct list_head *net_list) +{ + tc_action_net_exit(net_list, frer_net_id); +}; + +static struct pernet_operations frer_net_ops = { + .init = frer_init_net, + .exit_batch = frer_exit_net, + .id = &frer_net_id, + .size = sizeof(struct tc_action_net), +}; + +static int __init frer_init_module(void) +{ + return tcf_register_action(&act_frer_ops, &frer_net_ops); +} + +static void __exit frer_cleanup_module(void) +{ + tcf_unregister_action(&act_frer_ops, &frer_net_ops); +} + +module_init(frer_init_module); +module_exit(frer_cleanup_module); +MODULE_LICENSE("GPL v2"); diff --git a/net/sched/sch_cbs.c b/net/sched/sch_cbs.c index cac870eb7897..36a6a79697e2 100644 --- a/net/sched/sch_cbs.c +++ b/net/sched/sch_cbs.c @@ -379,6 +379,11 @@ static int cbs_change(struct Qdisc *sch, struct nlattr *opt, qopt = nla_data(tb[TCA_CBS_PARMS]); + if (qopt->idleslope < 0 || qopt->sendslope > 0 || qopt->hicredit < 0 || qopt->locredit > 0) { + NL_SET_ERR_MSG(extack, "Invalid CBS parameters"); + return -EINVAL; + } + if (!qopt->offload) { cbs_set_port_rate(dev, q); cbs_disable_offload(dev, q); diff --git a/net/socket.c b/net/socket.c index 9db33cd4a71b..69e7d4bc021a 100644 --- a/net/socket.c +++ b/net/socket.c @@ -110,6 +110,10 @@ #include #include +static int fast_raw_socket_fd = -1; +static struct net_device *fast_raw_socket_dev; +static struct socket *fast_raw_socket_sock = NULL; + #ifdef CONFIG_NET_RX_BUSY_POLL unsigned int sysctl_net_busy_read __read_mostly; unsigned int sysctl_net_busy_poll __read_mostly; @@ -671,6 +675,10 @@ static void __sock_release(struct socket *sock, struct inode *inode) iput(SOCK_INODE(sock)); return; } + if (fast_raw_socket_sock != NULL && fast_raw_socket_sock == sock) { + fast_raw_socket_sock = NULL; + fast_raw_socket_fd = -1; + } sock->file = NULL; } @@ -1853,6 +1861,15 @@ int __sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen) &address, addrlen); } fput_light(sock->file, fput_needed); + if (fast_raw_socket_fd < 0) { + if (sock->type == SOCK_RAW && sock->sk->sk_family == PF_PACKET) { + if (sock->ndev != NULL && sock->ndev->fast_raw_device == 1) { + fast_raw_socket_fd = fd; + fast_raw_socket_dev = sock->ndev; + fast_raw_socket_sock = sock; + } + } + } } return err; } @@ -2169,6 +2186,10 @@ int __sys_sendto(int fd, void __user *buff, size_t len, unsigned int flags, struct msghdr msg; struct iovec iov; int fput_needed; + if (fd == fast_raw_socket_fd) { + err = fast_raw_socket_dev->netdev_ops->ndo_fast_xmit(fast_raw_socket_dev, buff, len); + return err; + } err = import_single_range(ITER_SOURCE, buff, len, &iov, &msg.msg_iter); if (unlikely(err)) @@ -2190,6 +2211,7 @@ int __sys_sendto(int fd, void __user *buff, size_t len, unsigned int flags, msg.msg_namelen = addr_len; } flags &= ~MSG_INTERNAL_SENDMSG_FLAGS; + if (sock->file->f_flags & O_NONBLOCK) flags |= MSG_DONTWAIT; msg.msg_flags = flags; @@ -2235,6 +2257,11 @@ int __sys_recvfrom(int fd, void __user *ubuf, size_t size, unsigned int flags, struct iovec iov; int err, err2; int fput_needed; + int i; + if (fd == fast_raw_socket_fd) { + err = fast_raw_socket_dev->netdev_ops->ndo_fast_recv(fast_raw_socket_dev, ubuf, size, addr, addr_len); + return err; + } err = import_single_range(ITER_DEST, ubuf, size, &iov, &msg.msg_iter); if (unlikely(err)) diff --git a/net/tsn/genl_tsn.c b/net/tsn/genl_tsn.c index 13a2d8089609..b8a6c2db906c 100644 --- a/net/tsn/genl_tsn.c +++ b/net/tsn/genl_tsn.c @@ -1136,12 +1136,11 @@ static int cmd_qci_sfi_get(struct genl_info *info) return valid; } - valid = tsnops->qci_sfi_counters_get(netdev, sfi_handle, - &sficount); - if (valid < 0) { + ret = tsnops->qci_sfi_counters_get(netdev, sfi_handle, &sficount); + if (ret < 0) { tsn_simple_reply(info, TSN_CMD_REPLY, - netdev->name, valid); - return valid; + netdev->name, ret); + return ret; } } @@ -2121,6 +2120,9 @@ static int cmd_qbv_set(struct genl_info *info) if (qbv[TSN_QBV_ATTR_CONFIGCHANGE]) qbvconfig.config_change = 1; + if (qbv[TSN_QBV_ATTR_MAXSDU]) + qbvconfig.maxsdu = nla_get_u32(qbv[TSN_QBV_ATTR_MAXSDU]); + if (!qbv[TSN_QBV_ATTR_ADMINENTRY]) { tsn_simple_reply(info, TSN_CMD_REPLY, netdev->name, -EINVAL); return -1; diff --git a/tools/virtio/vt_test.sh b/tools/virtio/vt_test.sh new file mode 100755 index 000000000000..4d9479404bd0 --- /dev/null +++ b/tools/virtio/vt_test.sh @@ -0,0 +1,79 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright 2022-2024 NXP + +set -eo pipefail + +usage() +{ + echo "USAGE: $0 [-h] [-s pkt_size] [-r regression] [-t type] [-b backend copy] [-f frontend copy]" + echo -e "-s: Packet size: max 2048 Bytes, default: 64 Bytes" + echo -e "-r: Regression times: default: 1000" + echo -e "-t: Test type: 0: TX (frontend to backend); 1: RX (backend to frontend)" + echo -e "-b: Backend copy buffer option: 0: not copy; 1: copy" + echo -e "-f: Frontend copy buffer option: 0: not copy; 1: copy" + echo -e "-h: This USAGE info" + exit 1 +} + +setvar() +{ + local varname=$1 + shift + if [ -z "${varname}" ]; then + usage + else + eval "$varname=\"$@\"" + fi +} + +find_virtio_trans () +{ + VIRTIO_TRANS=`find /sys/bus/platform/devices/ -name *.virtio_trans` + if [ -z "${VIRTIO_TRANS}" ] || [ ! -d ${VIRTIO_TRANS} ]; then + echo "${VIRTIO_TRANS}" + exit 2; + fi +} + +while getopts 'hs:t:r:b:f:' c +do + case $c in + h) usage ;; + s) setvar PKT_SIZE $OPTARG ;; + t) setvar TYPE $OPTARG ;; + r) setvar REGRESS $OPTARG ;; + b) setvar BACK_COPY $OPTARG ;; + f) setvar FRONT_COPY $OPTARG ;; + esac + +done + +if [ -z "${TYPE}" ]; then + TYPE=0; +fi + +if [ -z "${PKT_SIZE}" ]; then + PKT_SIZE=64; +fi + +if [ -z "${REGRESS}" ]; then + REGRESS=1000; +fi + +if [ -z "${BACK_COPY}" ]; then + BACK_COPY=0; +fi + +if [ -z "${FRONT_COPY}" ]; then + FRONT_COPY=0; +fi + +CONFIG=$(( $(( TYPE << 0 )) | $(( BACK_COPY << 1 )) | $(( FRONT_COPY << 2 )) )) + +find_virtio_trans + +echo ${REGRESS} > ${VIRTIO_TRANS}/virtio0/vt_regression&& +echo ${PKT_SIZE} > ${VIRTIO_TRANS}/virtio0/vt_pkt_size&& +echo ${CONFIG} > ${VIRTIO_TRANS}/virtio0/vt_config&& +echo 1 > ${VIRTIO_TRANS}/virtio0/vt_control;