Commit ed0141d1 authored by David S. Miller's avatar David S. Miller

Merge branch 'Ingenic-SOC-mac-support'

Zhou Yanjie says:

====================
Add Ingenic SoCs MAC support.

v2->v3:
1.Add "ingenic,mac.yaml" for Ingenic SoCs.
2.Change tx clk delay and rx clk delay from hardware value to ps.
3.return -EINVAL when a unsupported value is encountered when
  parsing the binding.
4.Simplify the code of the RGMII part of X2000 SoC according to
  Andrew Lunn’s suggestion.
5.Follow the example of "dwmac-mediatek.c" to improve the code
  that handles delays according to Andrew Lunn’s suggestion.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 0a84a828 2bb4b98b
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/ingenic,mac.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Bindings for MAC in Ingenic SoCs
maintainers:
- 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
description:
The Ethernet Media Access Controller in Ingenic SoCs.
properties:
compatible:
enum:
- ingenic,jz4775-mac
- ingenic,x1000-mac
- ingenic,x1600-mac
- ingenic,x1830-mac
- ingenic,x2000-mac
reg:
maxItems: 1
interrupts:
maxItems: 1
interrupt-names:
const: macirq
clocks:
maxItems: 1
clock-names:
const: stmmaceth
mode-reg:
description: An extra syscon register that control ethernet interface and timing delay
rx-clk-delay-ps:
description: RGMII receive clock delay defined in pico seconds
tx-clk-delay-ps:
description: RGMII transmit clock delay defined in pico seconds
required:
- compatible
- reg
- interrupts
- interrupt-names
- clocks
- clock-names
- mode-reg
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/x1000-cgu.h>
mac: ethernet@134b0000 {
compatible = "ingenic,x1000-mac", "snps,dwmac";
reg = <0x134b0000 0x2000>;
interrupt-parent = <&intc>;
interrupts = <55>;
interrupt-names = "macirq";
clocks = <&cgu X1000_CLK_MAC>;
clock-names = "stmmaceth";
mode-reg = <&mac_phy_ctrl>;
};
...
......@@ -56,6 +56,11 @@ properties:
- amlogic,meson8m2-dwmac
- amlogic,meson-gxbb-dwmac
- amlogic,meson-axg-dwmac
- ingenic,jz4775-mac
- ingenic,x1000-mac
- ingenic,x1600-mac
- ingenic,x1830-mac
- ingenic,x2000-mac
- rockchip,px30-gmac
- rockchip,rk3128-gmac
- rockchip,rk3228-gmac
......@@ -310,6 +315,11 @@ allOf:
- allwinner,sun8i-r40-emac
- allwinner,sun8i-v3s-emac
- allwinner,sun50i-a64-emac
- ingenic,jz4775-mac
- ingenic,x1000-mac
- ingenic,x1600-mac
- ingenic,x1830-mac
- ingenic,x2000-mac
- snps,dwxgmac
- snps,dwxgmac-2.10
- st,spear600-gmac
......@@ -353,6 +363,11 @@ allOf:
- allwinner,sun8i-r40-emac
- allwinner,sun8i-v3s-emac
- allwinner,sun50i-a64-emac
- ingenic,jz4775-mac
- ingenic,x1000-mac
- ingenic,x1600-mac
- ingenic,x1830-mac
- ingenic,x2000-mac
- snps,dwmac-4.00
- snps,dwmac-4.10a
- snps,dwmac-4.20a
......
......@@ -66,6 +66,18 @@ config DWMAC_ANARION
This selects the Anarion SoC glue layer support for the stmmac driver.
config DWMAC_INGENIC
tristate "Ingenic MAC support"
default MACH_INGENIC
depends on OF && HAS_IOMEM && (MACH_INGENIC || COMPILE_TEST)
select MFD_SYSCON
help
Support for ethernet controller on Ingenic SoCs.
This selects Ingenic SoCs glue layer support for the stmmac
device driver. This driver is used on for the Ingenic SoCs
MAC ethernet controller.
config DWMAC_IPQ806X
tristate "QCA IPQ806x DWMAC support"
default ARCH_QCOM
......
......@@ -14,6 +14,7 @@ stmmac-$(CONFIG_STMMAC_SELFTESTS) += stmmac_selftests.o
# Ordering matters. Generic driver must be last.
obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o
obj-$(CONFIG_DWMAC_ANARION) += dwmac-anarion.o
obj-$(CONFIG_DWMAC_INGENIC) += dwmac-ingenic.o
obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o
obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o
obj-$(CONFIG_DWMAC_MEDIATEK) += dwmac-mediatek.o
......
// SPDX-License-Identifier: GPL-2.0
/*
* dwmac-ingenic.c - Ingenic SoCs DWMAC specific glue layer
*
* Copyright (c) 2021 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_net.h>
#include <linux/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/stmmac.h>
#include "stmmac_platform.h"
#define MACPHYC_TXCLK_SEL_MASK GENMASK(31, 31)
#define MACPHYC_TXCLK_SEL_OUTPUT 0x1
#define MACPHYC_TXCLK_SEL_INPUT 0x0
#define MACPHYC_MODE_SEL_MASK GENMASK(31, 31)
#define MACPHYC_MODE_SEL_RMII 0x0
#define MACPHYC_TX_SEL_MASK GENMASK(19, 19)
#define MACPHYC_TX_SEL_ORIGIN 0x0
#define MACPHYC_TX_SEL_DELAY 0x1
#define MACPHYC_TX_DELAY_MASK GENMASK(18, 12)
#define MACPHYC_RX_SEL_MASK GENMASK(11, 11)
#define MACPHYC_RX_SEL_ORIGIN 0x0
#define MACPHYC_RX_SEL_DELAY 0x1
#define MACPHYC_RX_DELAY_MASK GENMASK(10, 4)
#define MACPHYC_SOFT_RST_MASK GENMASK(3, 3)
#define MACPHYC_PHY_INFT_MASK GENMASK(2, 0)
#define MACPHYC_PHY_INFT_RMII 0x4
#define MACPHYC_PHY_INFT_RGMII 0x1
#define MACPHYC_PHY_INFT_GMII 0x0
#define MACPHYC_PHY_INFT_MII 0x0
#define MACPHYC_TX_DELAY_PS_MAX 2496
#define MACPHYC_TX_DELAY_PS_MIN 20
#define MACPHYC_RX_DELAY_PS_MAX 2496
#define MACPHYC_RX_DELAY_PS_MIN 20
enum ingenic_mac_version {
ID_JZ4775,
ID_X1000,
ID_X1600,
ID_X1830,
ID_X2000,
};
struct ingenic_mac {
const struct ingenic_soc_info *soc_info;
struct device *dev;
struct regmap *regmap;
int rx_delay;
int tx_delay;
};
struct ingenic_soc_info {
enum ingenic_mac_version version;
u32 mask;
int (*set_mode)(struct plat_stmmacenet_data *plat_dat);
};
static int ingenic_mac_init(struct plat_stmmacenet_data *plat_dat)
{
struct ingenic_mac *mac = plat_dat->bsp_priv;
int ret;
if (mac->soc_info->set_mode) {
ret = mac->soc_info->set_mode(plat_dat);
if (ret)
return ret;
}
return 0;
}
static int jz4775_mac_set_mode(struct plat_stmmacenet_data *plat_dat)
{
struct ingenic_mac *mac = plat_dat->bsp_priv;
unsigned int val;
switch (plat_dat->interface) {
case PHY_INTERFACE_MODE_MII:
val = FIELD_PREP(MACPHYC_TXCLK_SEL_MASK, MACPHYC_TXCLK_SEL_INPUT) |
FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_MII);
dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_MII\n");
break;
case PHY_INTERFACE_MODE_GMII:
val = FIELD_PREP(MACPHYC_TXCLK_SEL_MASK, MACPHYC_TXCLK_SEL_INPUT) |
FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_GMII);
dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_GMII\n");
break;
case PHY_INTERFACE_MODE_RMII:
val = FIELD_PREP(MACPHYC_TXCLK_SEL_MASK, MACPHYC_TXCLK_SEL_INPUT) |
FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RMII);
dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RMII\n");
break;
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_TXID:
case PHY_INTERFACE_MODE_RGMII_RXID:
val = FIELD_PREP(MACPHYC_TXCLK_SEL_MASK, MACPHYC_TXCLK_SEL_INPUT) |
FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RGMII);
dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RGMII\n");
break;
default:
dev_err(mac->dev, "Unsupported interface %d", plat_dat->interface);
return -EINVAL;
}
/* Update MAC PHY control register */
return regmap_update_bits(mac->regmap, 0, mac->soc_info->mask, val);
}
static int x1000_mac_set_mode(struct plat_stmmacenet_data *plat_dat)
{
struct ingenic_mac *mac = plat_dat->bsp_priv;
switch (plat_dat->interface) {
case PHY_INTERFACE_MODE_RMII:
dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RMII\n");
break;
default:
dev_err(mac->dev, "Unsupported interface %d", plat_dat->interface);
return -EINVAL;
}
/* Update MAC PHY control register */
return regmap_update_bits(mac->regmap, 0, mac->soc_info->mask, 0);
}
static int x1600_mac_set_mode(struct plat_stmmacenet_data *plat_dat)
{
struct ingenic_mac *mac = plat_dat->bsp_priv;
unsigned int val;
switch (plat_dat->interface) {
case PHY_INTERFACE_MODE_RMII:
val = FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RMII);
dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RMII\n");
break;
default:
dev_err(mac->dev, "Unsupported interface %d", plat_dat->interface);
return -EINVAL;
}
/* Update MAC PHY control register */
return regmap_update_bits(mac->regmap, 0, mac->soc_info->mask, val);
}
static int x1830_mac_set_mode(struct plat_stmmacenet_data *plat_dat)
{
struct ingenic_mac *mac = plat_dat->bsp_priv;
unsigned int val;
switch (plat_dat->interface) {
case PHY_INTERFACE_MODE_RMII:
val = FIELD_PREP(MACPHYC_MODE_SEL_MASK, MACPHYC_MODE_SEL_RMII) |
FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RMII);
dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RMII\n");
break;
default:
dev_err(mac->dev, "Unsupported interface %d", plat_dat->interface);
return -EINVAL;
}
/* Update MAC PHY control register */
return regmap_update_bits(mac->regmap, 0, mac->soc_info->mask, val);
}
static int x2000_mac_set_mode(struct plat_stmmacenet_data *plat_dat)
{
struct ingenic_mac *mac = plat_dat->bsp_priv;
unsigned int val;
switch (plat_dat->interface) {
case PHY_INTERFACE_MODE_RMII:
val = FIELD_PREP(MACPHYC_TX_SEL_MASK, MACPHYC_TX_SEL_ORIGIN) |
FIELD_PREP(MACPHYC_RX_SEL_MASK, MACPHYC_RX_SEL_ORIGIN) |
FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RMII);
dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RMII\n");
break;
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_TXID:
case PHY_INTERFACE_MODE_RGMII_RXID:
val = FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RGMII);
if (mac->tx_delay == 0)
val |= FIELD_PREP(MACPHYC_TX_SEL_MASK, MACPHYC_TX_SEL_ORIGIN);
else
val |= FIELD_PREP(MACPHYC_TX_SEL_MASK, MACPHYC_TX_SEL_DELAY) |
FIELD_PREP(MACPHYC_TX_DELAY_MASK, (mac->tx_delay + 9750) / 19500 - 1);
if (mac->rx_delay == 0)
val |= FIELD_PREP(MACPHYC_RX_SEL_MASK, MACPHYC_RX_SEL_ORIGIN);
else
val |= FIELD_PREP(MACPHYC_RX_SEL_MASK, MACPHYC_RX_SEL_DELAY) |
FIELD_PREP(MACPHYC_RX_DELAY_MASK, (mac->rx_delay + 9750) / 19500 - 1);
dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RGMII\n");
break;
default:
dev_err(mac->dev, "Unsupported interface %d", plat_dat->interface);
return -EINVAL;
}
/* Update MAC PHY control register */
return regmap_update_bits(mac->regmap, 0, mac->soc_info->mask, val);
}
static int ingenic_mac_probe(struct platform_device *pdev)
{
struct plat_stmmacenet_data *plat_dat;
struct stmmac_resources stmmac_res;
struct ingenic_mac *mac;
const struct ingenic_soc_info *data;
u32 tx_delay_ps, rx_delay_ps;
int ret;
ret = stmmac_get_platform_resources(pdev, &stmmac_res);
if (ret)
return ret;
plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
if (IS_ERR(plat_dat))
return PTR_ERR(plat_dat);
mac = devm_kzalloc(&pdev->dev, sizeof(*mac), GFP_KERNEL);
if (!mac) {
ret = -ENOMEM;
goto err_remove_config_dt;
}
data = of_device_get_match_data(&pdev->dev);
if (!data) {
dev_err(&pdev->dev, "No of match data provided\n");
ret = -EINVAL;
goto err_remove_config_dt;
}
/* Get MAC PHY control register */
mac->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "mode-reg");
if (IS_ERR(mac->regmap)) {
dev_err(&pdev->dev, "%s: Failed to get syscon regmap\n", __func__);
goto err_remove_config_dt;
}
if (!of_property_read_u32(pdev->dev.of_node, "tx-clk-delay-ps", &tx_delay_ps)) {
if (tx_delay_ps >= MACPHYC_TX_DELAY_PS_MIN &&
tx_delay_ps <= MACPHYC_TX_DELAY_PS_MAX) {
mac->tx_delay = tx_delay_ps * 1000;
} else {
dev_err(&pdev->dev, "Invalid TX clock delay: %dps\n", tx_delay_ps);
return -EINVAL;
}
}
if (!of_property_read_u32(pdev->dev.of_node, "rx-clk-delay-ps", &rx_delay_ps)) {
if (rx_delay_ps >= MACPHYC_RX_DELAY_PS_MIN &&
rx_delay_ps <= MACPHYC_RX_DELAY_PS_MAX) {
mac->rx_delay = rx_delay_ps * 1000;
} else {
dev_err(&pdev->dev, "Invalid RX clock delay: %dps\n", rx_delay_ps);
return -EINVAL;
}
}
mac->soc_info = data;
mac->dev = &pdev->dev;
plat_dat->bsp_priv = mac;
ret = ingenic_mac_init(plat_dat);
if (ret)
goto err_remove_config_dt;
ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
if (ret)
goto err_remove_config_dt;
return 0;
err_remove_config_dt:
stmmac_remove_config_dt(pdev, plat_dat);
return ret;
}
#ifdef CONFIG_PM_SLEEP
static int ingenic_mac_suspend(struct device *dev)
{
int ret;
ret = stmmac_suspend(dev);
return ret;
}
static int ingenic_mac_resume(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev);
int ret;
ret = ingenic_mac_init(priv->plat);
if (ret)
return ret;
ret = stmmac_resume(dev);
return ret;
}
#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(ingenic_mac_pm_ops, ingenic_mac_suspend, ingenic_mac_resume);
static struct ingenic_soc_info jz4775_soc_info = {
.version = ID_JZ4775,
.mask = MACPHYC_TXCLK_SEL_MASK | MACPHYC_SOFT_RST_MASK | MACPHYC_PHY_INFT_MASK,
.set_mode = jz4775_mac_set_mode,
};
static struct ingenic_soc_info x1000_soc_info = {
.version = ID_X1000,
.mask = MACPHYC_SOFT_RST_MASK,
.set_mode = x1000_mac_set_mode,
};
static struct ingenic_soc_info x1600_soc_info = {
.version = ID_X1600,
.mask = MACPHYC_SOFT_RST_MASK | MACPHYC_PHY_INFT_MASK,
.set_mode = x1600_mac_set_mode,
};
static struct ingenic_soc_info x1830_soc_info = {
.version = ID_X1830,
.mask = MACPHYC_MODE_SEL_MASK | MACPHYC_SOFT_RST_MASK | MACPHYC_PHY_INFT_MASK,
.set_mode = x1830_mac_set_mode,
};
static struct ingenic_soc_info x2000_soc_info = {
.version = ID_X2000,
.mask = MACPHYC_TX_SEL_MASK | MACPHYC_TX_DELAY_MASK | MACPHYC_RX_SEL_MASK |
MACPHYC_RX_DELAY_MASK | MACPHYC_SOFT_RST_MASK | MACPHYC_PHY_INFT_MASK,
.set_mode = x2000_mac_set_mode,
};
static const struct of_device_id ingenic_mac_of_matches[] = {
{ .compatible = "ingenic,jz4775-mac", .data = &jz4775_soc_info },
{ .compatible = "ingenic,x1000-mac", .data = &x1000_soc_info },
{ .compatible = "ingenic,x1600-mac", .data = &x1600_soc_info },
{ .compatible = "ingenic,x1830-mac", .data = &x1830_soc_info },
{ .compatible = "ingenic,x2000-mac", .data = &x2000_soc_info },
{ }
};
MODULE_DEVICE_TABLE(of, ingenic_mac_of_matches);
static struct platform_driver ingenic_mac_driver = {
.probe = ingenic_mac_probe,
.remove = stmmac_pltfr_remove,
.driver = {
.name = "ingenic-mac",
.pm = pm_ptr(&ingenic_mac_pm_ops),
.of_match_table = ingenic_mac_of_matches,
},
};
module_platform_driver(ingenic_mac_driver);
MODULE_AUTHOR("周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>");
MODULE_DESCRIPTION("Ingenic SoCs DWMAC specific glue layer");
MODULE_LICENSE("GPL v2");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment