|
@@ -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, ®);
|
|
|
|
|
|
+ regmap_field_read(gmac->regmap_field, ®);
|
|
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) {
|
|
|
|
- reg &= ~(SYSCON_ETXDC_MASK << SYSCON_ETXDC_SHIFT);
|
|
|
|
|
|
+ if (val <= gmac->variant->tx_delay_max) {
|
|
|
|
+ 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) {
|
|
|
|
- reg &= ~(SYSCON_ERXDC_MASK << SYSCON_ERXDC_SHIFT);
|
|
|
|
|
|
+ if (val <= gmac->variant->rx_delay_max) {
|
|
|
|
+ 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,
|
|
|
|
- "syscon");
|
|
|
|
- if (IS_ERR(gmac->regmap)) {
|
|
|
|
- ret = PTR_ERR(gmac->regmap);
|
|
|
|
|
|
+ /* The "GMAC clock control" register might be located in the
|
|
|
|
+ * CCU address range (on the R40), or the system control address
|
|
|
|
+ * range (on most other sun8i and later SoCs).
|
|
|
|
+ *
|
|
|
|
+ * 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 },
|
|
{ }
|
|
{ }
|