From bcc11addd773926a18b3d65beb06ed5a927bc916 Mon Sep 17 00:00:00 2001 From: Pablo Greco Date: Mar 21 2021 12:47:50 +0000 Subject: Honeycomb --- diff --git a/SOURCES/kernel-aarch64-debug-fedora.config b/SOURCES/kernel-aarch64-debug-fedora.config index 759817b..145b022 100644 --- a/SOURCES/kernel-aarch64-debug-fedora.config +++ b/SOURCES/kernel-aarch64-debug-fedora.config @@ -8183,3 +8183,4 @@ CONFIG_ZYNQMP_POWER=y # This option determines the default init for the system if no init= # warnings from C=1 sparse checker or -Wextra compilations. It has # You can set the size of pernuma CMA by specifying "cma_pernuma=size" +CONFIG_FSL_MC_UAPI_SUPPORT=y diff --git a/SOURCES/kernel-aarch64-fedora.config b/SOURCES/kernel-aarch64-fedora.config index aa50c6d..9cf27d7 100644 --- a/SOURCES/kernel-aarch64-fedora.config +++ b/SOURCES/kernel-aarch64-fedora.config @@ -8161,3 +8161,4 @@ CONFIG_ZYNQMP_POWER=y # This option determines the default init for the system if no init= # warnings from C=1 sparse checker or -Wextra compilations. It has # You can set the size of pernuma CMA by specifying "cma_pernuma=size" +CONFIG_FSL_MC_UAPI_SUPPORT=y diff --git a/SOURCES/linux-5.10-lx2160a-network.patch b/SOURCES/linux-5.10-lx2160a-network.patch new file mode 100644 index 0000000..648976f --- /dev/null +++ b/SOURCES/linux-5.10-lx2160a-network.patch @@ -0,0 +1,5228 @@ +From e89d20d3574d83dec8642079f92dac3059f24c7d Mon Sep 17 00:00:00 2001 +From: Laurentiu Tudor +Date: Thu, 5 Nov 2020 17:30:50 +0200 +Subject: [PATCH 01/44] bus: fsl-mc: make sure MC firmware is up and running + +Some bootloaders might pause the MC firmware before starting the +kernel to ensure that MC will not cause faults as soon as SMMU +probes due to no configuration being in place for the firmware. +Make sure that MC is resumed at probe time as its SMMU setup should +be done by now. +Also included, a comment fix on how PL and BMT bits are packed in +the StreamID. + +Signed-off-by: Laurentiu Tudor +Link: https://lore.kernel.org/r/20201105153050.19662-2-laurentiu.tudor@nxp.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/bus/fsl-mc/fsl-mc-bus.c | 42 +++++++++++++++++++++++---------- + 1 file changed, 30 insertions(+), 12 deletions(-) + +diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c +index 806766b1b45f..b8e6acdf932e 100644 +--- a/drivers/bus/fsl-mc/fsl-mc-bus.c ++++ b/drivers/bus/fsl-mc/fsl-mc-bus.c +@@ -60,6 +60,9 @@ struct fsl_mc_addr_translation_range { + phys_addr_t start_phys_addr; + }; + ++#define FSL_MC_GCR1 0x0 ++#define GCR1_P1_STOP BIT(31) ++ + #define FSL_MC_FAPR 0x28 + #define MC_FAPR_PL BIT(18) + #define MC_FAPR_BMT BIT(17) +@@ -973,21 +976,36 @@ static int fsl_mc_bus_probe(struct platform_device *pdev) + return PTR_ERR(mc->fsl_mc_regs); + } + +- if (mc->fsl_mc_regs && IS_ENABLED(CONFIG_ACPI) && +- !dev_of_node(&pdev->dev)) { +- mc_stream_id = readl(mc->fsl_mc_regs + FSL_MC_FAPR); ++ if (mc->fsl_mc_regs) { + /* +- * HW ORs the PL and BMT bit, places the result in bit 15 of +- * the StreamID and ORs in the ICID. Calculate it accordingly. ++ * Some bootloaders pause the MC firmware before booting the ++ * kernel so that MC will not cause faults as soon as the ++ * SMMU probes due to the fact that there's no configuration ++ * in place for MC. ++ * At this point MC should have all its SMMU setup done so make ++ * sure it is resumed. + */ +- mc_stream_id = (mc_stream_id & 0xffff) | ++ writel(readl(mc->fsl_mc_regs + FSL_MC_GCR1) & (~GCR1_P1_STOP), ++ mc->fsl_mc_regs + FSL_MC_GCR1); ++ ++ if (IS_ENABLED(CONFIG_ACPI) && !dev_of_node(&pdev->dev)) { ++ mc_stream_id = readl(mc->fsl_mc_regs + FSL_MC_FAPR); ++ /* ++ * HW ORs the PL and BMT bit, places the result in bit ++ * 14 of the StreamID and ORs in the ICID. Calculate it ++ * accordingly. ++ */ ++ mc_stream_id = (mc_stream_id & 0xffff) | + ((mc_stream_id & (MC_FAPR_PL | MC_FAPR_BMT)) ? +- 0x4000 : 0); +- error = acpi_dma_configure_id(&pdev->dev, DEV_DMA_COHERENT, +- &mc_stream_id); +- if (error) +- dev_warn(&pdev->dev, "failed to configure dma: %d.\n", +- error); ++ BIT(14) : 0); ++ error = acpi_dma_configure_id(&pdev->dev, ++ DEV_DMA_COHERENT, ++ &mc_stream_id); ++ if (error) ++ dev_warn(&pdev->dev, ++ "failed to configure dma: %d.\n", ++ error); ++ } + } + + /* +-- +2.27.0 + + +From f62794c7538c03c7ba8f3e744ff1d562207c2285 Mon Sep 17 00:00:00 2001 +From: Laurentiu Tudor +Date: Tue, 24 Nov 2020 13:12:00 +0200 +Subject: [PATCH 02/44] bus: fsl-mc: added missing fields to + dprc_rsp_get_obj_region structure + +'type' and 'flags' fields were missing from dprc_rsp_get_obj_region +structure therefore the MC Bus driver was not receiving proper flags +from MC like DPRC_REGION_CACHEABLE. + +Reviewed-by: Ioana Ciornei +Signed-off-by: Cristian Sovaiala +Signed-off-by: Laurentiu Tudor +Link: https://lore.kernel.org/r/20201124111200.1391-1-laurentiu.tudor@nxp.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/bus/fsl-mc/dprc.c | 2 ++ + drivers/bus/fsl-mc/fsl-mc-private.h | 5 +++-- + 2 files changed, 5 insertions(+), 2 deletions(-) + +diff --git a/drivers/bus/fsl-mc/dprc.c b/drivers/bus/fsl-mc/dprc.c +index 57b097caf255..27b0a01bad9b 100644 +--- a/drivers/bus/fsl-mc/dprc.c ++++ b/drivers/bus/fsl-mc/dprc.c +@@ -576,6 +576,8 @@ int dprc_get_obj_region(struct fsl_mc_io *mc_io, + rsp_params = (struct dprc_rsp_get_obj_region *)cmd.params; + region_desc->base_offset = le64_to_cpu(rsp_params->base_offset); + region_desc->size = le32_to_cpu(rsp_params->size); ++ region_desc->type = rsp_params->type; ++ region_desc->flags = le32_to_cpu(rsp_params->flags); + if (dprc_major_ver > 6 || (dprc_major_ver == 6 && dprc_minor_ver >= 3)) + region_desc->base_address = le64_to_cpu(rsp_params->base_addr); + else +diff --git a/drivers/bus/fsl-mc/fsl-mc-private.h b/drivers/bus/fsl-mc/fsl-mc-private.h +index 85ca5fdee581..c932387641fa 100644 +--- a/drivers/bus/fsl-mc/fsl-mc-private.h ++++ b/drivers/bus/fsl-mc/fsl-mc-private.h +@@ -211,12 +211,13 @@ struct dprc_cmd_get_obj_region { + + struct dprc_rsp_get_obj_region { + /* response word 0 */ +- __le64 pad; ++ __le64 pad0; + /* response word 1 */ + __le64 base_offset; + /* response word 2 */ + __le32 size; +- __le32 pad2; ++ u8 type; ++ u8 pad2[3]; + /* response word 3 */ + __le32 flags; + __le32 pad3; +-- +2.27.0 + + +From 0f49df78f3c29e954e7ecb1b9428075144b103f3 Mon Sep 17 00:00:00 2001 +From: Makarand Pawagi +Date: Tue, 21 Apr 2020 11:25:53 +0530 +Subject: [PATCH 03/44] soc: fsl: enable acpi support for Guts driver + +ACPI support is added in the Guts driver +This is in accordance with the DSDT table added for Guts + +Signed-off-by: Makarand Pawagi +--- + drivers/soc/fsl/guts.c | 27 ++++++++++++++++++++++----- + 1 file changed, 22 insertions(+), 5 deletions(-) + +diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c +index 34810f9bb2ee..56b6797c265e 100644 +--- a/drivers/soc/fsl/guts.c ++++ b/drivers/soc/fsl/guts.c +@@ -3,6 +3,7 @@ + * Freescale QorIQ Platforms GUTS Driver + * + * Copyright (C) 2016 Freescale Semiconductor, Inc. ++ * Copyright 2020 NXP + */ + + #include +@@ -138,7 +139,7 @@ static u32 fsl_guts_get_svr(void) + + static int fsl_guts_probe(struct platform_device *pdev) + { +- struct device_node *np = pdev->dev.of_node; ++ struct device_node *root; + struct device *dev = &pdev->dev; + struct resource *res; + const struct fsl_soc_die_attr *soc_die; +@@ -150,7 +151,8 @@ static int fsl_guts_probe(struct platform_device *pdev) + if (!guts) + return -ENOMEM; + +- guts->little_endian = of_property_read_bool(np, "little-endian"); ++ guts->little_endian = fwnode_property_read_bool(pdev->dev.fwnode, ++ "little-endian"); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + guts->regs = devm_ioremap_resource(dev, res); +@@ -158,9 +160,17 @@ static int fsl_guts_probe(struct platform_device *pdev) + return PTR_ERR(guts->regs); + + /* Register soc device */ +- root = of_find_node_by_path("/"); +- if (of_property_read_string(root, "model", &machine)) +- of_property_read_string_index(root, "compatible", 0, &machine); ++ if (dev_of_node(&pdev->dev)) { ++ root = of_find_node_by_path("/"); ++ if (of_property_read_string(root, "model", &machine)) ++ of_property_read_string_index(root, ++ "compatible", 0, &machine); ++ of_node_put(root); ++ } else { ++ fwnode_property_read_string(pdev->dev.fwnode, ++ "model", &machine); ++ } ++ + if (machine) + soc_dev_attr.machine = machine; + +@@ -234,10 +244,17 @@ static const struct of_device_id fsl_guts_of_match[] = { + }; + MODULE_DEVICE_TABLE(of, fsl_guts_of_match); + ++static const struct acpi_device_id fsl_guts_acpi_match[] = { ++ {"NXP0030", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(acpi, fsl_guts_acpi_match); ++ + static struct platform_driver fsl_guts_driver = { + .driver = { + .name = "fsl-guts", + .of_match_table = fsl_guts_of_match, ++ .acpi_match_table = fsl_guts_acpi_match, + }, + .probe = fsl_guts_probe, + .remove = fsl_guts_remove, +-- +2.27.0 + + +From 243f3dcb60a1ea9bd7e4fa979c2187a169fb069f Mon Sep 17 00:00:00 2001 +From: Meenakshi Aggarwal +Date: Wed, 27 May 2020 21:35:11 +0530 +Subject: [PATCH 04/44] mmc: sdhci-of-esdhc: Add ACPI support + +This patch is to add acpi support in esdhc controller driver + +Signed-off-by: Meenakshi Aggarwal +--- + drivers/mmc/core/core.c | 2 + + drivers/mmc/core/host.c | 30 +++++++++----- + drivers/mmc/host/of_mmc_spi.c | 2 +- + drivers/mmc/host/sdhci-esdhc-imx.c | 2 +- + drivers/mmc/host/sdhci-of-esdhc.c | 64 +++++++++++++++++++----------- + drivers/mmc/host/sdhci-pltfm.c | 1 + + include/linux/mmc/host.h | 2 +- + 7 files changed, 65 insertions(+), 38 deletions(-) + +diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c +index d42037f0f10d..35649b95a860 100644 +--- a/drivers/mmc/core/core.c ++++ b/drivers/mmc/core/core.c +@@ -6,6 +6,8 @@ + * SD support Copyright (C) 2004 Ian Molton, All Rights Reserved. + * Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved. + * MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved. ++ * Copyright 2020 NXP ++ * + */ + #include + #include +diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c +index 96b2ca1f1b06..7cea676d9c48 100644 +--- a/drivers/mmc/core/host.c ++++ b/drivers/mmc/core/host.c +@@ -341,22 +341,31 @@ EXPORT_SYMBOL(mmc_of_parse); + * found, negative errno if the voltage-range specification is invalid, + * or one if the voltage-range is specified and successfully parsed. + */ +-int mmc_of_parse_voltage(struct device_node *np, u32 *mask) ++int mmc_of_parse_voltage(struct device *dev, u32 *mask) + { +- const u32 *voltage_ranges; ++ u32 *voltage_ranges; + int num_ranges, i; + +- voltage_ranges = of_get_property(np, "voltage-ranges", &num_ranges); +- if (!voltage_ranges) { +- pr_debug("%pOF: voltage-ranges unspecified\n", np); +- return 0; +- } +- num_ranges = num_ranges / sizeof(*voltage_ranges) / 2; ++ num_ranges = device_property_read_u32_array(dev, ++ "voltage-ranges", NULL, 0); ++ + if (!num_ranges) { +- pr_err("%pOF: voltage-ranges empty\n", np); ++ pr_err("voltage-ranges empty\n"); + return -EINVAL; + } + ++ voltage_ranges = kcalloc(num_ranges, sizeof(u32), GFP_KERNEL); ++ if (!voltage_ranges) ++ return -ENOMEM; ++ ++ device_property_read_u32_array(dev, "voltage-ranges", ++ voltage_ranges, num_ranges); ++ ++ if (!voltage_ranges) { ++ pr_debug("voltage-ranges unspecified\n"); ++ return 0; ++ } ++ + for (i = 0; i < num_ranges; i++) { + const int j = i * 2; + u32 ocr_mask; +@@ -365,8 +374,7 @@ int mmc_of_parse_voltage(struct device_node *np, u32 *mask) + be32_to_cpu(voltage_ranges[j]), + be32_to_cpu(voltage_ranges[j + 1])); + if (!ocr_mask) { +- pr_err("%pOF: voltage-range #%d is invalid\n", +- np, i); ++ pr_err("voltage-range #%d is invalid\n", i); + return -EINVAL; + } + *mask |= ocr_mask; +diff --git a/drivers/mmc/host/of_mmc_spi.c b/drivers/mmc/host/of_mmc_spi.c +index 3c4d950a4755..3178554a9efc 100644 +--- a/drivers/mmc/host/of_mmc_spi.c ++++ b/drivers/mmc/host/of_mmc_spi.c +@@ -65,7 +65,7 @@ struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi) + if (!oms) + return NULL; + +- if (mmc_of_parse_voltage(np, &oms->pdata.ocr_mask) <= 0) ++ if (mmc_of_parse_voltage(dev, &oms->pdata.ocr_mask) <= 0) + goto err_ocr; + + oms->detect_irq = irq_of_parse_and_map(np, 0); +diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c +index 5d9b3106d2f7..4b846f5741ad 100644 +--- a/drivers/mmc/host/sdhci-esdhc-imx.c ++++ b/drivers/mmc/host/sdhci-esdhc-imx.c +@@ -1502,7 +1502,7 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, + if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line)) + boarddata->delay_line = 0; + +- mmc_of_parse_voltage(np, &host->ocr_mask); ++ mmc_of_parse_voltage(&pdev->dev, &host->ocr_mask); + + if (esdhc_is_usdhc(imx_data)) { + imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl, +diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c +index ab5ab969f711..62a70b39497d 100644 +--- a/drivers/mmc/host/sdhci-of-esdhc.c ++++ b/drivers/mmc/host/sdhci-of-esdhc.c +@@ -10,6 +10,7 @@ + * Anton Vorontsov + */ + ++#include + #include + #include + #include +@@ -73,6 +74,14 @@ static const struct of_device_id sdhci_esdhc_of_match[] = { + }; + MODULE_DEVICE_TABLE(of, sdhci_esdhc_of_match); + ++#ifdef CONFIG_ACPI ++static const struct acpi_device_id sdhci_esdhc_ids[] = { ++ {"NXP0003" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(acpi, sdhci_esdhc_ids); ++#endif ++ + struct sdhci_esdhc { + u8 vendor_ver; + u8 spec_ver; +@@ -1366,29 +1375,35 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host) + esdhc->clk_fixup = match->data; + np = pdev->dev.of_node; + +- if (of_device_is_compatible(np, "fsl,p2020-esdhc")) { +- esdhc->quirk_delay_before_data_reset = true; +- esdhc->quirk_trans_complete_erratum = true; +- } ++ /* in case of device tree, get clock from framework */ ++ if (np) { ++ if (of_device_is_compatible(np, "fsl,p2020-esdhc")) { ++ esdhc->quirk_delay_before_data_reset = true; ++ esdhc->quirk_trans_complete_erratum = true; ++ } + +- clk = of_clk_get(np, 0); +- if (!IS_ERR(clk)) { +- /* +- * esdhc->peripheral_clock would be assigned with a value +- * which is eSDHC base clock when use periperal clock. +- * For some platforms, the clock value got by common clk +- * API is peripheral clock while the eSDHC base clock is +- * 1/2 peripheral clock. +- */ +- if (of_device_is_compatible(np, "fsl,ls1046a-esdhc") || +- of_device_is_compatible(np, "fsl,ls1028a-esdhc") || +- of_device_is_compatible(np, "fsl,ls1088a-esdhc")) +- esdhc->peripheral_clock = clk_get_rate(clk) / 2; +- else +- esdhc->peripheral_clock = clk_get_rate(clk); +- +- clk_put(clk); +- } ++ clk = of_clk_get(np, 0); ++ if (!IS_ERR(clk)) { ++ /* ++ * esdhc->peripheral_clock would be assigned with a value ++ * which is eSDHC base clock when use periperal clock. ++ * For some platforms, the clock value got by common clk ++ * API is peripheral clock while the eSDHC base clock is ++ * 1/2 peripheral clock. ++ */ ++ if (of_device_is_compatible(np, "fsl,ls1046a-esdhc") || ++ of_device_is_compatible(np, "fsl,ls1028a-esdhc") || ++ of_device_is_compatible(np, "fsl,ls1088a-esdhc")) ++ esdhc->peripheral_clock = clk_get_rate(clk) / 2; ++ else ++ esdhc->peripheral_clock = clk_get_rate(clk); ++ ++ clk_put(clk); ++ } ++ } else { ++ device_property_read_u32(&pdev->dev, "clock-frequency", ++ &esdhc->peripheral_clock); ++ } + + esdhc_clock_enable(host, false); + val = sdhci_readl(host, ESDHC_DMA_SYSCTL); +@@ -1421,7 +1436,7 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) + + np = pdev->dev.of_node; + +- if (of_property_read_bool(np, "little-endian")) ++ if (device_property_read_bool(&pdev->dev, "little-endian")) + host = sdhci_pltfm_init(pdev, &sdhci_esdhc_le_pdata, + sizeof(struct sdhci_esdhc)); + else +@@ -1489,7 +1504,7 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) + if (ret) + goto err; + +- mmc_of_parse_voltage(np, &host->ocr_mask); ++ mmc_of_parse_voltage(&pdev->dev, &host->ocr_mask); + + ret = sdhci_add_host(host); + if (ret) +@@ -1506,6 +1521,7 @@ static struct platform_driver sdhci_esdhc_driver = { + .name = "sdhci-esdhc", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .of_match_table = sdhci_esdhc_of_match, ++ .acpi_match_table = sdhci_esdhc_ids, + .pm = &esdhc_of_dev_pm_ops, + }, + .probe = sdhci_esdhc_probe, +diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c +index 328b132bbe57..5053544edf48 100644 +--- a/drivers/mmc/host/sdhci-pltfm.c ++++ b/drivers/mmc/host/sdhci-pltfm.c +@@ -5,6 +5,7 @@ + * + * Copyright (c) 2007, 2011 Freescale Semiconductor, Inc. + * Copyright (c) 2009 MontaVista Software, Inc. ++ * Copyright 2020 NXP + * + * Authors: Xiaobo Xie + * Anton Vorontsov +diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h +index c079b932330f..e26382febc0a 100644 +--- a/include/linux/mmc/host.h ++++ b/include/linux/mmc/host.h +@@ -484,7 +484,7 @@ int mmc_add_host(struct mmc_host *); + void mmc_remove_host(struct mmc_host *); + void mmc_free_host(struct mmc_host *); + int mmc_of_parse(struct mmc_host *host); +-int mmc_of_parse_voltage(struct device_node *np, u32 *mask); ++int mmc_of_parse_voltage(struct device *dev, u32 *mask); + + static inline void *mmc_priv(struct mmc_host *host) + { +-- +2.27.0 + + +From 6e7e988629b1a305e6d5711085952622336e7afe Mon Sep 17 00:00:00 2001 +From: Meharbaan +Date: Tue, 28 Jul 2020 17:41:31 +0530 +Subject: [PATCH 05/44] drivers/mmc/host/sdhci-of-esdhc : Fix DMA coherent + check in ACPI mode. + +DMA-coherent check to set ESDHC_DMA_SNOOP mask was bypassed +when booted in ACPI mode. Now it also checks the acpi device and +its parents for _CCA property in the device, and sets the flag +accordingly. + +Signed-off-by: Meharbaan +--- + drivers/base/property.c | 38 +++++++++++++++++++++++++++++++ + drivers/mmc/host/sdhci-of-esdhc.c | 7 ++++-- + include/linux/property.h | 2 ++ + 3 files changed, 45 insertions(+), 2 deletions(-) + +diff --git a/drivers/base/property.c b/drivers/base/property.c +index 4c43d30145c6..c9d76e55a226 100644 +--- a/drivers/base/property.c ++++ b/drivers/base/property.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + + struct fwnode_handle *dev_fwnode(struct device *dev) + { +@@ -837,6 +838,43 @@ enum dev_dma_attr device_get_dma_attr(struct device *dev) + } + EXPORT_SYMBOL_GPL(device_get_dma_attr); + ++/** ++ * device_match_fw_node - Check if the device is the parent node. ++ * @dev: Pointer to the device. ++ * @parent_fwnode Pointer to the parent's firmware node. ++ * ++ * The function returns true if the device has no parent. ++ * ++ */ ++static int device_match_fw_node(struct device *dev, const void *parent_fwnode) ++{ ++ return dev->fwnode == parent_fwnode; ++} ++ ++/** ++ * dev_dma_is_coherent - Check if the device or any of its parents has ++ * dma support enabled. ++ * @dev: Pointer to the device. ++ * ++ * The function gets the device pointer and check for device_dma_supported() ++ * on the device pointer passed and then recursively on its parent nodes. ++ */ ++ ++bool dev_dma_is_coherent(struct device *dev) ++{ ++ struct fwnode_handle *parent_fwnode; ++ ++ while (dev) { ++ if (device_dma_supported(dev)) ++ return true; ++ parent_fwnode = fwnode_get_next_parent(dev->fwnode); ++ dev = bus_find_device(&platform_bus_type, NULL, parent_fwnode, ++ device_match_fw_node); ++ } ++ return false; ++} ++EXPORT_SYMBOL_GPL(dev_dma_is_coherent); ++ + /** + * fwnode_get_phy_mode - Get phy mode for given firmware node + * @fwnode: Pointer to the given node +diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c +index 62a70b39497d..bb7e456ad17b 100644 +--- a/drivers/mmc/host/sdhci-of-esdhc.c ++++ b/drivers/mmc/host/sdhci-of-esdhc.c +@@ -541,8 +541,11 @@ static int esdhc_of_enable_dma(struct sdhci_host *host) + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40)); + + value = sdhci_readl(host, ESDHC_DMA_SYSCTL); +- +- if (of_dma_is_coherent(dev->of_node)) ++ /* ++ * of_dma_is_coherent() returns false in case of acpi hence ++ * dev_dma_is_coherent() is used along with it. ++ */ ++ if (of_dma_is_coherent(dev->of_node) || dev_dma_is_coherent(dev)) + value |= ESDHC_DMA_SNOOP; + else + value &= ~ESDHC_DMA_SNOOP; +diff --git a/include/linux/property.h b/include/linux/property.h +index 2d4542629d80..3e0a1a5655f0 100644 +--- a/include/linux/property.h ++++ b/include/linux/property.h +@@ -379,6 +379,8 @@ bool device_dma_supported(struct device *dev); + + enum dev_dma_attr device_get_dma_attr(struct device *dev); + ++bool dev_dma_is_coherent(struct device *dev); ++ + const void *device_get_match_data(struct device *dev); + + int device_get_phy_mode(struct device *dev); +-- +2.27.0 + + +From 287dea1fbd948fd84964d92ca7947245fe5473d8 Mon Sep 17 00:00:00 2001 +From: Shameer Kolothum +Date: Thu, 19 Nov 2020 12:11:43 +0000 +Subject: [PATCH 06/44] ACPICA: IORT: Update for revision E +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +IORT revision E contains a few additions like, +    -Added an identifier field in the node descriptors to aid table +     cross-referencing. +    -Introduced the Reserved Memory Range(RMR) node. This is used +     to describe memory ranges that are used by endpoints and requires +     a unity mapping in SMMU. + -Introduced a flag in the RC node to express support for PRI. + +Signed-off-by: Shameer Kolothum +--- + include/acpi/actbl2.h | 25 +++++++++++++++++++------ + 1 file changed, 19 insertions(+), 6 deletions(-) + +diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h +index ec66779cb193..274fce7b5c01 100644 +--- a/include/acpi/actbl2.h ++++ b/include/acpi/actbl2.h +@@ -68,7 +68,7 @@ + * IORT - IO Remapping Table + * + * Conforms to "IO Remapping Table System Software on ARM Platforms", +- * Document number: ARM DEN 0049D, March 2018 ++ * Document number: ARM DEN 0049E, June 2020 + * + ******************************************************************************/ + +@@ -86,7 +86,8 @@ struct acpi_iort_node { + u8 type; + u16 length; + u8 revision; +- u32 reserved; ++ u16 reserved; ++ u16 identifier; + u32 mapping_count; + u32 mapping_offset; + char node_data[1]; +@@ -100,7 +101,8 @@ enum acpi_iort_node_type { + ACPI_IORT_NODE_PCI_ROOT_COMPLEX = 0x02, + ACPI_IORT_NODE_SMMU = 0x03, + ACPI_IORT_NODE_SMMU_V3 = 0x04, +- ACPI_IORT_NODE_PMCG = 0x05 ++ ACPI_IORT_NODE_PMCG = 0x05, ++ ACPI_IORT_NODE_RMR = 0x06, + }; + + struct acpi_iort_id_mapping { +@@ -167,10 +169,10 @@ struct acpi_iort_root_complex { + u8 reserved[3]; /* Reserved, must be zero */ + }; + +-/* Values for ats_attribute field above */ ++/* Masks for ats_attribute field above */ + +-#define ACPI_IORT_ATS_SUPPORTED 0x00000001 /* The root complex supports ATS */ +-#define ACPI_IORT_ATS_UNSUPPORTED 0x00000000 /* The root complex doesn't support ATS */ ++#define ACPI_IORT_ATS_SUPPORTED (1) /* The root complex supports ATS */ ++#define ACPI_IORT_PRI_SUPPORTED (1<<1) /* The root complex supports PRI */ + + struct acpi_iort_smmu { + u64 base_address; /* SMMU base address */ +@@ -241,6 +243,17 @@ struct acpi_iort_pmcg { + u64 page1_base_address; + }; + ++struct acpi_iort_rmr { ++ u32 rmr_count; ++ u32 rmr_offset; ++}; ++ ++struct acpi_iort_rmr_desc { ++ u64 base_address; ++ u64 length; ++ u32 reserved; ++}; ++ + /******************************************************************************* + * + * IVRS - I/O Virtualization Reporting Structure +-- +2.27.0 + + +From a4225192809e805517c41af17a5cc0073ce7c600 Mon Sep 17 00:00:00 2001 +From: Shameer Kolothum +Date: Thu, 19 Nov 2020 12:11:44 +0000 +Subject: [PATCH 07/44] ACPI/IORT: Add support for RMR node parsing +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add support for parsing RMR node information from ACPI. +Find associated stream ids and smmu node info from the +RMR node and populate a linked list with RMR memory +descriptors. + +Signed-off-by: Shameer Kolothum +--- + drivers/acpi/arm64/iort.c | 122 +++++++++++++++++++++++++++++++++++++- + 1 file changed, 121 insertions(+), 1 deletion(-) + +diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c +index 2494138a6905..69a20d7e0c84 100644 +--- a/drivers/acpi/arm64/iort.c ++++ b/drivers/acpi/arm64/iort.c +@@ -40,6 +40,25 @@ struct iort_fwnode { + static LIST_HEAD(iort_fwnode_list); + static DEFINE_SPINLOCK(iort_fwnode_lock); + ++struct iort_rmr_id { ++ u32 sid; ++ struct acpi_iort_node *smmu; ++}; ++ ++/* ++ * One entry for IORT RMR. ++ */ ++struct iort_rmr_entry { ++ struct list_head list; ++ ++ unsigned int rmr_ids_num; ++ struct iort_rmr_id *rmr_ids; ++ ++ struct acpi_iort_rmr_desc *rmr_desc; ++}; ++ ++static LIST_HEAD(iort_rmr_list); /* list of RMR regions from ACPI */ ++ + /** + * iort_set_fwnode() - Create iort_fwnode and use it to register + * iommu data in the iort_fwnode_list +@@ -393,7 +412,8 @@ static struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node, + if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT || + node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX || + node->type == ACPI_IORT_NODE_SMMU_V3 || +- node->type == ACPI_IORT_NODE_PMCG) { ++ node->type == ACPI_IORT_NODE_PMCG || ++ node->type == ACPI_IORT_NODE_RMR) { + *id_out = map->output_base; + return parent; + } +@@ -1659,6 +1679,103 @@ static void __init iort_enable_acs(struct acpi_iort_node *iort_node) + #else + static inline void iort_enable_acs(struct acpi_iort_node *iort_node) { } + #endif ++static int iort_rmr_desc_valid(struct acpi_iort_rmr_desc *desc) ++{ ++ struct iort_rmr_entry *e; ++ u64 end, start = desc->base_address, length = desc->length; ++ ++ if (!IS_ALIGNED(start, SZ_64K) || !IS_ALIGNED(length, SZ_64K)) ++ return -EINVAL; ++ ++ end = start + length - 1; ++ ++ /* Check for address overlap */ ++ list_for_each_entry(e, &iort_rmr_list, list) { ++ u64 e_start = e->rmr_desc->base_address; ++ u64 e_end = e_start + e->rmr_desc->length - 1; ++ ++ if (start <= e_end && end >= e_start) ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int __init iort_parse_rmr(struct acpi_iort_node *iort_node) ++{ ++ struct iort_rmr_id *rmr_ids, *ids; ++ struct iort_rmr_entry *e; ++ struct acpi_iort_rmr *rmr; ++ struct acpi_iort_rmr_desc *rmr_desc; ++ u32 map_count = iort_node->mapping_count; ++ int i, ret = 0, desc_count = 0; ++ ++ if (iort_node->type != ACPI_IORT_NODE_RMR) ++ return 0; ++ ++ if (!iort_node->mapping_offset || !map_count) { ++ pr_err(FW_BUG "Invalid ID mapping, skipping RMR node %p\n", ++ iort_node); ++ return -EINVAL; ++ } ++ ++ rmr_ids = kmalloc(sizeof(*rmr_ids) * map_count, GFP_KERNEL); ++ if (!rmr_ids) ++ return -ENOMEM; ++ ++ /* Retrieve associated smmu and stream id */ ++ ids = rmr_ids; ++ for (i = 0; i < map_count; i++, ids++) { ++ ids->smmu = iort_node_get_id(iort_node, &ids->sid, i); ++ if (!ids->smmu) { ++ pr_err(FW_BUG "Invalid SMMU reference, skipping RMR node %p\n", ++ iort_node); ++ ret = -EINVAL; ++ goto out; ++ } ++ } ++ ++ /* Retrieve RMR data */ ++ rmr = (struct acpi_iort_rmr *)iort_node->node_data; ++ if (!rmr->rmr_offset || !rmr->rmr_count) { ++ pr_err(FW_BUG "Invalid RMR descriptor array, skipping RMR node %p\n", ++ iort_node); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ rmr_desc = ACPI_ADD_PTR(struct acpi_iort_rmr_desc, iort_node, ++ rmr->rmr_offset); ++ ++ for (i = 0; i < rmr->rmr_count; i++, rmr_desc++) { ++ ret = iort_rmr_desc_valid(rmr_desc); ++ if (ret) { ++ pr_err(FW_BUG "Invalid RMR descriptor[%d] for node %p, skipping...\n", ++ i, iort_node); ++ goto out; ++ } ++ ++ e = kmalloc(sizeof(*e), GFP_KERNEL); ++ if (!e) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ e->rmr_ids_num = map_count; ++ e->rmr_ids = rmr_ids; ++ e->rmr_desc = rmr_desc; ++ ++ list_add_tail(&e->list, &iort_rmr_list); ++ desc_count++; ++ } ++ ++ return 0; ++ ++out: ++ if (!desc_count) ++ kfree(rmr_ids); ++ return ret; ++} + + static void __init iort_init_platform_devices(void) + { +@@ -1688,6 +1805,9 @@ static void __init iort_init_platform_devices(void) + + iort_enable_acs(iort_node); + ++ if (iort_table->revision == 1) ++ iort_parse_rmr(iort_node); ++ + ops = iort_get_dev_cfg(iort_node); + if (ops) { + fwnode = acpi_alloc_fwnode_static(); +-- +2.27.0 + + +From dc79d5283e1e9fc0e2d002c06c898f98765d1c4d Mon Sep 17 00:00:00 2001 +From: Shameer Kolothum +Date: Thu, 19 Nov 2020 12:11:45 +0000 +Subject: [PATCH 08/44] iommu/dma: Introduce generic helper to retrieve RMR + info + +Reserved Memory Regions(RMR) associated with an IOMMU may be +described either through ACPI tables or DT in systems with +devices that require a unity mapping or bypass for those +regions in IOMMU drivers. + +Introduce a generic interface so that IOMMU drivers can retrieve +and set up necessary mappings. + +Signed-off-by: Shameer Kolothum +--- + drivers/iommu/dma-iommu.c | 36 ++++++++++++++++++++++++++++++++++++ + include/linux/dma-iommu.h | 7 +++++++ + include/linux/iommu.h | 16 ++++++++++++++++ + 3 files changed, 59 insertions(+) + +diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c +index 0cbcd3fc3e7e..d73768ecdd1a 100644 +--- a/drivers/iommu/dma-iommu.c ++++ b/drivers/iommu/dma-iommu.c +@@ -166,6 +166,42 @@ void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list) + } + EXPORT_SYMBOL(iommu_dma_get_resv_regions); + ++/** ++ * iommu_dma_get_rmrs - Retrieve Reserved Memory Regions(RMRs) associated ++ * with a given IOMMU ++ * @iommu_fwnode: fwnode associated with IOMMU ++ * @list: RMR list to be populated ++ * ++ */ ++int iommu_dma_get_rmrs(struct fwnode_handle *iommu_fwnode, ++ struct list_head *list) ++{ ++ return 0; ++} ++EXPORT_SYMBOL(iommu_dma_get_rmrs); ++ ++struct iommu_rmr *iommu_dma_alloc_rmr(u64 base, u64 length, ++ u32 *ids, int num_ids) ++{ ++ struct iommu_rmr *rmr; ++ int i; ++ ++ rmr = kzalloc(struct_size(rmr, ids, num_ids), GFP_KERNEL); ++ if (!rmr) ++ return NULL; ++ ++ INIT_LIST_HEAD(&rmr->list); ++ rmr->base_address = base; ++ rmr->length = length; ++ rmr->num_ids = num_ids; ++ ++ for (i = 0; i < num_ids; i++) ++ rmr->ids[i] = ids[i]; ++ ++ return rmr; ++} ++EXPORT_SYMBOL(iommu_dma_alloc_rmr); ++ + static int cookie_init_hw_msi_region(struct iommu_dma_cookie *cookie, + phys_addr_t start, phys_addr_t end) + { +diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h +index 2112f21f73d8..f502855e4da0 100644 +--- a/include/linux/dma-iommu.h ++++ b/include/linux/dma-iommu.h +@@ -37,6 +37,9 @@ void iommu_dma_compose_msi_msg(struct msi_desc *desc, + + void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list); + ++int iommu_dma_get_rmrs(struct fwnode_handle *iommu, struct list_head *list); ++struct iommu_rmr *iommu_dma_alloc_rmr(u64 base, u64 length, ++ u32 *ids, int num_ids); + #else /* CONFIG_IOMMU_DMA */ + + struct iommu_domain; +@@ -78,5 +81,9 @@ static inline void iommu_dma_get_resv_regions(struct device *dev, struct list_he + { + } + ++int iommu_dma_get_rmrs(struct fwnode_handle *iommu, struct list_head *list) ++{ ++ return 0; ++} + #endif /* CONFIG_IOMMU_DMA */ + #endif /* __DMA_IOMMU_H */ +diff --git a/include/linux/iommu.h b/include/linux/iommu.h +index f11f5072af5d..d76a6da8df0b 100644 +--- a/include/linux/iommu.h ++++ b/include/linux/iommu.h +@@ -592,6 +592,22 @@ struct iommu_sva { + struct device *dev; + }; + ++/** ++ * struct iommu_rmr - Reserved Memory Region details per IOMMU ++ * @list: Linked list pointers to hold RMR region info ++ * @base_address: base address of Reserved Memory Region ++ * @length: length of memory region ++ * @num_ids: number of associated device IDs ++ * @ids: associated device IDs ++ */ ++struct iommu_rmr { ++ struct list_head list; ++ phys_addr_t base_address; ++ u64 length; ++ unsigned int num_ids; ++ u32 ids[]; ++}; ++ + int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode, + const struct iommu_ops *ops); + void iommu_fwspec_free(struct device *dev); +-- +2.27.0 + + +From 67844f76ed8feae4d831f191efd3b7a3384b4f16 Mon Sep 17 00:00:00 2001 +From: Shameer Kolothum +Date: Thu, 19 Nov 2020 12:11:46 +0000 +Subject: [PATCH 09/44] ACPI/IORT: Add RMR memory regions reservation helper + +Add a helper function that retrieves RMR memory descriptors +associated with a given IOMMU. This will be used by IOMMU +drivers to setup necessary mappings. + +Now that we have this, invoke this from the generic helper +interface. + +Signed-off-by: Shameer Kolothum +--- + drivers/acpi/arm64/iort.c | 60 +++++++++++++++++++++++++++++++++++++++ + drivers/iommu/dma-iommu.c | 3 ++ + include/linux/acpi_iort.h | 7 +++++ + 3 files changed, 70 insertions(+) + +diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c +index 69a20d7e0c84..8f86779d283a 100644 +--- a/drivers/acpi/arm64/iort.c ++++ b/drivers/acpi/arm64/iort.c +@@ -12,6 +12,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -843,6 +844,63 @@ static inline int iort_add_device_replay(struct device *dev) + return err; + } + ++/** ++ * iort_iommu_get_rmrs - Helper to retrieve RMR info associated with IOMMU ++ * @iommu: fwnode for the IOMMU ++ * @head: RMR list head to be populated ++ * ++ * Returns: 0 on success, <0 failure ++ */ ++int iort_iommu_get_rmrs(struct fwnode_handle *iommu_fwnode, ++ struct list_head *head) ++{ ++ struct iort_rmr_entry *e; ++ struct acpi_iort_node *iommu; ++ ++ iommu = iort_get_iort_node(iommu_fwnode); ++ if (!iommu) ++ return 0; ++ ++ list_for_each_entry(e, &iort_rmr_list, list) { ++ struct iort_rmr_id *rmr_ids = e->rmr_ids; ++ struct acpi_iort_rmr_desc *rmr_desc; ++ struct iommu_rmr *rmr; ++ u32 *ids, num_ids = 0; ++ int i, j = 0; ++ ++ for (i = 0; i < e->rmr_ids_num; i++) { ++ if (rmr_ids[i].smmu == iommu) ++ num_ids++; ++ } ++ ++ if (!num_ids) ++ continue; ++ ++ ids = kmalloc_array(num_ids, sizeof(*ids), GFP_KERNEL); ++ if (!ids) ++ return -ENOMEM; ++ ++ for (i = 0; i < e->rmr_ids_num; i++) { ++ if (rmr_ids[i].smmu == iommu) ++ ids[j++] = rmr_ids[i].sid; ++ } ++ ++ rmr_desc = e->rmr_desc; ++ rmr = iommu_dma_alloc_rmr(rmr_desc->base_address, ++ rmr_desc->length, ++ ids, num_ids); ++ if (!rmr) { ++ kfree(ids); ++ return -ENOMEM; ++ } ++ ++ list_add_tail(&rmr->list, head); ++ kfree(ids); ++ } ++ ++ return 0; ++} ++ + /** + * iort_iommu_msi_get_resv_regions - Reserved region driver helper + * @dev: Device from iommu_get_resv_regions() +@@ -1113,6 +1171,8 @@ int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head) + const struct iommu_ops *iort_iommu_configure_id(struct device *dev, + const u32 *input_id) + { return NULL; } ++int iort_iommu_get_rmrs(struct fwnode_handle *fwnode, struct list_head *head) ++{ return 0; } + #endif + + static int nc_dma_get_range(struct device *dev, u64 *size) +diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c +index d73768ecdd1a..aa8304e50786 100644 +--- a/drivers/iommu/dma-iommu.c ++++ b/drivers/iommu/dma-iommu.c +@@ -176,6 +176,9 @@ EXPORT_SYMBOL(iommu_dma_get_resv_regions); + int iommu_dma_get_rmrs(struct fwnode_handle *iommu_fwnode, + struct list_head *list) + { ++ if (!is_of_node(iommu_fwnode)) ++ return iort_iommu_get_rmrs(iommu_fwnode, list); ++ + return 0; + } + EXPORT_SYMBOL(iommu_dma_get_rmrs); +diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h +index 1a12baa58e40..c837fc6e95fe 100644 +--- a/include/linux/acpi_iort.h ++++ b/include/linux/acpi_iort.h +@@ -39,6 +39,8 @@ const struct iommu_ops *iort_iommu_configure_id(struct device *dev, + const u32 *id_in); + int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head); + phys_addr_t acpi_iort_dma_get_max_cpu_address(void); ++int iort_iommu_get_rmrs(struct fwnode_handle *iommu_fwnode, ++ struct list_head *list); + #else + static inline void acpi_iort_init(void) { } + static inline u32 iort_msi_map_id(struct device *dev, u32 id) +@@ -59,6 +61,10 @@ int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head) + + static inline phys_addr_t acpi_iort_dma_get_max_cpu_address(void) + { return PHYS_ADDR_MAX; } ++static inline ++int iort_iommu_get_rmrs(struct fwnode_handle *iommu_fwnode, ++ struct list_head *list) ++{ return 0; } + #endif + + #endif /* __ACPI_IORT_H__ */ +-- +2.27.0 + + +From 9479d764bc71f0a60f7861359f477af08a4082a1 Mon Sep 17 00:00:00 2001 +From: Shameer Kolothum +Date: Thu, 19 Nov 2020 12:11:47 +0000 +Subject: [PATCH 10/44] iommu/arm-smmu-v3: Introduce strtab init helper + +Introduce a helper to check the sid range and to init the l2 strtab +entries(bypass). This will be useful when we have to initialize the +l2 strtab for RMR SIDs. + +Signed-off-by: Shameer Kolothum +--- + drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 26 ++++++++++++--------- + 1 file changed, 15 insertions(+), 11 deletions(-) + +diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +index 7067b7c11626..f8f596c5c086 100644 +--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c ++++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +@@ -2308,6 +2308,19 @@ static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid) + + static struct iommu_ops arm_smmu_ops; + ++static int arm_smmu_init_sid_strtab(struct arm_smmu_device *smmu, u32 sid) ++{ ++ /* Check the SIDs are in range of the SMMU and our stream table */ ++ if (!arm_smmu_sid_in_range(smmu, sid)) ++ return -ERANGE; ++ ++ /* Ensure l2 strtab is initialised */ ++ if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) ++ return arm_smmu_init_l2_strtab(smmu, sid); ++ ++ return 0; ++} ++ + static struct iommu_device *arm_smmu_probe_device(struct device *dev) + { + int i, ret; +@@ -2336,21 +2349,12 @@ static struct iommu_device *arm_smmu_probe_device(struct device *dev) + INIT_LIST_HEAD(&master->bonds); + dev_iommu_priv_set(dev, master); + +- /* Check the SIDs are in range of the SMMU and our stream table */ + for (i = 0; i < master->num_sids; i++) { + u32 sid = master->sids[i]; + +- if (!arm_smmu_sid_in_range(smmu, sid)) { +- ret = -ERANGE; ++ ret = arm_smmu_init_sid_strtab(smmu, sid); ++ if (ret) + goto err_free_master; +- } +- +- /* Ensure l2 strtab is initialised */ +- if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) { +- ret = arm_smmu_init_l2_strtab(smmu, sid); +- if (ret) +- goto err_free_master; +- } + } + + master->ssid_bits = min(smmu->ssid_bits, fwspec->num_pasid_bits); +-- +2.27.0 + + +From d7e16ba5576168effd83fa2ba11204492b9b0c2a Mon Sep 17 00:00:00 2001 +From: Shameer Kolothum +Date: Thu, 19 Nov 2020 12:11:48 +0000 +Subject: [PATCH 11/44] =?UTF-8?q?iommu/arm-smmu-v3:=20Add=20bypass=20flag?= + =?UTF-8?q?=20to=C2=A0arm=5Fsmmu=5Fwrite=5Fstrtab=5Fent()?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +By default, disable_bypass is set and any dev without an iommu domain +installs STE with CFG_ABORT during arm_smmu_init_bypass_stes(). Introduce +a "bypass" flag to arm_smmu_write_strtab_ent() so that we can force it to +install CFG_BYPASS STE for specific SIDs. This will be useful for RMR +related SIDs. + +Signed-off-by: Shameer Kolothum +--- + drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +index f8f596c5c086..8988c63e7c00 100644 +--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c ++++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +@@ -1174,7 +1174,7 @@ static void arm_smmu_sync_ste_for_sid(struct arm_smmu_device *smmu, u32 sid) + } + + static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, +- __le64 *dst) ++ __le64 *dst, bool bypass) + { + /* + * This is hideously complicated, but we only really care about +@@ -1245,7 +1245,7 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, + + /* Bypass/fault */ + if (!smmu_domain || !(s1_cfg || s2_cfg)) { +- if (!smmu_domain && disable_bypass) ++ if (!smmu_domain && disable_bypass && !bypass) + val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_ABORT); + else + val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS); +@@ -1317,7 +1317,7 @@ static void arm_smmu_init_bypass_stes(__le64 *strtab, unsigned int nent) + unsigned int i; + + for (i = 0; i < nent; ++i) { +- arm_smmu_write_strtab_ent(NULL, -1, strtab); ++ arm_smmu_write_strtab_ent(NULL, -1, strtab, false); + strtab += STRTAB_STE_DWORDS; + } + } +@@ -2038,7 +2038,7 @@ static void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master) + if (j < i) + continue; + +- arm_smmu_write_strtab_ent(master, sid, step); ++ arm_smmu_write_strtab_ent(master, sid, step, false); + } + } + +-- +2.27.0 + + +From eb04e7b17294dce306a446662f8f3a1b313d27ba Mon Sep 17 00:00:00 2001 +From: Shameer Kolothum +Date: Thu, 19 Nov 2020 12:11:49 +0000 +Subject: [PATCH 12/44] iommu/arm-smmu-v3: Get associated RMR info and install + bypass STE +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Check if there is any RMR info associated with the devices behind +the SMMUv3 and if any, install bypass STEs for them. This is to +keep any ongoing traffic associated with these devices alive +when we enable/reset SMMUv3 during probe(). + +Signed-off-by: Shameer Kolothum +--- + drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 40 +++++++++++++++++++++ + drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 2 ++ + 2 files changed, 42 insertions(+) + +diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +index 8988c63e7c00..00f19d37a3df 100644 +--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c ++++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +@@ -3486,6 +3486,42 @@ static void __iomem *arm_smmu_ioremap(struct device *dev, resource_size_t start, + return devm_ioremap_resource(dev, &res); + } + ++static void arm_smmu_rmr_install_bypass_ste(struct arm_smmu_device *smmu) ++{ ++ struct iommu_rmr *e; ++ int i, ret; ++ ++ /* ++ * Since, we don't have a mechanism to differentiate the RMR ++ * SIDs that has an ongoing live stream, install bypass STEs ++ * for all the reported ones.  ++ * FixMe: Avoid duplicate SIDs in the list as one sid may ++ * associate with multiple RMRs. ++ */ ++ list_for_each_entry(e, &smmu->rmr_list, list) { ++ for (i = 0; i < e->num_ids; i++) { ++ __le64 *step; ++ u32 sid = e->ids[i]; ++ ++ ret = arm_smmu_init_sid_strtab(smmu, sid); ++ if (ret) { ++ dev_err(smmu->dev, "RMR bypass(0x%x) failed\n", ++ sid); ++ continue; ++ } ++ ++ step = arm_smmu_get_step_for_sid(smmu, sid); ++ arm_smmu_write_strtab_ent(NULL, sid, step, true); ++ } ++ } ++} ++ ++static int arm_smmu_get_rmr(struct arm_smmu_device *smmu) ++{ ++ INIT_LIST_HEAD(&smmu->rmr_list); ++ return iommu_dma_get_rmrs(dev_fwnode(smmu->dev), &smmu->rmr_list); ++} ++ + static int arm_smmu_device_probe(struct platform_device *pdev) + { + int irq, ret; +@@ -3569,6 +3605,10 @@ static int arm_smmu_device_probe(struct platform_device *pdev) + /* Record our private device structure */ + platform_set_drvdata(pdev, smmu); + ++ /* Check for RMRs and install bypass STEs if any */ ++ if (!arm_smmu_get_rmr(smmu)) ++ arm_smmu_rmr_install_bypass_ste(smmu); ++ + /* Reset the device */ + ret = arm_smmu_device_reset(smmu, bypass); + if (ret) +diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +index d4b7f40ccb02..17b517ddecee 100644 +--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h ++++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +@@ -636,6 +636,8 @@ struct arm_smmu_device { + + /* IOMMU core code handle */ + struct iommu_device iommu; ++ ++ struct list_head rmr_list; + }; + + /* SMMU private data for each master */ +-- +2.27.0 + + +From c9fa17c72291b8dda67cee25a45de65283391483 Mon Sep 17 00:00:00 2001 +From: Shameer Kolothum +Date: Thu, 19 Nov 2020 12:11:50 +0000 +Subject: [PATCH 13/44] =?UTF-8?q?iommu/arm-smmu-v3:=20Reserve=20any=20RMR?= + =?UTF-8?q?=20regions=20associated=20with=20a=C2=A0dev?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Get RMR regions associated with a dev reserved so that there is +a unity mapping for them in SMMU. + +Signed-off-by: Shameer Kolothum +--- + drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 38 +++++++++++++++++++++ + 1 file changed, 38 insertions(+) + +diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +index 00f19d37a3df..23c0c4d5ad09 100644 +--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c ++++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +@@ -2492,6 +2492,43 @@ static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args) + return iommu_fwspec_add_ids(dev, args->args, 1); + } + ++static bool arm_smmu_dev_has_rmr(struct arm_smmu_master *master, ++ struct iommu_rmr *e) ++{ ++ int i, j; ++ ++ for (i = 0; i < master->num_sids; i++) { ++ for (j = 0; j < e->num_ids; j++) { ++ if (e->ids[j] == master->sids[i]) ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++static void arm_smmu_rmr_get_resv_regions(struct device *dev, ++ struct list_head *head) ++{ ++ struct arm_smmu_master *master = dev_iommu_priv_get(dev); ++ struct arm_smmu_device *smmu = master->smmu; ++ struct iommu_rmr *rmr; ++ ++ list_for_each_entry(rmr, &smmu->rmr_list, list) { ++ struct iommu_resv_region *region; ++ int prot = IOMMU_READ | IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO; ++ ++ if (!arm_smmu_dev_has_rmr(master, rmr)) ++ continue; ++ region = iommu_alloc_resv_region(rmr->base_address, ++ rmr->length, prot, ++ IOMMU_RESV_DIRECT); ++ if (!region) ++ return; ++ ++ list_add_tail(®ion->list, head); ++ } ++} + static void arm_smmu_get_resv_regions(struct device *dev, + struct list_head *head) + { +@@ -2506,6 +2543,7 @@ static void arm_smmu_get_resv_regions(struct device *dev, + list_add_tail(®ion->list, head); + + iommu_dma_get_resv_regions(dev, head); ++ arm_smmu_rmr_get_resv_regions(dev, head); + } + + static bool arm_smmu_dev_has_feature(struct device *dev, +-- +2.27.0 + + +From 1a4c374e2cf1ae43fb8638cb9b8ebb918e1794a1 Mon Sep 17 00:00:00 2001 +From: Jon Nettleton +Date: Fri, 18 Dec 2020 05:04:43 -0500 +Subject: [PATCH 14/44] iommu/arm-smmu: Get associated RMR info and install + bypass SMR +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Check if there is any RMR info associated with the devices behind +the SMMU and if any, install bypass SMRs for them. This is to +keep any ongoing traffic associated with these devices alive +when we enable/reset SMMU during probe(). + +Signed-off-by: Jon Nettleton +--- + drivers/iommu/arm/arm-smmu/arm-smmu.c | 44 +++++++++++++++++++++++++++ + drivers/iommu/arm/arm-smmu/arm-smmu.h | 2 ++ + 2 files changed, 46 insertions(+) + +diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c +index bcbacf22331d..338850966fe8 100644 +--- a/drivers/iommu/arm/arm-smmu/arm-smmu.c ++++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c +@@ -2095,6 +2095,45 @@ err_reset_platform_ops: __maybe_unused; + return err; + } + ++static void arm_smmu_rmr_install_bypass_smr(struct arm_smmu_device *smmu) ++{ ++ struct iommu_rmr *e; ++ int i, j, cnt = 0; ++ u32 smr; ++ ++ for (i = 0; i < smmu->num_mapping_groups; i++) { ++ smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(i)); ++ if (!FIELD_GET(ARM_SMMU_SMR_VALID, smr)) ++ continue; ++ ++ list_for_each_entry(e, &smmu->rmr_list, list) { ++ for (j = 0; j < e->num_ids; j++) { ++ if (FIELD_GET(ARM_SMMU_SMR_ID, smr) != e->ids[j]) ++ continue; ++ ++ smmu->smrs[i].id = FIELD_GET(ARM_SMMU_SMR_ID, smr); ++ smmu->smrs[i].mask = FIELD_GET(ARM_SMMU_SMR_MASK, smr); ++ smmu->smrs[i].valid = true; ++ ++ smmu->s2crs[i].type = S2CR_TYPE_BYPASS; ++ smmu->s2crs[i].privcfg = S2CR_PRIVCFG_DEFAULT; ++ smmu->s2crs[i].cbndx = 0xff; ++ ++ cnt++; ++ } ++ } ++ } ++ ++ dev_notice(smmu->dev, "\tpreserved %d boot mapping%s\n", cnt, ++ cnt == 1 ? "" : "s"); ++} ++ ++static int arm_smmu_get_rmr(struct arm_smmu_device *smmu) ++{ ++ INIT_LIST_HEAD(&smmu->rmr_list); ++ return iommu_dma_get_rmrs(dev_fwnode(smmu->dev), &smmu->rmr_list); ++} ++ + static int arm_smmu_device_probe(struct platform_device *pdev) + { + struct resource *res; +@@ -2224,6 +2263,11 @@ static int arm_smmu_device_probe(struct platform_device *pdev) + } + + platform_set_drvdata(pdev, smmu); ++ ++ /* Check for RMRs and install bypass SMRs if any */ ++ if (!arm_smmu_get_rmr(smmu)) ++ arm_smmu_rmr_install_bypass_smr(smmu); ++ + arm_smmu_device_reset(smmu); + arm_smmu_test_smr_masks(smmu); + +diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.h b/drivers/iommu/arm/arm-smmu/arm-smmu.h +index b71647eaa319..a3c3442e7d4c 100644 +--- a/drivers/iommu/arm/arm-smmu/arm-smmu.h ++++ b/drivers/iommu/arm/arm-smmu/arm-smmu.h +@@ -325,6 +325,8 @@ struct arm_smmu_device { + + /* IOMMU core code handle */ + struct iommu_device iommu; ++ ++ struct list_head rmr_list; + }; + + enum arm_smmu_context_fmt { +-- +2.27.0 + + +From 27aae2dfb64e6f5d0263c28bc6001e84cd857253 Mon Sep 17 00:00:00 2001 +From: Jon Nettleton +Date: Fri, 18 Dec 2020 05:06:31 -0500 +Subject: [PATCH 15/44] =?UTF-8?q?iommu/arm-smmu:=20Reserve=20any=20RMR=20r?= + =?UTF-8?q?egions=20associated=20with=20a=C2=A0dev?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Get RMR regions associated with a dev reserved so that there is +a unity mapping for them in SMMU. + +Signed-off-by: Jon Nettleton +--- + drivers/iommu/arm/arm-smmu/arm-smmu.c | 43 +++++++++++++++++++++++++++ + 1 file changed, 43 insertions(+) + +diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c +index 338850966fe8..183960395579 100644 +--- a/drivers/iommu/arm/arm-smmu/arm-smmu.c ++++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c +@@ -1584,6 +1584,48 @@ static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args) + return iommu_fwspec_add_ids(dev, &fwid, 1); + } + ++static bool arm_smmu_dev_has_rmr(struct arm_smmu_master_cfg *cfg, ++ struct iommu_fwspec *fwspec, ++ struct iommu_rmr *e) ++{ ++ struct arm_smmu_device *smmu = cfg->smmu; ++ struct arm_smmu_smr *smrs = smmu->smrs; ++ int i, idx, j; ++ ++ for_each_cfg_sme(cfg, fwspec, i, idx) { ++ for (j = 0; j < e->num_ids; j++) { ++ if (e->ids[j] == smrs[idx].id) ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++static void arm_smmu_rmr_get_resv_regions(struct device *dev, ++ struct list_head *head) ++{ ++ struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev); ++ struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); ++ struct arm_smmu_device *smmu = cfg->smmu; ++ struct iommu_rmr *rmr; ++ ++ list_for_each_entry(rmr, &smmu->rmr_list, list) { ++ struct iommu_resv_region *region; ++ int prot = IOMMU_READ | IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO; ++ ++ if (!arm_smmu_dev_has_rmr(cfg, fwspec, rmr)) ++ continue; ++ region = iommu_alloc_resv_region(rmr->base_address, ++ rmr->length, prot, ++ IOMMU_RESV_DIRECT); ++ if (!region) ++ return; ++ ++ list_add_tail(®ion->list, head); ++ } ++} ++ + static void arm_smmu_get_resv_regions(struct device *dev, + struct list_head *head) + { +@@ -1598,6 +1640,7 @@ static void arm_smmu_get_resv_regions(struct device *dev, + list_add_tail(®ion->list, head); + + iommu_dma_get_resv_regions(dev, head); ++ arm_smmu_rmr_get_resv_regions(dev, head); + } + + static int arm_smmu_def_domain_type(struct device *dev) +-- +2.27.0 + + +From 6b1e54549e8190821f7d85a6eac7d0bdae75613a Mon Sep 17 00:00:00 2001 +From: Ioana Ciornei +Date: Fri, 8 Jan 2021 11:07:22 +0200 +Subject: [PATCH 16/44] dpaa2-mac: split up initializing the MAC object from + connecting to it + +Split up the initialization phase of the dpmac object from actually +configuring the phylink instance, connecting to it and configuring the +MAC. This is done so that even though the dpni object is connected to a +dpmac which has link management handled by the firmware we are still +able to export the MAC counters. + +Signed-off-by: Ioana Ciornei +Signed-off-by: Jakub Kicinski +--- + .../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 14 +++- + .../net/ethernet/freescale/dpaa2/dpaa2-mac.c | 69 +++++++++++-------- + .../net/ethernet/freescale/dpaa2/dpaa2-mac.h | 5 ++ + 3 files changed, 59 insertions(+), 29 deletions(-) + +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +index f91c67489e62..0351b2b65bff 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +@@ -4066,15 +4066,24 @@ static int dpaa2_eth_connect_mac(struct dpaa2_eth_priv *priv) + mac->mc_io = priv->mc_io; + mac->net_dev = priv->net_dev; + ++ err = dpaa2_mac_open(mac); ++ if (err) ++ goto err_free_mac; ++ + err = dpaa2_mac_connect(mac); + if (err) { + netdev_err(priv->net_dev, "Error connecting to the MAC endpoint\n"); +- kfree(mac); +- return err; ++ goto err_close_mac; + } + priv->mac = mac; + + return 0; ++ ++err_close_mac: ++ dpaa2_mac_close(mac); ++err_free_mac: ++ kfree(mac); ++ return err; + } + + static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv) +@@ -4083,6 +4092,7 @@ static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv) + return; + + dpaa2_mac_disconnect(priv->mac); ++ dpaa2_mac_close(priv->mac); + kfree(priv->mac); + priv->mac = NULL; + } +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +index 828c177df03d..50dd302abcf4 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +@@ -302,36 +302,20 @@ static void dpaa2_pcs_destroy(struct dpaa2_mac *mac) + + int dpaa2_mac_connect(struct dpaa2_mac *mac) + { +- struct fsl_mc_device *dpmac_dev = mac->mc_dev; + struct net_device *net_dev = mac->net_dev; + struct device_node *dpmac_node; + struct phylink *phylink; +- struct dpmac_attr attr; + int err; + +- err = dpmac_open(mac->mc_io, 0, dpmac_dev->obj_desc.id, +- &dpmac_dev->mc_handle); +- if (err || !dpmac_dev->mc_handle) { +- netdev_err(net_dev, "dpmac_open() = %d\n", err); +- return -ENODEV; +- } +- +- err = dpmac_get_attributes(mac->mc_io, 0, dpmac_dev->mc_handle, &attr); +- if (err) { +- netdev_err(net_dev, "dpmac_get_attributes() = %d\n", err); +- goto err_close_dpmac; +- } +- +- mac->if_link_type = attr.link_type; ++ mac->if_link_type = mac->attr.link_type; + +- dpmac_node = dpaa2_mac_get_node(attr.id); ++ dpmac_node = dpaa2_mac_get_node(mac->attr.id); + if (!dpmac_node) { +- netdev_err(net_dev, "No dpmac@%d node found.\n", attr.id); +- err = -ENODEV; +- goto err_close_dpmac; ++ netdev_err(net_dev, "No dpmac@%d node found.\n", mac->attr.id); ++ return -ENODEV; + } + +- err = dpaa2_mac_get_if_mode(dpmac_node, attr); ++ err = dpaa2_mac_get_if_mode(dpmac_node, mac->attr); + if (err < 0) { + err = -EINVAL; + goto err_put_node; +@@ -351,9 +335,9 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac) + goto err_put_node; + } + +- if (attr.link_type == DPMAC_LINK_TYPE_PHY && +- attr.eth_if != DPMAC_ETH_IF_RGMII) { +- err = dpaa2_pcs_create(mac, dpmac_node, attr.id); ++ if (mac->attr.link_type == DPMAC_LINK_TYPE_PHY && ++ mac->attr.eth_if != DPMAC_ETH_IF_RGMII) { ++ err = dpaa2_pcs_create(mac, dpmac_node, mac->attr.id); + if (err) + goto err_put_node; + } +@@ -389,8 +373,7 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac) + dpaa2_pcs_destroy(mac); + err_put_node: + of_node_put(dpmac_node); +-err_close_dpmac: +- dpmac_close(mac->mc_io, 0, dpmac_dev->mc_handle); ++ + return err; + } + +@@ -402,8 +385,40 @@ void dpaa2_mac_disconnect(struct dpaa2_mac *mac) + phylink_disconnect_phy(mac->phylink); + phylink_destroy(mac->phylink); + dpaa2_pcs_destroy(mac); ++} ++ ++int dpaa2_mac_open(struct dpaa2_mac *mac) ++{ ++ struct fsl_mc_device *dpmac_dev = mac->mc_dev; ++ struct net_device *net_dev = mac->net_dev; ++ int err; ++ ++ err = dpmac_open(mac->mc_io, 0, dpmac_dev->obj_desc.id, ++ &dpmac_dev->mc_handle); ++ if (err || !dpmac_dev->mc_handle) { ++ netdev_err(net_dev, "dpmac_open() = %d\n", err); ++ return -ENODEV; ++ } ++ ++ err = dpmac_get_attributes(mac->mc_io, 0, dpmac_dev->mc_handle, ++ &mac->attr); ++ if (err) { ++ netdev_err(net_dev, "dpmac_get_attributes() = %d\n", err); ++ goto err_close_dpmac; ++ } ++ ++ return 0; ++ ++err_close_dpmac: ++ dpmac_close(mac->mc_io, 0, dpmac_dev->mc_handle); ++ return err; ++} ++ ++void dpaa2_mac_close(struct dpaa2_mac *mac) ++{ ++ struct fsl_mc_device *dpmac_dev = mac->mc_dev; + +- dpmac_close(mac->mc_io, 0, mac->mc_dev->mc_handle); ++ dpmac_close(mac->mc_io, 0, dpmac_dev->mc_handle); + } + + static char dpaa2_mac_ethtool_stats[][ETH_GSTRING_LEN] = { +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h +index 955a52856210..13d42dd58ec9 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h +@@ -17,6 +17,7 @@ struct dpaa2_mac { + struct dpmac_link_state state; + struct net_device *net_dev; + struct fsl_mc_io *mc_io; ++ struct dpmac_attr attr; + + struct phylink_config phylink_config; + struct phylink *phylink; +@@ -28,6 +29,10 @@ struct dpaa2_mac { + bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev, + struct fsl_mc_io *mc_io); + ++int dpaa2_mac_open(struct dpaa2_mac *mac); ++ ++void dpaa2_mac_close(struct dpaa2_mac *mac); ++ + int dpaa2_mac_connect(struct dpaa2_mac *mac); + + void dpaa2_mac_disconnect(struct dpaa2_mac *mac); +-- +2.27.0 + + +From 0f95faffc5d299ede2605a3415ccbaca0b593498 Mon Sep 17 00:00:00 2001 +From: Calvin Johnson +Date: Thu, 11 Mar 2021 11:49:56 +0530 +Subject: [PATCH 17/44] Documentation: ACPI: DSD: Document MDIO PHY + +Introduce ACPI mechanism to get PHYs registered on a MDIO bus and +provide them to be connected to MAC. + +Describe properties "phy-handle" and "phy-mode". + +Signed-off-by: Calvin Johnson +--- + Documentation/firmware-guide/acpi/dsd/phy.rst | 133 ++++++++++++++++++ + 1 file changed, 133 insertions(+) + create mode 100644 Documentation/firmware-guide/acpi/dsd/phy.rst + +diff --git a/Documentation/firmware-guide/acpi/dsd/phy.rst b/Documentation/firmware-guide/acpi/dsd/phy.rst +new file mode 100644 +index 000000000000..7d01ae8b3cc6 +--- /dev/null ++++ b/Documentation/firmware-guide/acpi/dsd/phy.rst +@@ -0,0 +1,133 @@ ++.. SPDX-License-Identifier: GPL-2.0 ++ ++========================= ++MDIO bus and PHYs in ACPI ++========================= ++ ++The PHYs on an MDIO bus [1] are probed and registered using ++fwnode_mdiobus_register_phy(). ++ ++Later, for connecting these PHYs to their respective MACs, the PHYs registered ++on the MDIO bus have to be referenced. ++ ++This document introduces two _DSD properties that are to be used ++for connecting PHYs on the MDIO bus [3] to the MAC layer. ++ ++These properties are defined in accordance with the "Device ++Properties UUID For _DSD" [2] document and the ++daffd814-6eba-4d8c-8a91-bc9bbf4aa301 UUID must be used in the Device ++Data Descriptors containing them. ++ ++phy-handle ++---------- ++For each MAC node, a device property "phy-handle" is used to reference ++the PHY that is registered on an MDIO bus. This is mandatory for ++network interfaces that have PHYs connected to MAC via MDIO bus. ++ ++During the MDIO bus driver initialization, PHYs on this bus are probed ++using the _ADR object as shown below and are registered on the MDIO bus. ++ ++:: ++ Scope(\_SB.MDI0) ++ { ++ Device(PHY1) { ++ Name (_ADR, 0x1) ++ } // end of PHY1 ++ ++ Device(PHY2) { ++ Name (_ADR, 0x2) ++ } // end of PHY2 ++ } ++ ++Later, during the MAC driver initialization, the registered PHY devices ++have to be retrieved from the MDIO bus. For this, the MAC driver needs ++references to the previously registered PHYs which are provided ++as device object references (e.g. \_SB.MDI0.PHY1). ++ ++phy-mode ++-------- ++The "phy-mode" _DSD property is used to describe the connection to ++the PHY. The valid values for "phy-mode" are defined in [4]. ++ ++The following ASL example illustrates the usage of these properties. ++ ++DSDT entry for MDIO node ++------------------------ ++ ++The MDIO bus has an SoC component (MDIO controller) and a platform ++component (PHYs on the MDIO bus). ++ ++a) Silicon Component ++This node describes the MDIO controller, MDI0 ++--------------------------------------------- ++:: ++ Scope(_SB) ++ { ++ Device(MDI0) { ++ Name(_HID, "NXP0006") ++ Name(_CCA, 1) ++ Name(_UID, 0) ++ Name(_CRS, ResourceTemplate() { ++ Memory32Fixed(ReadWrite, MDI0_BASE, MDI_LEN) ++ Interrupt(ResourceConsumer, Level, ActiveHigh, Shared) ++ { ++ MDI0_IT ++ } ++ }) // end of _CRS for MDI0 ++ } // end of MDI0 ++ } ++ ++b) Platform Component ++The PHY1 and PHY2 nodes represent the PHYs connected to MDIO bus MDI0 ++--------------------------------------------------------------------- ++:: ++ Scope(\_SB.MDI0) ++ { ++ Device(PHY1) { ++ Name (_ADR, 0x1) ++ } // end of PHY1 ++ ++ Device(PHY2) { ++ Name (_ADR, 0x2) ++ } // end of PHY2 ++ } ++ ++DSDT entries representing MAC nodes ++----------------------------------- ++ ++Below are the MAC nodes where PHY nodes are referenced. ++phy-mode and phy-handle are used as explained earlier. ++------------------------------------------------------ ++:: ++ Scope(\_SB.MCE0.PR17) ++ { ++ Name (_DSD, Package () { ++ ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), ++ Package () { ++ Package (2) {"phy-mode", "rgmii-id"}, ++ Package (2) {"phy-handle", \_SB.MDI0.PHY1} ++ } ++ }) ++ } ++ ++ Scope(\_SB.MCE0.PR18) ++ { ++ Name (_DSD, Package () { ++ ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), ++ Package () { ++ Package (2) {"phy-mode", "rgmii-id"}, ++ Package (2) {"phy-handle", \_SB.MDI0.PHY2}} ++ } ++ }) ++ } ++ ++References ++========== ++ ++[1] Documentation/networking/phy.rst ++ ++[2] https://www.uefi.org/sites/default/files/resources/_DSD-device-properties-UUID.pdf ++ ++[3] Documentation/firmware-guide/acpi/DSD-properties-rules.rst ++ ++[4] Documentation/devicetree/bindings/net/ethernet-controller.yaml +-- +2.27.0 + + +From e1f94e1daf554db196203f2430fd0a9532980a53 Mon Sep 17 00:00:00 2001 +From: Calvin Johnson +Date: Thu, 11 Mar 2021 11:49:57 +0530 +Subject: [PATCH 18/44] net: phy: Introduce fwnode_mdio_find_device() + +Define fwnode_mdio_find_device() to get a pointer to the +mdio_device from fwnode passed to the function. + +Refactor of_mdio_find_device() to use fwnode_mdio_find_device(). + +Signed-off-by: Calvin Johnson +--- + drivers/net/mdio/of_mdio.c | 11 +---------- + drivers/net/phy/phy_device.c | 23 +++++++++++++++++++++++ + include/linux/phy.h | 6 ++++++ + 3 files changed, 30 insertions(+), 10 deletions(-) + +diff --git a/drivers/net/mdio/of_mdio.c b/drivers/net/mdio/of_mdio.c +index 4daf94bb56a5..7bd33b930116 100644 +--- a/drivers/net/mdio/of_mdio.c ++++ b/drivers/net/mdio/of_mdio.c +@@ -347,16 +347,7 @@ EXPORT_SYMBOL(of_mdiobus_register); + */ + struct mdio_device *of_mdio_find_device(struct device_node *np) + { +- struct device *d; +- +- if (!np) +- return NULL; +- +- d = bus_find_device_by_of_node(&mdio_bus_type, np); +- if (!d) +- return NULL; +- +- return to_mdio_device(d); ++ return fwnode_mdio_find_device(of_fwnode_handle(np)); + } + EXPORT_SYMBOL(of_mdio_find_device); + +diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c +index dd1f711140c3..aff42e17d441 100644 +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -2799,6 +2799,29 @@ static bool phy_drv_supports_irq(struct phy_driver *phydrv) + return phydrv->config_intr && phydrv->ack_interrupt; + } + ++/** ++ * fwnode_mdio_find_device - Given a fwnode, find the mdio_device ++ * @fwnode: pointer to the mdio_device's fwnode ++ * ++ * If successful, returns a pointer to the mdio_device with the embedded ++ * struct device refcount incremented by one, or NULL on failure. ++ * The caller should call put_device() on the mdio_device after its use. ++ */ ++struct mdio_device *fwnode_mdio_find_device(struct fwnode_handle *fwnode) ++{ ++ struct device *d; ++ ++ if (!fwnode) ++ return NULL; ++ ++ d = bus_find_device_by_fwnode(&mdio_bus_type, fwnode); ++ if (!d) ++ return NULL; ++ ++ return to_mdio_device(d); ++} ++EXPORT_SYMBOL(fwnode_mdio_find_device); ++ + /** + * phy_probe - probe and init a PHY device + * @dev: device to probe and init +diff --git a/include/linux/phy.h b/include/linux/phy.h +index 56563e5e0dc7..a90b0eb55481 100644 +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1348,11 +1348,17 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id, + bool is_c45, + struct phy_c45_device_ids *c45_ids); + #if IS_ENABLED(CONFIG_PHYLIB) ++struct mdio_device *fwnode_mdio_find_device(struct fwnode_handle *fwnode); + struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45); + int phy_device_register(struct phy_device *phy); + void phy_device_free(struct phy_device *phydev); + #else + static inline ++struct mdio_device *fwnode_mdio_find_device(struct fwnode_handle *fwnode) ++{ ++ return 0; ++} ++static inline + struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45) + { + return NULL; +-- +2.27.0 + + +From ab37ac2fa7c18374312c11b5583cb64b1ad0095c Mon Sep 17 00:00:00 2001 +From: Calvin Johnson +Date: Thu, 11 Mar 2021 11:49:58 +0530 +Subject: [PATCH 19/44] net: phy: Introduce phy related fwnode functions + +Define fwnode_phy_find_device() to iterate an mdiobus and find the +phy device of the provided phy fwnode. Additionally define +device_phy_find_device() to find phy device of provided device. + +Define fwnode_get_phy_node() to get phy_node using named reference. + +Signed-off-by: Calvin Johnson +--- + drivers/net/phy/phy_device.c | 62 ++++++++++++++++++++++++++++++++++++ + include/linux/phy.h | 20 ++++++++++++ + 2 files changed, 82 insertions(+) + +diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c +index aff42e17d441..662c388a77f5 100644 +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -9,6 +9,7 @@ + + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + ++#include + #include + #include + #include +@@ -2822,6 +2823,67 @@ struct mdio_device *fwnode_mdio_find_device(struct fwnode_handle *fwnode) + } + EXPORT_SYMBOL(fwnode_mdio_find_device); + ++/** ++ * fwnode_phy_find_device - For provided phy_fwnode, find phy_device. ++ * ++ * @phy_fwnode: Pointer to the phy's fwnode. ++ * ++ * If successful, returns a pointer to the phy_device with the embedded ++ * struct device refcount incremented by one, or NULL on failure. ++ */ ++struct phy_device *fwnode_phy_find_device(struct fwnode_handle *phy_fwnode) ++{ ++ struct mdio_device *mdiodev; ++ ++ mdiodev = fwnode_mdio_find_device(phy_fwnode); ++ if (!mdiodev) ++ return NULL; ++ ++ if (mdiodev->flags & MDIO_DEVICE_FLAG_PHY) ++ return to_phy_device(&mdiodev->dev); ++ ++ put_device(&mdiodev->dev); ++ ++ return NULL; ++} ++EXPORT_SYMBOL(fwnode_phy_find_device); ++ ++/** ++ * device_phy_find_device - For the given device, get the phy_device ++ * @dev: Pointer to the given device ++ * ++ * Refer return conditions of fwnode_phy_find_device(). ++ */ ++struct phy_device *device_phy_find_device(struct device *dev) ++{ ++ return fwnode_phy_find_device(dev_fwnode(dev)); ++} ++EXPORT_SYMBOL_GPL(device_phy_find_device); ++ ++/** ++ * fwnode_get_phy_node - Get the phy_node using the named reference. ++ * @fwnode: Pointer to fwnode from which phy_node has to be obtained. ++ * ++ * Refer return conditions of fwnode_find_reference(). ++ * For ACPI, only "phy-handle" is supported. Legacy DT properties "phy" ++ * and "phy-device" are not supported in ACPI. DT supports all the three ++ * named references to the phy node. ++ */ ++struct fwnode_handle *fwnode_get_phy_node(struct fwnode_handle *fwnode) ++{ ++ struct fwnode_handle *phy_node; ++ ++ /* Only phy-handle is used for ACPI */ ++ phy_node = fwnode_find_reference(fwnode, "phy-handle", 0); ++ if (is_acpi_node(fwnode) || !IS_ERR(phy_node)) ++ return phy_node; ++ phy_node = fwnode_find_reference(fwnode, "phy", 0); ++ if (IS_ERR(phy_node)) ++ phy_node = fwnode_find_reference(fwnode, "phy-device", 0); ++ return phy_node; ++} ++EXPORT_SYMBOL_GPL(fwnode_get_phy_node); ++ + /** + * phy_probe - probe and init a PHY device + * @dev: device to probe and init +diff --git a/include/linux/phy.h b/include/linux/phy.h +index a90b0eb55481..40b231a12f54 100644 +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1349,6 +1349,9 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id, + struct phy_c45_device_ids *c45_ids); + #if IS_ENABLED(CONFIG_PHYLIB) + struct mdio_device *fwnode_mdio_find_device(struct fwnode_handle *fwnode); ++struct phy_device *fwnode_phy_find_device(struct fwnode_handle *phy_fwnode); ++struct phy_device *device_phy_find_device(struct device *dev); ++struct fwnode_handle *fwnode_get_phy_node(struct fwnode_handle *fwnode); + struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45); + int phy_device_register(struct phy_device *phy); + void phy_device_free(struct phy_device *phydev); +@@ -1358,6 +1361,23 @@ struct mdio_device *fwnode_mdio_find_device(struct fwnode_handle *fwnode) + { + return 0; + } ++static inline ++struct phy_device *fwnode_phy_find_device(struct fwnode_handle *phy_fwnode) ++{ ++ return NULL; ++} ++ ++static inline struct phy_device *device_phy_find_device(struct device *dev) ++{ ++ return NULL; ++} ++ ++static inline ++struct fwnode_handle *fwnode_get_phy_node(struct fwnode_handle *fwnode) ++{ ++ return NULL; ++} ++ + static inline + struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45) + { +-- +2.27.0 + + +From b58a8a23b8c692adfb1f264f0555052e618fad44 Mon Sep 17 00:00:00 2001 +From: Calvin Johnson +Date: Thu, 11 Mar 2021 11:49:59 +0530 +Subject: [PATCH 20/44] of: mdio: Refactor of_phy_find_device() + +Refactor of_phy_find_device() to use fwnode_phy_find_device(). + +Signed-off-by: Calvin Johnson +--- + drivers/net/mdio/of_mdio.c | 13 +------------ + 1 file changed, 1 insertion(+), 12 deletions(-) + +diff --git a/drivers/net/mdio/of_mdio.c b/drivers/net/mdio/of_mdio.c +index 7bd33b930116..94ec421dd91b 100644 +--- a/drivers/net/mdio/of_mdio.c ++++ b/drivers/net/mdio/of_mdio.c +@@ -360,18 +360,7 @@ EXPORT_SYMBOL(of_mdio_find_device); + */ + struct phy_device *of_phy_find_device(struct device_node *phy_np) + { +- struct mdio_device *mdiodev; +- +- mdiodev = of_mdio_find_device(phy_np); +- if (!mdiodev) +- return NULL; +- +- if (mdiodev->flags & MDIO_DEVICE_FLAG_PHY) +- return to_phy_device(&mdiodev->dev); +- +- put_device(&mdiodev->dev); +- +- return NULL; ++ return fwnode_phy_find_device(of_fwnode_handle(phy_np)); + } + EXPORT_SYMBOL(of_phy_find_device); + +-- +2.27.0 + + +From efdd10e3f8be334e77592201bba65ff4395d75ef Mon Sep 17 00:00:00 2001 +From: Calvin Johnson +Date: Thu, 11 Mar 2021 11:50:00 +0530 +Subject: [PATCH 21/44] net: phy: Introduce fwnode_get_phy_id() + +Extract phy_id from compatible string. This will be used by +fwnode_mdiobus_register_phy() to create phy device using the +phy_id. + +Signed-off-by: Calvin Johnson +--- + drivers/net/phy/phy_device.c | 21 +++++++++++++++++++++ + include/linux/phy.h | 5 +++++ + 2 files changed, 26 insertions(+) + +diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c +index 662c388a77f5..5d0ad64b4673 100644 +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -818,6 +818,27 @@ static int get_phy_c22_id(struct mii_bus *bus, int addr, u32 *phy_id) + return 0; + } + ++/* Extract the phy ID from the compatible string of the form ++ * ethernet-phy-idAAAA.BBBB. ++ */ ++int fwnode_get_phy_id(struct fwnode_handle *fwnode, u32 *phy_id) ++{ ++ unsigned int upper, lower; ++ const char *cp; ++ int ret; ++ ++ ret = fwnode_property_read_string(fwnode, "compatible", &cp); ++ if (ret) ++ return ret; ++ ++ if (sscanf(cp, "ethernet-phy-id%4x.%4x", &upper, &lower) != 2) ++ return -EINVAL; ++ ++ *phy_id = ((upper & GENMASK(15, 0)) << 16) | (lower & GENMASK(15, 0)); ++ return 0; ++} ++EXPORT_SYMBOL(fwnode_get_phy_id); ++ + /** + * get_phy_device - reads the specified PHY device and returns its @phy_device + * struct +diff --git a/include/linux/phy.h b/include/linux/phy.h +index 40b231a12f54..d0c5d9034689 100644 +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1348,6 +1348,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id, + bool is_c45, + struct phy_c45_device_ids *c45_ids); + #if IS_ENABLED(CONFIG_PHYLIB) ++int fwnode_get_phy_id(struct fwnode_handle *fwnode, u32 *phy_id); + struct mdio_device *fwnode_mdio_find_device(struct fwnode_handle *fwnode); + struct phy_device *fwnode_phy_find_device(struct fwnode_handle *phy_fwnode); + struct phy_device *device_phy_find_device(struct device *dev); +@@ -1356,6 +1357,10 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45); + int phy_device_register(struct phy_device *phy); + void phy_device_free(struct phy_device *phydev); + #else ++static inline int fwnode_get_phy_id(struct fwnode_handle *fwnode, u32 *phy_id) ++{ ++ return 0; ++} + static inline + struct mdio_device *fwnode_mdio_find_device(struct fwnode_handle *fwnode) + { +-- +2.27.0 + + +From 2a11e8cb084e0db52b971e1e0076b0f832163b8b Mon Sep 17 00:00:00 2001 +From: Calvin Johnson +Date: Thu, 11 Mar 2021 11:50:01 +0530 +Subject: [PATCH 22/44] of: mdio: Refactor of_get_phy_id() + +With the introduction of fwnode_get_phy_id(), refactor of_get_phy_id() +to use fwnode equivalent. + +Signed-off-by: Calvin Johnson +--- + drivers/net/mdio/of_mdio.c | 12 +----------- + 1 file changed, 1 insertion(+), 11 deletions(-) + +diff --git a/drivers/net/mdio/of_mdio.c b/drivers/net/mdio/of_mdio.c +index 94ec421dd91b..d4cc293358f7 100644 +--- a/drivers/net/mdio/of_mdio.c ++++ b/drivers/net/mdio/of_mdio.c +@@ -29,17 +29,7 @@ MODULE_LICENSE("GPL"); + * ethernet-phy-idAAAA.BBBB */ + static int of_get_phy_id(struct device_node *device, u32 *phy_id) + { +- struct property *prop; +- const char *cp; +- unsigned int upper, lower; +- +- of_property_for_each_string(device, "compatible", prop, cp) { +- if (sscanf(cp, "ethernet-phy-id%4x.%4x", &upper, &lower) == 2) { +- *phy_id = ((upper & 0xFFFF) << 16) | (lower & 0xFFFF); +- return 0; +- } +- } +- return -EINVAL; ++ return fwnode_get_phy_id(of_fwnode_handle(device), phy_id); + } + + static struct mii_timestamper *of_find_mii_timestamper(struct device_node *node) +-- +2.27.0 + + +From ee93bf13699df3eeee369d403166708bc2a9a775 Mon Sep 17 00:00:00 2001 +From: Calvin Johnson +Date: Thu, 11 Mar 2021 11:50:02 +0530 +Subject: [PATCH 23/44] net: mii_timestamper: check NULL in + unregister_mii_timestamper() + +Callers of unregister_mii_timestamper() currently check for NULL +value of mii_ts before calling it. + +Place the NULL check inside unregister_mii_timestamper() and update +the callers accordingly. + +Signed-off-by: Calvin Johnson +Reviewed-by: Andy Shevchenko +--- + drivers/net/mdio/of_mdio.c | 6 ++---- + drivers/net/phy/mii_timestamper.c | 3 +++ + drivers/net/phy/phy_device.c | 3 +-- + 3 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/mdio/of_mdio.c b/drivers/net/mdio/of_mdio.c +index d4cc293358f7..c926442dca82 100644 +--- a/drivers/net/mdio/of_mdio.c ++++ b/drivers/net/mdio/of_mdio.c +@@ -115,15 +115,13 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, + else + phy = get_phy_device(mdio, addr, is_c45); + if (IS_ERR(phy)) { +- if (mii_ts) +- unregister_mii_timestamper(mii_ts); ++ unregister_mii_timestamper(mii_ts); + return PTR_ERR(phy); + } + + rc = of_mdiobus_phy_device_register(mdio, phy, child, addr); + if (rc) { +- if (mii_ts) +- unregister_mii_timestamper(mii_ts); ++ unregister_mii_timestamper(mii_ts); + phy_device_free(phy); + return rc; + } +diff --git a/drivers/net/phy/mii_timestamper.c b/drivers/net/phy/mii_timestamper.c +index b71b7456462d..51ae0593a04f 100644 +--- a/drivers/net/phy/mii_timestamper.c ++++ b/drivers/net/phy/mii_timestamper.c +@@ -111,6 +111,9 @@ void unregister_mii_timestamper(struct mii_timestamper *mii_ts) + struct mii_timestamping_desc *desc; + struct list_head *this; + ++ if (!mii_ts) ++ return; ++ + /* mii_timestamper statically registered by the PHY driver won't use the + * register_mii_timestamper() and thus don't have ->device set. Don't + * try to unregister these. +diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c +index 5d0ad64b4673..9f6b449cf388 100644 +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -929,8 +929,7 @@ EXPORT_SYMBOL(phy_device_register); + */ + void phy_device_remove(struct phy_device *phydev) + { +- if (phydev->mii_ts) +- unregister_mii_timestamper(phydev->mii_ts); ++ unregister_mii_timestamper(phydev->mii_ts); + + device_del(&phydev->mdio.dev); + +-- +2.27.0 + + +From 19cd8ebabe432b4d49438403a25532cdf2f21e3b Mon Sep 17 00:00:00 2001 +From: Calvin Johnson +Date: Thu, 11 Mar 2021 11:50:03 +0530 +Subject: [PATCH 24/44] net: mdiobus: Introduce fwnode_mdiobus_register_phy() + +Introduce fwnode_mdiobus_register_phy() to register PHYs on the +mdiobus. From the compatible string, identify whether the PHY is +c45 and based on this create a PHY device instance which is +registered on the mdiobus. + +uninitialized symbol 'mii_ts' +Reported-by: kernel test robot +Reported-by: Dan Carpenter + +Signed-off-by: Calvin Johnson +--- + MAINTAINERS | 1 + + drivers/net/mdio/Kconfig | 9 ++++ + drivers/net/mdio/Makefile | 3 +- + drivers/net/mdio/fwnode_mdio.c | 77 ++++++++++++++++++++++++++++++++++ + drivers/net/mdio/of_mdio.c | 3 +- + include/linux/fwnode_mdio.h | 24 +++++++++++ + include/linux/of_mdio.h | 6 ++- + 7 files changed, 120 insertions(+), 3 deletions(-) + create mode 100644 drivers/net/mdio/fwnode_mdio.c + create mode 100644 include/linux/fwnode_mdio.h + +diff --git a/MAINTAINERS b/MAINTAINERS +index 281de213ef47..821adf657115 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -6585,6 +6585,7 @@ F: Documentation/devicetree/bindings/net/mdio* + F: Documentation/devicetree/bindings/net/qca,ar803x.yaml + F: Documentation/networking/phy.rst + F: drivers/net/mdio/ ++F: drivers/net/mdio/fwnode_mdio.c + F: drivers/net/mdio/of_mdio.c + F: drivers/net/pcs/ + F: drivers/net/phy/ +diff --git a/drivers/net/mdio/Kconfig b/drivers/net/mdio/Kconfig +index a10cc460d7cf..2d5bf5ccffb5 100644 +--- a/drivers/net/mdio/Kconfig ++++ b/drivers/net/mdio/Kconfig +@@ -19,6 +19,15 @@ config MDIO_BUS + reflects whether the mdio_bus/mdio_device code is built as a + loadable module or built-in. + ++config FWNODE_MDIO ++ def_tristate PHYLIB ++ depends on ACPI ++ depends on OF ++ depends on PHYLIB ++ select FIXED_PHY ++ help ++ FWNODE MDIO bus (Ethernet PHY) accessors ++ + config OF_MDIO + def_tristate PHYLIB + depends on OF +diff --git a/drivers/net/mdio/Makefile b/drivers/net/mdio/Makefile +index 5c498dde463f..ea5390e2ef84 100644 +--- a/drivers/net/mdio/Makefile ++++ b/drivers/net/mdio/Makefile +@@ -1,7 +1,8 @@ + # SPDX-License-Identifier: GPL-2.0 + # Makefile for Linux MDIO bus drivers + +-obj-$(CONFIG_OF_MDIO) += of_mdio.o ++obj-$(CONFIG_FWNODE_MDIO) += fwnode_mdio.o ++obj-$(CONFIG_OF_MDIO) += of_mdio.o + + obj-$(CONFIG_MDIO_ASPEED) += mdio-aspeed.o + obj-$(CONFIG_MDIO_BCM_IPROC) += mdio-bcm-iproc.o +diff --git a/drivers/net/mdio/fwnode_mdio.c b/drivers/net/mdio/fwnode_mdio.c +new file mode 100644 +index 000000000000..0982e816a6fb +--- /dev/null ++++ b/drivers/net/mdio/fwnode_mdio.c +@@ -0,0 +1,77 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * fwnode helpers for the MDIO (Ethernet PHY) API ++ * ++ * This file provides helper functions for extracting PHY device information ++ * out of the fwnode and using it to populate an mii_bus. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++MODULE_AUTHOR("Calvin Johnson "); ++MODULE_LICENSE("GPL"); ++ ++int fwnode_mdiobus_register_phy(struct mii_bus *bus, ++ struct fwnode_handle *child, u32 addr) ++{ ++ struct mii_timestamper *mii_ts = NULL; ++ struct phy_device *phy; ++ bool is_c45 = false; ++ u32 phy_id; ++ int rc; ++ ++ if (is_of_node(child)) { ++ mii_ts = of_find_mii_timestamper(to_of_node(child)); ++ if (IS_ERR(mii_ts)) ++ return PTR_ERR(mii_ts); ++ } ++ ++ rc = fwnode_property_match_string(child, "compatible", "ethernet-phy-ieee802.3-c45"); ++ if (rc >= 0) ++ is_c45 = true; ++ ++ if (is_c45 || fwnode_get_phy_id(child, &phy_id)) ++ phy = get_phy_device(bus, addr, is_c45); ++ else ++ phy = phy_device_create(bus, addr, phy_id, 0, NULL); ++ if (IS_ERR(phy)) { ++ unregister_mii_timestamper(mii_ts); ++ return PTR_ERR(phy); ++ } ++ ++ if (is_acpi_node(child)) { ++ phy->irq = bus->irq[addr]; ++ ++ /* Associate the fwnode with the device structure so it ++ * can be looked up later. ++ */ ++ phy->mdio.dev.fwnode = child; ++ ++ /* All data is now stored in the phy struct, so register it */ ++ rc = phy_device_register(phy); ++ if (rc) { ++ phy_device_free(phy); ++ fwnode_handle_put(phy->mdio.dev.fwnode); ++ return rc; ++ } ++ } else if (is_of_node(child)) { ++ rc = of_mdiobus_phy_device_register(bus, phy, to_of_node(child), addr); ++ if (rc) { ++ unregister_mii_timestamper(mii_ts); ++ phy_device_free(phy); ++ return rc; ++ } ++ } ++ ++ /* phy->mii_ts may already be defined by the PHY driver. A ++ * mii_timestamper probed via the device tree will still have ++ * precedence. ++ */ ++ if (mii_ts) ++ phy->mii_ts = mii_ts; ++ return 0; ++} ++EXPORT_SYMBOL(fwnode_mdiobus_register_phy); +diff --git a/drivers/net/mdio/of_mdio.c b/drivers/net/mdio/of_mdio.c +index c926442dca82..932912c585c0 100644 +--- a/drivers/net/mdio/of_mdio.c ++++ b/drivers/net/mdio/of_mdio.c +@@ -32,7 +32,7 @@ static int of_get_phy_id(struct device_node *device, u32 *phy_id) + return fwnode_get_phy_id(of_fwnode_handle(device), phy_id); + } + +-static struct mii_timestamper *of_find_mii_timestamper(struct device_node *node) ++struct mii_timestamper *of_find_mii_timestamper(struct device_node *node) + { + struct of_phandle_args arg; + int err; +@@ -49,6 +49,7 @@ static struct mii_timestamper *of_find_mii_timestamper(struct device_node *node) + + return register_mii_timestamper(arg.np, arg.args[0]); + } ++EXPORT_SYMBOL(of_find_mii_timestamper); + + int of_mdiobus_phy_device_register(struct mii_bus *mdio, struct phy_device *phy, + struct device_node *child, u32 addr) +diff --git a/include/linux/fwnode_mdio.h b/include/linux/fwnode_mdio.h +new file mode 100644 +index 000000000000..8c0392845916 +--- /dev/null ++++ b/include/linux/fwnode_mdio.h +@@ -0,0 +1,24 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * FWNODE helper for the MDIO (Ethernet PHY) API ++ */ ++ ++#ifndef __LINUX_FWNODE_MDIO_H ++#define __LINUX_FWNODE_MDIO_H ++ ++#include ++ ++#if IS_ENABLED(CONFIG_FWNODE_MDIO) ++int fwnode_mdiobus_register_phy(struct mii_bus *bus, ++ struct fwnode_handle *child, u32 addr); ++ ++#else /* CONFIG_FWNODE_MDIO */ ++static inline int fwnode_mdiobus_register_phy(struct mii_bus *bus, ++ struct fwnode_handle *child, ++ u32 addr) ++{ ++ return -EINVAL; ++} ++#endif ++ ++#endif /* __LINUX_FWNODE_MDIO_H */ +diff --git a/include/linux/of_mdio.h b/include/linux/of_mdio.h +index cfe8c607a628..3b66016f18aa 100644 +--- a/include/linux/of_mdio.h ++++ b/include/linux/of_mdio.h +@@ -34,6 +34,7 @@ struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np); + int of_phy_register_fixed_link(struct device_node *np); + void of_phy_deregister_fixed_link(struct device_node *np); + bool of_phy_is_fixed_link(struct device_node *np); ++struct mii_timestamper *of_find_mii_timestamper(struct device_node *np); + int of_mdiobus_phy_device_register(struct mii_bus *mdio, struct phy_device *phy, + struct device_node *child, u32 addr); + +@@ -128,7 +129,10 @@ static inline bool of_phy_is_fixed_link(struct device_node *np) + { + return false; + } +- ++static inline struct mii_timestamper *of_find_mii_timestamper(struct device_node *np) ++{ ++ return NULL; ++} + static inline int of_mdiobus_phy_device_register(struct mii_bus *mdio, + struct phy_device *phy, + struct device_node *child, u32 addr) +-- +2.27.0 + + +From 91d4b9891111f2cdab14930a44c43ee91c70ecc0 Mon Sep 17 00:00:00 2001 +From: Calvin Johnson +Date: Thu, 11 Mar 2021 11:50:04 +0530 +Subject: [PATCH 25/44] of: mdio: Refactor of_mdiobus_register_phy() + +Refactor of_mdiobus_register_phy() to use fwnode_mdiobus_register_phy(). + +Signed-off-by: Calvin Johnson +--- + drivers/net/mdio/of_mdio.c | 39 ++------------------------------------ + 1 file changed, 2 insertions(+), 37 deletions(-) + +diff --git a/drivers/net/mdio/of_mdio.c b/drivers/net/mdio/of_mdio.c +index 932912c585c0..513afdd5e0ab 100644 +--- a/drivers/net/mdio/of_mdio.c ++++ b/drivers/net/mdio/of_mdio.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -98,43 +99,7 @@ EXPORT_SYMBOL(of_mdiobus_phy_device_register); + static int of_mdiobus_register_phy(struct mii_bus *mdio, + struct device_node *child, u32 addr) + { +- struct mii_timestamper *mii_ts; +- struct phy_device *phy; +- bool is_c45; +- int rc; +- u32 phy_id; +- +- mii_ts = of_find_mii_timestamper(child); +- if (IS_ERR(mii_ts)) +- return PTR_ERR(mii_ts); +- +- is_c45 = of_device_is_compatible(child, +- "ethernet-phy-ieee802.3-c45"); +- +- if (!is_c45 && !of_get_phy_id(child, &phy_id)) +- phy = phy_device_create(mdio, addr, phy_id, 0, NULL); +- else +- phy = get_phy_device(mdio, addr, is_c45); +- if (IS_ERR(phy)) { +- unregister_mii_timestamper(mii_ts); +- return PTR_ERR(phy); +- } +- +- rc = of_mdiobus_phy_device_register(mdio, phy, child, addr); +- if (rc) { +- unregister_mii_timestamper(mii_ts); +- phy_device_free(phy); +- return rc; +- } +- +- /* phy->mii_ts may already be defined by the PHY driver. A +- * mii_timestamper probed via the device tree will still have +- * precedence. +- */ +- if (mii_ts) +- phy->mii_ts = mii_ts; +- +- return 0; ++ return fwnode_mdiobus_register_phy(mdio, of_fwnode_handle(child), addr); + } + + static int of_mdiobus_register_device(struct mii_bus *mdio, +-- +2.27.0 + + +From 80efa126324583a3f0117e93f6bc9fea8871518e Mon Sep 17 00:00:00 2001 +From: Calvin Johnson +Date: Thu, 11 Mar 2021 11:50:05 +0530 +Subject: [PATCH 26/44] ACPI: utils: Introduce acpi_get_local_address() + +Introduce a wrapper around the _ADR evaluation. + +Signed-off-by: Calvin Johnson +Reviewed-by: Andy Shevchenko +--- + drivers/acpi/utils.c | 14 ++++++++++++++ + include/linux/acpi.h | 7 +++++++ + 2 files changed, 21 insertions(+) + +diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c +index d5411a166685..0d3a2b111c0f 100644 +--- a/drivers/acpi/utils.c ++++ b/drivers/acpi/utils.c +@@ -296,6 +296,20 @@ acpi_evaluate_integer(acpi_handle handle, + + EXPORT_SYMBOL(acpi_evaluate_integer); + ++int acpi_get_local_address(acpi_handle handle, u32 *addr) ++{ ++ unsigned long long adr; ++ acpi_status status; ++ ++ status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); ++ if (ACPI_FAILURE(status)) ++ return -ENODATA; ++ ++ *addr = (u32)adr; ++ return 0; ++} ++EXPORT_SYMBOL(acpi_get_local_address); ++ + acpi_status + acpi_evaluate_reference(acpi_handle handle, + acpi_string pathname, +diff --git a/include/linux/acpi.h b/include/linux/acpi.h +index 5b1dc1ad4fb3..f62724b3849c 100644 +--- a/include/linux/acpi.h ++++ b/include/linux/acpi.h +@@ -699,6 +699,8 @@ static inline u64 acpi_arch_get_root_pointer(void) + } + #endif + ++int acpi_get_local_address(acpi_handle handle, u32 *addr); ++ + #else /* !CONFIG_ACPI */ + + #define acpi_disabled 1 +@@ -946,6 +948,11 @@ static inline struct acpi_device *acpi_resource_consumer(struct resource *res) + return NULL; + } + ++static inline int acpi_get_local_address(acpi_handle handle, u32 *addr) ++{ ++ return -ENODEV; ++} ++ + #endif /* !CONFIG_ACPI */ + + #ifdef CONFIG_ACPI_HOTPLUG_IOAPIC +-- +2.27.0 + + +From d0808b96b36584f5358fd466d87cfa5924161674 Mon Sep 17 00:00:00 2001 +From: Calvin Johnson +Date: Thu, 11 Mar 2021 11:50:06 +0530 +Subject: [PATCH 27/44] net: mdio: Add ACPI support code for mdio + +Define acpi_mdiobus_register() to Register mii_bus and create PHYs for +each ACPI child node. + +Signed-off-by: Calvin Johnson +--- + MAINTAINERS | 1 + + drivers/net/mdio/Kconfig | 7 +++++ + drivers/net/mdio/Makefile | 1 + + drivers/net/mdio/acpi_mdio.c | 56 ++++++++++++++++++++++++++++++++++++ + include/linux/acpi_mdio.h | 25 ++++++++++++++++ + 5 files changed, 90 insertions(+) + create mode 100644 drivers/net/mdio/acpi_mdio.c + create mode 100644 include/linux/acpi_mdio.h + +diff --git a/MAINTAINERS b/MAINTAINERS +index 821adf657115..79be0e518f45 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -6585,6 +6585,7 @@ F: Documentation/devicetree/bindings/net/mdio* + F: Documentation/devicetree/bindings/net/qca,ar803x.yaml + F: Documentation/networking/phy.rst + F: drivers/net/mdio/ ++F: drivers/net/mdio/acpi_mdio.c + F: drivers/net/mdio/fwnode_mdio.c + F: drivers/net/mdio/of_mdio.c + F: drivers/net/pcs/ +diff --git a/drivers/net/mdio/Kconfig b/drivers/net/mdio/Kconfig +index 2d5bf5ccffb5..fc8c787b448f 100644 +--- a/drivers/net/mdio/Kconfig ++++ b/drivers/net/mdio/Kconfig +@@ -36,6 +36,13 @@ config OF_MDIO + help + OpenFirmware MDIO bus (Ethernet PHY) accessors + ++config ACPI_MDIO ++ def_tristate PHYLIB ++ depends on ACPI ++ depends on PHYLIB ++ help ++ ACPI MDIO bus (Ethernet PHY) accessors ++ + if MDIO_BUS + + config MDIO_DEVRES +diff --git a/drivers/net/mdio/Makefile b/drivers/net/mdio/Makefile +index ea5390e2ef84..e8b739a3df1c 100644 +--- a/drivers/net/mdio/Makefile ++++ b/drivers/net/mdio/Makefile +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + # Makefile for Linux MDIO bus drivers + ++obj-$(CONFIG_ACPI_MDIO) += acpi_mdio.o + obj-$(CONFIG_FWNODE_MDIO) += fwnode_mdio.o + obj-$(CONFIG_OF_MDIO) += of_mdio.o + +diff --git a/drivers/net/mdio/acpi_mdio.c b/drivers/net/mdio/acpi_mdio.c +new file mode 100644 +index 000000000000..60a86e3fc246 +--- /dev/null ++++ b/drivers/net/mdio/acpi_mdio.c +@@ -0,0 +1,56 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * ACPI helpers for the MDIO (Ethernet PHY) API ++ * ++ * This file provides helper functions for extracting PHY device information ++ * out of the ACPI ASL and using it to populate an mii_bus. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_AUTHOR("Calvin Johnson "); ++MODULE_LICENSE("GPL"); ++ ++/** ++ * acpi_mdiobus_register - Register mii_bus and create PHYs from the ACPI ASL. ++ * @mdio: pointer to mii_bus structure ++ * @fwnode: pointer to fwnode of MDIO bus. ++ * ++ * This function registers the mii_bus structure and registers a phy_device ++ * for each child node of @fwnode. ++ */ ++int acpi_mdiobus_register(struct mii_bus *mdio, struct fwnode_handle *fwnode) ++{ ++ struct fwnode_handle *child; ++ u32 addr; ++ int ret; ++ ++ /* Mask out all PHYs from auto probing. */ ++ mdio->phy_mask = GENMASK(31, 0); ++ ret = mdiobus_register(mdio); ++ if (ret) ++ return ret; ++ ++ ACPI_COMPANION_SET(&mdio->dev, to_acpi_device_node(fwnode)); ++ ++ /* Loop over the child nodes and register a phy_device for each PHY */ ++ fwnode_for_each_child_node(fwnode, child) { ++ ret = acpi_get_local_address(ACPI_HANDLE_FWNODE(child), &addr); ++ if (ret || addr >= PHY_MAX_ADDR) ++ continue; ++ ++ ret = fwnode_mdiobus_register_phy(mdio, child, addr); ++ if (ret == -ENODEV) ++ dev_err(&mdio->dev, ++ "MDIO device at address %d is missing.\n", ++ addr); ++ } ++ return 0; ++} ++EXPORT_SYMBOL(acpi_mdiobus_register); +diff --git a/include/linux/acpi_mdio.h b/include/linux/acpi_mdio.h +new file mode 100644 +index 000000000000..748d261fe2f9 +--- /dev/null ++++ b/include/linux/acpi_mdio.h +@@ -0,0 +1,25 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * ACPI helper for the MDIO (Ethernet PHY) API ++ */ ++ ++#ifndef __LINUX_ACPI_MDIO_H ++#define __LINUX_ACPI_MDIO_H ++ ++#include ++ ++#if IS_ENABLED(CONFIG_ACPI_MDIO) ++int acpi_mdiobus_register(struct mii_bus *mdio, struct fwnode_handle *fwnode); ++#else /* CONFIG_ACPI_MDIO */ ++static inline int acpi_mdiobus_register(struct mii_bus *mdio, struct fwnode_handle *fwnode) ++{ ++ /* ++ * Fall back to mdiobus_register() function to register a bus. ++ * This way, we don't have to keep compat bits around in drivers. ++ */ ++ ++ return mdiobus_register(mdio); ++} ++#endif ++ ++#endif /* __LINUX_ACPI_MDIO_H */ +-- +2.27.0 + + +From 24beb04f83e410fbb746f7a69f1f0911dc22cc8b Mon Sep 17 00:00:00 2001 +From: Calvin Johnson +Date: Thu, 11 Mar 2021 11:50:07 +0530 +Subject: [PATCH 28/44] net: mdiobus: Introduce fwnode_mdiobus_register() + +Introduce fwnode_mdiobus_register() to register PHYs on the mdiobus. +If the fwnode is DT node, then call of_mdiobus_register(). +If it is an ACPI node, then call acpi_mdiobus_register(). + +Signed-off-by: Calvin Johnson +--- + drivers/net/mdio/fwnode_mdio.c | 21 +++++++++++++++++++++ + include/linux/fwnode_mdio.h | 5 +++++ + 2 files changed, 26 insertions(+) + +diff --git a/drivers/net/mdio/fwnode_mdio.c b/drivers/net/mdio/fwnode_mdio.c +index 0982e816a6fb..523c2778b287 100644 +--- a/drivers/net/mdio/fwnode_mdio.c ++++ b/drivers/net/mdio/fwnode_mdio.c +@@ -7,6 +7,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -75,3 +76,23 @@ int fwnode_mdiobus_register_phy(struct mii_bus *bus, + return 0; + } + EXPORT_SYMBOL(fwnode_mdiobus_register_phy); ++ ++/** ++ * fwnode_mdiobus_register - Register mii_bus and create PHYs from fwnode ++ * @mdio: pointer to mii_bus structure ++ * @fwnode: pointer to fwnode of MDIO bus. ++ * ++ * This function returns of_mdiobus_register() for DT and ++ * acpi_mdiobus_register() for ACPI. ++ */ ++int fwnode_mdiobus_register(struct mii_bus *mdio, struct fwnode_handle *fwnode) ++{ ++ if (is_of_node(fwnode)) ++ return of_mdiobus_register(mdio, to_of_node(fwnode)); ++ ++ if (is_acpi_node(fwnode)) ++ return acpi_mdiobus_register(mdio, fwnode); ++ ++ return -EINVAL; ++} ++EXPORT_SYMBOL(fwnode_mdiobus_register); +diff --git a/include/linux/fwnode_mdio.h b/include/linux/fwnode_mdio.h +index 8c0392845916..20f22211260b 100644 +--- a/include/linux/fwnode_mdio.h ++++ b/include/linux/fwnode_mdio.h +@@ -12,6 +12,7 @@ + int fwnode_mdiobus_register_phy(struct mii_bus *bus, + struct fwnode_handle *child, u32 addr); + ++int fwnode_mdiobus_register(struct mii_bus *mdio, struct fwnode_handle *fwnode); + #else /* CONFIG_FWNODE_MDIO */ + static inline int fwnode_mdiobus_register_phy(struct mii_bus *bus, + struct fwnode_handle *child, +@@ -19,6 +20,10 @@ static inline int fwnode_mdiobus_register_phy(struct mii_bus *bus, + { + return -EINVAL; + } ++static int fwnode_mdiobus_register(struct mii_bus *mdio, struct fwnode_handle *fwnode) ++{ ++ return -EINVAL; ++} + #endif + + #endif /* __LINUX_FWNODE_MDIO_H */ +-- +2.27.0 + + +From c5597d49b769cf06689afd2266d109c94c6a46d9 Mon Sep 17 00:00:00 2001 +From: Calvin Johnson +Date: Thu, 11 Mar 2021 11:50:08 +0530 +Subject: [PATCH 29/44] net/fsl: Use fwnode_mdiobus_register() + +fwnode_mdiobus_register() internally takes care of both DT +and ACPI cases to register mdiobus. Replace existing +of_mdiobus_register() with fwnode_mdiobus_register(). + +Note: For both ACPI and DT cases, endianness of MDIO controller +need to be specified using "little-endian" property. + +Signed-off-by: Calvin Johnson +--- + drivers/net/ethernet/freescale/xgmac_mdio.c | 22 ++++++++++++--------- + 1 file changed, 13 insertions(+), 9 deletions(-) + +diff --git a/drivers/net/ethernet/freescale/xgmac_mdio.c b/drivers/net/ethernet/freescale/xgmac_mdio.c +index bfa2826c5545..6daf1fb2e9ea 100644 +--- a/drivers/net/ethernet/freescale/xgmac_mdio.c ++++ b/drivers/net/ethernet/freescale/xgmac_mdio.c +@@ -2,6 +2,7 @@ + * QorIQ 10G MDIO Controller + * + * Copyright 2012 Freescale Semiconductor, Inc. ++ * Copyright 2021 NXP + * + * Authors: Andy Fleming + * Timur Tabi +@@ -11,15 +12,16 @@ + * kind, whether express or implied. + */ + +-#include +-#include ++#include + #include +-#include +-#include ++#include + #include ++#include + #include +-#include + #include ++#include ++#include ++#include + + /* Number of microseconds to wait for a register to respond */ + #define TIMEOUT 1000 +@@ -243,10 +245,9 @@ static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum) + + static int xgmac_mdio_probe(struct platform_device *pdev) + { +- struct device_node *np = pdev->dev.of_node; +- struct mii_bus *bus; +- struct resource *res; + struct mdio_fsl_priv *priv; ++ struct resource *res; ++ struct mii_bus *bus; + int ret; + + /* In DPAA-1, MDIO is one of the many FMan sub-devices. The FMan +@@ -279,13 +280,16 @@ static int xgmac_mdio_probe(struct platform_device *pdev) + goto err_ioremap; + } + ++ /* For both ACPI and DT cases, endianness of MDIO controller ++ * needs to be specified using "little-endian" property. ++ */ + priv->is_little_endian = device_property_read_bool(&pdev->dev, + "little-endian"); + + priv->has_a011043 = device_property_read_bool(&pdev->dev, + "fsl,erratum-a011043"); + +- ret = of_mdiobus_register(bus, np); ++ ret = fwnode_mdiobus_register(bus, pdev->dev.fwnode); + if (ret) { + dev_err(&pdev->dev, "cannot register MDIO bus\n"); + goto err_registration; +-- +2.27.0 + + +From 13745fcb80857b1402c788e1d16e39f622c36866 Mon Sep 17 00:00:00 2001 +From: Calvin Johnson +Date: Thu, 11 Mar 2021 11:50:09 +0530 +Subject: [PATCH 30/44] net: phylink: introduce phylink_fwnode_phy_connect() + +Define phylink_fwnode_phy_connect() to connect phy specified by +a fwnode to a phylink instance. + +Signed-off-by: Calvin Johnson +--- + drivers/net/phy/phylink.c | 54 +++++++++++++++++++++++++++++++++++++++ + include/linux/phylink.h | 3 +++ + 2 files changed, 57 insertions(+) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index fe2296fdda19..479ffbf335a3 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -5,6 +5,7 @@ + * + * Copyright (C) 2015 Russell King + */ ++#include + #include + #include + #include +@@ -1120,6 +1121,59 @@ int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn, + } + EXPORT_SYMBOL_GPL(phylink_of_phy_connect); + ++/** ++ * phylink_fwnode_phy_connect() - connect the PHY specified in the fwnode. ++ * @pl: a pointer to a &struct phylink returned from phylink_create() ++ * @fwnode: a pointer to a &struct fwnode_handle. ++ * @flags: PHY-specific flags to communicate to the PHY device driver ++ * ++ * Connect the phy specified @fwnode to the phylink instance specified ++ * by @pl. ++ * ++ * Returns 0 on success or a negative errno. ++ */ ++int phylink_fwnode_phy_connect(struct phylink *pl, ++ struct fwnode_handle *fwnode, ++ u32 flags) ++{ ++ struct fwnode_handle *phy_fwnode; ++ struct phy_device *phy_dev; ++ int ret; ++ ++ /* Fixed links and 802.3z are handled without needing a PHY */ ++ if (pl->cfg_link_an_mode == MLO_AN_FIXED || ++ (pl->cfg_link_an_mode == MLO_AN_INBAND && ++ phy_interface_mode_is_8023z(pl->link_interface))) ++ return 0; ++ ++ phy_fwnode = fwnode_get_phy_node(fwnode); ++ if (IS_ERR(phy_fwnode)) { ++ if (pl->cfg_link_an_mode == MLO_AN_PHY) ++ return -ENODEV; ++ return 0; ++ } ++ ++ phy_dev = fwnode_phy_find_device(phy_fwnode); ++ /* We're done with the phy_node handle */ ++ fwnode_handle_put(phy_fwnode); ++ if (!phy_dev) ++ return -ENODEV; ++ ++ ret = phy_attach_direct(pl->netdev, phy_dev, flags, ++ pl->link_interface); ++ if (ret) { ++ phy_device_free(phy_dev); ++ return ret; ++ } ++ ++ ret = phylink_bringup_phy(pl, phy_dev, pl->link_config.interface); ++ if (ret) ++ phy_detach(phy_dev); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(phylink_fwnode_phy_connect); ++ + /** + * phylink_disconnect_phy() - disconnect any PHY attached to the phylink + * instance. +diff --git a/include/linux/phylink.h b/include/linux/phylink.h +index d81a714cfbbd..75d4f99090fd 100644 +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -439,6 +439,9 @@ void phylink_destroy(struct phylink *); + + int phylink_connect_phy(struct phylink *, struct phy_device *); + int phylink_of_phy_connect(struct phylink *, struct device_node *, u32 flags); ++int phylink_fwnode_phy_connect(struct phylink *pl, ++ struct fwnode_handle *fwnode, ++ u32 flags); + void phylink_disconnect_phy(struct phylink *); + + void phylink_mac_change(struct phylink *, bool up); +-- +2.27.0 + + +From d34f59033bb4e370047886e26463246cc9e64ec3 Mon Sep 17 00:00:00 2001 +From: Calvin Johnson +Date: Thu, 11 Mar 2021 11:50:10 +0530 +Subject: [PATCH 31/44] net: phylink: Refactor phylink_of_phy_connect() + +Refactor phylink_of_phy_connect() to use phylink_fwnode_phy_connect(). + +Signed-off-by: Calvin Johnson +--- + drivers/net/phy/phylink.c | 39 +-------------------------------------- + 1 file changed, 1 insertion(+), 38 deletions(-) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index 479ffbf335a3..2aa31ff60d9b 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1080,44 +1080,7 @@ EXPORT_SYMBOL_GPL(phylink_connect_phy); + int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn, + u32 flags) + { +- struct device_node *phy_node; +- struct phy_device *phy_dev; +- int ret; +- +- /* Fixed links and 802.3z are handled without needing a PHY */ +- if (pl->cfg_link_an_mode == MLO_AN_FIXED || +- (pl->cfg_link_an_mode == MLO_AN_INBAND && +- phy_interface_mode_is_8023z(pl->link_interface))) +- return 0; +- +- phy_node = of_parse_phandle(dn, "phy-handle", 0); +- if (!phy_node) +- phy_node = of_parse_phandle(dn, "phy", 0); +- if (!phy_node) +- phy_node = of_parse_phandle(dn, "phy-device", 0); +- +- if (!phy_node) { +- if (pl->cfg_link_an_mode == MLO_AN_PHY) +- return -ENODEV; +- return 0; +- } +- +- phy_dev = of_phy_find_device(phy_node); +- /* We're done with the phy_node handle */ +- of_node_put(phy_node); +- if (!phy_dev) +- return -ENODEV; +- +- ret = phy_attach_direct(pl->netdev, phy_dev, flags, +- pl->link_interface); +- if (ret) +- return ret; +- +- ret = phylink_bringup_phy(pl, phy_dev, pl->link_config.interface); +- if (ret) +- phy_detach(phy_dev); +- +- return ret; ++ return phylink_fwnode_phy_connect(pl, of_fwnode_handle(dn), flags); + } + EXPORT_SYMBOL_GPL(phylink_of_phy_connect); + +-- +2.27.0 + + +From 15b6e2fbb96d2be7517c229b2dba386ddb79ca72 Mon Sep 17 00:00:00 2001 +From: Calvin Johnson +Date: Thu, 11 Mar 2021 11:50:11 +0530 +Subject: [PATCH 32/44] net: dpaa2-mac: Add ACPI support for DPAA2 MAC driver + +Modify dpaa2_mac_get_node() to get the dpmac fwnode from either +DT or ACPI. + +Modify dpaa2_mac_get_if_mode() to get interface mode from dpmac_node +which is a fwnode. + +Modify dpaa2_pcs_create() to create pcs from dpmac_node fwnode. + +Modify dpaa2_mac_connect() to support ACPI along with DT. + +Signed-off-by: Calvin Johnson +--- + .../net/ethernet/freescale/dpaa2/dpaa2-mac.c | 84 +++++++++++-------- + 1 file changed, 50 insertions(+), 34 deletions(-) + +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +index 50dd302abcf4..efd804d4a1bf 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +@@ -1,6 +1,9 @@ + // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) + /* Copyright 2019 NXP */ + ++#include ++#include ++ + #include "dpaa2-eth.h" + #include "dpaa2-mac.h" + +@@ -34,39 +37,51 @@ static int phy_mode(enum dpmac_eth_if eth_if, phy_interface_t *if_mode) + return 0; + } + +-/* Caller must call of_node_put on the returned value */ +-static struct device_node *dpaa2_mac_get_node(u16 dpmac_id) ++static struct fwnode_handle *dpaa2_mac_get_node(struct device *dev, ++ u16 dpmac_id) + { +- struct device_node *dpmacs, *dpmac = NULL; +- u32 id; ++ struct fwnode_handle *fwnode, *parent, *child = NULL; ++ struct device_node *dpmacs = NULL; + int err; ++ u32 id; + +- dpmacs = of_find_node_by_name(NULL, "dpmacs"); +- if (!dpmacs) +- return NULL; ++ fwnode = dev_fwnode(dev->parent); ++ if (is_of_node(fwnode)) { ++ dpmacs = of_find_node_by_name(NULL, "dpmacs"); ++ if (!dpmacs) ++ return NULL; ++ parent = of_fwnode_handle(dpmacs); ++ } else if (is_acpi_node(fwnode)) { ++ parent = fwnode; ++ } + +- while ((dpmac = of_get_next_child(dpmacs, dpmac)) != NULL) { +- err = of_property_read_u32(dpmac, "reg", &id); ++ fwnode_for_each_child_node(parent, child) { ++ err = -EINVAL; ++ if (is_acpi_device_node(child)) ++ err = acpi_get_local_address(ACPI_HANDLE_FWNODE(child), &id); ++ else if (is_of_node(child)) ++ err = of_property_read_u32(to_of_node(child), "reg", &id); + if (err) + continue; +- if (id == dpmac_id) +- break; +- } + ++ if (id == dpmac_id) { ++ of_node_put(dpmacs); ++ return child; ++ } ++ } + of_node_put(dpmacs); +- +- return dpmac; ++ return NULL; + } + +-static int dpaa2_mac_get_if_mode(struct device_node *node, ++static int dpaa2_mac_get_if_mode(struct fwnode_handle *dpmac_node, + struct dpmac_attr attr) + { + phy_interface_t if_mode; + int err; + +- err = of_get_phy_mode(node, &if_mode); +- if (!err) +- return if_mode; ++ err = fwnode_get_phy_mode(dpmac_node); ++ if (err > 0) ++ return err; + + err = phy_mode(attr.eth_if, &if_mode); + if (!err) +@@ -255,26 +270,27 @@ bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev, + } + + static int dpaa2_pcs_create(struct dpaa2_mac *mac, +- struct device_node *dpmac_node, int id) ++ struct fwnode_handle *dpmac_node, ++ int id) + { + struct mdio_device *mdiodev; +- struct device_node *node; ++ struct fwnode_handle *node; + +- node = of_parse_phandle(dpmac_node, "pcs-handle", 0); +- if (!node) { ++ node = fwnode_find_reference(dpmac_node, "pcs-handle", 0); ++ if (IS_ERR(node)) { + /* do not error out on old DTS files */ + netdev_warn(mac->net_dev, "pcs-handle node not found\n"); + return 0; + } + +- if (!of_device_is_available(node)) { ++ if (!fwnode_device_is_available(node)) { + netdev_err(mac->net_dev, "pcs-handle node not available\n"); +- of_node_put(node); ++ fwnode_handle_put(node); + return -ENODEV; + } + +- mdiodev = of_mdio_find_device(node); +- of_node_put(node); ++ mdiodev = fwnode_mdio_find_device(node); ++ fwnode_handle_put(node); + if (!mdiodev) + return -EPROBE_DEFER; + +@@ -303,13 +319,13 @@ static void dpaa2_pcs_destroy(struct dpaa2_mac *mac) + int dpaa2_mac_connect(struct dpaa2_mac *mac) + { + struct net_device *net_dev = mac->net_dev; +- struct device_node *dpmac_node; ++ struct fwnode_handle *dpmac_node = NULL; + struct phylink *phylink; + int err; + + mac->if_link_type = mac->attr.link_type; + +- dpmac_node = dpaa2_mac_get_node(mac->attr.id); ++ dpmac_node = dpaa2_mac_get_node(&mac->mc_dev->dev, mac->attr.id); + if (!dpmac_node) { + netdev_err(net_dev, "No dpmac@%d node found.\n", mac->attr.id); + return -ENODEV; +@@ -326,7 +342,7 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac) + * error out if the interface mode requests them and there is no PHY + * to act upon them + */ +- if (of_phy_is_fixed_link(dpmac_node) && ++ if (of_phy_is_fixed_link(to_of_node(dpmac_node)) && + (mac->if_mode == PHY_INTERFACE_MODE_RGMII_ID || + mac->if_mode == PHY_INTERFACE_MODE_RGMII_RXID || + mac->if_mode == PHY_INTERFACE_MODE_RGMII_TXID)) { +@@ -346,7 +362,7 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac) + mac->phylink_config.type = PHYLINK_NETDEV; + + phylink = phylink_create(&mac->phylink_config, +- of_fwnode_handle(dpmac_node), mac->if_mode, ++ dpmac_node, mac->if_mode, + &dpaa2_mac_phylink_ops); + if (IS_ERR(phylink)) { + err = PTR_ERR(phylink); +@@ -357,13 +373,13 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac) + if (mac->pcs) + phylink_set_pcs(mac->phylink, &mac->pcs->pcs); + +- err = phylink_of_phy_connect(mac->phylink, dpmac_node, 0); ++ err = phylink_fwnode_phy_connect(mac->phylink, dpmac_node, 0); + if (err) { +- netdev_err(net_dev, "phylink_of_phy_connect() = %d\n", err); ++ netdev_err(net_dev, "phylink_fwnode_phy_connect() = %d\n", err); + goto err_phylink_destroy; + } + +- of_node_put(dpmac_node); ++ fwnode_handle_put(dpmac_node); + + return 0; + +@@ -372,7 +388,7 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac) + err_pcs_destroy: + dpaa2_pcs_destroy(mac); + err_put_node: +- of_node_put(dpmac_node); ++ fwnode_handle_put(dpmac_node); + + return err; + } +-- +2.27.0 + + +From a523b29f4a96892a4747ed68b08194a6c8061508 Mon Sep 17 00:00:00 2001 +From: Ioana Ciornei +Date: Thu, 14 Jan 2021 19:07:48 +0200 +Subject: [PATCH 33/44] bus: fsl-mc: move fsl_mc_command struct in a uapi + header + +Define "struct fsl_mc_command" as a structure that can cross the +user/kernel boundary. + +Signed-off-by: Ioana Ciornei +--- + MAINTAINERS | 1 + + include/linux/fsl/mc.h | 8 +------- + include/uapi/linux/fsl_mc.h | 25 +++++++++++++++++++++++++ + 3 files changed, 27 insertions(+), 7 deletions(-) + create mode 100644 include/uapi/linux/fsl_mc.h + +diff --git a/MAINTAINERS b/MAINTAINERS +index 79be0e518f45..e0a8bdb3567c 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -14425,6 +14425,7 @@ S: Maintained + F: Documentation/devicetree/bindings/misc/fsl,qoriq-mc.txt + F: Documentation/networking/device_drivers/ethernet/freescale/dpaa2/overview.rst + F: drivers/bus/fsl-mc/ ++F: include/uapi/linux/fsl_mc.h + + QT1010 MEDIA DRIVER + M: Antti Palosaari +diff --git a/include/linux/fsl/mc.h b/include/linux/fsl/mc.h +index db244874e834..63b56aba925a 100644 +--- a/include/linux/fsl/mc.h ++++ b/include/linux/fsl/mc.h +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + + #define FSL_MC_VENDOR_FREESCALE 0x1957 + +@@ -209,8 +210,6 @@ struct fsl_mc_device { + #define to_fsl_mc_device(_dev) \ + container_of(_dev, struct fsl_mc_device, dev) + +-#define MC_CMD_NUM_OF_PARAMS 7 +- + struct mc_cmd_header { + u8 src_id; + u8 flags_hw; +@@ -220,11 +219,6 @@ struct mc_cmd_header { + __le16 cmd_id; + }; + +-struct fsl_mc_command { +- __le64 header; +- __le64 params[MC_CMD_NUM_OF_PARAMS]; +-}; +- + enum mc_cmd_status { + MC_CMD_STATUS_OK = 0x0, /* Completed successfully */ + MC_CMD_STATUS_READY = 0x1, /* Ready to be processed */ +diff --git a/include/uapi/linux/fsl_mc.h b/include/uapi/linux/fsl_mc.h +new file mode 100644 +index 000000000000..cf56d46f052e +--- /dev/null ++++ b/include/uapi/linux/fsl_mc.h +@@ -0,0 +1,25 @@ ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ ++/* ++ * Management Complex (MC) userspace public interface ++ * ++ * Copyright 2021 NXP ++ * ++ */ ++#ifndef _UAPI_FSL_MC_H_ ++#define _UAPI_FSL_MC_H_ ++ ++#include ++ ++#define MC_CMD_NUM_OF_PARAMS 7 ++ ++/** ++ * struct fsl_mc_command - Management Complex (MC) command structure ++ * @header: MC command header ++ * @params: MC command parameters ++ */ ++struct fsl_mc_command { ++ __le64 header; ++ __le64 params[MC_CMD_NUM_OF_PARAMS]; ++}; ++ ++#endif /* _UAPI_FSL_MC_H_ */ +-- +2.27.0 + + +From a60453c68943570d705d78a1ffe0581154dca0c1 Mon Sep 17 00:00:00 2001 +From: Ioana Ciornei +Date: Thu, 14 Jan 2021 19:07:49 +0200 +Subject: [PATCH 34/44] bus: fsl-mc: export mc_cmd_hdr_read_cmdid() to the + fsl-mc bus + +Export the mc_cmd_hdr_read_cmdid() function to the entire fsl-mc bus +since it will be needed in the following patch. + +Signed-off-by: Ioana Ciornei +--- + drivers/bus/fsl-mc/fsl-mc-private.h | 2 ++ + drivers/bus/fsl-mc/mc-sys.c | 2 +- + 2 files changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/bus/fsl-mc/fsl-mc-private.h b/drivers/bus/fsl-mc/fsl-mc-private.h +index c932387641fa..be686c363121 100644 +--- a/drivers/bus/fsl-mc/fsl-mc-private.h ++++ b/drivers/bus/fsl-mc/fsl-mc-private.h +@@ -612,4 +612,6 @@ void fsl_mc_get_root_dprc(struct device *dev, + struct fsl_mc_device *fsl_mc_device_lookup(struct fsl_mc_obj_desc *obj_desc, + struct fsl_mc_device *mc_bus_dev); + ++u16 mc_cmd_hdr_read_cmdid(struct fsl_mc_command *cmd); ++ + #endif /* _FSL_MC_PRIVATE_H_ */ +diff --git a/drivers/bus/fsl-mc/mc-sys.c b/drivers/bus/fsl-mc/mc-sys.c +index 85a0225db522..b291b35e3884 100644 +--- a/drivers/bus/fsl-mc/mc-sys.c ++++ b/drivers/bus/fsl-mc/mc-sys.c +@@ -35,7 +35,7 @@ static enum mc_cmd_status mc_cmd_hdr_read_status(struct fsl_mc_command *cmd) + return (enum mc_cmd_status)hdr->status; + } + +-static u16 mc_cmd_hdr_read_cmdid(struct fsl_mc_command *cmd) ++u16 mc_cmd_hdr_read_cmdid(struct fsl_mc_command *cmd) + { + struct mc_cmd_header *hdr = (struct mc_cmd_header *)&cmd->header; + u16 cmd_id = le16_to_cpu(hdr->cmd_id); +-- +2.27.0 + + +From 4e2a5dcbaba4070b3920865836c2f597762a6b30 Mon Sep 17 00:00:00 2001 +From: Ioana Ciornei +Date: Thu, 14 Jan 2021 19:07:50 +0200 +Subject: [PATCH 35/44] bus: fsl-mc: add fsl-mc userspace support + +Adding userspace support for the MC (Management Complex) means exporting +an ioctl capable device file representing the root resource container. + +This new functionality in the fsl-mc bus driver intends to provide +userspace applications an interface to interact with the MC firmware. + +Commands that are composed in userspace are sent to the MC firmware +through the FSL_MC_SEND_MC_COMMAND ioctl. By default the implicit MC +I/O portal is used for this operation, but if the implicit one is busy, +a dynamic portal is allocated and then freed upon execution. + +The command received through the ioctl interface is checked against a +known whitelist of accepted MC commands. Commands that attempt a change +in hardware configuration will need CAP_NET_ADMIN, while commands used +in debugging do not need it. + +Signed-off-by: Ioana Ciornei +--- + .../userspace-api/ioctl/ioctl-number.rst | 1 + + drivers/bus/fsl-mc/Kconfig | 7 + + drivers/bus/fsl-mc/Makefile | 3 + + drivers/bus/fsl-mc/dprc-driver.c | 12 + + drivers/bus/fsl-mc/fsl-mc-private.h | 39 ++ + drivers/bus/fsl-mc/fsl-mc-uapi.c | 547 ++++++++++++++++++ + include/uapi/linux/fsl_mc.h | 9 + + 7 files changed, 618 insertions(+) + create mode 100644 drivers/bus/fsl-mc/fsl-mc-uapi.c + +diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst +index 55a2d9b2ce33..3851e64b067b 100644 +--- a/Documentation/userspace-api/ioctl/ioctl-number.rst ++++ b/Documentation/userspace-api/ioctl/ioctl-number.rst +@@ -180,6 +180,7 @@ Code Seq# Include File Comments + 'R' 00-1F linux/random.h conflict! + 'R' 01 linux/rfkill.h conflict! + 'R' C0-DF net/bluetooth/rfcomm.h ++'R' E0 uapi/linux/fsl_mc.h + 'S' all linux/cdrom.h conflict! + 'S' 80-81 scsi/scsi_ioctl.h conflict! + 'S' 82-FF scsi/scsi.h conflict! +diff --git a/drivers/bus/fsl-mc/Kconfig b/drivers/bus/fsl-mc/Kconfig +index c23c77c9b705..b1fd55901c50 100644 +--- a/drivers/bus/fsl-mc/Kconfig ++++ b/drivers/bus/fsl-mc/Kconfig +@@ -14,3 +14,10 @@ config FSL_MC_BUS + architecture. The fsl-mc bus driver handles discovery of + DPAA2 objects (which are represented as Linux devices) and + binding objects to drivers. ++ ++config FSL_MC_UAPI_SUPPORT ++ bool "Management Complex (MC) userspace support" ++ depends on FSL_MC_BUS ++ help ++ Provides userspace support for interrogating, creating, destroying or ++ configuring DPAA2 objects exported by the Management Complex. +diff --git a/drivers/bus/fsl-mc/Makefile b/drivers/bus/fsl-mc/Makefile +index 3c518c7e8374..4ae292a30e53 100644 +--- a/drivers/bus/fsl-mc/Makefile ++++ b/drivers/bus/fsl-mc/Makefile +@@ -16,3 +16,6 @@ mc-bus-driver-objs := fsl-mc-bus.o \ + fsl-mc-allocator.o \ + fsl-mc-msi.o \ + dpmcp.o ++ ++# MC userspace support ++obj-$(CONFIG_FSL_MC_UAPI_SUPPORT) += fsl-mc-uapi.o +diff --git a/drivers/bus/fsl-mc/dprc-driver.c b/drivers/bus/fsl-mc/dprc-driver.c +index 91dc015963a8..ca2ce38a5d51 100644 +--- a/drivers/bus/fsl-mc/dprc-driver.c ++++ b/drivers/bus/fsl-mc/dprc-driver.c +@@ -603,6 +603,7 @@ int dprc_setup(struct fsl_mc_device *mc_dev) + struct irq_domain *mc_msi_domain; + bool mc_io_created = false; + bool msi_domain_set = false; ++ bool uapi_created = false; + u16 major_ver, minor_ver; + size_t region_size; + int error; +@@ -635,6 +636,11 @@ int dprc_setup(struct fsl_mc_device *mc_dev) + return error; + + mc_io_created = true; ++ } else { ++ error = fsl_mc_uapi_create_device_file(mc_bus); ++ if (error < 0) ++ return -EPROBE_DEFER; ++ uapi_created = true; + } + + mc_msi_domain = fsl_mc_find_msi_domain(&mc_dev->dev); +@@ -694,6 +700,9 @@ int dprc_setup(struct fsl_mc_device *mc_dev) + mc_dev->mc_io = NULL; + } + ++ if (uapi_created) ++ fsl_mc_uapi_remove_device_file(mc_bus); ++ + return error; + } + EXPORT_SYMBOL_GPL(dprc_setup); +@@ -765,6 +774,7 @@ static void dprc_teardown_irq(struct fsl_mc_device *mc_dev) + + int dprc_cleanup(struct fsl_mc_device *mc_dev) + { ++ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); + int error; + + /* this function should be called only for DPRCs, it +@@ -795,6 +805,8 @@ int dprc_cleanup(struct fsl_mc_device *mc_dev) + if (!fsl_mc_is_root_dprc(&mc_dev->dev)) { + fsl_destroy_mc_io(mc_dev->mc_io); + mc_dev->mc_io = NULL; ++ } else { ++ fsl_mc_uapi_remove_device_file(mc_bus); + } + + return 0; +diff --git a/drivers/bus/fsl-mc/fsl-mc-private.h b/drivers/bus/fsl-mc/fsl-mc-private.h +index be686c363121..6293a24de456 100644 +--- a/drivers/bus/fsl-mc/fsl-mc-private.h ++++ b/drivers/bus/fsl-mc/fsl-mc-private.h +@@ -10,6 +10,8 @@ + + #include + #include ++#include ++#include + + /* + * Data Path Management Complex (DPMNG) General API +@@ -542,6 +544,22 @@ struct fsl_mc_resource_pool { + struct fsl_mc_bus *mc_bus; + }; + ++/** ++ * struct fsl_mc_uapi - information associated with a device file ++ * @misc: struct miscdevice linked to the root dprc ++ * @device: newly created device in /dev ++ * @mutex: mutex lock to serialize the open/release operations ++ * @local_instance_in_use: local MC I/O instance in use or not ++ * @static_mc_io: pointer to the static MC I/O object ++ */ ++struct fsl_mc_uapi { ++ struct miscdevice misc; ++ struct device *device; ++ struct mutex mutex; /* serialize open/release operations */ ++ u32 local_instance_in_use; ++ struct fsl_mc_io *static_mc_io; ++}; ++ + /** + * struct fsl_mc_bus - logical bus that corresponds to a physical DPRC + * @mc_dev: fsl-mc device for the bus device itself. +@@ -551,6 +569,7 @@ struct fsl_mc_resource_pool { + * @irq_resources: Pointer to array of IRQ objects for the IRQ pool + * @scan_mutex: Serializes bus scanning + * @dprc_attr: DPRC attributes ++ * @uapi_misc: struct that abstracts the interaction with userspace + */ + struct fsl_mc_bus { + struct fsl_mc_device mc_dev; +@@ -558,6 +577,7 @@ struct fsl_mc_bus { + struct fsl_mc_device_irq *irq_resources; + struct mutex scan_mutex; /* serializes bus scanning */ + struct dprc_attributes dprc_attr; ++ struct fsl_mc_uapi uapi_misc; + }; + + #define to_fsl_mc_bus(_mc_dev) \ +@@ -614,4 +634,23 @@ struct fsl_mc_device *fsl_mc_device_lookup(struct fsl_mc_obj_desc *obj_desc, + + u16 mc_cmd_hdr_read_cmdid(struct fsl_mc_command *cmd); + ++#ifdef CONFIG_FSL_MC_UAPI_SUPPORT ++ ++int fsl_mc_uapi_create_device_file(struct fsl_mc_bus *mc_bus); ++ ++void fsl_mc_uapi_remove_device_file(struct fsl_mc_bus *mc_bus); ++ ++#else ++ ++static inline int fsl_mc_uapi_create_device_file(struct fsl_mc_bus *mc_bus) ++{ ++ return 0; ++} ++ ++static inline void fsl_mc_uapi_remove_device_file(struct fsl_mc_bus *mc_bus) ++{ ++} ++ ++#endif ++ + #endif /* _FSL_MC_PRIVATE_H_ */ +diff --git a/drivers/bus/fsl-mc/fsl-mc-uapi.c b/drivers/bus/fsl-mc/fsl-mc-uapi.c +new file mode 100644 +index 000000000000..eeb988c9f4bb +--- /dev/null ++++ b/drivers/bus/fsl-mc/fsl-mc-uapi.c +@@ -0,0 +1,547 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Management Complex (MC) userspace support ++ * ++ * Copyright 2021 NXP ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "fsl-mc-private.h" ++ ++struct uapi_priv_data { ++ struct fsl_mc_uapi *uapi; ++ struct fsl_mc_io *mc_io; ++}; ++ ++struct fsl_mc_cmd_desc { ++ u16 cmdid_value; ++ u16 cmdid_mask; ++ int size; ++ bool token; ++ int flags; ++}; ++ ++#define FSL_MC_CHECK_MODULE_ID BIT(0) ++#define FSL_MC_CAP_NET_ADMIN_NEEDED BIT(1) ++ ++enum fsl_mc_cmd_index { ++ DPDBG_DUMP = 0, ++ DPDBG_SET, ++ DPRC_GET_CONTAINER_ID, ++ DPRC_CREATE_CONT, ++ DPRC_DESTROY_CONT, ++ DPRC_ASSIGN, ++ DPRC_UNASSIGN, ++ DPRC_GET_OBJ_COUNT, ++ DPRC_GET_OBJ, ++ DPRC_GET_RES_COUNT, ++ DPRC_GET_RES_IDS, ++ DPRC_SET_OBJ_LABEL, ++ DPRC_SET_LOCKED, ++ DPRC_CONNECT, ++ DPRC_DISCONNECT, ++ DPRC_GET_POOL, ++ DPRC_GET_POOL_COUNT, ++ DPRC_GET_CONNECTION, ++ DPCI_GET_LINK_STATE, ++ DPCI_GET_PEER_ATTR, ++ DPAIOP_GET_SL_VERSION, ++ DPAIOP_GET_STATE, ++ DPMNG_GET_VERSION, ++ DPSECI_GET_TX_QUEUE, ++ DPMAC_GET_COUNTER, ++ DPMAC_GET_MAC_ADDR, ++ DPNI_SET_PRIM_MAC, ++ DPNI_GET_PRIM_MAC, ++ DPNI_GET_STATISTICS, ++ DPNI_GET_LINK_STATE, ++ GET_ATTR, ++ GET_IRQ_MASK, ++ GET_IRQ_STATUS, ++ CLOSE, ++ OPEN, ++ GET_API_VERSION, ++ DESTROY, ++ CREATE, ++}; ++ ++static struct fsl_mc_cmd_desc fsl_mc_accepted_cmds[] = { ++ [DPDBG_DUMP] = { ++ .cmdid_value = 0x1300, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 28, ++ }, ++ [DPDBG_SET] = { ++ .cmdid_value = 0x1400, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 28, ++ }, ++ [DPRC_GET_CONTAINER_ID] = { ++ .cmdid_value = 0x8300, ++ .cmdid_mask = 0xFFF0, ++ .token = false, ++ .size = 8, ++ }, ++ [DPRC_CREATE_CONT] = { ++ .cmdid_value = 0x1510, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 40, ++ .flags = FSL_MC_CAP_NET_ADMIN_NEEDED, ++ }, ++ [DPRC_DESTROY_CONT] = { ++ .cmdid_value = 0x1520, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 12, ++ .flags = FSL_MC_CAP_NET_ADMIN_NEEDED, ++ }, ++ [DPRC_ASSIGN] = { ++ .cmdid_value = 0x1570, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 40, ++ .flags = FSL_MC_CAP_NET_ADMIN_NEEDED, ++ }, ++ [DPRC_UNASSIGN] = { ++ .cmdid_value = 0x1580, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 40, ++ .flags = FSL_MC_CAP_NET_ADMIN_NEEDED, ++ }, ++ [DPRC_GET_OBJ_COUNT] = { ++ .cmdid_value = 0x1590, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 16, ++ }, ++ [DPRC_GET_OBJ] = { ++ .cmdid_value = 0x15A0, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 12, ++ }, ++ [DPRC_GET_RES_COUNT] = { ++ .cmdid_value = 0x15B0, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 32, ++ }, ++ [DPRC_GET_RES_IDS] = { ++ .cmdid_value = 0x15C0, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 40, ++ }, ++ [DPRC_SET_OBJ_LABEL] = { ++ .cmdid_value = 0x1610, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 48, ++ .flags = FSL_MC_CAP_NET_ADMIN_NEEDED, ++ }, ++ [DPRC_SET_LOCKED] = { ++ .cmdid_value = 0x16B0, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 16, ++ .flags = FSL_MC_CAP_NET_ADMIN_NEEDED, ++ }, ++ [DPRC_CONNECT] = { ++ .cmdid_value = 0x1670, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 56, ++ .flags = FSL_MC_CAP_NET_ADMIN_NEEDED, ++ }, ++ [DPRC_DISCONNECT] = { ++ .cmdid_value = 0x1680, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 32, ++ .flags = FSL_MC_CAP_NET_ADMIN_NEEDED, ++ }, ++ [DPRC_GET_POOL] = { ++ .cmdid_value = 0x1690, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 12, ++ }, ++ [DPRC_GET_POOL_COUNT] = { ++ .cmdid_value = 0x16A0, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 8, ++ }, ++ [DPRC_GET_CONNECTION] = { ++ .cmdid_value = 0x16C0, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 32, ++ }, ++ ++ [DPCI_GET_LINK_STATE] = { ++ .cmdid_value = 0x0E10, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 8, ++ }, ++ [DPCI_GET_PEER_ATTR] = { ++ .cmdid_value = 0x0E20, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 8, ++ }, ++ [DPAIOP_GET_SL_VERSION] = { ++ .cmdid_value = 0x2820, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 8, ++ }, ++ [DPAIOP_GET_STATE] = { ++ .cmdid_value = 0x2830, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 8, ++ }, ++ [DPMNG_GET_VERSION] = { ++ .cmdid_value = 0x8310, ++ .cmdid_mask = 0xFFF0, ++ .token = false, ++ .size = 8, ++ }, ++ [DPSECI_GET_TX_QUEUE] = { ++ .cmdid_value = 0x1970, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 14, ++ }, ++ [DPMAC_GET_COUNTER] = { ++ .cmdid_value = 0x0c40, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 9, ++ }, ++ [DPMAC_GET_MAC_ADDR] = { ++ .cmdid_value = 0x0c50, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 8, ++ }, ++ [DPNI_SET_PRIM_MAC] = { ++ .cmdid_value = 0x2240, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 16, ++ .flags = FSL_MC_CAP_NET_ADMIN_NEEDED, ++ }, ++ [DPNI_GET_PRIM_MAC] = { ++ .cmdid_value = 0x2250, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 8, ++ }, ++ [DPNI_GET_STATISTICS] = { ++ .cmdid_value = 0x25D0, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 10, ++ }, ++ [DPNI_GET_LINK_STATE] = { ++ .cmdid_value = 0x2150, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 8, ++ }, ++ [GET_ATTR] = { ++ .cmdid_value = 0x0040, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 8, ++ }, ++ [GET_IRQ_MASK] = { ++ .cmdid_value = 0x0150, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 13, ++ }, ++ [GET_IRQ_STATUS] = { ++ .cmdid_value = 0x0160, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 13, ++ }, ++ [CLOSE] = { ++ .cmdid_value = 0x8000, ++ .cmdid_mask = 0xFFF0, ++ .token = true, ++ .size = 8, ++ }, ++ ++ /* Common commands amongst all types of objects. Must be checked last. */ ++ [OPEN] = { ++ .cmdid_value = 0x8000, ++ .cmdid_mask = 0xFC00, ++ .token = false, ++ .size = 12, ++ .flags = FSL_MC_CHECK_MODULE_ID, ++ }, ++ [GET_API_VERSION] = { ++ .cmdid_value = 0xA000, ++ .cmdid_mask = 0xFC00, ++ .token = false, ++ .size = 8, ++ .flags = FSL_MC_CHECK_MODULE_ID, ++ }, ++ [DESTROY] = { ++ .cmdid_value = 0x9800, ++ .cmdid_mask = 0xFC00, ++ .token = true, ++ .size = 12, ++ .flags = FSL_MC_CHECK_MODULE_ID | FSL_MC_CAP_NET_ADMIN_NEEDED, ++ }, ++ [CREATE] = { ++ .cmdid_value = 0x9000, ++ .cmdid_mask = 0xFC00, ++ .token = true, ++ .size = 64, ++ .flags = FSL_MC_CHECK_MODULE_ID | FSL_MC_CAP_NET_ADMIN_NEEDED, ++ }, ++}; ++ ++#define FSL_MC_NUM_ACCEPTED_CMDS ARRAY_SIZE(fsl_mc_accepted_cmds) ++ ++#define FSL_MC_MAX_MODULE_ID 0x10 ++ ++static int fsl_mc_command_check(struct fsl_mc_device *mc_dev, ++ struct fsl_mc_command *mc_cmd) ++{ ++ struct fsl_mc_cmd_desc *desc = NULL; ++ int mc_cmd_max_size, i; ++ bool token_provided; ++ u16 cmdid, module_id; ++ char *mc_cmd_end; ++ char sum = 0; ++ ++ /* Check if this is an accepted MC command */ ++ cmdid = mc_cmd_hdr_read_cmdid(mc_cmd); ++ for (i = 0; i < FSL_MC_NUM_ACCEPTED_CMDS; i++) { ++ desc = &fsl_mc_accepted_cmds[i]; ++ if ((cmdid & desc->cmdid_mask) == desc->cmdid_value) ++ break; ++ } ++ if (!desc) { ++ dev_err(&mc_dev->dev, "MC command 0x%04x: cmdid not accepted\n", cmdid); ++ return -EACCES; ++ } ++ ++ /* Check if the size of the command is honored. Anything beyond the ++ * last valid byte of the command should be zeroed. ++ */ ++ mc_cmd_max_size = sizeof(*mc_cmd); ++ mc_cmd_end = ((char *)mc_cmd) + desc->size; ++ for (i = desc->size; i < mc_cmd_max_size; i++) ++ sum |= *mc_cmd_end++; ++ if (sum) { ++ dev_err(&mc_dev->dev, "MC command 0x%04x: garbage beyond max size of %d bytes!\n", ++ cmdid, desc->size); ++ return -EACCES; ++ } ++ ++ /* Some MC commands request a token to be passed so that object ++ * identification is possible. Check if the token passed in the command ++ * is as expected. ++ */ ++ token_provided = mc_cmd_hdr_read_token(mc_cmd) ? true : false; ++ if (token_provided != desc->token) { ++ dev_err(&mc_dev->dev, "MC command 0x%04x: token 0x%04x is invalid!\n", ++ cmdid, mc_cmd_hdr_read_token(mc_cmd)); ++ return -EACCES; ++ } ++ ++ /* If needed, check if the module ID passed is valid */ ++ if (desc->flags & FSL_MC_CHECK_MODULE_ID) { ++ /* The module ID is represented by bits [4:9] from the cmdid */ ++ module_id = (cmdid & GENMASK(9, 4)) >> 4; ++ if (module_id == 0 || module_id > FSL_MC_MAX_MODULE_ID) { ++ dev_err(&mc_dev->dev, "MC command 0x%04x: unknown module ID 0x%x\n", ++ cmdid, module_id); ++ return -EACCES; ++ } ++ } ++ ++ /* Some commands alter how hardware resources are managed. For these ++ * commands, check for CAP_NET_ADMIN. ++ */ ++ if (desc->flags & FSL_MC_CAP_NET_ADMIN_NEEDED) { ++ if (!capable(CAP_NET_ADMIN)) { ++ dev_err(&mc_dev->dev, "MC command 0x%04x: needs CAP_NET_ADMIN!\n", ++ cmdid); ++ return -EPERM; ++ } ++ } ++ ++ return 0; ++} ++ ++static int fsl_mc_uapi_send_command(struct fsl_mc_device *mc_dev, unsigned long arg, ++ struct fsl_mc_io *mc_io) ++{ ++ struct fsl_mc_command mc_cmd; ++ int error; ++ ++ error = copy_from_user(&mc_cmd, (void __user *)arg, sizeof(mc_cmd)); ++ if (error) ++ return -EFAULT; ++ ++ error = fsl_mc_command_check(mc_dev, &mc_cmd); ++ if (error) ++ return error; ++ ++ error = mc_send_command(mc_io, &mc_cmd); ++ if (error) ++ return error; ++ ++ error = copy_to_user((void __user *)arg, &mc_cmd, sizeof(mc_cmd)); ++ if (error) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++static int fsl_mc_uapi_dev_open(struct inode *inode, struct file *filep) ++{ ++ struct fsl_mc_device *root_mc_device; ++ struct uapi_priv_data *priv_data; ++ struct fsl_mc_io *dynamic_mc_io; ++ struct fsl_mc_uapi *mc_uapi; ++ struct fsl_mc_bus *mc_bus; ++ int error; ++ ++ priv_data = kzalloc(sizeof(*priv_data), GFP_KERNEL); ++ if (!priv_data) ++ return -ENOMEM; ++ ++ mc_uapi = container_of(filep->private_data, struct fsl_mc_uapi, misc); ++ mc_bus = container_of(mc_uapi, struct fsl_mc_bus, uapi_misc); ++ root_mc_device = &mc_bus->mc_dev; ++ ++ mutex_lock(&mc_uapi->mutex); ++ ++ if (!mc_uapi->local_instance_in_use) { ++ priv_data->mc_io = mc_uapi->static_mc_io; ++ mc_uapi->local_instance_in_use = 1; ++ } else { ++ error = fsl_mc_portal_allocate(root_mc_device, 0, ++ &dynamic_mc_io); ++ if (error) { ++ dev_dbg(&root_mc_device->dev, ++ "Could not allocate MC portal\n"); ++ goto error_portal_allocate; ++ } ++ ++ priv_data->mc_io = dynamic_mc_io; ++ } ++ priv_data->uapi = mc_uapi; ++ filep->private_data = priv_data; ++ ++ mutex_unlock(&mc_uapi->mutex); ++ ++ return 0; ++ ++error_portal_allocate: ++ mutex_unlock(&mc_uapi->mutex); ++ kfree(priv_data); ++ ++ return error; ++} ++ ++static int fsl_mc_uapi_dev_release(struct inode *inode, struct file *filep) ++{ ++ struct uapi_priv_data *priv_data; ++ struct fsl_mc_uapi *mc_uapi; ++ struct fsl_mc_io *mc_io; ++ ++ priv_data = filep->private_data; ++ mc_uapi = priv_data->uapi; ++ mc_io = priv_data->mc_io; ++ ++ mutex_lock(&mc_uapi->mutex); ++ ++ if (mc_io == mc_uapi->static_mc_io) ++ mc_uapi->local_instance_in_use = 0; ++ else ++ fsl_mc_portal_free(mc_io); ++ ++ kfree(filep->private_data); ++ filep->private_data = NULL; ++ ++ mutex_unlock(&mc_uapi->mutex); ++ ++ return 0; ++} ++ ++static long fsl_mc_uapi_dev_ioctl(struct file *file, ++ unsigned int cmd, ++ unsigned long arg) ++{ ++ struct uapi_priv_data *priv_data = file->private_data; ++ struct fsl_mc_device *root_mc_device; ++ struct fsl_mc_bus *mc_bus; ++ int error; ++ ++ mc_bus = container_of(priv_data->uapi, struct fsl_mc_bus, uapi_misc); ++ root_mc_device = &mc_bus->mc_dev; ++ ++ switch (cmd) { ++ case FSL_MC_SEND_MC_COMMAND: ++ error = fsl_mc_uapi_send_command(root_mc_device, arg, priv_data->mc_io); ++ break; ++ default: ++ dev_dbg(&root_mc_device->dev, "unexpected ioctl call number\n"); ++ error = -EINVAL; ++ } ++ ++ return error; ++} ++ ++static const struct file_operations fsl_mc_uapi_dev_fops = { ++ .owner = THIS_MODULE, ++ .open = fsl_mc_uapi_dev_open, ++ .release = fsl_mc_uapi_dev_release, ++ .unlocked_ioctl = fsl_mc_uapi_dev_ioctl, ++}; ++ ++int fsl_mc_uapi_create_device_file(struct fsl_mc_bus *mc_bus) ++{ ++ struct fsl_mc_device *mc_dev = &mc_bus->mc_dev; ++ struct fsl_mc_uapi *mc_uapi = &mc_bus->uapi_misc; ++ int error; ++ ++ mc_uapi->misc.minor = MISC_DYNAMIC_MINOR; ++ mc_uapi->misc.name = dev_name(&mc_dev->dev); ++ mc_uapi->misc.fops = &fsl_mc_uapi_dev_fops; ++ ++ error = misc_register(&mc_uapi->misc); ++ if (error) ++ return error; ++ ++ mc_uapi->static_mc_io = mc_bus->mc_dev.mc_io; ++ ++ mutex_init(&mc_uapi->mutex); ++ ++ return 0; ++} ++ ++void fsl_mc_uapi_remove_device_file(struct fsl_mc_bus *mc_bus) ++{ ++ misc_deregister(&mc_bus->uapi_misc.misc); ++} +diff --git a/include/uapi/linux/fsl_mc.h b/include/uapi/linux/fsl_mc.h +index cf56d46f052e..e57451570033 100644 +--- a/include/uapi/linux/fsl_mc.h ++++ b/include/uapi/linux/fsl_mc.h +@@ -16,10 +16,19 @@ + * struct fsl_mc_command - Management Complex (MC) command structure + * @header: MC command header + * @params: MC command parameters ++ * ++ * Used by FSL_MC_SEND_MC_COMMAND + */ + struct fsl_mc_command { + __le64 header; + __le64 params[MC_CMD_NUM_OF_PARAMS]; + }; + ++#define FSL_MC_SEND_CMD_IOCTL_TYPE 'R' ++#define FSL_MC_SEND_CMD_IOCTL_SEQ 0xE0 ++ ++#define FSL_MC_SEND_MC_COMMAND \ ++ _IOWR(FSL_MC_SEND_CMD_IOCTL_TYPE, FSL_MC_SEND_CMD_IOCTL_SEQ, \ ++ struct fsl_mc_command) ++ + #endif /* _UAPI_FSL_MC_H_ */ +-- +2.27.0 + + +From e4c686ebcbba8431f97eb044e3b6c70dda36bd91 Mon Sep 17 00:00:00 2001 +From: Ioana Ciornei +Date: Thu, 14 Jan 2021 19:07:51 +0200 +Subject: [PATCH 36/44] bus: fsl-mc: add bus rescan attribute + +Introduce the rescan attribute as a bus attribute to +synchronize the fsl-mc bus objects and the MC firmware. + +To rescan the fsl-mc bus, e.g., +echo 1 > /sys/bus/fsl-mc/rescan + +Signed-off-by: Ioana Ciornei +--- + Documentation/ABI/stable/sysfs-bus-fsl-mc | 9 +++++ + MAINTAINERS | 1 + + drivers/bus/fsl-mc/dprc-driver.c | 4 +-- + drivers/bus/fsl-mc/fsl-mc-bus.c | 41 +++++++++++++++++++++++ + drivers/bus/fsl-mc/fsl-mc-private.h | 3 ++ + 5 files changed, 56 insertions(+), 2 deletions(-) + create mode 100644 Documentation/ABI/stable/sysfs-bus-fsl-mc + +diff --git a/Documentation/ABI/stable/sysfs-bus-fsl-mc b/Documentation/ABI/stable/sysfs-bus-fsl-mc +new file mode 100644 +index 000000000000..a4d384df9ba8 +--- /dev/null ++++ b/Documentation/ABI/stable/sysfs-bus-fsl-mc +@@ -0,0 +1,9 @@ ++What: /sys/bus/fsl-mc/rescan ++Date: January 2021 ++KernelVersion: 5.12 ++Contact: Ioana Ciornei ++Description: Writing a non-zero value to this attribute will ++ force a rescan of fsl-mc bus in the system and ++ synchronize the objects under fsl-mc bus and the ++ Management Complex firmware. ++Users: Userspace drivers and management tools +diff --git a/MAINTAINERS b/MAINTAINERS +index e0a8bdb3567c..590779774dd2 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -14422,6 +14422,7 @@ M: Stuart Yoder + M: Laurentiu Tudor + L: linux-kernel@vger.kernel.org + S: Maintained ++F: Documentation/ABI/stable/sysfs-bus-fsl-mc + F: Documentation/devicetree/bindings/misc/fsl,qoriq-mc.txt + F: Documentation/networking/device_drivers/ethernet/freescale/dpaa2/overview.rst + F: drivers/bus/fsl-mc/ +diff --git a/drivers/bus/fsl-mc/dprc-driver.c b/drivers/bus/fsl-mc/dprc-driver.c +index ca2ce38a5d51..57a59f7f9802 100644 +--- a/drivers/bus/fsl-mc/dprc-driver.c ++++ b/drivers/bus/fsl-mc/dprc-driver.c +@@ -237,8 +237,8 @@ static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev, + * populated before they can get allocation requests from probe callbacks + * of the device drivers for the non-allocatable devices. + */ +-static int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, +- bool alloc_interrupts) ++int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, ++ bool alloc_interrupts) + { + int num_child_objects; + int dprc_get_obj_failures; +diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c +index b8e6acdf932e..cb031287b190 100644 +--- a/drivers/bus/fsl-mc/fsl-mc-bus.c ++++ b/drivers/bus/fsl-mc/fsl-mc-bus.c +@@ -208,12 +208,53 @@ static struct attribute *fsl_mc_dev_attrs[] = { + + ATTRIBUTE_GROUPS(fsl_mc_dev); + ++static int scan_fsl_mc_bus(struct device *dev, void *data) ++{ ++ struct fsl_mc_device *root_mc_dev; ++ struct fsl_mc_bus *root_mc_bus; ++ ++ if (!fsl_mc_is_root_dprc(dev)) ++ goto exit; ++ ++ root_mc_dev = to_fsl_mc_device(dev); ++ root_mc_bus = to_fsl_mc_bus(root_mc_dev); ++ mutex_lock(&root_mc_bus->scan_mutex); ++ dprc_scan_objects(root_mc_dev, NULL); ++ mutex_unlock(&root_mc_bus->scan_mutex); ++ ++exit: ++ return 0; ++} ++ ++static ssize_t rescan_store(struct bus_type *bus, ++ const char *buf, size_t count) ++{ ++ unsigned long val; ++ ++ if (kstrtoul(buf, 0, &val) < 0) ++ return -EINVAL; ++ ++ if (val) ++ bus_for_each_dev(bus, NULL, NULL, scan_fsl_mc_bus); ++ ++ return count; ++} ++static BUS_ATTR_WO(rescan); ++ ++static struct attribute *fsl_mc_bus_attrs[] = { ++ &bus_attr_rescan.attr, ++ NULL, ++}; ++ ++ATTRIBUTE_GROUPS(fsl_mc_bus); ++ + struct bus_type fsl_mc_bus_type = { + .name = "fsl-mc", + .match = fsl_mc_bus_match, + .uevent = fsl_mc_bus_uevent, + .dma_configure = fsl_mc_dma_configure, + .dev_groups = fsl_mc_dev_groups, ++ .bus_groups = fsl_mc_bus_groups, + }; + EXPORT_SYMBOL_GPL(fsl_mc_bus_type); + +diff --git a/drivers/bus/fsl-mc/fsl-mc-private.h b/drivers/bus/fsl-mc/fsl-mc-private.h +index 6293a24de456..42bdb8679a36 100644 +--- a/drivers/bus/fsl-mc/fsl-mc-private.h ++++ b/drivers/bus/fsl-mc/fsl-mc-private.h +@@ -594,6 +594,9 @@ int __init dprc_driver_init(void); + + void dprc_driver_exit(void); + ++int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, ++ bool alloc_interrupts); ++ + int __init fsl_mc_allocator_driver_init(void); + + void fsl_mc_allocator_driver_exit(void); +-- +2.27.0 + + +From 7c1ac9e45f0b634e0cde8106c49b21fb65961b60 Mon Sep 17 00:00:00 2001 +From: Ioana Ciornei +Date: Thu, 14 Jan 2021 19:07:52 +0200 +Subject: [PATCH 37/44] bus: fsl-mc: add autorescan sysfs + +Add the autorescan sysfs in order to enable/disable the DPRC IRQs on +which automatic rescan of the bus is performed. This is important when +dynamic creation of objects is needed to happen in a timely manner because +object creation can be bundled together. + +Signed-off-by: Ioana Ciornei +--- + Documentation/ABI/stable/sysfs-bus-fsl-mc | 10 +++++ + drivers/bus/fsl-mc/dprc-driver.c | 17 ++++++- + drivers/bus/fsl-mc/fsl-mc-bus.c | 55 +++++++++++++++++++++++ + drivers/bus/fsl-mc/fsl-mc-private.h | 5 +++ + 4 files changed, 85 insertions(+), 2 deletions(-) + +diff --git a/Documentation/ABI/stable/sysfs-bus-fsl-mc b/Documentation/ABI/stable/sysfs-bus-fsl-mc +index a4d384df9ba8..58f06c7eeed7 100644 +--- a/Documentation/ABI/stable/sysfs-bus-fsl-mc ++++ b/Documentation/ABI/stable/sysfs-bus-fsl-mc +@@ -7,3 +7,13 @@ Description: Writing a non-zero value to this attribute will + synchronize the objects under fsl-mc bus and the + Management Complex firmware. + Users: Userspace drivers and management tools ++ ++What: /sys/bus/fsl-mc/autorescan ++Date: January 2021 ++KernelVersion: 5.12 ++Contact: Ioana Ciornei ++Description: Writing a zero value to this attribute will ++ disable the DPRC IRQs on which automatic rescan ++ of the fsl-mc bus is performed. A non-zero value ++ will enable the DPRC IRQs. ++Users: Userspace drivers and management tools +diff --git a/drivers/bus/fsl-mc/dprc-driver.c b/drivers/bus/fsl-mc/dprc-driver.c +index 57a59f7f9802..df71c16f8a73 100644 +--- a/drivers/bus/fsl-mc/dprc-driver.c ++++ b/drivers/bus/fsl-mc/dprc-driver.c +@@ -458,8 +458,9 @@ static irqreturn_t dprc_irq0_handler_thread(int irq_num, void *arg) + /* + * Disable and clear interrupt for a given DPRC object + */ +-static int disable_dprc_irq(struct fsl_mc_device *mc_dev) ++int disable_dprc_irq(struct fsl_mc_device *mc_dev) + { ++ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); + int error; + struct fsl_mc_io *mc_io = mc_dev->mc_io; + +@@ -496,9 +497,18 @@ static int disable_dprc_irq(struct fsl_mc_device *mc_dev) + return error; + } + ++ mc_bus->irq_enabled = 0; ++ + return 0; + } + ++int get_dprc_irq_state(struct fsl_mc_device *mc_dev) ++{ ++ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); ++ ++ return mc_bus->irq_enabled; ++} ++ + static int register_dprc_irq_handler(struct fsl_mc_device *mc_dev) + { + int error; +@@ -525,8 +535,9 @@ static int register_dprc_irq_handler(struct fsl_mc_device *mc_dev) + return 0; + } + +-static int enable_dprc_irq(struct fsl_mc_device *mc_dev) ++int enable_dprc_irq(struct fsl_mc_device *mc_dev) + { ++ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); + int error; + + /* +@@ -554,6 +565,8 @@ static int enable_dprc_irq(struct fsl_mc_device *mc_dev) + return error; + } + ++ mc_bus->irq_enabled = 1; ++ + return 0; + } + +diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c +index cb031287b190..34811db074b7 100644 +--- a/drivers/bus/fsl-mc/fsl-mc-bus.c ++++ b/drivers/bus/fsl-mc/fsl-mc-bus.c +@@ -241,8 +241,63 @@ static ssize_t rescan_store(struct bus_type *bus, + } + static BUS_ATTR_WO(rescan); + ++static int fsl_mc_bus_set_autorescan(struct device *dev, void *data) ++{ ++ struct fsl_mc_device *root_mc_dev; ++ unsigned long val; ++ char *buf = data; ++ ++ if (!fsl_mc_is_root_dprc(dev)) ++ goto exit; ++ ++ root_mc_dev = to_fsl_mc_device(dev); ++ ++ if (kstrtoul(buf, 0, &val) < 0) ++ return -EINVAL; ++ ++ if (val) ++ enable_dprc_irq(root_mc_dev); ++ else ++ disable_dprc_irq(root_mc_dev); ++ ++exit: ++ return 0; ++} ++ ++static int fsl_mc_bus_get_autorescan(struct device *dev, void *data) ++{ ++ struct fsl_mc_device *root_mc_dev; ++ char *buf = data; ++ ++ if (!fsl_mc_is_root_dprc(dev)) ++ goto exit; ++ ++ root_mc_dev = to_fsl_mc_device(dev); ++ ++ sprintf(buf, "%d\n", get_dprc_irq_state(root_mc_dev)); ++exit: ++ return 0; ++} ++ ++static ssize_t autorescan_store(struct bus_type *bus, ++ const char *buf, size_t count) ++{ ++ bus_for_each_dev(bus, NULL, (void *)buf, fsl_mc_bus_set_autorescan); ++ ++ return count; ++} ++ ++static ssize_t autorescan_show(struct bus_type *bus, char *buf) ++{ ++ bus_for_each_dev(bus, NULL, (void *)buf, fsl_mc_bus_get_autorescan); ++ return strlen(buf); ++} ++ ++static BUS_ATTR_RW(autorescan); ++ + static struct attribute *fsl_mc_bus_attrs[] = { + &bus_attr_rescan.attr, ++ &bus_attr_autorescan.attr, + NULL, + }; + +diff --git a/drivers/bus/fsl-mc/fsl-mc-private.h b/drivers/bus/fsl-mc/fsl-mc-private.h +index 42bdb8679a36..1958fa065360 100644 +--- a/drivers/bus/fsl-mc/fsl-mc-private.h ++++ b/drivers/bus/fsl-mc/fsl-mc-private.h +@@ -578,6 +578,7 @@ struct fsl_mc_bus { + struct mutex scan_mutex; /* serializes bus scanning */ + struct dprc_attributes dprc_attr; + struct fsl_mc_uapi uapi_misc; ++ int irq_enabled; + }; + + #define to_fsl_mc_bus(_mc_dev) \ +@@ -656,4 +657,8 @@ static inline void fsl_mc_uapi_remove_device_file(struct fsl_mc_bus *mc_bus) + + #endif + ++int disable_dprc_irq(struct fsl_mc_device *mc_dev); ++int enable_dprc_irq(struct fsl_mc_device *mc_dev); ++int get_dprc_irq_state(struct fsl_mc_device *mc_dev); ++ + #endif /* _FSL_MC_PRIVATE_H_ */ +-- +2.27.0 + + +From e8fc84bfe4ba6372d55ed9f64410b0b93b32b91e Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Tue, 24 Dec 2019 14:46:48 +0000 +Subject: [PATCH 38/44] bus: fsl-mc: fix dprc object reading race + +When modifying the objects attached to a DPRC, we may end up reading +the list of objects from the firmware while another thread is changing +changing the list. Since we read the objects via: + +- Read the number of DPRC objects +- Iterate over this number of objects retrieving their details + +and objects can be added in the middle of the list, this causes the +last few objects to unexpectedly disappear. The side effect of this +is if network interfaces are added after boot, they come and go. This +can result in already configured interfaces unexpectedly disappearing. + +This has been easy to provoke with the restool interface added, and a +script which adds network interfaces one after each other; the kernel +rescanning runs asynchronously to restool. + +NXP's approach to fixing this was to introduce a sysfs "attribute" in +their vendor tree, /sys/bus/fsl-mc/rescan, which userspace poked at to +request the kernel to rescan the DPRC object tree each time the +"restool" command completed (whether or not the tool changed anything.) +This has the effect of making the kernel's rescan synchronous with a +scripted restool, but still fails if we have multiple restools running +concurrently. + +This patch takes a different approach: +- Read the number of DPRC objects +- Iterate over this number of objects retrieving their details +- Re-read the number of DPRC objects +- If the number of DPRC objects has changed while reading, repeat. + +This solves the issue where network interfaces unexpectedly disappear +while adding others via ls-addni, because they've fallen off the end +of the object list. + +This does *not* solve the issue that if an object is deleted while +another is added while we are reading the objects - that requires +firmware modification, or a more elaborate solution on the Linux side +(e.g., CRCing the object details and reading all objects at least +twice to check the CRC is stable.) + +However, without firmware modification, this is probably the best way +to ensure that we read all the objects. + +Signed-off-by: Russell King +--- + drivers/bus/fsl-mc/dprc-driver.c | 32 +++++++++++++++++++++++++++++--- + 1 file changed, 29 insertions(+), 3 deletions(-) + +diff --git a/drivers/bus/fsl-mc/dprc-driver.c b/drivers/bus/fsl-mc/dprc-driver.c +index df71c16f8a73..787844f0b636 100644 +--- a/drivers/bus/fsl-mc/dprc-driver.c ++++ b/drivers/bus/fsl-mc/dprc-driver.c +@@ -240,11 +240,11 @@ static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev, + int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, + bool alloc_interrupts) + { +- int num_child_objects; ++ int num_child_objects, num_child_objects2; + int dprc_get_obj_failures; + int error; +- unsigned int irq_count = mc_bus_dev->obj_desc.irq_count; +- struct fsl_mc_obj_desc *child_obj_desc_array = NULL; ++ unsigned int irq_count; ++ struct fsl_mc_obj_desc *child_obj_desc_array; + struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); + + error = dprc_get_obj_count(mc_bus_dev->mc_io, +@@ -257,6 +257,9 @@ int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, + return error; + } + ++retry: ++ irq_count = mc_bus_dev->obj_desc.irq_count; ++ child_obj_desc_array = NULL; + if (num_child_objects != 0) { + int i; + +@@ -315,6 +318,29 @@ int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, + } + } + ++ error = dprc_get_obj_count(mc_bus_dev->mc_io, ++ 0, ++ mc_bus_dev->mc_handle, ++ &num_child_objects2); ++ if (error < 0) { ++ if (child_obj_desc_array) ++ devm_kfree(&mc_bus_dev->dev, child_obj_desc_array); ++ dev_err(&mc_bus_dev->dev, "dprc_get_obj_count() failed: %d\n", ++ error); ++ return error; ++ } ++ ++ if (num_child_objects != num_child_objects2) { ++ /* ++ * Something changed while reading the number of objects. ++ * Retry reading the child object list. ++ */ ++ if (child_obj_desc_array) ++ devm_kfree(&mc_bus_dev->dev, child_obj_desc_array); ++ num_child_objects = num_child_objects2; ++ goto retry; ++ } ++ + /* + * Allocate IRQ's before binding the scanned devices with their + * respective drivers. +-- +2.27.0 + + +From ee4993484ed04fad479529d9c8f2df012df66f23 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Fri, 24 Jan 2020 17:59:49 +0000 +Subject: [PATCH 39/44] iommu: silence iommu group prints + +On the LX2160A, there are lots (about 160) of IOMMU messages produced +during boot; this is excessive. Reduce the severity of these messages +to debug level. + +Signed-off-by: Russell King +--- + drivers/iommu/iommu.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c +index 0d9adce6d812..a3b968fd0db0 100644 +--- a/drivers/iommu/iommu.c ++++ b/drivers/iommu/iommu.c +@@ -845,7 +845,7 @@ int iommu_group_add_device(struct iommu_group *group, struct device *dev) + + trace_add_device_to_group(group->id, dev); + +- dev_info(dev, "Adding to iommu group %d\n", group->id); ++ dev_dbg(dev, "Adding to iommu group %d\n", group->id); + + return 0; + +@@ -879,7 +879,7 @@ void iommu_group_remove_device(struct device *dev) + struct iommu_group *group = dev->iommu_group; + struct group_device *tmp_device, *device = NULL; + +- dev_info(dev, "Removing from iommu group %d\n", group->id); ++ dev_dbg(dev, "Removing from iommu group %d\n", group->id); + + /* Pre-notify listeners that a device is being removed. */ + blocking_notifier_call_chain(&group->notifier, +-- +2.27.0 + + +From 98c61f7a421af20214a5aec5a28885cac0eef605 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Fri, 8 Jan 2021 17:28:23 +0000 +Subject: [PATCH 40/44] net: pcs: add pcs-lynx 1000BASE-X support + +Add support for 1000BASE-X to pcs-lynx for the LX2160A. + +This commit prepares the ground work for allowing 1G fiber connections +to be used with DPAA2 on the SolidRun CEX7 platforms. + +Signed-off-by: Russell King +--- + drivers/net/pcs/pcs-lynx.c | 36 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + +diff --git a/drivers/net/pcs/pcs-lynx.c b/drivers/net/pcs/pcs-lynx.c +index 62bb9272dcb2..af36cd647bf5 100644 +--- a/drivers/net/pcs/pcs-lynx.c ++++ b/drivers/net/pcs/pcs-lynx.c +@@ -11,6 +11,7 @@ + #define LINK_TIMER_VAL(ns) ((u32)((ns) / SGMII_CLOCK_PERIOD_NS)) + + #define SGMII_AN_LINK_TIMER_NS 1600000 /* defined by SGMII spec */ ++#define IEEE8023_LINK_TIMER_NS 10000000 + + #define LINK_TIMER_LO 0x12 + #define LINK_TIMER_HI 0x13 +@@ -83,6 +84,7 @@ static void lynx_pcs_get_state(struct phylink_pcs *pcs, + struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs); + + switch (state->interface) { ++ case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + phylink_mii_c22_pcs_get_state(lynx->mdio, state); +@@ -108,6 +110,30 @@ static void lynx_pcs_get_state(struct phylink_pcs *pcs, + state->link, state->an_enabled, state->an_complete); + } + ++static int lynx_pcs_config_1000basex(struct mdio_device *pcs, ++ unsigned int mode, ++ const unsigned long *advertising) ++{ ++ struct mii_bus *bus = pcs->bus; ++ int addr = pcs->addr; ++ u32 link_timer; ++ int err; ++ ++ link_timer = LINK_TIMER_VAL(IEEE8023_LINK_TIMER_NS); ++ mdiobus_write(bus, addr, LINK_TIMER_LO, link_timer & 0xffff); ++ mdiobus_write(bus, addr, LINK_TIMER_HI, link_timer >> 16); ++ ++ err = mdiobus_modify(bus, addr, IF_MODE, ++ IF_MODE_SGMII_EN | IF_MODE_USE_SGMII_AN, ++ 0); ++ if (err) ++ return err; ++ ++ return phylink_mii_c22_pcs_config(pcs, mode, ++ PHY_INTERFACE_MODE_1000BASEX, ++ advertising); ++} ++ + static int lynx_pcs_config_sgmii(struct mdio_device *pcs, unsigned int mode, + const unsigned long *advertising) + { +@@ -163,6 +189,8 @@ static int lynx_pcs_config(struct phylink_pcs *pcs, unsigned int mode, + struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs); + + switch (ifmode) { ++ case PHY_INTERFACE_MODE_1000BASEX: ++ return lynx_pcs_config_1000basex(lynx->mdio, mode, advertising); + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + return lynx_pcs_config_sgmii(lynx->mdio, mode, advertising); +@@ -185,6 +213,13 @@ static int lynx_pcs_config(struct phylink_pcs *pcs, unsigned int mode, + return 0; + } + ++static void lynx_pcs_an_restart(struct phylink_pcs *pcs) ++{ ++ struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs); ++ ++ phylink_mii_c22_pcs_an_restart(lynx->mdio); ++} ++ + static void lynx_pcs_link_up_sgmii(struct mdio_device *pcs, unsigned int mode, + int speed, int duplex) + { +@@ -290,6 +325,7 @@ static void lynx_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, + static const struct phylink_pcs_ops lynx_pcs_phylink_ops = { + .pcs_get_state = lynx_pcs_get_state, + .pcs_config = lynx_pcs_config, ++ .pcs_an_restart = lynx_pcs_an_restart, + .pcs_link_up = lynx_pcs_link_up, + }; + +-- +2.27.0 + + +From 97f4006a98bc3fccad8318f3a728fd270db3aff6 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Thu, 30 Jan 2020 22:42:38 +0000 +Subject: [PATCH 41/44] net: dpaa2-mac: add 1000BASE-X support + +Now that pcs-lynx supports 1000BASE-X, add support for this interface +mode to dpaa2-mac. pcs-lynx can be switched at runtime between SGMII +and 1000BASE-X mode, so allow dpaa2-mac to switch between these as +well. + +This commit prepares the ground work for allowing 1G fiber connections +to be used with DPAA2 on the SolidRun CEX7 platforms. + +Signed-off-by: Russell King +--- + .../net/ethernet/freescale/dpaa2/dpaa2-mac.c | 20 ++++++++++++++++--- + 1 file changed, 17 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +index efd804d4a1bf..1b347dbced9f 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +@@ -94,10 +94,20 @@ static bool dpaa2_mac_phy_mode_mismatch(struct dpaa2_mac *mac, + phy_interface_t interface) + { + switch (interface) { ++ /* We can switch between SGMII and 1000BASE-X at runtime with ++ * pcs-lynx ++ */ ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ if (mac->pcs && ++ (mac->if_mode == PHY_INTERFACE_MODE_SGMII || ++ mac->if_mode == PHY_INTERFACE_MODE_1000BASEX)) ++ return false; ++ return interface != mac->if_mode; ++ + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_QSGMII: +- case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: +@@ -137,13 +147,17 @@ static void dpaa2_mac_validate(struct phylink_config *config, + fallthrough; + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: +- phylink_set(mask, 10baseT_Full); +- phylink_set(mask, 100baseT_Full); ++ phylink_set(mask, 1000baseX_Full); + phylink_set(mask, 1000baseT_Full); ++ if (state->interface == PHY_INTERFACE_MODE_1000BASEX) ++ break; ++ phylink_set(mask, 100baseT_Full); ++ phylink_set(mask, 10baseT_Full); + break; + default: + goto empty_set; +-- +2.27.0 + + +From a6e20163eca58246419e02a26e0c4a6b163cbb2f Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Thu, 30 Jan 2020 22:42:38 +0000 +Subject: [PATCH 42/44] net: dpaa2-mac: add backplane link mode support + +Add support for backplane link mode, which is, according to discussions +with NXP earlier in the year, is a mode where the OS (Linux) is able to +manage the PCS and Serdes itself. + +This commit prepares the ground work for allowing 1G fiber connections +to be used with DPAA2 on the SolidRun CEX7 platforms. + +Signed-off-by: Russell King +--- + drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +index 1b347dbced9f..272b4b4aac92 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +@@ -365,8 +365,9 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac) + goto err_put_node; + } + +- if (mac->attr.link_type == DPMAC_LINK_TYPE_PHY && +- mac->attr.eth_if != DPMAC_ETH_IF_RGMII) { ++ if ((mac->attr.link_type == DPMAC_LINK_TYPE_PHY && ++ mac->attr.eth_if != DPMAC_ETH_IF_RGMII) || ++ mac->attr.link_type == DPMAC_LINK_TYPE_BACKPLANE) { + err = dpaa2_pcs_create(mac, dpmac_node, mac->attr.id); + if (err) + goto err_put_node; +-- +2.27.0 + + +From cf1521adcf9e9cdfe76709896f15328e4a573c49 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Thu, 30 Jan 2020 22:42:38 +0000 +Subject: [PATCH 43/44] net: dpaa2-mac: add support for more 10G modes + +Phylink documentation says: + * Note that the PHY may be able to transform from one connection + * technology to another, so, eg, don't clear 1000BaseX just + * because the MAC is unable to BaseX mode. This is more about + * clearing unsupported speeds and duplex settings. The port modes + * should not be cleared; phylink_set_port_modes() will help with this. + +So add the missing 10G modes. + +Signed-off-by: Russell King +--- + drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +index 272b4b4aac92..708aced0ed47 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +@@ -140,6 +140,12 @@ static void dpaa2_mac_validate(struct phylink_config *config, + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_USXGMII: + phylink_set(mask, 10000baseT_Full); ++ phylink_set(mask, 10000baseKR_Full); ++ phylink_set(mask, 10000baseCR_Full); ++ phylink_set(mask, 10000baseSR_Full); ++ phylink_set(mask, 10000baseLR_Full); ++ phylink_set(mask, 10000baseLRM_Full); ++ phylink_set(mask, 10000baseER_Full); + if (state->interface == PHY_INTERFACE_MODE_10GBASER) + break; + phylink_set(mask, 5000baseT_Full); +-- +2.27.0 + + +From 4c71505dce5df9254daacb96c7a869741289a461 Mon Sep 17 00:00:00 2001 +From: Ioana Ciornei +Date: Thu, 21 Nov 2019 21:15:25 +0200 +Subject: [PATCH 44/44] dpaa2-eth: do not hold rtnl_lock on phylink_create() or + _destroy() + +The rtnl_lock should not be held when calling phylink_create() or +phylink_destroy() since it leads to the deadlock listed below: + +[ 18.656576] rtnl_lock+0x18/0x20 +[ 18.659798] sfp_bus_add_upstream+0x28/0x90 +[ 18.663974] phylink_create+0x2cc/0x828 +[ 18.667803] dpaa2_mac_connect+0x14c/0x2a8 +[ 18.671890] dpaa2_eth_connect_mac+0x94/0xd8 + +Fix this by moving the _lock() and _unlock() calls just outside of +phylink_of_phy_connect() and phylink_disconnect_phy(). + +Fixes: 719479230893 ("dpaa2-eth: add MAC/PHY support through phylink") +Reported-by: Russell King +Signed-off-by: Ioana Ciornei +Signed-off-by: Russell King +--- + drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 4 ---- + drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 4 ++++ + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +index 0351b2b65bff..a63c35abf96b 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +@@ -4120,12 +4120,10 @@ static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg) + dpaa2_eth_set_mac_addr(netdev_priv(net_dev)); + dpaa2_eth_update_tx_fqids(priv); + +- rtnl_lock(); + if (priv->mac) + dpaa2_eth_disconnect_mac(priv); + else + dpaa2_eth_connect_mac(priv); +- rtnl_unlock(); + } + + return IRQ_HANDLED; +@@ -4415,9 +4413,7 @@ static int dpaa2_eth_remove(struct fsl_mc_device *ls_dev) + #ifdef CONFIG_DEBUG_FS + dpaa2_dbg_remove(priv); + #endif +- rtnl_lock(); + dpaa2_eth_disconnect_mac(priv); +- rtnl_unlock(); + + unregister_netdev(net_dev); + +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +index 708aced0ed47..31222b2305da 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +@@ -394,7 +394,9 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac) + if (mac->pcs) + phylink_set_pcs(mac->phylink, &mac->pcs->pcs); + ++ rtnl_lock(); + err = phylink_fwnode_phy_connect(mac->phylink, dpmac_node, 0); ++ rtnl_unlock(); + if (err) { + netdev_err(net_dev, "phylink_fwnode_phy_connect() = %d\n", err); + goto err_phylink_destroy; +@@ -419,7 +421,9 @@ void dpaa2_mac_disconnect(struct dpaa2_mac *mac) + if (!mac->phylink) + return; + ++ rtnl_lock(); + phylink_disconnect_phy(mac->phylink); ++ rtnl_unlock(); + phylink_destroy(mac->phylink); + dpaa2_pcs_destroy(mac); + } +-- +2.27.0 + diff --git a/SPECS/kernel.spec b/SPECS/kernel.spec index 8c76718..6c64695 100644 --- a/SPECS/kernel.spec +++ b/SPECS/kernel.spec @@ -877,6 +877,7 @@ Patch105: arm-dts-rpi-4-disable-wifi-frequencies.patch # END OF PATCH DEFINITIONS +Patch10000: linux-5.10-lx2160a-network.patch %endif