Merge branch 'stmmac-dwmac-sun8i-Support-R40'

Chen-Yu Tsai says:

====================
net: stmmac: dwmac-sun8i: Support R40

This is a resend of the patches for net-next split out from my R40
Ethernet support v2 series, as requested by David Miller. The arm-soc
bits will follow, once I rework the A64 system controller compatible.

Patches 1, 2, and 3 clean up the dwmac-sun8i binding.

Patch 4 adds device tree binding for Allwinner R40's Ethernet
controller.

Patch 5 converts regmap access of the syscon region in the dwmac-sun8i
driver to regmap_field, in anticipation of different field widths on
the R40.

Patch 6 introduces custom plumbing in the dwmac-sun8i driver to fetch
a regmap from another device, by looking up said device via a phandle,
then getting the regmap associated with that device.

Patch 7 adds support for different or absent TX/RX delay chain ranges
to the dwmac-sun8i driver.

Patch 8 adds support for the R40's ethernet controller.

Excerpt from original cover letter:

Changes since v1:

  - Default to fetching regmap from device pointed to by syscon phandle,
    and falling back to syscon API if that fails.

  - Dropped .syscon_from_dev field in device data as a result of the
    previous change.

  - Added a large comment block explaining the first change.

  - Simplified description of syscon property in sun8i-dwmac binding.

  - Regmap now only exposes the EMAC/GMAC register, but retains the
    offset within its address space.

  - Added patches for A64, which reuse the same sun8i-dwmac changes.

This series adds support for the DWMAC based Ethernet controller found
on the Allwinner R40 SoC. The controller is either a DWMAC clone or
DWMAC core with its registers rearranged. This is already supported by
the dwmac-sun8i driver. The glue layer control registers, unlike other
sun8i family SoCs, is not in the system controller region, but in the
clock control unit, like with the older A20 and A31 SoCs.

While we reuse the bindings for dwmac-sun8i using a syscon phandle
reference, we need some custom plumbing for the clock driver to export
a regmap that only allows access to the GMAC register to the dwmac-sun8i
driver. An alternative would be to allow drivers to register custom
syscon devices with their own regmap and locking.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2018-05-14 15:06:55 -04:00
commit 4def47835c
2 changed files with 130 additions and 30 deletions

View File

@ -7,6 +7,7 @@ Required properties:
- compatible: must be one of the following string: - compatible: must be one of the following string:
"allwinner,sun8i-a83t-emac" "allwinner,sun8i-a83t-emac"
"allwinner,sun8i-h3-emac" "allwinner,sun8i-h3-emac"
"allwinner,sun8i-r40-gmac"
"allwinner,sun8i-v3s-emac" "allwinner,sun8i-v3s-emac"
"allwinner,sun50i-a64-emac" "allwinner,sun50i-a64-emac"
- reg: address and length of the register for the device. - reg: address and length of the register for the device.
@ -20,18 +21,18 @@ Required properties:
- phy-handle: See ethernet.txt - phy-handle: See ethernet.txt
- #address-cells: shall be 1 - #address-cells: shall be 1
- #size-cells: shall be 0 - #size-cells: shall be 0
- syscon: A phandle to the syscon of the SoC with one of the following - syscon: A phandle to the device containing the EMAC or GMAC clock register
compatible string:
- allwinner,sun8i-h3-system-controller
- allwinner,sun8i-v3s-system-controller
- allwinner,sun50i-a64-system-controller
- allwinner,sun8i-a83t-system-controller
Optional properties: Optional properties:
- allwinner,tx-delay-ps: TX clock delay chain value in ps. Range value is 0-700. Default is 0) - allwinner,tx-delay-ps: TX clock delay chain value in ps.
- allwinner,rx-delay-ps: RX clock delay chain value in ps. Range value is 0-3100. Default is 0) Range is 0-700. Default is 0.
Both delay properties need to be a multiple of 100. They control the delay for Unavailable for allwinner,sun8i-r40-gmac
external PHY. - allwinner,rx-delay-ps: RX clock delay chain value in ps.
Range is 0-3100. Default is 0.
Range is 0-700 for allwinner,sun8i-r40-gmac
Both delay properties need to be a multiple of 100. They control the
clock delay for external RGMII PHY. They do not apply to the internal
PHY or external non-RGMII PHYs.
Optional properties for the following compatibles: Optional properties for the following compatibles:
- "allwinner,sun8i-h3-emac", - "allwinner,sun8i-h3-emac",

View File

@ -42,17 +42,27 @@
* This value is used for disabling properly EMAC * This value is used for disabling properly EMAC
* and used as a good starting value in case of the * and used as a good starting value in case of the
* boot process(uboot) leave some stuff. * boot process(uboot) leave some stuff.
* @syscon_field reg_field for the syscon's gmac register
* @soc_has_internal_phy: Does the MAC embed an internal PHY * @soc_has_internal_phy: Does the MAC embed an internal PHY
* @support_mii: Does the MAC handle MII * @support_mii: Does the MAC handle MII
* @support_rmii: Does the MAC handle RMII * @support_rmii: Does the MAC handle RMII
* @support_rgmii: Does the MAC handle RGMII * @support_rgmii: Does the MAC handle RGMII
*
* @rx_delay_max: Maximum raw value for RX delay chain
* @tx_delay_max: Maximum raw value for TX delay chain
* These two also indicate the bitmask for
* the RX and TX delay chain registers. A
* value of zero indicates this is not supported.
*/ */
struct emac_variant { struct emac_variant {
u32 default_syscon_value; u32 default_syscon_value;
const struct reg_field *syscon_field;
bool soc_has_internal_phy; bool soc_has_internal_phy;
bool support_mii; bool support_mii;
bool support_rmii; bool support_rmii;
bool support_rgmii; bool support_rgmii;
u8 rx_delay_max;
u8 tx_delay_max;
}; };
/* struct sunxi_priv_data - hold all sunxi private data /* struct sunxi_priv_data - hold all sunxi private data
@ -71,38 +81,70 @@ struct sunxi_priv_data {
struct regulator *regulator; struct regulator *regulator;
struct reset_control *rst_ephy; struct reset_control *rst_ephy;
const struct emac_variant *variant; const struct emac_variant *variant;
struct regmap *regmap; struct regmap_field *regmap_field;
bool internal_phy_powered; bool internal_phy_powered;
void *mux_handle; void *mux_handle;
}; };
/* EMAC clock register @ 0x30 in the "system control" address range */
static const struct reg_field sun8i_syscon_reg_field = {
.reg = 0x30,
.lsb = 0,
.msb = 31,
};
/* EMAC clock register @ 0x164 in the CCU address range */
static const struct reg_field sun8i_ccu_reg_field = {
.reg = 0x164,
.lsb = 0,
.msb = 31,
};
static const struct emac_variant emac_variant_h3 = { static const struct emac_variant emac_variant_h3 = {
.default_syscon_value = 0x58000, .default_syscon_value = 0x58000,
.syscon_field = &sun8i_syscon_reg_field,
.soc_has_internal_phy = true, .soc_has_internal_phy = true,
.support_mii = true, .support_mii = true,
.support_rmii = true, .support_rmii = true,
.support_rgmii = true .support_rgmii = true,
.rx_delay_max = 31,
.tx_delay_max = 7,
}; };
static const struct emac_variant emac_variant_v3s = { static const struct emac_variant emac_variant_v3s = {
.default_syscon_value = 0x38000, .default_syscon_value = 0x38000,
.syscon_field = &sun8i_syscon_reg_field,
.soc_has_internal_phy = true, .soc_has_internal_phy = true,
.support_mii = true .support_mii = true
}; };
static const struct emac_variant emac_variant_a83t = { static const struct emac_variant emac_variant_a83t = {
.default_syscon_value = 0, .default_syscon_value = 0,
.syscon_field = &sun8i_syscon_reg_field,
.soc_has_internal_phy = false, .soc_has_internal_phy = false,
.support_mii = true, .support_mii = true,
.support_rgmii = true .support_rgmii = true,
.rx_delay_max = 31,
.tx_delay_max = 7,
};
static const struct emac_variant emac_variant_r40 = {
.default_syscon_value = 0,
.syscon_field = &sun8i_ccu_reg_field,
.support_mii = true,
.support_rgmii = true,
.rx_delay_max = 7,
}; };
static const struct emac_variant emac_variant_a64 = { static const struct emac_variant emac_variant_a64 = {
.default_syscon_value = 0, .default_syscon_value = 0,
.syscon_field = &sun8i_syscon_reg_field,
.soc_has_internal_phy = false, .soc_has_internal_phy = false,
.support_mii = true, .support_mii = true,
.support_rmii = true, .support_rmii = true,
.support_rgmii = true .support_rgmii = true,
.rx_delay_max = 31,
.tx_delay_max = 7,
}; };
#define EMAC_BASIC_CTL0 0x00 #define EMAC_BASIC_CTL0 0x00
@ -206,9 +248,7 @@ static const struct emac_variant emac_variant_a64 = {
#define SYSCON_RMII_EN BIT(13) /* 1: enable RMII (overrides EPIT) */ #define SYSCON_RMII_EN BIT(13) /* 1: enable RMII (overrides EPIT) */
/* Generic system control EMAC_CLK bits */ /* Generic system control EMAC_CLK bits */
#define SYSCON_ETXDC_MASK GENMASK(2, 0)
#define SYSCON_ETXDC_SHIFT 10 #define SYSCON_ETXDC_SHIFT 10
#define SYSCON_ERXDC_MASK GENMASK(4, 0)
#define SYSCON_ERXDC_SHIFT 5 #define SYSCON_ERXDC_SHIFT 5
/* EMAC PHY Interface Type */ /* EMAC PHY Interface Type */
#define SYSCON_EPIT BIT(2) /* 1: RGMII, 0: MII */ #define SYSCON_EPIT BIT(2) /* 1: RGMII, 0: MII */
@ -216,7 +256,6 @@ static const struct emac_variant emac_variant_a64 = {
#define SYSCON_ETCS_MII 0x0 #define SYSCON_ETCS_MII 0x0
#define SYSCON_ETCS_EXT_GMII 0x1 #define SYSCON_ETCS_EXT_GMII 0x1
#define SYSCON_ETCS_INT_GMII 0x2 #define SYSCON_ETCS_INT_GMII 0x2
#define SYSCON_EMAC_REG 0x30
/* sun8i_dwmac_dma_reset() - reset the EMAC /* sun8i_dwmac_dma_reset() - reset the EMAC
* Called from stmmac via stmmac_dma_ops->reset * Called from stmmac via stmmac_dma_ops->reset
@ -745,7 +784,7 @@ static int mdio_mux_syscon_switch_fn(int current_child, int desired_child,
bool need_power_ephy = false; bool need_power_ephy = false;
if (current_child ^ desired_child) { if (current_child ^ desired_child) {
regmap_read(gmac->regmap, SYSCON_EMAC_REG, &reg); regmap_field_read(gmac->regmap_field, &reg);
switch (desired_child) { switch (desired_child) {
case DWMAC_SUN8I_MDIO_MUX_INTERNAL_ID: case DWMAC_SUN8I_MDIO_MUX_INTERNAL_ID:
dev_info(priv->device, "Switch mux to internal PHY"); dev_info(priv->device, "Switch mux to internal PHY");
@ -763,7 +802,7 @@ static int mdio_mux_syscon_switch_fn(int current_child, int desired_child,
desired_child); desired_child);
return -EINVAL; return -EINVAL;
} }
regmap_write(gmac->regmap, SYSCON_EMAC_REG, val); regmap_field_write(gmac->regmap_field, val);
if (need_power_ephy) { if (need_power_ephy) {
ret = sun8i_dwmac_power_internal_phy(priv); ret = sun8i_dwmac_power_internal_phy(priv);
if (ret) if (ret)
@ -801,7 +840,7 @@ static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv)
int ret; int ret;
u32 reg, val; u32 reg, val;
regmap_read(gmac->regmap, SYSCON_EMAC_REG, &val); regmap_field_read(gmac->regmap_field, &val);
reg = gmac->variant->default_syscon_value; reg = gmac->variant->default_syscon_value;
if (reg != val) if (reg != val)
dev_warn(priv->device, dev_warn(priv->device,
@ -835,8 +874,9 @@ static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv)
} }
val /= 100; val /= 100;
dev_dbg(priv->device, "set tx-delay to %x\n", val); dev_dbg(priv->device, "set tx-delay to %x\n", val);
if (val <= SYSCON_ETXDC_MASK) { if (val <= gmac->variant->tx_delay_max) {
reg &= ~(SYSCON_ETXDC_MASK << SYSCON_ETXDC_SHIFT); reg &= ~(gmac->variant->tx_delay_max <<
SYSCON_ETXDC_SHIFT);
reg |= (val << SYSCON_ETXDC_SHIFT); reg |= (val << SYSCON_ETXDC_SHIFT);
} else { } else {
dev_err(priv->device, "Invalid TX clock delay: %d\n", dev_err(priv->device, "Invalid TX clock delay: %d\n",
@ -852,8 +892,9 @@ static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv)
} }
val /= 100; val /= 100;
dev_dbg(priv->device, "set rx-delay to %x\n", val); dev_dbg(priv->device, "set rx-delay to %x\n", val);
if (val <= SYSCON_ERXDC_MASK) { if (val <= gmac->variant->rx_delay_max) {
reg &= ~(SYSCON_ERXDC_MASK << SYSCON_ERXDC_SHIFT); reg &= ~(gmac->variant->rx_delay_max <<
SYSCON_ERXDC_SHIFT);
reg |= (val << SYSCON_ERXDC_SHIFT); reg |= (val << SYSCON_ERXDC_SHIFT);
} else { } else {
dev_err(priv->device, "Invalid RX clock delay: %d\n", dev_err(priv->device, "Invalid RX clock delay: %d\n",
@ -883,7 +924,7 @@ static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv)
return -EINVAL; return -EINVAL;
} }
regmap_write(gmac->regmap, SYSCON_EMAC_REG, reg); regmap_field_write(gmac->regmap_field, reg);
return 0; return 0;
} }
@ -892,7 +933,7 @@ static void sun8i_dwmac_unset_syscon(struct sunxi_priv_data *gmac)
{ {
u32 reg = gmac->variant->default_syscon_value; u32 reg = gmac->variant->default_syscon_value;
regmap_write(gmac->regmap, SYSCON_EMAC_REG, reg); regmap_field_write(gmac->regmap_field, reg);
} }
static void sun8i_dwmac_exit(struct platform_device *pdev, void *priv) static void sun8i_dwmac_exit(struct platform_device *pdev, void *priv)
@ -971,6 +1012,34 @@ static struct mac_device_info *sun8i_dwmac_setup(void *ppriv)
return mac; return mac;
} }
static struct regmap *sun8i_dwmac_get_syscon_from_dev(struct device_node *node)
{
struct device_node *syscon_node;
struct platform_device *syscon_pdev;
struct regmap *regmap = NULL;
syscon_node = of_parse_phandle(node, "syscon", 0);
if (!syscon_node)
return ERR_PTR(-ENODEV);
syscon_pdev = of_find_device_by_node(syscon_node);
if (!syscon_pdev) {
/* platform device might not be probed yet */
regmap = ERR_PTR(-EPROBE_DEFER);
goto out_put_node;
}
/* If no regmap is found then the other device driver is at fault */
regmap = dev_get_regmap(&syscon_pdev->dev, NULL);
if (!regmap)
regmap = ERR_PTR(-EINVAL);
platform_device_put(syscon_pdev);
out_put_node:
of_node_put(syscon_node);
return regmap;
}
static int sun8i_dwmac_probe(struct platform_device *pdev) static int sun8i_dwmac_probe(struct platform_device *pdev)
{ {
struct plat_stmmacenet_data *plat_dat; struct plat_stmmacenet_data *plat_dat;
@ -980,6 +1049,7 @@ static int sun8i_dwmac_probe(struct platform_device *pdev)
int ret; int ret;
struct stmmac_priv *priv; struct stmmac_priv *priv;
struct net_device *ndev; struct net_device *ndev;
struct regmap *regmap;
ret = stmmac_get_platform_resources(pdev, &stmmac_res); ret = stmmac_get_platform_resources(pdev, &stmmac_res);
if (ret) if (ret)
@ -1014,14 +1084,41 @@ static int sun8i_dwmac_probe(struct platform_device *pdev)
gmac->regulator = NULL; gmac->regulator = NULL;
} }
gmac->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, /* The "GMAC clock control" register might be located in the
"syscon"); * CCU address range (on the R40), or the system control address
if (IS_ERR(gmac->regmap)) { * range (on most other sun8i and later SoCs).
ret = PTR_ERR(gmac->regmap); *
* The former controls most if not all clocks in the SoC. The
* latter has an SoC identification register, and on some SoCs,
* controls to map device specific SRAM to either the intended
* peripheral, or the CPU address space.
*
* In either case, there should be a coordinated and restricted
* method of accessing the register needed here. This is done by
* having the device export a custom regmap, instead of a generic
* syscon, which grants all access to all registers.
*
* To support old device trees, we fall back to using the syscon
* interface if possible.
*/
regmap = sun8i_dwmac_get_syscon_from_dev(pdev->dev.of_node);
if (IS_ERR(regmap))
regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"syscon");
if (IS_ERR(regmap)) {
ret = PTR_ERR(regmap);
dev_err(&pdev->dev, "Unable to map syscon: %d\n", ret); dev_err(&pdev->dev, "Unable to map syscon: %d\n", ret);
return ret; return ret;
} }
gmac->regmap_field = devm_regmap_field_alloc(dev, regmap,
*gmac->variant->syscon_field);
if (IS_ERR(gmac->regmap_field)) {
ret = PTR_ERR(gmac->regmap_field);
dev_err(dev, "Unable to map syscon register: %d\n", ret);
return ret;
}
plat_dat->interface = of_get_phy_mode(dev->of_node); plat_dat->interface = of_get_phy_mode(dev->of_node);
/* platform data specifying hardware features and callbacks. /* platform data specifying hardware features and callbacks.
@ -1078,6 +1175,8 @@ static const struct of_device_id sun8i_dwmac_match[] = {
.data = &emac_variant_v3s }, .data = &emac_variant_v3s },
{ .compatible = "allwinner,sun8i-a83t-emac", { .compatible = "allwinner,sun8i-a83t-emac",
.data = &emac_variant_a83t }, .data = &emac_variant_a83t },
{ .compatible = "allwinner,sun8i-r40-gmac",
.data = &emac_variant_r40 },
{ .compatible = "allwinner,sun50i-a64-emac", { .compatible = "allwinner,sun50i-a64-emac",
.data = &emac_variant_a64 }, .data = &emac_variant_a64 },
{ } { }