* Re: [PATCH 2/6] soc: ti: omap-prm: Add basic power domain support
From: Tero Kristo @ 2020-05-14 8:04 UTC (permalink / raw)
To: Tony Lindgren, linux-omap
Cc: Andrew F . Davis, Santosh Shilimkar, Suman Anna, linux-kernel,
linux-arm-kernel, Rob Herring, devicetree
In-Reply-To: <20200512203852.29499-3-tony@atomide.com>
On 12/05/2020 23:38, Tony Lindgren wrote:
> The PRM controller has currently only support for resets while the power
> domains are still handled in the platform code.
>
> Let's add basic power domain support to enable and disable a PRM
> controlled power domain if configured in the devicetree. This can be
> used for various hardware accelerators, and interconnect instances.
>
> Further support can be added later on as needed for runtime configuration
> based on domain-idle-states.
>
> Signed-off-by: Tony Lindgren <tony@atomide.com>
> ---
> arch/arm/mach-omap2/Kconfig | 1 +
> drivers/soc/ti/omap_prm.c | 281 +++++++++++++++++++++++++++++++++++-
> 2 files changed, 281 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
> --- a/arch/arm/mach-omap2/Kconfig
> +++ b/arch/arm/mach-omap2/Kconfig
> @@ -7,6 +7,7 @@ config ARCH_OMAP2
> depends on ARCH_MULTI_V6
> select ARCH_OMAP2PLUS
> select CPU_V6
> + select PM_GENERIC_DOMAINS if PM
> select SOC_HAS_OMAP2_SDRC
>
> config ARCH_OMAP3
> diff --git a/drivers/soc/ti/omap_prm.c b/drivers/soc/ti/omap_prm.c
> --- a/drivers/soc/ti/omap_prm.c
> +++ b/drivers/soc/ti/omap_prm.c
> @@ -6,18 +6,50 @@
> * Tero Kristo <t-kristo@ti.com>
> */
>
> +#include <linux/clk.h>
> #include <linux/kernel.h>
> #include <linux/device.h>
> #include <linux/io.h>
> #include <linux/iopoll.h>
> +#include <linux/module.h>
> #include <linux/of.h>
> #include <linux/of_device.h>
> #include <linux/platform_device.h>
> +#include <linux/pm_domain.h>
> #include <linux/reset-controller.h>
> #include <linux/delay.h>
>
> #include <linux/platform_data/ti-prm.h>
>
> +enum omap_prm_clocks {
> + OMAP_PRM_FCK,
> + OMAP_PRM_ICK,
> + OMAP_PRM_NR_CLOCKS,
> +};
> +
> +enum omap_prm_domain_mode {
> + OMAP_PRMD_OFF,
> + OMAP_PRMD_RETENTION,
> + OMAP_PRMD_ON_INACTIVE,
> + OMAP_PRMD_ON_ACTIVE,
> +};
> +
> +struct omap_prm_domain_map {
> + unsigned int usable_modes; /* Mask of hardware supported modes */
> + unsigned long statechange:1; /* Optional low-power state change */
> + unsigned long logicretstate:1; /* Optional logic off mode */
> +};
> +
> +struct omap_prm_domain {
> + struct device *dev;
> + struct omap_prm *prm;
> + struct generic_pm_domain pd;
> + void __iomem *pwrstctrl;
> + void __iomem *pwrstst;
I think the pwrstst is not really used as of now, it is just part of
couple of dev_dbg prints.
> + const struct omap_prm_domain_map *cap;
> + u32 pwrstctrl_saved;
> +};
> +
> struct omap_rst_map {
> s8 rst;
> s8 st;
> @@ -27,6 +59,9 @@ struct omap_prm_data {
> u32 base;
> const char *name;
> const char *clkdm_name;
> + u16 pwrstctrl;
> + u16 pwrstst;
> + const struct omap_prm_domain_map *dmap;
> u16 rstctrl;
> u16 rstst;
> const struct omap_rst_map *rstmap;
> @@ -36,6 +71,8 @@ struct omap_prm_data {
> struct omap_prm {
> const struct omap_prm_data *data;
> void __iomem *base;
> + struct clk *clocks[OMAP_PRM_NR_CLOCKS];
> + struct omap_prm_domain *prmd;
> };
>
> struct omap_reset_data {
> @@ -47,6 +84,7 @@ struct omap_reset_data {
> struct device *dev;
> };
>
> +#define genpd_to_prm_domain(gpd) container_of(gpd, struct omap_prm_domain, pd)
> #define to_omap_reset_data(p) container_of((p), struct omap_reset_data, rcdev)
>
> #define OMAP_MAX_RESETS 8
> @@ -58,6 +96,40 @@ struct omap_reset_data {
>
> #define OMAP_PRM_HAS_RESETS (OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_RSTST)
>
> +#define PRM_LOGICRETSTATE BIT(2)
> +#define PRM_LOWPOWERSTATECHANGE BIT(4)
> +#define PRM_POWERSTATE_MASK OMAP_PRMD_ON_ACTIVE
> +
> +static const struct __maybe_unused
> +omap_prm_domain_map omap_prm_all = {
> + .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_ON_INACTIVE) |
> + BIT(OMAP_PRMD_RETENTION) | BIT(OMAP_PRMD_OFF),
> + .statechange = 1,
> + .logicretstate = 1,
> +};
> +
> +static const struct __maybe_unused
> +omap_prm_domain_map omap_prm_noinact = {
> + .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_RETENTION) |
> + BIT(OMAP_PRMD_OFF),
> + .statechange = 1,
> + .logicretstate = 1,
> +};
> +
> +static const struct __maybe_unused
> +omap_prm_domain_map omap_prm_nooff = {
> + .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_ON_INACTIVE) |
> + BIT(OMAP_PRMD_RETENTION),
> + .statechange = 1,
> + .logicretstate = 1,
> +};
> +
> +static const struct __maybe_unused
> +omap_prm_domain_map omap_prm_onoff_noauto = {
> + .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_OFF),
> + .statechange = 1,
> +};
> +
> static const struct omap_rst_map rst_map_0[] = {
> { .rst = 0, .st = 0 },
> { .rst = -1 },
> @@ -151,6 +223,152 @@ static const struct of_device_id omap_prm_id_table[] = {
> { },
> };
>
> +static int omap_prm_domain_power_on(struct generic_pm_domain *domain)
> +{
> + struct omap_prm_domain *prmd;
> + u32 v;
> +
> + prmd = genpd_to_prm_domain(domain);
> + if (!prmd->cap)
> + return 0;
> +
> + dev_dbg(prmd->dev, "%s: %s: old state: pwrstctrl: %08x pwrstst: %08x\n",
> + __func__, prmd->pd.name, readl_relaxed(prmd->pwrstctrl),
> + readl_relaxed(prmd->pwrstst));
> +
> + if (prmd->pwrstctrl_saved)
> + v = prmd->pwrstctrl_saved;
> + else
> + v = readl_relaxed(prmd->pwrstctrl);
> +
> + writel_relaxed(v | OMAP_PRMD_ON_ACTIVE, prmd->pwrstctrl);
> + dev_dbg(prmd->dev, "%s: %s: new state pwrstctrl: %08x\n",
> + __func__, prmd->pd.name, readl_relaxed(prmd->pwrstctrl));
Should we wait for the transition to complete here?
> +
> + return 0;
> +}
> +
> +/* No need to check for holes in the mask for the lowest mode */
> +static int omap_prm_domain_find_lowest(struct omap_prm_domain *prmd)
> +{
> + return __ffs(prmd->cap->usable_modes);
> +}
> +
> +static int omap_prm_domain_power_off(struct generic_pm_domain *domain)
> +{
> + struct omap_prm_domain *prmd;
> + u32 v;
> +
> + prmd = genpd_to_prm_domain(domain);
> + if (!prmd->cap)
> + return 0;
> +
> + v = readl_relaxed(prmd->pwrstctrl);
> + prmd->pwrstctrl_saved = v;
> +
> + dev_dbg(prmd->dev, "%s: %s: old state: pwrstctrl: %08x pwrstst: %08x\n",
> + __func__, prmd->pd.name, v, readl_relaxed(prmd->pwrstst));
> +
> + v &= ~PRM_POWERSTATE_MASK;
> + v |= omap_prm_domain_find_lowest(prmd);
> +
> + if (prmd->cap->statechange)
> + v |= PRM_LOWPOWERSTATECHANGE;
> + if (prmd->cap->logicretstate)
> + v &= ~PRM_LOGICRETSTATE;
> + else
> + v |= PRM_LOGICRETSTATE;
> +
> + writel_relaxed(v, prmd->pwrstctrl);
Should we wait for the transition to complete here?
> +
> + dev_dbg(prmd->dev, "%s: %s new state pwrstctrl: %08x\n",
> + __func__, prmd->pd.name, readl_relaxed(prmd->pwrstctrl));
> +
> + return 0;
> +}
> +
> +static int omap_prm_domain_attach_dev(struct generic_pm_domain *domain,
> + struct device *dev)
> +{
> + struct generic_pm_domain_data *genpd_data;
> + struct of_phandle_args pd_args;
> + struct omap_prm_domain *prmd;
> + struct device_node *np;
> + int ret;
> +
> + prmd = genpd_to_prm_domain(domain);
> + np = dev->of_node;
> +
> + ret = of_parse_phandle_with_args(np, "power-domains",
> + "#power-domain-cells", 0, &pd_args);
> + if (ret < 0)
> + return ret;
> +
> + if (pd_args.args_count != 0)
> + dev_warn(dev, "%s: unusupported #power-domain-cells: %i\n",
> + prmd->pd.name, pd_args.args_count);
> +
> + genpd_data = dev_gpd_data(dev);
> + genpd_data->data = NULL;
> +
> + return 0;
> +}
> +
> +static void omap_prm_domain_detach_dev(struct generic_pm_domain *domain,
> + struct device *dev)
> +{
> + struct generic_pm_domain_data *genpd_data;
> + struct omap_prm_domain *prmd;
> +
> + prmd = genpd_to_prm_domain(domain);
> +
> + genpd_data = dev_gpd_data(dev);
> + genpd_data->data = NULL;
> +}
> +
> +static int omap_prm_domain_init(struct device *dev, struct omap_prm *prm)
> +{
> + struct omap_prm_domain *prmd;
> + struct device_node *np = dev->of_node;
> + const struct omap_prm_data *data;
> + const char *name;
> + int error;
> +
> + if (!of_find_property(dev->of_node, "#power-domain-cells", NULL))
> + return 0;
> +
> + of_node_put(dev->of_node);
> +
> + prmd = devm_kzalloc(dev, sizeof(*prmd), GFP_KERNEL);
> + if (!prmd)
> + return -ENOMEM;
> +
> + data = prm->data;
> + name = devm_kasprintf(dev, GFP_KERNEL, "prm_%s",
> + data->name);
> +
> + prmd->dev = dev;
> + prmd->prm = prm;
> + prmd->cap = prmd->prm->data->dmap;
> + prmd->pwrstctrl = prmd->prm->base + prmd->prm->data->pwrstctrl;
> + prmd->pwrstst = prmd->prm->base + prmd->prm->data->pwrstst;
> +
> + prmd->pd.name = name;
> + prmd->pd.power_on = omap_prm_domain_power_on;
> + prmd->pd.power_off = omap_prm_domain_power_off;
> + prmd->pd.attach_dev = omap_prm_domain_attach_dev;
> + prmd->pd.detach_dev = omap_prm_domain_detach_dev;
> +
> + pm_genpd_init(&prmd->pd, NULL, true);
> + error = of_genpd_add_provider_simple(np, &prmd->pd);
> + if (error)
> + pm_genpd_remove(&prmd->pd);
> + else
> + prm->prmd = prmd;
> +
> + return error;
> +}
> +
> static bool _is_valid_reset(struct omap_reset_data *reset, unsigned long id)
> {
> if (reset->mask & BIT(id))
> @@ -345,12 +563,48 @@ static int omap_prm_reset_init(struct platform_device *pdev,
> return devm_reset_controller_register(&pdev->dev, &reset->rcdev);
> }
>
Is any of the following clock handling needed, and if yes, whats its
purpose?
It looks like this is only used for ABE clkctrl handling on omap4/omap5
(at least for now), but afaik, ABE clkctrl is read only so this code
would effectively do nothing (and potentially just even fail.)
-Tero
> +static int omap_prm_init_clock(struct device *dev, struct omap_prm *prm,
> + const char *name, enum omap_prm_clocks index)
> +{
> + struct clk *clock;
> + int error;
> +
> + clock = devm_clk_get(dev, name);
> + if (IS_ERR(clock)) {
> + if (PTR_ERR(clock) == -ENOENT)
> + error = 0;
> + else
> + error = PTR_ERR(prm->clocks[index]);
> +
> + goto out_done;
> + }
> +
> + error = clk_prepare_enable(clock);
> +
> +out_done:
> + prm->clocks[index] = clock;
> +
> + return error;
> +}
> +
> +static void omap_prm_disable_clocks(struct omap_prm *prm)
> +{
> + int i;
> +
> + for (i = 0; i < OMAP_PRM_NR_CLOCKS; i++) {
> + if (IS_ERR(prm->clocks[i]))
> + continue;
> + clk_disable_unprepare(prm->clocks[i]);
> + }
> +}
> +
> static int omap_prm_probe(struct platform_device *pdev)
> {
> struct resource *res;
> const struct omap_prm_data *data;
> struct omap_prm *prm;
> const struct of_device_id *match;
> + int ret;
>
> res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> if (!res)
> @@ -378,7 +632,32 @@ static int omap_prm_probe(struct platform_device *pdev)
> if (IS_ERR(prm->base))
> return PTR_ERR(prm->base);
>
> - return omap_prm_reset_init(pdev, prm);
> + ret = omap_prm_init_clock(&pdev->dev, prm, "fck", OMAP_PRM_FCK);
> + if (ret)
> + goto err_disable;
> +
> + ret = omap_prm_init_clock(&pdev->dev, prm, "ick", OMAP_PRM_ICK);
> + if (ret)
> + goto err_disable;
> +
> + ret = omap_prm_domain_init(&pdev->dev, prm);
> + if (ret)
> + goto err_disable;
> +
> + ret = omap_prm_reset_init(pdev, prm);
> + if (ret)
> + goto err_domain;
> +
> + return 0;
> +
> +err_domain:
> + of_genpd_del_provider(pdev->dev.of_node);
> + pm_genpd_remove(&prm->prmd->pd);
> +
> +err_disable:
> + omap_prm_disable_clocks(prm);
> +
> + return ret;
> }
>
> static struct platform_driver omap_prm_driver = {
>
--
Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
^ permalink raw reply
* [PATCH v3 01/15] dt-bindings: convert the binding document for mediatek PERICFG to yaml
From: Bartosz Golaszewski @ 2020-05-14 7:59 UTC (permalink / raw)
To: Jonathan Corbet, Rob Herring, David S . Miller, Matthias Brugger,
John Crispin, Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann,
Fabien Parent, Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200514075942.10136-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Convert the DT binding .txt file for MediaTek's peripheral configuration
controller to YAML. There's one special case where the compatible has
three positions. Otherwise, it's a pretty normal syscon.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
.../arm/mediatek/mediatek,pericfg.txt | 36 -----------
.../arm/mediatek/mediatek,pericfg.yaml | 63 +++++++++++++++++++
2 files changed, 63 insertions(+), 36 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt
create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.yaml
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt
deleted file mode 100644
index ecf027a9003a..000000000000
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-Mediatek pericfg controller
-===========================
-
-The Mediatek pericfg controller provides various clocks and reset
-outputs to the system.
-
-Required Properties:
-
-- compatible: Should be one of:
- - "mediatek,mt2701-pericfg", "syscon"
- - "mediatek,mt2712-pericfg", "syscon"
- - "mediatek,mt7622-pericfg", "syscon"
- - "mediatek,mt7623-pericfg", "mediatek,mt2701-pericfg", "syscon"
- - "mediatek,mt7629-pericfg", "syscon"
- - "mediatek,mt8135-pericfg", "syscon"
- - "mediatek,mt8173-pericfg", "syscon"
- - "mediatek,mt8183-pericfg", "syscon"
-- #clock-cells: Must be 1
-- #reset-cells: Must be 1
-
-The pericfg controller uses the common clk binding from
-Documentation/devicetree/bindings/clock/clock-bindings.txt
-The available clocks are defined in dt-bindings/clock/mt*-clk.h.
-Also it uses the common reset controller binding from
-Documentation/devicetree/bindings/reset/reset.txt.
-The available reset outputs are defined in
-dt-bindings/reset/mt*-resets.h
-
-Example:
-
-pericfg: power-controller@10003000 {
- compatible = "mediatek,mt8173-pericfg", "syscon";
- reg = <0 0x10003000 0 0x1000>;
- #clock-cells = <1>;
- #reset-cells = <1>;
-};
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.yaml b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.yaml
new file mode 100644
index 000000000000..1340c6288024
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.yaml
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/arm/mediatek/mediatek,pericfg.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: MediaTek Peripheral Configuration Controller
+
+maintainers:
+ - Bartosz Golaszewski <bgolaszewski@baylibre.com>
+
+description:
+ The Mediatek pericfg controller provides various clocks and reset outputs
+ to the system.
+
+properties:
+ compatible:
+ oneOf:
+ - items:
+ - enum:
+ - mediatek,mt2701-pericfg
+ - mediatek,mt2712-pericfg
+ - mediatek,mt7622-pericfg
+ - mediatek,mt7629-pericfg
+ - mediatek,mt8135-pericfg
+ - mediatek,mt8173-pericfg
+ - mediatek,mt8183-pericfg
+ - const: syscon
+ - items:
+ # Special case for mt7623 for backward compatibility
+ - const: mediatek,mt7623-pericfg
+ - const: mediatek,mt2701-pericfg
+ - const: syscon
+
+ reg:
+ maxItems: 1
+
+ '#clock-cells':
+ const: 1
+
+ '#reset-cells':
+ const: 1
+
+required:
+ - compatible
+ - reg
+
+examples:
+ - |
+ pericfg@10003000 {
+ compatible = "mediatek,mt8173-pericfg", "syscon";
+ reg = <0x10003000 0x1000>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
+ - |
+ pericfg@10003000 {
+ compatible = "mediatek,mt7623-pericfg", "mediatek,mt2701-pericfg", "syscon";
+ reg = <0x10003000 0x1000>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
--
2.25.0
^ permalink raw reply related
* [PATCH v3 03/15] dt-bindings: net: add a binding document for MediaTek Ethernet MAC
From: Bartosz Golaszewski @ 2020-05-14 7:59 UTC (permalink / raw)
To: Jonathan Corbet, Rob Herring, David S . Miller, Matthias Brugger,
John Crispin, Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann,
Fabien Parent, Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200514075942.10136-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
This adds yaml DT bindings for the MediaTek Ethernet MAC present on the
mt8* family of SoCs.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
.../bindings/net/mediatek,eth-mac.yaml | 89 +++++++++++++++++++
1 file changed, 89 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/mediatek,eth-mac.yaml
diff --git a/Documentation/devicetree/bindings/net/mediatek,eth-mac.yaml b/Documentation/devicetree/bindings/net/mediatek,eth-mac.yaml
new file mode 100644
index 000000000000..8ffd0b762c0f
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/mediatek,eth-mac.yaml
@@ -0,0 +1,89 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/mediatek,eth-mac.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek Ethernet MAC Controller
+
+maintainers:
+ - Bartosz Golaszewski <bgolaszewski@baylibre.com>
+
+description:
+ This Ethernet MAC is used on the MT8* family of SoCs from MediaTek.
+ It's compliant with 802.3 standards and supports half- and full-duplex
+ modes with flow-control as well as CRC offloading and VLAN tags.
+
+allOf:
+ - $ref: "ethernet-controller.yaml#"
+
+properties:
+ compatible:
+ enum:
+ - mediatek,mt8516-eth
+ - mediatek,mt8518-eth
+ - mediatek,mt8175-eth
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ minItems: 3
+ maxItems: 3
+
+ clock-names:
+ additionalItems: false
+ items:
+ - const: core
+ - const: reg
+ - const: trans
+
+ mediatek,pericfg:
+ $ref: /schemas/types.yaml#definitions/phandle
+ description:
+ Phandle to the device containing the PERICFG register range. This is used
+ to control the MII mode.
+
+ mdio:
+ type: object
+ description:
+ Creates and registers an MDIO bus.
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+ - mediatek,pericfg
+ - phy-handle
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/mt8516-clk.h>
+
+ ethernet: ethernet@11180000 {
+ compatible = "mediatek,mt8516-eth";
+ reg = <0x11180000 0x1000>;
+ mediatek,pericfg = <&pericfg>;
+ interrupts = <GIC_SPI 111 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&topckgen CLK_TOP_RG_ETH>,
+ <&topckgen CLK_TOP_66M_ETH>,
+ <&topckgen CLK_TOP_133M_ETH>;
+ clock-names = "core", "reg", "trans";
+ phy-handle = <ð_phy>;
+ phy-mode = "rmii";
+
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eth_phy: ethernet-phy@0 {
+ reg = <0>;
+ };
+ };
+ };
--
2.25.0
^ permalink raw reply related
* [PATCH v3 02/15] dt-bindings: add new compatible to mediatek,pericfg
From: Bartosz Golaszewski @ 2020-05-14 7:59 UTC (permalink / raw)
To: Jonathan Corbet, Rob Herring, David S . Miller, Matthias Brugger,
John Crispin, Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann,
Fabien Parent, Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200514075942.10136-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
The PERICFG controller is present on the MT8516 SoC. Add an appropriate
compatible variant.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
.../devicetree/bindings/arm/mediatek/mediatek,pericfg.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.yaml b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.yaml
index 1340c6288024..55209a2baedc 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.yaml
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.yaml
@@ -25,6 +25,7 @@ properties:
- mediatek,mt8135-pericfg
- mediatek,mt8173-pericfg
- mediatek,mt8183-pericfg
+ - mediatek,mt8516-pericfg
- const: syscon
- items:
# Special case for mt7623 for backward compatibility
--
2.25.0
^ permalink raw reply related
* [PATCH v3 05/15] net: ethernet: mediatek: remove unnecessary spaces from Makefile
From: Bartosz Golaszewski @ 2020-05-14 7:59 UTC (permalink / raw)
To: Jonathan Corbet, Rob Herring, David S . Miller, Matthias Brugger,
John Crispin, Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann,
Fabien Parent, Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200514075942.10136-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
The Makefile formatting in the kernel tree usually doesn't use tabs,
so remove them before we add a second driver.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
drivers/net/ethernet/mediatek/Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile
index 2d8362f9341b..3362fb7ef859 100644
--- a/drivers/net/ethernet/mediatek/Makefile
+++ b/drivers/net/ethernet/mediatek/Makefile
@@ -3,5 +3,5 @@
# Makefile for the Mediatek SoCs built-in ethernet macs
#
-obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o
+obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o
mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o
--
2.25.0
^ permalink raw reply related
* [PATCH v3 04/15] net: ethernet: mediatek: rename Kconfig prompt
From: Bartosz Golaszewski @ 2020-05-14 7:59 UTC (permalink / raw)
To: Jonathan Corbet, Rob Herring, David S . Miller, Matthias Brugger,
John Crispin, Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann,
Fabien Parent, Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200514075942.10136-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
We'll soon by adding a second MediaTek Ethernet driver so modify the
Kconfig prompt.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
drivers/net/ethernet/mediatek/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/mediatek/Kconfig b/drivers/net/ethernet/mediatek/Kconfig
index 4968352ba188..5079b8090f16 100644
--- a/drivers/net/ethernet/mediatek/Kconfig
+++ b/drivers/net/ethernet/mediatek/Kconfig
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
config NET_VENDOR_MEDIATEK
- bool "MediaTek ethernet driver"
+ bool "MediaTek devices"
depends on ARCH_MEDIATEK || SOC_MT7621 || SOC_MT7620
---help---
If you have a Mediatek SoC with ethernet, say Y.
--
2.25.0
^ permalink raw reply related
* [PATCH v3 07/15] net: move devres helpers into a separate source file
From: Bartosz Golaszewski @ 2020-05-14 7:59 UTC (permalink / raw)
To: Jonathan Corbet, Rob Herring, David S . Miller, Matthias Brugger,
John Crispin, Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann,
Fabien Parent, Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200514075942.10136-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
There's currently only a single devres helper in net/ - devm variant
of alloc_etherdev. Let's move it to net/devres.c with the intention of
assing a second one: devm_register_netdev(). This new routine will need
to know the address of the release function of devm_alloc_etherdev() so
that it can verify (using devres_find()) that the struct net_device
that's being passed to it is also resource managed.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
net/Makefile | 2 +-
net/devres.c | 36 ++++++++++++++++++++++++++++++++++++
net/ethernet/eth.c | 28 ----------------------------
3 files changed, 37 insertions(+), 29 deletions(-)
create mode 100644 net/devres.c
diff --git a/net/Makefile b/net/Makefile
index 07ea48160874..5744bf1997fd 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -6,7 +6,7 @@
# Rewritten to use lists instead of if-statements.
#
-obj-$(CONFIG_NET) := socket.o core/
+obj-$(CONFIG_NET) := devres.o socket.o core/
tmp-$(CONFIG_COMPAT) := compat.o
obj-$(CONFIG_NET) += $(tmp-y)
diff --git a/net/devres.c b/net/devres.c
new file mode 100644
index 000000000000..c1465d9f9019
--- /dev/null
+++ b/net/devres.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * This file contains all networking devres helpers.
+ */
+
+#include <linux/device.h>
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+
+static void devm_free_netdev(struct device *dev, void *res)
+{
+ free_netdev(*(struct net_device **)res);
+}
+
+struct net_device *devm_alloc_etherdev_mqs(struct device *dev, int sizeof_priv,
+ unsigned int txqs, unsigned int rxqs)
+{
+ struct net_device **dr;
+ struct net_device *netdev;
+
+ dr = devres_alloc(devm_free_netdev, sizeof(*dr), GFP_KERNEL);
+ if (!dr)
+ return NULL;
+
+ netdev = alloc_etherdev_mqs(sizeof_priv, txqs, rxqs);
+ if (!netdev) {
+ devres_free(dr);
+ return NULL;
+ }
+
+ *dr = netdev;
+ devres_add(dev, dr);
+
+ return netdev;
+}
+EXPORT_SYMBOL(devm_alloc_etherdev_mqs);
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index c8b903302ff2..dac65180c4ef 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -400,34 +400,6 @@ struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs,
}
EXPORT_SYMBOL(alloc_etherdev_mqs);
-static void devm_free_netdev(struct device *dev, void *res)
-{
- free_netdev(*(struct net_device **)res);
-}
-
-struct net_device *devm_alloc_etherdev_mqs(struct device *dev, int sizeof_priv,
- unsigned int txqs, unsigned int rxqs)
-{
- struct net_device **dr;
- struct net_device *netdev;
-
- dr = devres_alloc(devm_free_netdev, sizeof(*dr), GFP_KERNEL);
- if (!dr)
- return NULL;
-
- netdev = alloc_etherdev_mqs(sizeof_priv, txqs, rxqs);
- if (!netdev) {
- devres_free(dr);
- return NULL;
- }
-
- *dr = netdev;
- devres_add(dev, dr);
-
- return netdev;
-}
-EXPORT_SYMBOL(devm_alloc_etherdev_mqs);
-
ssize_t sysfs_format_mac(char *buf, const unsigned char *addr, int len)
{
return scnprintf(buf, PAGE_SIZE, "%*phC\n", len, addr);
--
2.25.0
^ permalink raw reply related
* [PATCH v3 08/15] net: devres: define a separate devres structure for devm_alloc_etherdev()
From: Bartosz Golaszewski @ 2020-05-14 7:59 UTC (permalink / raw)
To: Jonathan Corbet, Rob Herring, David S . Miller, Matthias Brugger,
John Crispin, Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann,
Fabien Parent, Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200514075942.10136-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Not using a proxy structure to store struct net_device doesn't save
anything in terms of compiled code size or memory usage but significantly
decreases the readability of the code with all the pointer casting.
Define struct net_device_devres and use it in devm_alloc_etherdev_mqs().
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
net/devres.c | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/net/devres.c b/net/devres.c
index c1465d9f9019..b97b0c5a8216 100644
--- a/net/devres.c
+++ b/net/devres.c
@@ -7,30 +7,34 @@
#include <linux/etherdevice.h>
#include <linux/netdevice.h>
-static void devm_free_netdev(struct device *dev, void *res)
+struct net_device_devres {
+ struct net_device *ndev;
+};
+
+static void devm_free_netdev(struct device *dev, void *this)
{
- free_netdev(*(struct net_device **)res);
+ struct net_device_devres *res = this;
+
+ free_netdev(res->ndev);
}
struct net_device *devm_alloc_etherdev_mqs(struct device *dev, int sizeof_priv,
unsigned int txqs, unsigned int rxqs)
{
- struct net_device **dr;
- struct net_device *netdev;
+ struct net_device_devres *dr;
dr = devres_alloc(devm_free_netdev, sizeof(*dr), GFP_KERNEL);
if (!dr)
return NULL;
- netdev = alloc_etherdev_mqs(sizeof_priv, txqs, rxqs);
- if (!netdev) {
+ dr->ndev = alloc_etherdev_mqs(sizeof_priv, txqs, rxqs);
+ if (!dr->ndev) {
devres_free(dr);
return NULL;
}
- *dr = netdev;
devres_add(dev, dr);
- return netdev;
+ return dr->ndev;
}
EXPORT_SYMBOL(devm_alloc_etherdev_mqs);
--
2.25.0
^ permalink raw reply related
* [PATCH v3 09/15] net: devres: provide devm_register_netdev()
From: Bartosz Golaszewski @ 2020-05-14 7:59 UTC (permalink / raw)
To: Jonathan Corbet, Rob Herring, David S . Miller, Matthias Brugger,
John Crispin, Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann,
Fabien Parent, Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200514075942.10136-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Provide devm_register_netdev() - a device resource managed variant
of register_netdev(). This new helper will only work for net_device
structs that are also already managed by devres.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
.../driver-api/driver-model/devres.rst | 1 +
include/linux/netdevice.h | 2 +
net/devres.c | 55 +++++++++++++++++++
3 files changed, 58 insertions(+)
diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst
index 50df28d20fa7..fc242ed4bde5 100644
--- a/Documentation/driver-api/driver-model/devres.rst
+++ b/Documentation/driver-api/driver-model/devres.rst
@@ -375,6 +375,7 @@ MUX
NET
devm_alloc_etherdev()
devm_alloc_etherdev_mqs()
+ devm_register_netdev()
PER-CPU MEM
devm_alloc_percpu()
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 130a668049ab..c4ad728993dd 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -4208,6 +4208,8 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
int register_netdev(struct net_device *dev);
void unregister_netdev(struct net_device *dev);
+int devm_register_netdev(struct device *dev, struct net_device *ndev);
+
/* General hardware address lists handling functions */
int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
struct netdev_hw_addr_list *from_list, int addr_len);
diff --git a/net/devres.c b/net/devres.c
index b97b0c5a8216..57a6a88d11f6 100644
--- a/net/devres.c
+++ b/net/devres.c
@@ -38,3 +38,58 @@ struct net_device *devm_alloc_etherdev_mqs(struct device *dev, int sizeof_priv,
return dr->ndev;
}
EXPORT_SYMBOL(devm_alloc_etherdev_mqs);
+
+static void devm_netdev_release(struct device *dev, void *this)
+{
+ struct net_device_devres *res = this;
+
+ unregister_netdev(res->ndev);
+}
+
+static int netdev_devres_match(struct device *dev, void *this, void *match_data)
+{
+ struct net_device_devres *res = this;
+ struct net_device *ndev = match_data;
+
+ return ndev == res->ndev;
+}
+
+/**
+ * devm_register_netdev - resource managed variant of register_netdev()
+ * @dev: managing device for this netdev - usually the parent device
+ * @ndev: device to register
+ *
+ * This is a devres variant of register_netdev() for which the unregister
+ * function will be call automatically when the managing device is
+ * detached. Note: the net_device used must also be resource managed by
+ * the same struct device.
+ */
+int devm_register_netdev(struct device *dev, struct net_device *ndev)
+{
+ struct net_device_devres *dr;
+ int ret;
+
+ /* struct net_device must itself be managed. For now a managed netdev
+ * can only be allocated by devm_alloc_etherdev_mqs() so the check is
+ * straightforward.
+ */
+ if (WARN_ON(!devres_find(dev, devm_free_netdev,
+ netdev_devres_match, ndev)))
+ return -EINVAL;
+
+ dr = devres_alloc(devm_netdev_release, sizeof(*dr), GFP_KERNEL);
+ if (!dr)
+ return -ENOMEM;
+
+ ret = register_netdev(ndev);
+ if (ret) {
+ devres_free(dr);
+ return ret;
+ }
+
+ dr->ndev = ndev;
+ devres_add(ndev->dev.parent, dr);
+
+ return 0;
+}
+EXPORT_SYMBOL(devm_register_netdev);
--
2.25.0
^ permalink raw reply related
* [PATCH v3 10/15] net: ethernet: mtk-eth-mac: new driver
From: Bartosz Golaszewski @ 2020-05-14 7:59 UTC (permalink / raw)
To: Jonathan Corbet, Rob Herring, David S . Miller, Matthias Brugger,
John Crispin, Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann,
Fabien Parent, Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200514075942.10136-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
This adds the driver for the MediaTek Ethernet MAC used on the MT8* SoC
family. For now we only support full-duplex.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
drivers/net/ethernet/mediatek/Kconfig | 6 +
drivers/net/ethernet/mediatek/Makefile | 1 +
drivers/net/ethernet/mediatek/mtk_eth_mac.c | 1578 +++++++++++++++++++
3 files changed, 1585 insertions(+)
create mode 100644 drivers/net/ethernet/mediatek/mtk_eth_mac.c
diff --git a/drivers/net/ethernet/mediatek/Kconfig b/drivers/net/ethernet/mediatek/Kconfig
index 5079b8090f16..5c3793076765 100644
--- a/drivers/net/ethernet/mediatek/Kconfig
+++ b/drivers/net/ethernet/mediatek/Kconfig
@@ -14,4 +14,10 @@ config NET_MEDIATEK_SOC
This driver supports the gigabit ethernet MACs in the
MediaTek SoC family.
+config NET_MEDIATEK_MAC
+ tristate "MediaTek Ethernet MAC support"
+ select PHYLIB
+ help
+ This driver supports the ethernet IP on MediaTek MT85** SoCs.
+
endif #NET_VENDOR_MEDIATEK
diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile
index 3362fb7ef859..f7f5638943a0 100644
--- a/drivers/net/ethernet/mediatek/Makefile
+++ b/drivers/net/ethernet/mediatek/Makefile
@@ -5,3 +5,4 @@
obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o
mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o
+obj-$(CONFIG_NET_MEDIATEK_MAC) += mtk_eth_mac.o
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_mac.c b/drivers/net/ethernet/mediatek/mtk_eth_mac.c
new file mode 100644
index 000000000000..6fbe49e861d6
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/mtk_eth_mac.c
@@ -0,0 +1,1578 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020 MediaTek Corporation
+ * Copyright (c) 2020 BayLibre SAS
+ *
+ * Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+
+#define MTK_MAC_DRVNAME "mtk_eth_mac"
+
+#define MTK_MAC_WAIT_TIMEOUT 300
+#define MTK_MAC_MAX_FRAME_SIZE 1514
+#define MTK_MAC_SKB_ALIGNMENT 16
+#define MTK_MAC_NAPI_WEIGHT 64
+#define MTK_MAC_HASHTABLE_MC_LIMIT 256
+#define MTK_MAC_HASHTABLE_SIZE_MAX 512
+
+/* This is defined to 0 on arm64 in arch/arm64/include/asm/processor.h but
+ * this IP doesn't work without this alignment being equal to 2.
+ */
+#ifdef NET_IP_ALIGN
+#undef NET_IP_ALIGN
+#endif
+#define NET_IP_ALIGN 2
+
+static const char *const mtk_mac_clk_names[] = { "core", "reg", "trans" };
+#define MTK_MAC_NCLKS ARRAY_SIZE(mtk_mac_clk_names)
+
+/* PHY Control Register 0 */
+#define MTK_MAC_REG_PHY_CTRL0 0x0000
+#define MTK_MAC_BIT_PHY_CTRL0_WTCMD BIT(13)
+#define MTK_MAC_BIT_PHY_CTRL0_RDCMD BIT(14)
+#define MTK_MAC_BIT_PHY_CTRL0_RWOK BIT(15)
+#define MTK_MAC_MSK_PHY_CTRL0_PREG GENMASK(12, 8)
+#define MTK_MAC_OFF_PHY_CTRL0_PREG 8
+#define MTK_MAC_MSK_PHY_CTRL0_RWDATA GENMASK(31, 16)
+#define MTK_MAC_OFF_PHY_CTRL0_RWDATA 16
+
+/* PHY Control Register 1 */
+#define MTK_MAC_REG_PHY_CTRL1 0x0004
+#define MTK_MAC_BIT_PHY_CTRL1_LINK_ST BIT(0)
+#define MTK_MAC_BIT_PHY_CTRL1_AN_EN BIT(8)
+#define MTK_MAC_OFF_PHY_CTRL1_FORCE_SPD 9
+#define MTK_MAC_VAL_PHY_CTRL1_FORCE_SPD_10M 0x00
+#define MTK_MAC_VAL_PHY_CTRL1_FORCE_SPD_100M 0x01
+#define MTK_MAC_VAL_PHY_CTRL1_FORCE_SPD_1000M 0x02
+#define MTK_MAC_BIT_PHY_CTRL1_FORCE_DPX BIT(11)
+#define MTK_MAC_BIT_PHY_CTRL1_FORCE_FC_RX BIT(12)
+#define MTK_MAC_BIT_PHY_CTRL1_FORCE_FC_TX BIT(13)
+
+/* MAC Configuration Register */
+#define MTK_MAC_REG_MAC_CFG 0x0008
+#define MTK_MAC_OFF_MAC_CFG_IPG 10
+#define MTK_MAC_VAL_MAC_CFG_IPG_96BIT GENMASK(4, 0)
+#define MTK_MAC_BIT_MAC_CFG_MAXLEN_1522 BIT(16)
+#define MTK_MAC_BIT_MAC_CFG_AUTO_PAD BIT(19)
+#define MTK_MAC_BIT_MAC_CFG_CRC_STRIP BIT(20)
+#define MTK_MAC_BIT_MAC_CFG_VLAN_STRIP BIT(22)
+#define MTK_MAC_BIT_MAC_CFG_NIC_PD BIT(31)
+
+/* Flow-Control Configuration Register */
+#define MTK_MAC_REG_FC_CFG 0x000c
+#define MTK_MAC_BIT_FC_CFG_BP_EN BIT(7)
+#define MTK_MAC_BIT_FC_CFG_UC_PAUSE_DIR BIT(8)
+#define MTK_MAC_OFF_FC_CFG_SEND_PAUSE_TH 16
+#define MTK_MAC_MSK_FC_CFG_SEND_PAUSE_TH GENMASK(27, 16)
+#define MTK_MAC_VAL_FC_CFG_SEND_PAUSE_TH_2K 0x800
+
+/* ARL Configuration Register */
+#define MTK_MAC_REG_ARL_CFG 0x0010
+#define MTK_MAC_BIT_ARL_CFG_HASH_ALG BIT(0)
+#define MTK_MAC_BIT_ARL_CFG_MISC_MODE BIT(4)
+
+/* MAC High and Low Bytes Registers */
+#define MTK_MAC_REG_MY_MAC_H 0x0014
+#define MTK_MAC_REG_MY_MAC_L 0x0018
+
+/* Hash Table Control Register */
+#define MTK_MAC_REG_HASH_CTRL 0x001c
+#define MTK_MAC_MSK_HASH_CTRL_HASH_BIT_ADDR GENMASK(8, 0)
+#define MTK_MAC_BIT_HASH_CTRL_HASH_BIT_DATA BIT(12)
+#define MTK_MAC_BIT_HASH_CTRL_ACC_CMD BIT(13)
+#define MTK_MAC_BIT_HASH_CTRL_CMD_START BIT(14)
+#define MTK_MAC_BIT_HASH_CTRL_BIST_OK BIT(16)
+#define MTK_MAC_BIT_HASH_CTRL_BIST_DONE BIT(17)
+#define MTK_MAC_BIT_HASH_CTRL_BIST_EN BIT(31)
+
+/* TX DMA Control Register */
+#define MTK_MAC_REG_TX_DMA_CTRL 0x0034
+#define MTK_MAC_BIT_TX_DMA_CTRL_START BIT(0)
+#define MTK_MAC_BIT_TX_DMA_CTRL_STOP BIT(1)
+#define MTK_MAC_BIT_TX_DMA_CTRL_RESUME BIT(2)
+
+/* RX DMA Control Register */
+#define MTK_MAC_REG_RX_DMA_CTRL 0x0038
+#define MTK_MAC_BIT_RX_DMA_CTRL_START BIT(0)
+#define MTK_MAC_BIT_RX_DMA_CTRL_STOP BIT(1)
+#define MTK_MAC_BIT_RX_DMA_CTRL_RESUME BIT(2)
+
+/* DMA Address Registers */
+#define MTK_MAC_REG_TX_DPTR 0x003c
+#define MTK_MAC_REG_RX_DPTR 0x0040
+#define MTK_MAC_REG_TX_BASE_ADDR 0x0044
+#define MTK_MAC_REG_RX_BASE_ADDR 0x0048
+
+/* Interrupt Status Register */
+#define MTK_MAC_REG_INT_STS 0x0050
+#define MTK_MAC_REG_INT_STS_PORT_STS_CHG BIT(2)
+#define MTK_MAC_REG_INT_STS_MIB_CNT_TH BIT(3)
+#define MTK_MAC_BIT_INT_STS_FNRC BIT(6)
+#define MTK_MAC_BIT_INT_STS_TNTC BIT(8)
+
+/* Interrupt Mask Register */
+#define MTK_MAC_REG_INT_MASK 0x0054
+#define MTK_MAC_BIT_INT_MASK_FNRC BIT(6)
+
+/* Misc. Config Register */
+#define MTK_MAC_REG_TEST1 0x005c
+#define MTK_MAC_BIT_TEST1_RST_HASH_MBIST BIT(31)
+
+/* Extended Configuration Register */
+#define MTK_MAC_REG_EXT_CFG 0x0060
+#define MTK_MAC_OFF_EXT_CFG_SND_PAUSE_RLS 16
+#define MTK_MAC_MSK_EXT_CFG_SND_PAUSE_RLS GENMASK(26, 16)
+#define MTK_MAC_VAL_EXT_CFG_SND_PAUSE_RLS_1K 0x400
+
+/* EthSys Configuration Register */
+#define MTK_MAC_REG_SYS_CONF 0x0094
+#define MTK_MAC_BIT_MII_PAD_OUT_ENABLE BIT(0)
+#define MTK_MAC_BIT_EXT_MDC_MODE BIT(1)
+#define MTK_MAC_BIT_SWC_MII_MODE BIT(2)
+
+/* MAC Clock Configuration Register */
+#define MTK_MAC_REG_MAC_CLK_CONF 0x00ac
+#define MTK_MAC_MSK_MAC_CLK_CONF GENMASK(7, 0)
+#define MTK_MAC_BIT_CLK_DIV_10 0x0a
+
+/* Counter registers. */
+#define MTK_MAC_REG_C_RXOKPKT 0x0100
+#define MTK_MAC_REG_C_RXOKBYTE 0x0104
+#define MTK_MAC_REG_C_RXRUNT 0x0108
+#define MTK_MAC_REG_C_RXLONG 0x010c
+#define MTK_MAC_REG_C_RXDROP 0x0110
+#define MTK_MAC_REG_C_RXCRC 0x0114
+#define MTK_MAC_REG_C_RXARLDROP 0x0118
+#define MTK_MAC_REG_C_RXVLANDROP 0x011c
+#define MTK_MAC_REG_C_RXCSERR 0x0120
+#define MTK_MAC_REG_C_RXPAUSE 0x0124
+#define MTK_MAC_REG_C_TXOKPKT 0x0128
+#define MTK_MAC_REG_C_TXOKBYTE 0x012c
+#define MTK_MAC_REG_C_TXPAUSECOL 0x0130
+#define MTK_MAC_REG_C_TXRTY 0x0134
+#define MTK_MAC_REG_C_TXSKIP 0x0138
+#define MTK_MAC_REG_C_TX_ARP 0x013c
+#define MTK_MAC_REG_C_RX_RERR 0x01d8
+#define MTK_MAC_REG_C_RX_UNI 0x01dc
+#define MTK_MAC_REG_C_RX_MULTI 0x01e0
+#define MTK_MAC_REG_C_RX_BROAD 0x01e4
+#define MTK_MAC_REG_C_RX_ALIGNERR 0x01e8
+#define MTK_MAC_REG_C_TX_UNI 0x01ec
+#define MTK_MAC_REG_C_TX_MULTI 0x01f0
+#define MTK_MAC_REG_C_TX_BROAD 0x01f4
+#define MTK_MAC_REG_C_TX_TIMEOUT 0x01f8
+#define MTK_MAC_REG_C_TX_LATECOL 0x01fc
+#define MTK_MAC_REG_C_RX_LENGTHERR 0x0214
+#define MTK_MAC_REG_C_RX_TWIST 0x0218
+
+/* Ethernet CFG Control */
+#define MTK_PERICFG_REG_NIC_CFG_CON 0x03c4
+#define MTK_PERICFG_MSK_NIC_CFG_CON_CFG_MII GENMASK(3, 0)
+#define MTK_PERICFG_BIT_NIC_CFG_CON_RMII BIT(0)
+
+/* Represents the actual structure of descriptors used by the MAC. We can
+ * reuse the same structure for both TX and RX - the layout is the same, only
+ * the flags differ slightly.
+ */
+struct mtk_mac_ring_desc {
+ /* Contains both the status flags as well as packet length. */
+ u32 status;
+ u32 data_ptr;
+ u32 vtag;
+ u32 reserved;
+};
+
+#define MTK_MAC_DESC_MSK_LEN GENMASK(15, 0)
+#define MTK_MAC_DESC_BIT_RX_CRCE BIT(24)
+#define MTK_MAC_DESC_BIT_RX_OSIZE BIT(25)
+#define MTK_MAC_DESC_BIT_INT BIT(27)
+#define MTK_MAC_DESC_BIT_LS BIT(28)
+#define MTK_MAC_DESC_BIT_FS BIT(29)
+#define MTK_MAC_DESC_BIT_EOR BIT(30)
+#define MTK_MAC_DESC_BIT_COWN BIT(31)
+
+/* Helper structure for storing data read from/written to descriptors in order
+ * to limit reads from/writes to DMA memory.
+ */
+struct mtk_mac_ring_desc_data {
+ unsigned int len;
+ unsigned int flags;
+ dma_addr_t dma_addr;
+ struct sk_buff *skb;
+};
+
+#define MTK_MAC_RING_NUM_DESCS 128
+#define MTK_MAC_NUM_TX_DESCS MTK_MAC_RING_NUM_DESCS
+#define MTK_MAC_NUM_RX_DESCS MTK_MAC_RING_NUM_DESCS
+#define MTK_MAC_NUM_DESCS_TOTAL (MTK_MAC_RING_NUM_DESCS * 2)
+#define MTK_MAC_DMA_SIZE \
+ (MTK_MAC_NUM_DESCS_TOTAL * sizeof(struct mtk_mac_ring_desc))
+
+struct mtk_mac_ring {
+ struct mtk_mac_ring_desc *descs;
+ struct sk_buff *skbs[MTK_MAC_RING_NUM_DESCS];
+ dma_addr_t dma_addrs[MTK_MAC_RING_NUM_DESCS];
+ unsigned int head;
+ unsigned int tail;
+ unsigned int count;
+};
+
+struct mtk_mac_priv {
+ struct net_device *ndev;
+
+ struct regmap *regs;
+ struct regmap *pericfg;
+
+ struct clk_bulk_data clks[MTK_MAC_NCLKS];
+
+ void *ring_base;
+ struct mtk_mac_ring_desc *descs_base;
+ dma_addr_t dma_addr;
+ struct mtk_mac_ring tx_ring;
+ struct mtk_mac_ring rx_ring;
+
+ struct mii_bus *mii;
+ struct napi_struct napi;
+
+ struct device_node *phy_node;
+ phy_interface_t phy_intf;
+ struct phy_device *phydev;
+ unsigned int link;
+ int speed;
+ int duplex;
+ int pause;
+
+ /* Protects against concurrent descriptor access. */
+ spinlock_t lock;
+ unsigned long lock_flags;
+
+ struct rtnl_link_stats64 stats;
+};
+
+static struct device *mtk_mac_get_dev(struct mtk_mac_priv *priv)
+{
+ struct net_device *ndev = priv->ndev;
+
+ return ndev->dev.parent;
+}
+
+static const struct regmap_config mtk_mac_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .fast_io = true,
+};
+
+static void mtk_mac_ring_init(struct mtk_mac_ring *ring,
+ struct mtk_mac_ring_desc *descs,
+ unsigned int start_count)
+{
+ memset(ring, 0, sizeof(*ring));
+ ring->descs = descs;
+ ring->head = 0;
+ ring->tail = 0;
+ ring->count = start_count;
+}
+
+static int mtk_mac_ring_pop_tail(struct mtk_mac_ring *ring,
+ struct mtk_mac_ring_desc_data *desc_data)
+{
+ struct mtk_mac_ring_desc *desc = &ring->descs[ring->tail];
+ unsigned int status;
+
+ /* Let the device release the descriptor. */
+ dma_rmb();
+ status = desc->status;
+
+ if (!(status & MTK_MAC_DESC_BIT_COWN))
+ return -1;
+
+ desc_data->len = status & MTK_MAC_DESC_MSK_LEN;
+ desc_data->flags = status & ~MTK_MAC_DESC_MSK_LEN;
+ desc_data->dma_addr = ring->dma_addrs[ring->tail];
+ desc_data->skb = ring->skbs[ring->tail];
+
+ desc->data_ptr = 0;
+ desc->status = MTK_MAC_DESC_BIT_COWN;
+ if (status & MTK_MAC_DESC_BIT_EOR)
+ desc->status |= MTK_MAC_DESC_BIT_EOR;
+
+ /* Flush writes to descriptor memory. */
+ dma_wmb();
+
+ ring->tail = (ring->tail + 1) % MTK_MAC_RING_NUM_DESCS;
+ ring->count--;
+
+ return 0;
+}
+
+static void mtk_mac_ring_push_head(struct mtk_mac_ring *ring,
+ struct mtk_mac_ring_desc_data *desc_data,
+ unsigned int flags)
+{
+ struct mtk_mac_ring_desc *desc = &ring->descs[ring->head];
+ unsigned int status;
+
+ status = desc->status;
+
+ ring->skbs[ring->head] = desc_data->skb;
+ ring->dma_addrs[ring->head] = desc_data->dma_addr;
+ desc->data_ptr = desc_data->dma_addr;
+
+ status |= desc_data->len;
+ if (flags)
+ status |= flags;
+ desc->status = status;
+
+ /* Flush modifications before ownership change. */
+ dma_wmb();
+ desc->status &= ~MTK_MAC_DESC_BIT_COWN;
+
+ ring->head = (ring->head + 1) % MTK_MAC_RING_NUM_DESCS;
+ ring->count++;
+}
+
+static void mtk_mac_ring_push_head_rx(struct mtk_mac_ring *ring,
+ struct mtk_mac_ring_desc_data *desc_data)
+{
+ mtk_mac_ring_push_head(ring, desc_data, 0);
+}
+
+static void mtk_mac_ring_push_head_tx(struct mtk_mac_ring *ring,
+ struct mtk_mac_ring_desc_data *desc_data)
+{
+ static const unsigned int flags = MTK_MAC_DESC_BIT_FS |
+ MTK_MAC_DESC_BIT_LS |
+ MTK_MAC_DESC_BIT_INT;
+
+ mtk_mac_ring_push_head(ring, desc_data, flags);
+}
+
+static bool mtk_mac_ring_full(struct mtk_mac_ring *ring)
+{
+ return ring->count == MTK_MAC_RING_NUM_DESCS;
+}
+
+static bool mtk_mac_ring_descs_available(struct mtk_mac_ring *ring)
+{
+ return ring->count > 0;
+}
+
+static void mtk_mac_lock(struct mtk_mac_priv *priv)
+{
+ spin_lock_irqsave(&priv->lock, priv->lock_flags);
+}
+
+static void mtk_mac_unlock(struct mtk_mac_priv *priv)
+{
+ spin_unlock_irqrestore(&priv->lock, priv->lock_flags);
+}
+
+static dma_addr_t mtk_mac_dma_map_rx(struct mtk_mac_priv *priv,
+ struct sk_buff *skb)
+{
+ struct device *dev = mtk_mac_get_dev(priv);
+
+ /* Data pointer for the RX DMA descriptor must be aligned to 4N + 2. */
+ return dma_map_single(dev, skb_tail_pointer(skb) - 2,
+ skb_tailroom(skb), DMA_FROM_DEVICE);
+}
+
+static void mtk_mac_dma_unmap_rx(struct mtk_mac_priv *priv,
+ struct mtk_mac_ring_desc_data *desc_data)
+{
+ struct device *dev = mtk_mac_get_dev(priv);
+
+ dma_unmap_single(dev, desc_data->dma_addr,
+ skb_tailroom(desc_data->skb), DMA_FROM_DEVICE);
+}
+
+static dma_addr_t mtk_mac_dma_map_tx(struct mtk_mac_priv *priv,
+ struct sk_buff *skb)
+{
+ struct device *dev = mtk_mac_get_dev(priv);
+
+ return dma_map_single(dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE);
+}
+
+static void mtk_mac_dma_unmap_tx(struct mtk_mac_priv *priv,
+ struct mtk_mac_ring_desc_data *desc_data)
+{
+ struct device *dev = mtk_mac_get_dev(priv);
+
+ return dma_unmap_single(dev, desc_data->dma_addr,
+ skb_headlen(desc_data->skb), DMA_TO_DEVICE);
+}
+
+static void mtk_mac_nic_disable_pd(struct mtk_mac_priv *priv)
+{
+ regmap_update_bits(priv->regs, MTK_MAC_REG_MAC_CFG,
+ MTK_MAC_BIT_MAC_CFG_NIC_PD, 0);
+}
+
+static void mtk_mac_intr_unmask_all(struct mtk_mac_priv *priv)
+{
+ regmap_write(priv->regs, MTK_MAC_REG_INT_MASK, 0);
+}
+
+static void mtk_mac_intr_mask_all(struct mtk_mac_priv *priv)
+{
+ regmap_write(priv->regs, MTK_MAC_REG_INT_MASK, ~0);
+}
+
+static unsigned int mtk_mac_intr_read_and_clear(struct mtk_mac_priv *priv)
+{
+ unsigned int val;
+
+ regmap_read(priv->regs, MTK_MAC_REG_INT_STS, &val);
+ regmap_write(priv->regs, MTK_MAC_REG_INT_STS, val);
+
+ return val;
+}
+
+static void mtk_mac_dma_init(struct mtk_mac_priv *priv)
+{
+ struct mtk_mac_ring_desc *desc;
+ unsigned int val;
+ int i;
+
+ priv->descs_base = (struct mtk_mac_ring_desc *)priv->ring_base;
+
+ for (i = 0; i < MTK_MAC_NUM_DESCS_TOTAL; i++) {
+ desc = &priv->descs_base[i];
+
+ memset(desc, 0, sizeof(*desc));
+ desc->status = MTK_MAC_DESC_BIT_COWN;
+ if ((i == MTK_MAC_NUM_TX_DESCS - 1) ||
+ (i == MTK_MAC_NUM_DESCS_TOTAL - 1))
+ desc->status |= MTK_MAC_DESC_BIT_EOR;
+ }
+
+ mtk_mac_ring_init(&priv->tx_ring, priv->descs_base, 0);
+ mtk_mac_ring_init(&priv->rx_ring,
+ priv->descs_base + MTK_MAC_NUM_TX_DESCS,
+ MTK_MAC_NUM_RX_DESCS);
+
+ /* Set DMA pointers. */
+ val = (unsigned int)priv->dma_addr;
+ regmap_write(priv->regs, MTK_MAC_REG_TX_BASE_ADDR, val);
+ regmap_write(priv->regs, MTK_MAC_REG_TX_DPTR, val);
+
+ val += sizeof(struct mtk_mac_ring_desc) * MTK_MAC_NUM_TX_DESCS;
+ regmap_write(priv->regs, MTK_MAC_REG_RX_BASE_ADDR, val);
+ regmap_write(priv->regs, MTK_MAC_REG_RX_DPTR, val);
+}
+
+static void mtk_mac_dma_start(struct mtk_mac_priv *priv)
+{
+ regmap_update_bits(priv->regs, MTK_MAC_REG_TX_DMA_CTRL,
+ MTK_MAC_BIT_TX_DMA_CTRL_START,
+ MTK_MAC_BIT_TX_DMA_CTRL_START);
+ regmap_update_bits(priv->regs, MTK_MAC_REG_RX_DMA_CTRL,
+ MTK_MAC_BIT_RX_DMA_CTRL_START,
+ MTK_MAC_BIT_RX_DMA_CTRL_START);
+}
+
+static void mtk_mac_dma_stop(struct mtk_mac_priv *priv)
+{
+ regmap_write(priv->regs, MTK_MAC_REG_TX_DMA_CTRL,
+ MTK_MAC_BIT_TX_DMA_CTRL_STOP);
+ regmap_write(priv->regs, MTK_MAC_REG_RX_DMA_CTRL,
+ MTK_MAC_BIT_RX_DMA_CTRL_STOP);
+}
+
+static void mtk_mac_dma_disable(struct mtk_mac_priv *priv)
+{
+ int i;
+
+ mtk_mac_dma_stop(priv);
+
+ /* Take back all descriptors. */
+ for (i = 0; i < MTK_MAC_NUM_DESCS_TOTAL; i++)
+ priv->descs_base[i].status |= MTK_MAC_DESC_BIT_COWN;
+}
+
+static void mtk_mac_dma_resume_rx(struct mtk_mac_priv *priv)
+{
+ regmap_update_bits(priv->regs, MTK_MAC_REG_RX_DMA_CTRL,
+ MTK_MAC_BIT_RX_DMA_CTRL_RESUME,
+ MTK_MAC_BIT_RX_DMA_CTRL_RESUME);
+}
+
+static void mtk_mac_dma_resume_tx(struct mtk_mac_priv *priv)
+{
+ regmap_update_bits(priv->regs, MTK_MAC_REG_TX_DMA_CTRL,
+ MTK_MAC_BIT_TX_DMA_CTRL_RESUME,
+ MTK_MAC_BIT_TX_DMA_CTRL_RESUME);
+}
+
+static void mtk_mac_set_mac_addr(struct net_device *ndev)
+{
+ struct mtk_mac_priv *priv = netdev_priv(ndev);
+ u8 *mac_addr = ndev->dev_addr;
+ unsigned int high, low;
+
+ high = mac_addr[0] << 8 | mac_addr[1] << 0;
+ low = mac_addr[2] << 24 | mac_addr[3] << 16 |
+ mac_addr[4] << 8 | mac_addr[5];
+
+ regmap_write(priv->regs, MTK_MAC_REG_MY_MAC_H, high);
+ regmap_write(priv->regs, MTK_MAC_REG_MY_MAC_L, low);
+}
+
+static void mtk_mac_reset_counters(struct mtk_mac_priv *priv)
+{
+ static const unsigned int counter_regs[] = {
+ MTK_MAC_REG_C_RXOKPKT,
+ MTK_MAC_REG_C_RXOKBYTE,
+ MTK_MAC_REG_C_RXRUNT,
+ MTK_MAC_REG_C_RXLONG,
+ MTK_MAC_REG_C_RXDROP,
+ MTK_MAC_REG_C_RXCRC,
+ MTK_MAC_REG_C_RXARLDROP,
+ MTK_MAC_REG_C_RXVLANDROP,
+ MTK_MAC_REG_C_RXCSERR,
+ MTK_MAC_REG_C_RXPAUSE,
+ MTK_MAC_REG_C_TXOKPKT,
+ MTK_MAC_REG_C_TXOKBYTE,
+ MTK_MAC_REG_C_TXPAUSECOL,
+ MTK_MAC_REG_C_TXRTY,
+ MTK_MAC_REG_C_TXSKIP,
+ MTK_MAC_REG_C_TX_ARP,
+ MTK_MAC_REG_C_RX_RERR,
+ MTK_MAC_REG_C_RX_UNI,
+ MTK_MAC_REG_C_RX_MULTI,
+ MTK_MAC_REG_C_RX_BROAD,
+ MTK_MAC_REG_C_RX_ALIGNERR,
+ MTK_MAC_REG_C_TX_UNI,
+ MTK_MAC_REG_C_TX_MULTI,
+ MTK_MAC_REG_C_TX_BROAD,
+ MTK_MAC_REG_C_TX_TIMEOUT,
+ MTK_MAC_REG_C_TX_LATECOL,
+ MTK_MAC_REG_C_RX_LENGTHERR,
+ MTK_MAC_REG_C_RX_TWIST,
+ };
+
+ unsigned int i, val;
+
+ for (i = 0; i < ARRAY_SIZE(counter_regs); i++)
+ regmap_read(priv->regs, counter_regs[i], &val);
+}
+
+static void mtk_mac_update_stat(struct mtk_mac_priv *priv,
+ unsigned int reg, u64 *stat)
+{
+ unsigned int val;
+
+ regmap_read(priv->regs, reg, &val);
+ *stat += val;
+}
+
+/* Try to get as many stats as possible from the internal registers instead
+ * of tracking them ourselves.
+ */
+static void mtk_mac_update_stats(struct mtk_mac_priv *priv)
+{
+ struct rtnl_link_stats64 *stats = &priv->stats;
+
+ /* OK packets and bytes. */
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_RXOKPKT, &stats->rx_packets);
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_TXOKPKT, &stats->tx_packets);
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_RXOKBYTE, &stats->rx_bytes);
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_TXOKBYTE, &stats->tx_bytes);
+
+ /* RX & TX multicast. */
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_RX_MULTI, &stats->multicast);
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_TX_MULTI, &stats->multicast);
+
+ /* Collisions. */
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_TXPAUSECOL, &stats->collisions);
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_TX_LATECOL, &stats->collisions);
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_RXRUNT, &stats->collisions);
+
+ /* RX Errors. */
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_RX_LENGTHERR,
+ &stats->rx_length_errors);
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_RXLONG, &stats->rx_over_errors);
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_RXCRC, &stats->rx_crc_errors);
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_RX_ALIGNERR,
+ &stats->rx_frame_errors);
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_RXDROP, &stats->rx_fifo_errors);
+ /* Sum of the general RX error counter + all of the above. */
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_RX_RERR, &stats->rx_errors);
+ stats->rx_errors += stats->rx_length_errors;
+ stats->rx_errors += stats->rx_over_errors;
+ stats->rx_errors += stats->rx_crc_errors;
+ stats->rx_errors += stats->rx_frame_errors;
+ stats->rx_errors += stats->rx_fifo_errors;
+}
+
+static struct sk_buff *mtk_mac_alloc_skb(struct net_device *ndev)
+{
+ uintptr_t tail, offset;
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(MTK_MAC_MAX_FRAME_SIZE);
+ if (!skb)
+ return NULL;
+
+ /* Align to 16 bytes. */
+ tail = (uintptr_t)skb_tail_pointer(skb);
+ if (tail & (MTK_MAC_SKB_ALIGNMENT - 1)) {
+ offset = tail & (MTK_MAC_SKB_ALIGNMENT - 1);
+ skb_reserve(skb, MTK_MAC_SKB_ALIGNMENT - offset);
+ }
+
+ /* Ensure 16-byte alignment of the skb pointer: eth_type_trans() will
+ * extract the Ethernet header (14 bytes) so we need two more bytes.
+ */
+ skb_reserve(skb, NET_IP_ALIGN);
+
+ return skb;
+}
+
+static int mtk_mac_prepare_rx_skbs(struct net_device *ndev)
+{
+ struct mtk_mac_priv *priv = netdev_priv(ndev);
+ struct mtk_mac_ring *ring = &priv->rx_ring;
+ struct device *dev = mtk_mac_get_dev(priv);
+ struct mtk_mac_ring_desc *desc;
+ struct sk_buff *skb;
+ dma_addr_t dma_addr;
+ int i;
+
+ for (i = 0; i < MTK_MAC_NUM_RX_DESCS; i++) {
+ skb = mtk_mac_alloc_skb(ndev);
+ if (!skb)
+ return -ENOMEM;
+
+ dma_addr = mtk_mac_dma_map_rx(priv, skb);
+ if (dma_mapping_error(dev, dma_addr)) {
+ dev_kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ desc = &ring->descs[i];
+ desc->data_ptr = dma_addr;
+ desc->status |= skb_tailroom(skb) & MTK_MAC_DESC_MSK_LEN;
+ desc->status &= ~MTK_MAC_DESC_BIT_COWN;
+ ring->skbs[i] = skb;
+ ring->dma_addrs[i] = dma_addr;
+ }
+
+ ring->count = MTK_MAC_NUM_RX_DESCS;
+
+ return 0;
+}
+
+/* All processing for TX and RX happens in the napi poll callback. */
+static irqreturn_t mtk_mac_handle_irq(int irq, void *data)
+{
+ struct mtk_mac_priv *priv;
+ struct net_device *ndev;
+
+ ndev = data;
+ priv = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ mtk_mac_intr_mask_all(priv);
+ napi_schedule(&priv->napi);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void mtk_mac_free_rx_skbs(struct net_device *ndev)
+{
+ struct mtk_mac_priv *priv = netdev_priv(ndev);
+ struct mtk_mac_ring *ring = &priv->rx_ring;
+ struct mtk_mac_ring_desc_data desc_data;
+ struct mtk_mac_ring_desc *desc;
+ int i;
+
+ for (i = 0; i < MTK_MAC_NUM_RX_DESCS; i++) {
+ desc = &ring->descs[i];
+
+ if (!desc->data_ptr)
+ continue;
+
+ desc_data.dma_addr = ring->dma_addrs[i];
+ desc_data.skb = ring->skbs[i];
+
+ mtk_mac_dma_unmap_rx(priv, &desc_data);
+ dev_kfree_skb(desc_data.skb);
+ }
+}
+
+static void mtk_mac_free_tx_skbs(struct net_device *ndev)
+{
+ struct mtk_mac_priv *priv = netdev_priv(ndev);
+ struct mtk_mac_ring *ring = &priv->tx_ring;
+ struct mtk_mac_ring_desc_data desc_data;
+ struct mtk_mac_ring_desc *desc;
+ int i;
+
+ for (i = 0; i < MTK_MAC_NUM_TX_DESCS; i++) {
+ desc = &ring->descs[i];
+
+ if (!desc->data_ptr)
+ continue;
+
+ desc_data.len = desc->status & MTK_MAC_DESC_MSK_LEN;
+ desc_data.dma_addr = ring->dma_addrs[i];
+ desc_data.skb = ring->skbs[i];
+
+ mtk_mac_dma_unmap_tx(priv, &desc_data);
+ dev_kfree_skb(desc_data.skb);
+ }
+}
+
+/* Wait for the completion of any previous command - CMD_START bit must be
+ * cleared by hardware.
+ */
+static int mtk_mac_hash_wait_cmd_start(struct mtk_mac_priv *priv)
+{
+ unsigned int val;
+
+ return regmap_read_poll_timeout_atomic(priv->regs,
+ MTK_MAC_REG_HASH_CTRL, val,
+ !(val & MTK_MAC_BIT_HASH_CTRL_CMD_START),
+ 10, MTK_MAC_WAIT_TIMEOUT);
+}
+
+static int mtk_mac_hash_wait_ok(struct mtk_mac_priv *priv)
+{
+ unsigned int val;
+ int ret;
+
+ /* Wait for BIST_DONE bit. */
+ ret = regmap_read_poll_timeout_atomic(priv->regs,
+ MTK_MAC_REG_HASH_CTRL, val,
+ val & MTK_MAC_BIT_HASH_CTRL_BIST_DONE,
+ 10, MTK_MAC_WAIT_TIMEOUT);
+ if (ret)
+ return ret;
+
+ /* Check the BIST_OK bit. */
+ regmap_read(priv->regs, MTK_MAC_REG_HASH_CTRL, &val);
+ if (!(val & MTK_MAC_BIT_HASH_CTRL_BIST_OK))
+ return -EIO;
+
+ return 0;
+}
+
+static int mtk_mac_set_hashbit(struct mtk_mac_priv *priv,
+ unsigned int hash_addr)
+{
+ unsigned int val;
+ int ret;
+
+ ret = mtk_mac_hash_wait_cmd_start(priv);
+ if (ret)
+ return ret;
+
+ val = hash_addr & MTK_MAC_MSK_HASH_CTRL_HASH_BIT_ADDR;
+ val |= MTK_MAC_BIT_HASH_CTRL_ACC_CMD;
+ val |= MTK_MAC_BIT_HASH_CTRL_CMD_START;
+ val |= MTK_MAC_BIT_HASH_CTRL_BIST_EN;
+ val |= MTK_MAC_BIT_HASH_CTRL_HASH_BIT_DATA;
+ regmap_write(priv->regs, MTK_MAC_REG_HASH_CTRL, val);
+
+ return mtk_mac_hash_wait_ok(priv);
+}
+
+static int mtk_mac_reset_hash_table(struct mtk_mac_priv *priv)
+{
+ int ret;
+
+ ret = mtk_mac_hash_wait_cmd_start(priv);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(priv->regs, MTK_MAC_REG_HASH_CTRL,
+ MTK_MAC_BIT_HASH_CTRL_BIST_EN,
+ MTK_MAC_BIT_HASH_CTRL_BIST_EN);
+ regmap_update_bits(priv->regs, MTK_MAC_REG_TEST1,
+ MTK_MAC_BIT_TEST1_RST_HASH_MBIST,
+ MTK_MAC_BIT_TEST1_RST_HASH_MBIST);
+
+ return mtk_mac_hash_wait_ok(priv);
+}
+
+static void mtk_mac_phy_config(struct mtk_mac_priv *priv)
+{
+ unsigned int val;
+
+ if (priv->speed == SPEED_1000)
+ val = MTK_MAC_VAL_PHY_CTRL1_FORCE_SPD_1000M;
+ else if (priv->speed == SPEED_100)
+ val = MTK_MAC_VAL_PHY_CTRL1_FORCE_SPD_100M;
+ else
+ val = MTK_MAC_VAL_PHY_CTRL1_FORCE_SPD_10M;
+ val <<= MTK_MAC_OFF_PHY_CTRL1_FORCE_SPD;
+
+ val |= MTK_MAC_BIT_PHY_CTRL1_AN_EN;
+ val |= MTK_MAC_BIT_PHY_CTRL1_FORCE_FC_RX;
+ val |= MTK_MAC_BIT_PHY_CTRL1_FORCE_FC_TX;
+ /* Only full-duplex supported for now. */
+ val |= MTK_MAC_BIT_PHY_CTRL1_FORCE_DPX;
+
+ regmap_write(priv->regs, MTK_MAC_REG_PHY_CTRL1, val);
+
+ if (priv->pause) {
+ val = MTK_MAC_VAL_FC_CFG_SEND_PAUSE_TH_2K;
+ val <<= MTK_MAC_OFF_FC_CFG_SEND_PAUSE_TH;
+ val |= MTK_MAC_BIT_FC_CFG_UC_PAUSE_DIR;
+ } else {
+ val = 0;
+ }
+
+ regmap_update_bits(priv->regs, MTK_MAC_REG_FC_CFG,
+ MTK_MAC_MSK_FC_CFG_SEND_PAUSE_TH |
+ MTK_MAC_BIT_FC_CFG_UC_PAUSE_DIR, val);
+
+ if (priv->pause) {
+ val = MTK_MAC_VAL_EXT_CFG_SND_PAUSE_RLS_1K;
+ val <<= MTK_MAC_OFF_EXT_CFG_SND_PAUSE_RLS;
+ } else {
+ val = 0;
+ }
+
+ regmap_update_bits(priv->regs, MTK_MAC_REG_EXT_CFG,
+ MTK_MAC_MSK_EXT_CFG_SND_PAUSE_RLS, val);
+}
+
+static void mtk_mac_adjust_link(struct net_device *ndev)
+{
+ struct mtk_mac_priv *priv = netdev_priv(ndev);
+ struct phy_device *phydev = priv->phydev;
+ bool new_state = false;
+
+ if (phydev->link) {
+ if (!priv->link) {
+ priv->link = phydev->link;
+ new_state = true;
+ }
+
+ if (priv->speed != phydev->speed) {
+ priv->speed = phydev->speed;
+ new_state = true;
+ }
+
+ if (priv->pause != phydev->pause) {
+ priv->pause = phydev->pause;
+ new_state = true;
+ }
+ } else {
+ if (priv->link) {
+ priv->link = phydev->link;
+ new_state = true;
+ }
+ }
+
+ if (new_state) {
+ if (phydev->link)
+ mtk_mac_phy_config(priv);
+
+ phy_print_status(ndev->phydev);
+ }
+}
+
+static void mtk_mac_init_config(struct mtk_mac_priv *priv)
+{
+ unsigned int val;
+
+ val = (MTK_MAC_BIT_MII_PAD_OUT_ENABLE |
+ MTK_MAC_BIT_EXT_MDC_MODE |
+ MTK_MAC_BIT_SWC_MII_MODE);
+
+ regmap_write(priv->regs, MTK_MAC_REG_SYS_CONF, val);
+ regmap_update_bits(priv->regs, MTK_MAC_REG_MAC_CLK_CONF,
+ MTK_MAC_MSK_MAC_CLK_CONF,
+ MTK_MAC_BIT_CLK_DIV_10);
+}
+
+static void mtk_mac_set_mode_rmii(struct mtk_mac_priv *priv)
+{
+ regmap_update_bits(priv->pericfg, MTK_PERICFG_REG_NIC_CFG_CON,
+ MTK_PERICFG_MSK_NIC_CFG_CON_CFG_MII,
+ MTK_PERICFG_BIT_NIC_CFG_CON_RMII);
+}
+
+static int mtk_mac_enable(struct net_device *ndev)
+{
+ struct mtk_mac_priv *priv = netdev_priv(ndev);
+ unsigned int val;
+ int ret;
+
+ mtk_mac_nic_disable_pd(priv);
+ mtk_mac_intr_mask_all(priv);
+ mtk_mac_dma_stop(priv);
+
+ mtk_mac_set_mac_addr(ndev);
+
+ /* Configure the MAC */
+ val = MTK_MAC_VAL_MAC_CFG_IPG_96BIT;
+ val <<= MTK_MAC_OFF_MAC_CFG_IPG;
+ val |= MTK_MAC_BIT_MAC_CFG_MAXLEN_1522;
+ val |= MTK_MAC_BIT_MAC_CFG_AUTO_PAD;
+ val |= MTK_MAC_BIT_MAC_CFG_CRC_STRIP;
+ regmap_write(priv->regs, MTK_MAC_REG_MAC_CFG, val);
+
+ /* Enable Hash Table BIST and reset it */
+ ret = mtk_mac_reset_hash_table(priv);
+ if (ret)
+ return ret;
+
+ /* Setup the hashing algorithm */
+ regmap_update_bits(priv->regs, MTK_MAC_REG_ARL_CFG,
+ MTK_MAC_BIT_ARL_CFG_HASH_ALG |
+ MTK_MAC_BIT_ARL_CFG_MISC_MODE, 0);
+
+ /* Don't strip VLAN tags */
+ regmap_update_bits(priv->regs, MTK_MAC_REG_MAC_CFG,
+ MTK_MAC_BIT_MAC_CFG_VLAN_STRIP, 0);
+
+ /* Setup DMA */
+ mtk_mac_dma_init(priv);
+
+ ret = mtk_mac_prepare_rx_skbs(ndev);
+ if (ret)
+ goto err_out;
+
+ /* Request the interrupt */
+ ret = request_irq(ndev->irq, mtk_mac_handle_irq,
+ IRQF_TRIGGER_FALLING, ndev->name, ndev);
+ if (ret)
+ goto err_free_skbs;
+
+ napi_enable(&priv->napi);
+
+ mtk_mac_intr_read_and_clear(priv);
+ mtk_mac_intr_unmask_all(priv);
+
+ /* Connect to and start PHY */
+ priv->phydev = of_phy_connect(ndev, priv->phy_node,
+ mtk_mac_adjust_link, 0, priv->phy_intf);
+ if (!priv->phydev) {
+ netdev_err(ndev, "failed to connect to PHY\n");
+ goto err_free_irq;
+ }
+
+ mtk_mac_dma_start(priv);
+ phy_start(priv->phydev);
+ netif_start_queue(ndev);
+
+ return 0;
+
+err_free_irq:
+ free_irq(ndev->irq, ndev);
+err_free_skbs:
+ mtk_mac_free_rx_skbs(ndev);
+err_out:
+ return ret;
+}
+
+static void mtk_mac_disable(struct net_device *ndev)
+{
+ struct mtk_mac_priv *priv = netdev_priv(ndev);
+
+ netif_stop_queue(ndev);
+ napi_disable(&priv->napi);
+ mtk_mac_intr_mask_all(priv);
+ mtk_mac_dma_disable(priv);
+ mtk_mac_intr_read_and_clear(priv);
+ phy_stop(priv->phydev);
+ phy_disconnect(priv->phydev);
+ free_irq(ndev->irq, ndev);
+ mtk_mac_free_rx_skbs(ndev);
+ mtk_mac_free_tx_skbs(ndev);
+}
+
+static int mtk_mac_netdev_open(struct net_device *ndev)
+{
+ return mtk_mac_enable(ndev);
+}
+
+static int mtk_mac_netdev_stop(struct net_device *ndev)
+{
+ mtk_mac_disable(ndev);
+
+ return 0;
+}
+
+static int mtk_mac_netdev_ioctl(struct net_device *ndev,
+ struct ifreq *req, int cmd)
+{
+ if (!netif_running(ndev))
+ return -EINVAL;
+
+ return phy_mii_ioctl(ndev->phydev, req, cmd);
+}
+
+static int mtk_mac_netdev_start_xmit(struct sk_buff *skb,
+ struct net_device *ndev)
+{
+ struct mtk_mac_priv *priv = netdev_priv(ndev);
+ struct mtk_mac_ring *ring = &priv->tx_ring;
+ struct device *dev = mtk_mac_get_dev(priv);
+ struct mtk_mac_ring_desc_data desc_data;
+
+ desc_data.dma_addr = mtk_mac_dma_map_tx(priv, skb);
+ if (dma_mapping_error(dev, desc_data.dma_addr))
+ goto err_drop_packet;
+
+ desc_data.skb = skb;
+ desc_data.len = skb->len;
+
+ mtk_mac_lock(priv);
+ mtk_mac_ring_push_head_tx(ring, &desc_data);
+
+ if (mtk_mac_ring_full(ring))
+ netif_stop_queue(ndev);
+ mtk_mac_unlock(priv);
+
+ mtk_mac_dma_resume_tx(priv);
+
+ return NETDEV_TX_OK;
+
+err_drop_packet:
+ dev_kfree_skb(skb);
+ ndev->stats.tx_dropped++;
+ return NETDEV_TX_BUSY;
+}
+
+static int mtk_mac_tx_complete_one(struct mtk_mac_priv *priv)
+{
+ struct mtk_mac_ring *ring = &priv->tx_ring;
+ struct mtk_mac_ring_desc_data desc_data;
+ int ret;
+
+ ret = mtk_mac_ring_pop_tail(ring, &desc_data);
+ if (ret)
+ return ret;
+
+ mtk_mac_dma_unmap_tx(priv, &desc_data);
+ dev_kfree_skb_irq(desc_data.skb);
+
+ return 0;
+}
+
+static void mtk_mac_tx_complete_all(struct mtk_mac_priv *priv)
+{
+ struct mtk_mac_ring *ring = &priv->tx_ring;
+ struct net_device *ndev = priv->ndev;
+ int ret;
+
+ for (;;) {
+ mtk_mac_lock(priv);
+
+ if (!mtk_mac_ring_descs_available(ring)) {
+ mtk_mac_unlock(priv);
+ break;
+ }
+
+ ret = mtk_mac_tx_complete_one(priv);
+ if (ret) {
+ mtk_mac_unlock(priv);
+ break;
+ }
+
+ if (netif_queue_stopped(ndev))
+ netif_wake_queue(ndev);
+
+ mtk_mac_unlock(priv);
+ }
+}
+
+static void mtk_mac_netdev_get_stats64(struct net_device *ndev,
+ struct rtnl_link_stats64 *stats)
+{
+ struct mtk_mac_priv *priv = netdev_priv(ndev);
+
+ mtk_mac_update_stats(priv);
+
+ memcpy(stats, &priv->stats, sizeof(*stats));
+}
+
+static void mtk_mac_set_rx_mode(struct net_device *ndev)
+{
+ struct mtk_mac_priv *priv = netdev_priv(ndev);
+ struct netdev_hw_addr *hw_addr;
+ unsigned int hash_addr, i;
+ int ret;
+
+ if (ndev->flags & IFF_PROMISC) {
+ regmap_update_bits(priv->regs, MTK_MAC_REG_ARL_CFG,
+ MTK_MAC_BIT_ARL_CFG_MISC_MODE,
+ MTK_MAC_BIT_ARL_CFG_MISC_MODE);
+ } else if (netdev_mc_count(ndev) > MTK_MAC_HASHTABLE_MC_LIMIT ||
+ ndev->flags & IFF_ALLMULTI) {
+ for (i = 0; i < MTK_MAC_HASHTABLE_SIZE_MAX; i++) {
+ ret = mtk_mac_set_hashbit(priv, i);
+ if (ret)
+ goto hash_fail;
+ }
+ } else {
+ /* Clear previous settings. */
+ ret = mtk_mac_reset_hash_table(priv);
+ if (ret)
+ goto hash_fail;
+
+ netdev_for_each_mc_addr(hw_addr, ndev) {
+ hash_addr = (hw_addr->addr[0] & 0x01) << 8;
+ hash_addr += hw_addr->addr[5];
+ ret = mtk_mac_set_hashbit(priv, hash_addr);
+ if (ret)
+ goto hash_fail;
+ }
+ }
+
+ return;
+
+hash_fail:
+ if (ret == -ETIMEDOUT)
+ netdev_err(ndev, "setting hash bit timed out\n");
+ else
+ /* Should be -EIO */
+ netdev_err(ndev, "unable to set hash bit");
+}
+
+static const struct net_device_ops mtk_mac_netdev_ops = {
+ .ndo_open = mtk_mac_netdev_open,
+ .ndo_stop = mtk_mac_netdev_stop,
+ .ndo_start_xmit = mtk_mac_netdev_start_xmit,
+ .ndo_get_stats64 = mtk_mac_netdev_get_stats64,
+ .ndo_set_rx_mode = mtk_mac_set_rx_mode,
+ .ndo_do_ioctl = mtk_mac_netdev_ioctl,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static void mtk_mac_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, MTK_MAC_DRVNAME, sizeof(info->driver));
+}
+
+/* TODO Add ethtool stats. */
+static const struct ethtool_ops mtk_mac_ethtool_ops = {
+ .get_drvinfo = mtk_mac_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+ .get_link_ksettings = phy_ethtool_get_link_ksettings,
+ .set_link_ksettings = phy_ethtool_set_link_ksettings,
+};
+
+static int mtk_mac_receive_packet(struct mtk_mac_priv *priv)
+{
+ struct mtk_mac_ring *ring = &priv->rx_ring;
+ struct device *dev = mtk_mac_get_dev(priv);
+ struct mtk_mac_ring_desc_data desc_data;
+ struct net_device *ndev = priv->ndev;
+ struct sk_buff *curr_skb, *new_skb;
+ dma_addr_t new_dma_addr;
+ int ret;
+
+ mtk_mac_lock(priv);
+ ret = mtk_mac_ring_pop_tail(ring, &desc_data);
+ mtk_mac_unlock(priv);
+ if (ret)
+ return -1;
+
+ curr_skb = desc_data.skb;
+
+ if ((desc_data.flags & MTK_MAC_DESC_BIT_RX_CRCE) ||
+ (desc_data.flags & MTK_MAC_DESC_BIT_RX_OSIZE)) {
+ /* Error packet -> drop and reuse skb. */
+ new_skb = curr_skb;
+ goto push_new_skb;
+ }
+
+ /* Prepare new skb before receiving the current one. Reuse the current
+ * skb if we fail at any point.
+ */
+ new_skb = mtk_mac_alloc_skb(ndev);
+ if (!new_skb) {
+ ndev->stats.rx_dropped++;
+ new_skb = curr_skb;
+ goto push_new_skb;
+ }
+
+ new_dma_addr = mtk_mac_dma_map_rx(priv, new_skb);
+ if (dma_mapping_error(dev, new_dma_addr)) {
+ ndev->stats.rx_dropped++;
+ dev_kfree_skb(new_skb);
+ new_skb = curr_skb;
+ netdev_err(ndev, "DMA mapping error of RX descriptor\n");
+ goto push_new_skb;
+ }
+
+ /* We can't fail anymore at this point: it's safe to unmap the skb. */
+ mtk_mac_dma_unmap_rx(priv, &desc_data);
+
+ skb_put(desc_data.skb, desc_data.len);
+ desc_data.skb->ip_summed = CHECKSUM_NONE;
+ desc_data.skb->protocol = eth_type_trans(desc_data.skb, ndev);
+ desc_data.skb->dev = ndev;
+ netif_receive_skb(desc_data.skb);
+
+push_new_skb:
+ desc_data.dma_addr = new_dma_addr;
+ desc_data.len = skb_tailroom(new_skb);
+ desc_data.skb = new_skb;
+
+ mtk_mac_lock(priv);
+ mtk_mac_ring_push_head_rx(ring, &desc_data);
+ mtk_mac_unlock(priv);
+
+ return 0;
+}
+
+static int mtk_mac_process_rx(struct mtk_mac_priv *priv, int budget)
+{
+ int received, ret;
+
+ for (received = 0, ret = 0; received < budget && ret == 0; received++)
+ ret = mtk_mac_receive_packet(priv);
+
+ mtk_mac_dma_resume_rx(priv);
+
+ return received;
+}
+
+static int mtk_mac_poll(struct napi_struct *napi, int budget)
+{
+ struct mtk_mac_priv *priv;
+ unsigned int status;
+ int received = 0;
+
+ priv = container_of(napi, struct mtk_mac_priv, napi);
+
+ status = mtk_mac_intr_read_and_clear(priv);
+
+ /* Clean up TX */
+ if (status & MTK_MAC_BIT_INT_STS_TNTC)
+ mtk_mac_tx_complete_all(priv);
+
+ /* Receive up to $budget packets */
+ if (status & MTK_MAC_BIT_INT_STS_FNRC)
+ received = mtk_mac_process_rx(priv, budget);
+
+ /* One of the counter reached 0x8000000 - update stats and reset all
+ * counters.
+ */
+ if (status & MTK_MAC_REG_INT_STS_MIB_CNT_TH) {
+ mtk_mac_update_stats(priv);
+ mtk_mac_reset_counters(priv);
+ }
+
+ if (received < budget)
+ napi_complete_done(napi, received);
+
+ mtk_mac_intr_unmask_all(priv);
+
+ return received;
+}
+
+static void mtk_mac_mdio_rwok_clear(struct mtk_mac_priv *priv)
+{
+ regmap_write(priv->regs, MTK_MAC_REG_PHY_CTRL0,
+ MTK_MAC_BIT_PHY_CTRL0_RWOK);
+}
+
+static int mtk_mac_mdio_rwok_wait(struct mtk_mac_priv *priv)
+{
+ unsigned int val;
+
+ return regmap_read_poll_timeout(priv->regs, MTK_MAC_REG_PHY_CTRL0,
+ val, val & MTK_MAC_BIT_PHY_CTRL0_RWOK,
+ 10, MTK_MAC_WAIT_TIMEOUT);
+}
+
+static int mtk_mac_mdio_read(struct mii_bus *mii, int phy_id, int regnum)
+{
+ struct mtk_mac_priv *priv = mii->priv;
+ unsigned int val, data;
+ int ret;
+
+ if (regnum & MII_ADDR_C45)
+ return -EOPNOTSUPP;
+
+ mtk_mac_mdio_rwok_clear(priv);
+
+ val = (regnum << MTK_MAC_OFF_PHY_CTRL0_PREG);
+ val &= MTK_MAC_MSK_PHY_CTRL0_PREG;
+ val |= MTK_MAC_BIT_PHY_CTRL0_RDCMD;
+
+ regmap_write(priv->regs, MTK_MAC_REG_PHY_CTRL0, val);
+
+ ret = mtk_mac_mdio_rwok_wait(priv);
+ if (ret)
+ return ret;
+
+ regmap_read(priv->regs, MTK_MAC_REG_PHY_CTRL0, &data);
+
+ data &= MTK_MAC_MSK_PHY_CTRL0_RWDATA;
+ data >>= MTK_MAC_OFF_PHY_CTRL0_RWDATA;
+
+ return data;
+}
+
+static int mtk_mac_mdio_write(struct mii_bus *mii, int phy_id,
+ int regnum, u16 data)
+{
+ struct mtk_mac_priv *priv = mii->priv;
+ unsigned int val;
+
+ if (regnum & MII_ADDR_C45)
+ return -EOPNOTSUPP;
+
+ mtk_mac_mdio_rwok_clear(priv);
+
+ val = data;
+ val <<= MTK_MAC_OFF_PHY_CTRL0_RWDATA;
+ val &= MTK_MAC_MSK_PHY_CTRL0_RWDATA;
+ regnum <<= MTK_MAC_OFF_PHY_CTRL0_PREG;
+ regnum &= MTK_MAC_MSK_PHY_CTRL0_PREG;
+ val |= regnum;
+ val |= MTK_MAC_BIT_PHY_CTRL0_WTCMD;
+
+ regmap_write(priv->regs, MTK_MAC_REG_PHY_CTRL0, val);
+
+ return mtk_mac_mdio_rwok_wait(priv);
+}
+
+static int mtk_mac_mdio_init(struct net_device *ndev)
+{
+ struct mtk_mac_priv *priv = netdev_priv(ndev);
+ struct device *dev = mtk_mac_get_dev(priv);
+ struct device_node *of_node, *mdio_node;
+ int ret;
+
+ of_node = dev->of_node;
+
+ mdio_node = of_get_child_by_name(of_node, "mdio");
+ if (!mdio_node)
+ return -ENODEV;
+
+ if (!of_device_is_available(mdio_node)) {
+ ret = -ENODEV;
+ goto out_put_node;
+ }
+
+ priv->mii = devm_mdiobus_alloc(dev);
+ if (!priv->mii) {
+ ret = -ENOMEM;
+ goto out_put_node;
+ }
+
+ snprintf(priv->mii->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
+ priv->mii->name = "mtk-mac-mdio";
+ priv->mii->parent = dev;
+ priv->mii->read = mtk_mac_mdio_read;
+ priv->mii->write = mtk_mac_mdio_write;
+ priv->mii->priv = priv;
+
+ ret = of_mdiobus_register(priv->mii, mdio_node);
+
+out_put_node:
+ of_node_put(mdio_node);
+ return ret;
+}
+
+static int mtk_mac_suspend(struct device *dev)
+{
+ struct mtk_mac_priv *priv;
+ struct net_device *ndev;
+
+ ndev = dev_get_drvdata(dev);
+ priv = netdev_priv(ndev);
+
+ mtk_mac_disable(ndev);
+ clk_bulk_disable_unprepare(MTK_MAC_NCLKS, priv->clks);
+
+ return 0;
+}
+
+static int mtk_mac_resume(struct device *dev)
+{
+ struct mtk_mac_priv *priv;
+ struct net_device *ndev;
+ int ret;
+
+ ndev = dev_get_drvdata(dev);
+ priv = netdev_priv(ndev);
+
+ ret = clk_bulk_prepare_enable(MTK_MAC_NCLKS, priv->clks);
+ if (ret)
+ return ret;
+
+ ret = mtk_mac_enable(ndev);
+ if (ret)
+ clk_bulk_disable_unprepare(MTK_MAC_NCLKS, priv->clks);
+
+ return ret;
+}
+
+static void mtk_mac_clk_disable_unprepare(void *data)
+{
+ struct mtk_mac_priv *priv = data;
+
+ clk_bulk_disable_unprepare(MTK_MAC_NCLKS, priv->clks);
+}
+
+static int mtk_mac_probe(struct platform_device *pdev)
+{
+ struct device_node *of_node;
+ struct mtk_mac_priv *priv;
+ struct net_device *ndev;
+ struct device *dev;
+ void __iomem *base;
+ int ret, i;
+
+ dev = &pdev->dev;
+ of_node = dev->of_node;
+
+ ndev = devm_alloc_etherdev(dev, sizeof(*priv));
+ if (!ndev)
+ return -ENOMEM;
+
+ priv = netdev_priv(ndev);
+ priv->ndev = ndev;
+ SET_NETDEV_DEV(ndev, dev);
+ platform_set_drvdata(pdev, ndev);
+
+ ndev->min_mtu = ETH_ZLEN;
+ ndev->max_mtu = MTK_MAC_MAX_FRAME_SIZE;
+
+ spin_lock_init(&priv->lock);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ /* We won't be checking the return values of regmap read & write
+ * functions. They can only fail for mmio if there's a clock attached
+ * to regmap which is not the case here.
+ */
+ priv->regs = devm_regmap_init_mmio(dev, base,
+ &mtk_mac_regmap_config);
+ if (IS_ERR(priv->regs))
+ return PTR_ERR(priv->regs);
+
+ priv->pericfg = syscon_regmap_lookup_by_phandle(of_node,
+ "mediatek,pericfg");
+ if (IS_ERR(priv->pericfg)) {
+ dev_err(dev, "Failed to lookup the PERICFG syscon\n");
+ return PTR_ERR(priv->pericfg);
+ }
+
+ ndev->irq = platform_get_irq(pdev, 0);
+ if (ndev->irq < 0)
+ return ndev->irq;
+
+ for (i = 0; i < MTK_MAC_NCLKS; i++)
+ priv->clks[i].id = mtk_mac_clk_names[i];
+ ret = devm_clk_bulk_get(dev, MTK_MAC_NCLKS, priv->clks);
+ if (ret)
+ return ret;
+
+ ret = clk_bulk_prepare_enable(MTK_MAC_NCLKS, priv->clks);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev,
+ mtk_mac_clk_disable_unprepare, priv);
+ if (ret)
+ return ret;
+
+ ret = of_get_phy_mode(of_node, &priv->phy_intf);
+ if (ret) {
+ return ret;
+ } else if (priv->phy_intf != PHY_INTERFACE_MODE_RMII) {
+ dev_err(dev, "unsupported phy mode: %s\n",
+ phy_modes(priv->phy_intf));
+ return -EINVAL;
+ }
+
+ priv->phy_node = of_parse_phandle(of_node, "phy-handle", 0);
+ if (!priv->phy_node) {
+ dev_err(dev, "failed to retrieve the phy handle from device tree\n");
+ return -ENODEV;
+ }
+
+ mtk_mac_set_mode_rmii(priv);
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(dev, "unsupported DMA mask\n");
+ return ret;
+ }
+
+ priv->ring_base = dmam_alloc_coherent(dev, MTK_MAC_DMA_SIZE,
+ &priv->dma_addr,
+ GFP_KERNEL | GFP_DMA);
+ if (!priv->ring_base)
+ return -ENOMEM;
+
+ mtk_mac_nic_disable_pd(priv);
+ mtk_mac_init_config(priv);
+
+ ret = mtk_mac_mdio_init(ndev);
+ if (ret)
+ return ret;
+
+ ret = eth_platform_get_mac_address(dev, ndev->dev_addr);
+ if (ret || !is_valid_ether_addr(ndev->dev_addr))
+ eth_hw_addr_random(ndev);
+
+ ndev->netdev_ops = &mtk_mac_netdev_ops;
+ ndev->ethtool_ops = &mtk_mac_ethtool_ops;
+
+ netif_napi_add(ndev, &priv->napi, mtk_mac_poll, MTK_MAC_NAPI_WEIGHT);
+
+ return devm_register_netdev(dev, ndev);
+}
+
+static const struct of_device_id mtk_mac_of_match[] = {
+ { .compatible = "mediatek,mt8516-eth", },
+ { .compatible = "mediatek,mt8518-eth", },
+ { .compatible = "mediatek,mt8175-eth", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mtk_mac_of_match);
+
+static SIMPLE_DEV_PM_OPS(mtk_mac_pm_ops,
+ mtk_mac_suspend, mtk_mac_resume);
+
+static struct platform_driver mtk_mac_driver = {
+ .driver = {
+ .name = MTK_MAC_DRVNAME,
+ .pm = &mtk_mac_pm_ops,
+ .of_match_table = of_match_ptr(mtk_mac_of_match),
+ },
+ .probe = mtk_mac_probe,
+};
+module_platform_driver(mtk_mac_driver);
+
+MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>");
+MODULE_DESCRIPTION("Mediatek Ethernet MAC Driver");
+MODULE_LICENSE("GPL");
--
2.25.0
^ permalink raw reply related
* [PATCH v3 11/15] ARM64: dts: mediatek: add pericfg syscon to mt8516.dtsi
From: Bartosz Golaszewski @ 2020-05-14 7:59 UTC (permalink / raw)
To: Jonathan Corbet, Rob Herring, David S . Miller, Matthias Brugger,
John Crispin, Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann,
Fabien Parent, Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200514075942.10136-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
This adds support for the PERICFG register range as a syscon. This will
soon be used by the MediaTek Ethernet MAC driver for NIC configuration.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
arch/arm64/boot/dts/mediatek/mt8516.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/mt8516.dtsi b/arch/arm64/boot/dts/mediatek/mt8516.dtsi
index 2f8adf042195..8cedaf74ae86 100644
--- a/arch/arm64/boot/dts/mediatek/mt8516.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8516.dtsi
@@ -191,6 +191,11 @@ infracfg: infracfg@10001000 {
#clock-cells = <1>;
};
+ pericfg: pericfg@10003050 {
+ compatible = "mediatek,mt8516-pericfg", "syscon";
+ reg = <0 0x10003050 0 0x1000>;
+ };
+
apmixedsys: apmixedsys@10018000 {
compatible = "mediatek,mt8516-apmixedsys", "syscon";
reg = <0 0x10018000 0 0x710>;
--
2.25.0
^ permalink raw reply related
* [PATCH v3 13/15] ARM64: dts: mediatek: add an alias for ethernet0 for pumpkin boards
From: Bartosz Golaszewski @ 2020-05-14 7:59 UTC (permalink / raw)
To: Jonathan Corbet, Rob Herring, David S . Miller, Matthias Brugger,
John Crispin, Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann,
Fabien Parent, Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200514075942.10136-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Add the ethernet0 alias for ethernet so that u-boot can find this node
and fill in the MAC address.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi b/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
index a31093d7142b..97d9b000c37e 100644
--- a/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
+++ b/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
@@ -9,6 +9,7 @@
/ {
aliases {
serial0 = &uart0;
+ ethernet0 = ðernet;
};
chosen {
--
2.25.0
^ permalink raw reply related
* [PATCH v3 14/15] ARM64: dts: mediatek: add ethernet pins for pumpkin boards
From: Bartosz Golaszewski @ 2020-05-14 7:59 UTC (permalink / raw)
To: Jonathan Corbet, Rob Herring, David S . Miller, Matthias Brugger,
John Crispin, Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann,
Fabien Parent, Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200514075942.10136-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Setup the pin control for the Ethernet MAC.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi b/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
index 97d9b000c37e..4b1d5f69aba6 100644
--- a/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
+++ b/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
@@ -219,4 +219,19 @@ gpio_mux_int_n_pin {
bias-pull-up;
};
};
+
+ ethernet_pins_default: ethernet {
+ pins_ethernet {
+ pinmux = <MT8516_PIN_0_EINT0__FUNC_EXT_TXD0>,
+ <MT8516_PIN_1_EINT1__FUNC_EXT_TXD1>,
+ <MT8516_PIN_5_EINT5__FUNC_EXT_RXER>,
+ <MT8516_PIN_6_EINT6__FUNC_EXT_RXC>,
+ <MT8516_PIN_7_EINT7__FUNC_EXT_RXDV>,
+ <MT8516_PIN_8_EINT8__FUNC_EXT_RXD0>,
+ <MT8516_PIN_9_EINT9__FUNC_EXT_RXD1>,
+ <MT8516_PIN_12_EINT12__FUNC_EXT_TXEN>,
+ <MT8516_PIN_38_MRG_DI__FUNC_EXT_MDIO>,
+ <MT8516_PIN_39_MRG_DO__FUNC_EXT_MDC>;
+ };
+ };
};
--
2.25.0
^ permalink raw reply related
* [PATCH v3 12/15] ARM64: dts: mediatek: add the ethernet node to mt8516.dtsi
From: Bartosz Golaszewski @ 2020-05-14 7:59 UTC (permalink / raw)
To: Jonathan Corbet, Rob Herring, David S . Miller, Matthias Brugger,
John Crispin, Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann,
Fabien Parent, Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200514075942.10136-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Add the Ethernet MAC node to mt8516.dtsi. This defines parameters common
to all the boards based on this SoC.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
arch/arm64/boot/dts/mediatek/mt8516.dtsi | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/mt8516.dtsi b/arch/arm64/boot/dts/mediatek/mt8516.dtsi
index 8cedaf74ae86..89af661e7f63 100644
--- a/arch/arm64/boot/dts/mediatek/mt8516.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8516.dtsi
@@ -406,6 +406,18 @@ mmc2: mmc@11170000 {
status = "disabled";
};
+ ethernet: ethernet@11180000 {
+ compatible = "mediatek,mt8516-eth";
+ reg = <0 0x11180000 0 0x1000>;
+ mediatek,pericfg = <&pericfg>;
+ interrupts = <GIC_SPI 111 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&topckgen CLK_TOP_RG_ETH>,
+ <&topckgen CLK_TOP_66M_ETH>,
+ <&topckgen CLK_TOP_133M_ETH>;
+ clock-names = "core", "reg", "trans";
+ status = "disabled";
+ };
+
rng: rng@1020c000 {
compatible = "mediatek,mt8516-rng",
"mediatek,mt7623-rng";
--
2.25.0
^ permalink raw reply related
* [PATCH v3 15/15] ARM64: dts: mediatek: enable ethernet on pumpkin boards
From: Bartosz Golaszewski @ 2020-05-14 7:59 UTC (permalink / raw)
To: Jonathan Corbet, Rob Herring, David S . Miller, Matthias Brugger,
John Crispin, Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann,
Fabien Parent, Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200514075942.10136-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Add remaining properties to the ethernet node and enable it.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
.../boot/dts/mediatek/pumpkin-common.dtsi | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi b/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
index 4b1d5f69aba6..dfceffe6950a 100644
--- a/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
+++ b/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
@@ -167,6 +167,24 @@ &uart0 {
status = "okay";
};
+ðernet {
+ pinctrl-names = "default";
+ pinctrl-0 = <ðernet_pins_default>;
+ phy-handle = <ð_phy>;
+ phy-mode = "rmii";
+ mac-address = [00 00 00 00 00 00];
+ status = "okay";
+
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eth_phy: ethernet-phy@0 {
+ reg = <0>;
+ };
+ };
+};
+
&usb0 {
status = "okay";
dr_mode = "peripheral";
--
2.25.0
^ permalink raw reply related
* [PATCH v3 06/15] Documentation: devres: add a missing section for networking helpers
From: Bartosz Golaszewski @ 2020-05-14 7:59 UTC (permalink / raw)
To: Jonathan Corbet, Rob Herring, David S . Miller, Matthias Brugger,
John Crispin, Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann,
Fabien Parent, Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200514075942.10136-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Add a new section for networking devres helpers to devres.rst and list
the two existing devm functions.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
Documentation/driver-api/driver-model/devres.rst | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst
index 46c13780994c..50df28d20fa7 100644
--- a/Documentation/driver-api/driver-model/devres.rst
+++ b/Documentation/driver-api/driver-model/devres.rst
@@ -372,6 +372,10 @@ MUX
devm_mux_chip_register()
devm_mux_control_get()
+NET
+ devm_alloc_etherdev()
+ devm_alloc_etherdev_mqs()
+
PER-CPU MEM
devm_alloc_percpu()
devm_free_percpu()
--
2.25.0
^ permalink raw reply related
* [PATCH v3 00/15] mediatek: add support for MediaTek Ethernet MAC
From: Bartosz Golaszewski @ 2020-05-14 7:59 UTC (permalink / raw)
To: Jonathan Corbet, Rob Herring, David S . Miller, Matthias Brugger,
John Crispin, Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann,
Fabien Parent, Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
This adds support for the Ethernet Controller present on MediaTeK SoCs from
the MT8* family.
First we convert the existing DT bindings for the PERICFG controller to YAML
and add a new compatible string for mt8516 variant of it. Then we add the DT
bindings for the MAC.
Next we do some cleanup of the mediatek ethernet drivers directory and update
the devres documentation with existing networking devres helpers.
The following patches introduce a resource managed variant of
register_netdev() and move all networking devres helpers into a separate .c
file.
The largest patch in the series adds the actual new driver.
The rest of the patches add DT fixups for the boards already supported
upstream.
v1 -> v2:
- add a generic helper for retrieving the net_device associated with given
private data
- fix several typos in commit messages
- remove MTK_MAC_VERSION and don't set the driver version
- use NET_IP_ALIGN instead of a magic number (2) but redefine it as it defaults
to 0 on arm64
- don't manually turn the carrier off in mtk_mac_enable()
- process TX cleanup in napi poll callback
- configure pause in the adjust_link callback
- use regmap_read_poll_timeout() instead of handcoding the polling
- use devres_find() to verify that struct net_device is managed by devres in
devm_register_netdev()
- add a patch moving all networking devres helpers into net/devres.c
- tweak the dma barriers: remove where unnecessary and add comments to the
remaining barriers
- don't reset internal counters when enabling the NIC
- set the net_device's mtu size instead of checking the framesize in
ndo_start_xmit() callback
- fix a race condition in waking up the netif queue
- don't emit log messages on OOM errors
- use dma_set_mask_and_coherent()
- use eth_hw_addr_random()
- rework the receive callback so that we reuse the previous skb if unmapping
fails, like we already do if skb allocation fails
- rework hash table operations: add proper timeout handling and clear bits when
appropriate
v2 -> v3:
- drop the patch adding priv_to_netdev() and store the netdev pointer in the
driver private data
- add an additional dma_wmb() after reseting the descriptor in
mtk_mac_ring_pop_tail()
- check the return value of dma_set_mask_and_coherent()
- improve the DT bindings for mtk-eth-mac: make the reg property in the example
use single-cell address and size, extend the description of the PERICFG
phandle and document the mdio sub-node
- add a patch converting the old .txt bindings for PERICFG to yaml
- limit reading the DMA memory by storing the mapped addresses in the driver
private structure
- add a patch documenting the existing networking devres helpers
Bartosz Golaszewski (15):
dt-bindings: convert the binding document for mediatek PERICFG to yaml
dt-bindings: add new compatible to mediatek,pericfg
dt-bindings: net: add a binding document for MediaTek Ethernet MAC
net: ethernet: mediatek: rename Kconfig prompt
net: ethernet: mediatek: remove unnecessary spaces from Makefile
Documentation: devres: add a missing section for networking helpers
net: move devres helpers into a separate source file
net: devres: define a separate devres structure for
devm_alloc_etherdev()
net: devres: provide devm_register_netdev()
net: ethernet: mtk-eth-mac: new driver
ARM64: dts: mediatek: add pericfg syscon to mt8516.dtsi
ARM64: dts: mediatek: add the ethernet node to mt8516.dtsi
ARM64: dts: mediatek: add an alias for ethernet0 for pumpkin boards
ARM64: dts: mediatek: add ethernet pins for pumpkin boards
ARM64: dts: mediatek: enable ethernet on pumpkin boards
.../arm/mediatek/mediatek,pericfg.txt | 36 -
.../arm/mediatek/mediatek,pericfg.yaml | 64 +
.../bindings/net/mediatek,eth-mac.yaml | 89 +
.../driver-api/driver-model/devres.rst | 5 +
arch/arm64/boot/dts/mediatek/mt8516.dtsi | 17 +
.../boot/dts/mediatek/pumpkin-common.dtsi | 34 +
drivers/net/ethernet/mediatek/Kconfig | 8 +-
drivers/net/ethernet/mediatek/Makefile | 3 +-
drivers/net/ethernet/mediatek/mtk_eth_mac.c | 1578 +++++++++++++++++
include/linux/netdevice.h | 2 +
net/Makefile | 2 +-
net/devres.c | 95 +
net/ethernet/eth.c | 28 -
13 files changed, 1894 insertions(+), 67 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt
create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.yaml
create mode 100644 Documentation/devicetree/bindings/net/mediatek,eth-mac.yaml
create mode 100644 drivers/net/ethernet/mediatek/mtk_eth_mac.c
create mode 100644 net/devres.c
--
2.25.0
^ permalink raw reply
* Re: [PATCH] MIPS: dts: mscc: Updated changed name for miim pinctrl function
From: Thomas Bogendoerfer @ 2020-05-14 7:41 UTC (permalink / raw)
To: Lars Povlsen
Cc: Microchip Linux Driver Support, linux-mips, devicetree,
linux-kernel, Alexandre Belloni
In-Reply-To: <20200513132347.24975-1-lars.povlsen@microchip.com>
On Wed, May 13, 2020 at 03:23:47PM +0200, Lars Povlsen wrote:
> This is an add-on patch to the main SoC Sparx5 series
> (Message-ID: <20200513125532.24585-1-lars.povlsen@microchip.com>).
>
> This changes the miim pinctrl function name from "miim1" to "miim" due
> to refactoring in the driver, obsoleting the instance number.
>
> The change in the driver was to better fit new platforms, as the
> instance number is redundant information. Specifically, support for
> the Microchip Sparx5 SoC is being submitted, where this change became
> necessary.
>
> Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
> Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
> ---
> arch/mips/boot/dts/mscc/ocelot.dtsi | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
applied to mips-next.
Thomas.
--
Crap can work. Given enough thrust pigs will fly, but it's not necessarily a
good idea. [ RFC1925, 2.3 ]
^ permalink raw reply
* Re: [PATCH 3/3] usb: dwc3: keystone: Turn on USB3 PHY before controller
From: Roger Quadros @ 2020-05-14 7:40 UTC (permalink / raw)
To: Chunfeng Yun
Cc: balbi, robh+dt, vigneshr, linux-usb, devicetree, linux-kernel
In-Reply-To: <1589420265.5899.0.camel@mhfsdcap03>
On 14/05/2020 04:37, Chunfeng Yun wrote:
> On Wed, 2020-05-13 at 16:07 +0300, Roger Quadros wrote:
>> The Local Power Sleep Controller (LPSC) dependency on AM65
>> requires SERDES0 to be powered on before USB.
>>
>> We need to power up SERDES0 power domain and hold it on
>> throughout the reset, init, power on sequence.
>>
>> Signed-off-by: Roger Quadros <rogerq@ti.com>
>> ---
>> drivers/usb/dwc3/dwc3-keystone.c | 47 +++++++++++++++++++++++++++++++-
>> 1 file changed, 46 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c
>> index 1e14a6f4884b..46d46f3507fc 100644
>> --- a/drivers/usb/dwc3/dwc3-keystone.c
>> +++ b/drivers/usb/dwc3/dwc3-keystone.c
>> @@ -14,6 +14,7 @@
>> #include <linux/dma-mapping.h>
>> #include <linux/io.h>
>> #include <linux/of_platform.h>
>> +#include <linux/phy/phy.h>
>> #include <linux/pm_runtime.h>
>>
>> /* USBSS register offsets */
>> @@ -34,6 +35,7 @@
>> struct dwc3_keystone {
>> struct device *dev;
>> void __iomem *usbss;
>> + struct phy *usb3_phy;
>> };
>>
>> static inline u32 kdwc3_readl(void __iomem *base, u32 offset)
>> @@ -95,8 +97,44 @@ static int kdwc3_probe(struct platform_device *pdev)
>> if (IS_ERR(kdwc->usbss))
>> return PTR_ERR(kdwc->usbss);
>>
>> - pm_runtime_enable(kdwc->dev);
>> + /* PSC dependency on AM65 needs SERDES0 to be powered before USB0 */
>> + kdwc->usb3_phy = devm_phy_get(dev, "usb3-phy");
> Use devm_phy_optional_get() instead?
Indeed, it seems better suited.
cheers,
-roger
>
>> + if (IS_ERR(kdwc->usb3_phy)) {
>> + error = PTR_ERR(kdwc->usb3_phy);
>> + if (error == -ENOSYS || error == -ENODEV) {
>> + kdwc->usb3_phy = NULL;
>> + } else {
>> + if (error != -EPROBE_DEFER) {
>> + dev_err(dev, "couldn't get usb3 phy: %d\n",
>> + error);
>> + }
>> +
>> + return error;
>> + }
>> + }
>> +
>> + phy_pm_runtime_get_sync(kdwc->usb3_phy);
>> +
>> + error = phy_reset(kdwc->usb3_phy);
>> + if (error < 0) {
>> + dev_err(dev, "usb3 phy reset failed: %d\n", error);
>> + return error;
>> + }
>> +
>> + error = phy_init(kdwc->usb3_phy);
>> + if (error < 0) {
>> + dev_err(dev, "usb3 phy init failed: %d\n", error);
>> + return error;
>> + }
>>
>> + error = phy_power_on(kdwc->usb3_phy);
>> + if (error < 0) {
>> + dev_err(dev, "usb3 phy power on failed: %d\n", error);
>> + phy_exit(kdwc->usb3_phy);
>> + return error;
>> + }
>> +
>> + pm_runtime_enable(kdwc->dev);
>> error = pm_runtime_get_sync(kdwc->dev);
>> if (error < 0) {
>> dev_err(kdwc->dev, "pm_runtime_get_sync failed, error %d\n",
>> @@ -138,6 +176,9 @@ static int kdwc3_probe(struct platform_device *pdev)
>> err_irq:
>> pm_runtime_put_sync(kdwc->dev);
>> pm_runtime_disable(kdwc->dev);
>> + phy_power_off(kdwc->usb3_phy);
>> + phy_exit(kdwc->usb3_phy);
>> + phy_pm_runtime_put_sync(kdwc->usb3_phy);
>>
>> return error;
>> }
>> @@ -163,6 +204,10 @@ static int kdwc3_remove(struct platform_device *pdev)
>> pm_runtime_put_sync(kdwc->dev);
>> pm_runtime_disable(kdwc->dev);
>>
>> + phy_power_off(kdwc->usb3_phy);
>> + phy_exit(kdwc->usb3_phy);
>> + phy_pm_runtime_put_sync(kdwc->usb3_phy);
>> +
>> platform_set_drvdata(pdev, NULL);
>>
>> return 0;
>
--
Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
^ permalink raw reply
* Re: [PATCH v3] dt-bindings: mfd: Convert stmfx bindings to json-schema
From: Benjamin GAIGNARD @ 2020-05-14 7:39 UTC (permalink / raw)
To: Rob Herring
Cc: lee.jones@linaro.org, robh+dt@kernel.org, mark.rutland@arm.co,
Alexandre TORGUE, linus.walleij@linaro.org, Amelie DELAUNAY,
devicetree@vger.kernel.org,
linux-stm32@st-md-mailman.stormreply.com,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org
In-Reply-To: <20200226162125.GA13349@bogus>
On 2/26/20 5:21 PM, Rob Herring wrote:
> On Thu, 20 Feb 2020 17:22:46 +0100, Benjamin Gaignard wrote:
>> Convert stmfx bindings to json-schema
>>
>> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@st.com>
>> ---
>> .../devicetree/bindings/mfd/st,stmfx.yaml | 124 +++++++++++++++++++++
>> Documentation/devicetree/bindings/mfd/stmfx.txt | 28 -----
>> .../devicetree/bindings/pinctrl/pinctrl-stmfx.txt | 116 -------------------
>> 3 files changed, 124 insertions(+), 144 deletions(-)
>> create mode 100644 Documentation/devicetree/bindings/mfd/st,stmfx.yaml
>> delete mode 100644 Documentation/devicetree/bindings/mfd/stmfx.txt
>> delete mode 100644 Documentation/devicetree/bindings/pinctrl/pinctrl-stmfx.txt
>>
Hi Lee, Rob,
I haven't been able to found this patch in -next branches, can one of
you merge it ?
Thanks,
Benjamin
> Reviewed-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* Re: [PATCH 1/6] dt-bindings: omap: Update PRM binding for genpd
From: Tero Kristo @ 2020-05-14 7:39 UTC (permalink / raw)
To: Tony Lindgren, linux-omap
Cc: Andrew F . Davis, Santosh Shilimkar, Suman Anna, linux-kernel,
linux-arm-kernel, devicetree, Rob Herring
In-Reply-To: <20200512203852.29499-2-tony@atomide.com>
On 12/05/2020 23:38, Tony Lindgren wrote:
> The PRM (Power and Reset Module) has registers to enable and disable
> power domains, so let's update the binding for that.
>
> Cc: devicetree@vger.kernel.org
> Cc: Rob Herring <robh@kernel.org>
> Signed-off-by: Tony Lindgren <tony@atomide.com>
> ---
> Documentation/devicetree/bindings/arm/omap/prm-inst.txt | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/arm/omap/prm-inst.txt b/Documentation/devicetree/bindings/arm/omap/prm-inst.txt
> --- a/Documentation/devicetree/bindings/arm/omap/prm-inst.txt
> +++ b/Documentation/devicetree/bindings/arm/omap/prm-inst.txt
> @@ -18,12 +18,16 @@ Required properties:
> (base address and length)
>
> Optional properties:
> +- #power-domain-cells: Should be 0 if the PRM instance is a power domain.
> - #reset-cells: Should be 1 if the PRM instance in question supports resets.
> +- clocks: Functional and interface clocks managed by the power domain
> +- clock-names: Names for the clocks using "fck" and "ick" naming
Whats the purpose of the clocks for PRM? It looks like you are using
this with ABE domain on omap4/omap5, but why is this needed?
-Tero
>
> Example:
>
> prm_dsp2: prm@1b00 {
> compatible = "ti,dra7-prm-inst", "ti,omap-prm-inst";
> reg = <0x1b00 0x40>;
> + #power-domain-cells = <0>;
> #reset-cells = <1>;
> };
>
--
Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
^ permalink raw reply
* Re: [PATCH V5 4/7] spi: spi-geni-qcom: Add interconnect support
From: Akash Asthana @ 2020-05-14 7:37 UTC (permalink / raw)
To: Matthias Kaehlcke
Cc: gregkh, agross, bjorn.andersson, wsa, broonie, mark.rutland,
robh+dt, linux-i2c, linux-spi, devicetree, swboyd, mgautam,
linux-arm-msm, linux-serial, dianders, evgreen, georgi.djakov
In-Reply-To: <20200508182532.GD4525@google.com>
Hi Matthias,
....
;
>>
>> + ret = geni_icc_get(&mas->se, NULL);
>> + if (ret)
>> + goto spi_geni_probe_runtime_disable;
>> + /* Set the bus quota to a reasonable value for register access */
>> + geni_icc_bw_init(&mas->se.icc_paths[GENI_TO_CORE],
>> + Bps_to_icc(CORE_2X_50_MHZ), 0);
>> + geni_icc_bw_init(&mas->se.icc_paths[CPU_TO_GENI], GENI_DEFAULT_BW, 0);
>> +
>> + /* Set BW for register access */
> This comment doesn't add any value. Register access is mentioned a few lines
> above and from the function name it's evident that it sets the ICC bandwidth.
ok
>
>> + ret = geni_icc_set_bw(&mas->se);
>>
>> + return ret;
>>
>> return geni_se_resources_on(&mas->se);
>> }
> Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,\na Linux Foundation Collaborative Project
^ permalink raw reply
* Re: [PATCH V5 2/7] soc: qcom-geni-se: Add interconnect support to fix earlycon crash
From: Akash Asthana @ 2020-05-14 7:34 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Greg Kroah-Hartman, agross, Bjorn Andersson, Wolfram Sang,
Mark Brown, Mark Rutland, Rob Herring, linux-i2c, linux-spi,
devicetree, Stephen Boyd, mgautam, linux-arm-msm,
open list:SERIAL DRIVERS, Matthias Kaehlcke, Douglas Anderson,
Evan Green, Georgi Djakov
In-Reply-To: <CAHp75Vdjz7RBbyPwZwvNq5njwb_Jc76U=3pDpswmoFCFaGtNAQ@mail.gmail.com>
Hi Andy,
On 5/9/2020 5:41 PM, Andy Shevchenko wrote:
> On Fri, May 8, 2020 at 9:34 AM Akash Asthana <akashast@codeaurora.org> wrote:
>> QUP core clock is shared among all the SE drivers present on particular
>> QUP wrapper, the system will reset(unclocked access) if earlycon used after
>> QUP core clock is put to 0 from other SE drivers before real console comes
>> up.
>>
>> As earlycon can't vote for it's QUP core need, to fix this add ICC
>> support to common/QUP wrapper driver and put vote for QUP core from
>> probe on behalf of earlycon and remove vote during earlycon exit call.
> ...
>
>> + for_each_child_of_node(parent, child) {
>> + if (of_device_is_compatible(child, "qcom,geni-se-qup")) {
> if (!...)
> continue;
> will save you a readability of the loop body.
>
> Or...
Ok.
>
>> + wrapper = platform_get_drvdata(of_find_device_by_node(
>> + child));
> ...leave this on one line
ok
>
>> + icc_put(wrapper->to_core.path);
>> + wrapper->to_core.path = NULL;
>> + }
> And here is the question, what do you want to do if you find more
> devices with the same compatible string?
We are voting on core path (from probe *geni_se_probe*) for all the
devices which are compatible to string "qcom,geni-se-qup" so, here we'll
remove those vote and release the icc_path for all the those devices
We have to vote on each qup device instead ones to which console is
connected because all the qup shares the same core clocks. You may refer
previous thread@ https://www.spinics.net/lists/linux-spi/msg21000.html
for more info on this limitation.
Thankyou for taking time out and reviewing the patch.
Regards,
Akash
>
>> + }
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,\na Linux Foundation Collaborative Project
^ permalink raw reply
* Re: [PATCH v1.1 4/4] dt-bindings: display: bridge: renesas,lvds: Convert binding to YAML
From: Geert Uytterhoeven @ 2020-05-14 7:31 UTC (permalink / raw)
To: Laurent Pinchart
Cc: open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Linux-Renesas, Rob Herring, DRI Development
In-Reply-To: <20200513232840.22687-1-laurent.pinchart+renesas@ideasonboard.com>
Hi Laurent,
On Thu, May 14, 2020 at 1:29 AM Laurent Pinchart
<laurent.pinchart+renesas@ideasonboard.com> wrote:
> Convert the Renesas R-Car LVDS encoder text binding to YAML.
>
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> Acked-by: Maxime Ripard <mripard@kernel.org>
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/bridge/renesas,lvds.yaml
> +examples:
> + - |
> + #include <dt-bindings/clock/renesas-cpg-mssr.h>
> + #include <dt-bindings/power/r8a7795-sysc.h>
> +
> + lvds@feb90000 {
> + compatible = "renesas,r8a7795-lvds";
> + reg = <0 0xfeb90000 0 0x14>;
#{address,size}-cells = <1> for examples.
Applies to all nodes below, too.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* Re: [PATCH 5/4] dt-bindings: display: bridge: thc63lvd1024: Document dual-output mode
From: Jacopo Mondi @ 2020-05-14 7:18 UTC (permalink / raw)
To: Laurent Pinchart; +Cc: dri-devel, devicetree, linux-renesas-soc, Rob Herring
In-Reply-To: <20200513232127.21798-1-laurent.pinchart+renesas@ideasonboard.com>
Hi Laurent,
On Thu, May 14, 2020 at 02:21:27AM +0300, Laurent Pinchart wrote:
> The DT binding support both dual-input and dual-output mode, but only
> dual-input is documented. Document dual-output mode.
>
> Suggested-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Thanks
Acked-by: Jacopo Mondi <jacopo@jmondi.org>
> ---
> .../display/bridge/thine,thc63lvd1024.yaml | 16 +++++++++++-----
> 1 file changed, 11 insertions(+), 5 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/display/bridge/thine,thc63lvd1024.yaml b/Documentation/devicetree/bindings/display/bridge/thine,thc63lvd1024.yaml
> index 469ac4a34273..fedd3460d6f6 100644
> --- a/Documentation/devicetree/bindings/display/bridge/thine,thc63lvd1024.yaml
> +++ b/Documentation/devicetree/bindings/display/bridge/thine,thc63lvd1024.yaml
> @@ -30,11 +30,17 @@ properties:
> This device has four video ports. Their connections are modeled using the
> OF graph bindings specified in Documentation/devicetree/bindings/graph.txt.
>
> - The device can operate in single-link mode or dual-link mode. In
> - single-link mode, all pixels are received on port@0, and port@1 shall not
> - contain any endpoint. In dual-link mode, even-numbered pixels are
> - received on port@0 and odd-numbered pixels on port@1, and both port@0 and
> - port@1 shall contain endpoints.
> + The device can operate in single or dual input and output modes.
> +
> + When operating in single input mode, all pixels are received on port@0,
> + and port@1 shall not contain any endpoint. In dual input mode,
> + even-numbered pixels are received on port@0 and odd-numbered pixels on
> + port@1, and both port@0 and port@1 shall contain endpoints.
> +
> + When operating in single output mode all pixels are output from the first
> + CMOS/TTL port and port@3 shall not contain any endpoint. In dual output
> + mode pixels are output from both CMOS/TTL ports and both port@2 and
> + port@3 shall contain endpoints.
>
> properties:
> '#address-cells':
> --
> Regards,
>
> Laurent Pinchart
>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox