Lines Matching +full:sun8i +full:- +full:a83t +full:- +full:emac
1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * dwmac-sun8i.c - Allwinner sun8i DWMAC specific glue layer
11 #include <linux/mdio-mux.h>
28 /* General notes on dwmac-sun8i:
33 /* struct emac_variant - Describe dwmac-sun8i hardware variant
34 * @default_syscon_value: The default value of the EMAC register in syscon
35 * This value is used for disabling properly EMAC
61 /* struct sunxi_priv_data - hold all sunxi private data
69 * @mux_handle: Internal pointer used by mdio-mux lib
82 /* EMAC clock register @ 0x30 in the "system control" address range */
89 /* EMAC clock register @ 0x164 in the CCU address range */
147 * co-packaged AC200 chip instead.
276 /* EMAC PHY Interface Type */
283 /* sun8i_dwmac_dma_reset() - reset the EMAC
284 * Called from stmmac via stmmac_dma_ops->reset
298 /* sun8i_dwmac_dma_init() - initialize the EMAC
299 * Called from stmmac via stmmac_dma_ops->init
326 /* sun8i_dwmac_dump_regs() - Dump EMAC address space
327 * Called from stmmac_dma_ops->dump_regs
342 /* sun8i_dwmac_dump_mac_regs() - Dump EMAC address space
343 * Called from stmmac_ops->dump_regs
350 void __iomem *ioaddr = hw->pcsr; in sun8i_dwmac_dump_mac_regs()
444 struct stmmac_pcpu_stats *stats = this_cpu_ptr(priv->xstats.pcpu_stats); in sun8i_dwmac_dma_interrupt()
457 u64_stats_update_begin(&stats->syncp); in sun8i_dwmac_dma_interrupt()
458 u64_stats_inc(&stats->tx_normal_irq_n[chan]); in sun8i_dwmac_dma_interrupt()
459 u64_stats_update_end(&stats->syncp); in sun8i_dwmac_dma_interrupt()
463 x->tx_process_stopped_irq++; in sun8i_dwmac_dma_interrupt()
466 x->tx_process_stopped_irq++; in sun8i_dwmac_dma_interrupt()
473 x->tx_undeflow_irq++; in sun8i_dwmac_dma_interrupt()
477 x->tx_early_irq++; in sun8i_dwmac_dma_interrupt()
481 u64_stats_update_begin(&stats->syncp); in sun8i_dwmac_dma_interrupt()
482 u64_stats_inc(&stats->rx_normal_irq_n[chan]); in sun8i_dwmac_dma_interrupt()
483 u64_stats_update_end(&stats->syncp); in sun8i_dwmac_dma_interrupt()
487 x->rx_buf_unav_irq++; in sun8i_dwmac_dma_interrupt()
490 x->rx_process_stopped_irq++; in sun8i_dwmac_dma_interrupt()
497 x->rx_overflow_irq++; in sun8i_dwmac_dma_interrupt()
501 x->rx_early_irq++; in sun8i_dwmac_dma_interrupt()
504 x->irq_rgmii_n++; in sun8i_dwmac_dma_interrupt()
547 * especially when transmit store-and-forward is used." in sun8i_dwmac_dma_operation_mode_tx()
591 if (gmac->regulator) { in sun8i_dwmac_init()
592 ret = regulator_enable(gmac->regulator); in sun8i_dwmac_init()
594 dev_err(&pdev->dev, "Fail to enable regulator\n"); in sun8i_dwmac_init()
599 if (gmac->use_internal_phy) { in sun8i_dwmac_init()
608 if (gmac->regulator) in sun8i_dwmac_init()
609 regulator_disable(gmac->regulator); in sun8i_dwmac_init()
617 void __iomem *ioaddr = hw->pcsr; in sun8i_dwmac_core_init()
649 void __iomem *ioaddr = hw->pcsr; in sun8i_dwmac_set_umac_addr()
670 void __iomem *ioaddr = hw->pcsr; in sun8i_dwmac_get_umac_addr()
679 void __iomem *ioaddr = hw->pcsr; in sun8i_dwmac_rx_ipc_enable()
692 void __iomem *ioaddr = hw->pcsr; in sun8i_dwmac_set_filter()
700 if (dev->flags & IFF_PROMISC) { in sun8i_dwmac_set_filter()
702 } else if (dev->flags & IFF_ALLMULTI) { in sun8i_dwmac_set_filter()
704 } else if (macaddrs <= hw->unicast_filter_entries) { in sun8i_dwmac_set_filter()
707 sun8i_dwmac_set_umac_addr(hw, ha->addr, i); in sun8i_dwmac_set_filter()
713 sun8i_dwmac_set_umac_addr(hw, ha->addr, i); in sun8i_dwmac_set_filter()
724 while (i < hw->unicast_filter_entries) in sun8i_dwmac_set_filter()
734 void __iomem *ioaddr = hw->pcsr; in sun8i_dwmac_flow_ctrl()
757 v = readl(priv->ioaddr + EMAC_BASIC_CTL1); in sun8i_dwmac_reset()
758 writel(v | 0x01, priv->ioaddr + EMAC_BASIC_CTL1); in sun8i_dwmac_reset()
763 err = readl_poll_timeout(priv->ioaddr + EMAC_BASIC_CTL1, v, in sun8i_dwmac_reset()
767 dev_err(priv->device, "EMAC reset timeout\n"); in sun8i_dwmac_reset()
773 /* Search in mdio-mux node for internal PHY node and get its clk/reset */
776 struct sunxi_priv_data *gmac = priv->plat->bsp_priv; in get_ephy_nodes()
781 mdio_mux = of_get_child_by_name(priv->device->of_node, "mdio-mux"); in get_ephy_nodes()
783 dev_err(priv->device, "Cannot get mdio-mux node\n"); in get_ephy_nodes()
784 return -ENODEV; in get_ephy_nodes()
788 "allwinner,sun8i-h3-mdio-internal"); in get_ephy_nodes()
791 dev_err(priv->device, "Cannot get internal_mdio node\n"); in get_ephy_nodes()
792 return -ENODEV; in get_ephy_nodes()
797 gmac->ephy_clk = of_clk_get(iphynode, 0); in get_ephy_nodes()
798 if (IS_ERR(gmac->ephy_clk)) in get_ephy_nodes()
800 gmac->rst_ephy = of_reset_control_get_exclusive(iphynode, NULL); in get_ephy_nodes()
801 if (IS_ERR(gmac->rst_ephy)) { in get_ephy_nodes()
802 ret = PTR_ERR(gmac->rst_ephy); in get_ephy_nodes()
803 if (ret == -EPROBE_DEFER) { in get_ephy_nodes()
810 dev_info(priv->device, "Found internal PHY node\n"); in get_ephy_nodes()
817 return -ENODEV; in get_ephy_nodes()
822 struct sunxi_priv_data *gmac = priv->plat->bsp_priv; in sun8i_dwmac_power_internal_phy()
825 if (gmac->internal_phy_powered) { in sun8i_dwmac_power_internal_phy()
826 dev_warn(priv->device, "Internal PHY already powered\n"); in sun8i_dwmac_power_internal_phy()
830 dev_info(priv->device, "Powering internal PHY\n"); in sun8i_dwmac_power_internal_phy()
831 ret = clk_prepare_enable(gmac->ephy_clk); in sun8i_dwmac_power_internal_phy()
833 dev_err(priv->device, "Cannot enable internal PHY\n"); in sun8i_dwmac_power_internal_phy()
837 /* Make sure the EPHY is properly reseted, as U-Boot may leave in sun8i_dwmac_power_internal_phy()
838 * it at deasserted state, and thus it may fail to reset EMAC. in sun8i_dwmac_power_internal_phy()
842 ret = reset_control_reset(gmac->rst_ephy); in sun8i_dwmac_power_internal_phy()
844 dev_err(priv->device, "Cannot reset internal PHY\n"); in sun8i_dwmac_power_internal_phy()
845 clk_disable_unprepare(gmac->ephy_clk); in sun8i_dwmac_power_internal_phy()
849 gmac->internal_phy_powered = true; in sun8i_dwmac_power_internal_phy()
856 if (!gmac->internal_phy_powered) in sun8i_dwmac_unpower_internal_phy()
859 clk_disable_unprepare(gmac->ephy_clk); in sun8i_dwmac_unpower_internal_phy()
860 reset_control_assert(gmac->rst_ephy); in sun8i_dwmac_unpower_internal_phy()
861 gmac->internal_phy_powered = false; in sun8i_dwmac_unpower_internal_phy()
865 * This function is called by the mdio-mux layer when it thinks the mdio bus
870 * The first time this function is called, current_child == -1.
878 struct sunxi_priv_data *gmac = priv->plat->bsp_priv; in mdio_mux_syscon_switch_fn()
883 regmap_field_read(gmac->regmap_field, ®); in mdio_mux_syscon_switch_fn()
886 dev_info(priv->device, "Switch mux to internal PHY"); in mdio_mux_syscon_switch_fn()
888 gmac->use_internal_phy = true; in mdio_mux_syscon_switch_fn()
891 dev_info(priv->device, "Switch mux to external PHY"); in mdio_mux_syscon_switch_fn()
893 gmac->use_internal_phy = false; in mdio_mux_syscon_switch_fn()
896 dev_err(priv->device, "Invalid child ID %x\n", in mdio_mux_syscon_switch_fn()
898 return -EINVAL; in mdio_mux_syscon_switch_fn()
900 regmap_field_write(gmac->regmap_field, val); in mdio_mux_syscon_switch_fn()
901 if (gmac->use_internal_phy) { in mdio_mux_syscon_switch_fn()
920 struct sunxi_priv_data *gmac = priv->plat->bsp_priv; in sun8i_dwmac_register_mdio_mux()
922 mdio_mux = of_get_child_by_name(priv->device->of_node, "mdio-mux"); in sun8i_dwmac_register_mdio_mux()
924 return -ENODEV; in sun8i_dwmac_register_mdio_mux()
926 ret = mdio_mux_init(priv->device, mdio_mux, mdio_mux_syscon_switch_fn, in sun8i_dwmac_register_mdio_mux()
927 &gmac->mux_handle, priv, priv->mii); in sun8i_dwmac_register_mdio_mux()
935 struct sunxi_priv_data *gmac = plat->bsp_priv; in sun8i_dwmac_set_syscon()
936 struct device_node *node = dev->of_node; in sun8i_dwmac_set_syscon()
940 ret = regmap_field_read(gmac->regmap_field, &val); in sun8i_dwmac_set_syscon()
946 reg = gmac->variant->default_syscon_value; in sun8i_dwmac_set_syscon()
952 if (gmac->variant->soc_has_internal_phy) { in sun8i_dwmac_set_syscon()
953 if (of_property_read_bool(node, "allwinner,leds-active-low")) in sun8i_dwmac_set_syscon()
961 ret = of_mdio_parse_addr(dev, plat->phy_node); in sun8i_dwmac_set_syscon()
977 if (!of_property_read_u32(node, "allwinner,tx-delay-ps", &val)) { in sun8i_dwmac_set_syscon()
979 dev_err(dev, "tx-delay must be a multiple of 100\n"); in sun8i_dwmac_set_syscon()
980 return -EINVAL; in sun8i_dwmac_set_syscon()
983 dev_dbg(dev, "set tx-delay to %x\n", val); in sun8i_dwmac_set_syscon()
984 if (val <= gmac->variant->tx_delay_max) { in sun8i_dwmac_set_syscon()
985 reg &= ~(gmac->variant->tx_delay_max << in sun8i_dwmac_set_syscon()
991 return -EINVAL; in sun8i_dwmac_set_syscon()
995 if (!of_property_read_u32(node, "allwinner,rx-delay-ps", &val)) { in sun8i_dwmac_set_syscon()
997 dev_err(dev, "rx-delay must be a multiple of 100\n"); in sun8i_dwmac_set_syscon()
998 return -EINVAL; in sun8i_dwmac_set_syscon()
1001 dev_dbg(dev, "set rx-delay to %x\n", val); in sun8i_dwmac_set_syscon()
1002 if (val <= gmac->variant->rx_delay_max) { in sun8i_dwmac_set_syscon()
1003 reg &= ~(gmac->variant->rx_delay_max << in sun8i_dwmac_set_syscon()
1009 return -EINVAL; in sun8i_dwmac_set_syscon()
1015 if (gmac->variant->support_rmii) in sun8i_dwmac_set_syscon()
1018 switch (plat->mac_interface) { in sun8i_dwmac_set_syscon()
1033 phy_modes(plat->mac_interface)); in sun8i_dwmac_set_syscon()
1034 return -EINVAL; in sun8i_dwmac_set_syscon()
1037 regmap_field_write(gmac->regmap_field, reg); in sun8i_dwmac_set_syscon()
1044 u32 reg = gmac->variant->default_syscon_value; in sun8i_dwmac_unset_syscon()
1046 regmap_field_write(gmac->regmap_field, reg); in sun8i_dwmac_unset_syscon()
1053 if (gmac->variant->soc_has_internal_phy) in sun8i_dwmac_exit()
1056 if (gmac->regulator) in sun8i_dwmac_exit()
1057 regulator_disable(gmac->regulator); in sun8i_dwmac_exit()
1089 mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL); in sun8i_dwmac_setup()
1093 mac->pcsr = priv->ioaddr; in sun8i_dwmac_setup()
1094 mac->mac = &sun8i_dwmac_ops; in sun8i_dwmac_setup()
1095 mac->dma = &sun8i_dwmac_dma_ops; in sun8i_dwmac_setup()
1097 priv->dev->priv_flags |= IFF_UNICAST_FLT; in sun8i_dwmac_setup()
1099 mac->link.caps = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | in sun8i_dwmac_setup()
1101 /* The loopback bit seems to be re-set when link change in sun8i_dwmac_setup()
1105 mac->link.speed_mask = GENMASK(3, 2) | EMAC_LOOPBACK; in sun8i_dwmac_setup()
1106 mac->link.speed10 = EMAC_SPEED_10; in sun8i_dwmac_setup()
1107 mac->link.speed100 = EMAC_SPEED_100; in sun8i_dwmac_setup()
1108 mac->link.speed1000 = EMAC_SPEED_1000; in sun8i_dwmac_setup()
1109 mac->link.duplex = EMAC_DUPLEX_FULL; in sun8i_dwmac_setup()
1110 mac->mii.addr = EMAC_MDIO_CMD; in sun8i_dwmac_setup()
1111 mac->mii.data = EMAC_MDIO_DATA; in sun8i_dwmac_setup()
1112 mac->mii.reg_shift = 4; in sun8i_dwmac_setup()
1113 mac->mii.reg_mask = GENMASK(8, 4); in sun8i_dwmac_setup()
1114 mac->mii.addr_shift = 12; in sun8i_dwmac_setup()
1115 mac->mii.addr_mask = GENMASK(16, 12); in sun8i_dwmac_setup()
1116 mac->mii.clk_csr_shift = 20; in sun8i_dwmac_setup()
1117 mac->mii.clk_csr_mask = GENMASK(22, 20); in sun8i_dwmac_setup()
1118 mac->unicast_filter_entries = 8; in sun8i_dwmac_setup()
1121 priv->synopsys_id = 0; in sun8i_dwmac_setup()
1134 return ERR_PTR(-ENODEV); in sun8i_dwmac_get_syscon_from_dev()
1139 regmap = ERR_PTR(-EPROBE_DEFER); in sun8i_dwmac_get_syscon_from_dev()
1144 regmap = dev_get_regmap(&syscon_pdev->dev, NULL); in sun8i_dwmac_get_syscon_from_dev()
1146 regmap = ERR_PTR(-EINVAL); in sun8i_dwmac_get_syscon_from_dev()
1159 struct device *dev = &pdev->dev; in sun8i_dwmac_probe()
1172 return -ENOMEM; in sun8i_dwmac_probe()
1174 gmac->variant = of_device_get_match_data(&pdev->dev); in sun8i_dwmac_probe()
1175 if (!gmac->variant) { in sun8i_dwmac_probe()
1176 dev_err(&pdev->dev, "Missing dwmac-sun8i variant\n"); in sun8i_dwmac_probe()
1177 return -EINVAL; in sun8i_dwmac_probe()
1181 gmac->regulator = devm_regulator_get_optional(dev, "phy"); in sun8i_dwmac_probe()
1182 if (IS_ERR(gmac->regulator)) { in sun8i_dwmac_probe()
1183 if (PTR_ERR(gmac->regulator) == -EPROBE_DEFER) in sun8i_dwmac_probe()
1184 return -EPROBE_DEFER; in sun8i_dwmac_probe()
1186 gmac->regulator = NULL; in sun8i_dwmac_probe()
1191 * range (on most other sun8i and later SoCs). in sun8i_dwmac_probe()
1206 regmap = sun8i_dwmac_get_syscon_from_dev(pdev->dev.of_node); in sun8i_dwmac_probe()
1208 regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, in sun8i_dwmac_probe()
1212 dev_err(&pdev->dev, "Unable to map syscon: %d\n", ret); in sun8i_dwmac_probe()
1216 gmac->regmap_field = devm_regmap_field_alloc(dev, regmap, in sun8i_dwmac_probe()
1217 *gmac->variant->syscon_field); in sun8i_dwmac_probe()
1218 if (IS_ERR(gmac->regmap_field)) { in sun8i_dwmac_probe()
1219 ret = PTR_ERR(gmac->regmap_field); in sun8i_dwmac_probe()
1224 ret = of_get_phy_mode(dev->of_node, &interface); in sun8i_dwmac_probe()
1226 return -EINVAL; in sun8i_dwmac_probe()
1235 plat_dat->mac_interface = interface; in sun8i_dwmac_probe()
1236 plat_dat->rx_coe = STMMAC_RX_COE_TYPE2; in sun8i_dwmac_probe()
1237 plat_dat->tx_coe = 1; in sun8i_dwmac_probe()
1238 plat_dat->flags |= STMMAC_FLAG_HAS_SUN8I; in sun8i_dwmac_probe()
1239 plat_dat->bsp_priv = gmac; in sun8i_dwmac_probe()
1240 plat_dat->init = sun8i_dwmac_init; in sun8i_dwmac_probe()
1241 plat_dat->exit = sun8i_dwmac_exit; in sun8i_dwmac_probe()
1242 plat_dat->setup = sun8i_dwmac_setup; in sun8i_dwmac_probe()
1243 plat_dat->tx_fifo_size = 4096; in sun8i_dwmac_probe()
1244 plat_dat->rx_fifo_size = 16384; in sun8i_dwmac_probe()
1246 ret = sun8i_dwmac_set_syscon(&pdev->dev, plat_dat); in sun8i_dwmac_probe()
1250 ret = sun8i_dwmac_init(pdev, plat_dat->bsp_priv); in sun8i_dwmac_probe()
1254 ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); in sun8i_dwmac_probe()
1258 ndev = dev_get_drvdata(&pdev->dev); in sun8i_dwmac_probe()
1265 pm_runtime_get_sync(&pdev->dev); in sun8i_dwmac_probe()
1270 if (gmac->variant->soc_has_internal_phy) { in sun8i_dwmac_probe()
1276 dev_err(&pdev->dev, "Failed to register mux\n"); in sun8i_dwmac_probe()
1285 pm_runtime_put(&pdev->dev); in sun8i_dwmac_probe()
1290 reset_control_put(gmac->rst_ephy); in sun8i_dwmac_probe()
1291 clk_put(gmac->ephy_clk); in sun8i_dwmac_probe()
1293 pm_runtime_put_noidle(&pdev->dev); in sun8i_dwmac_probe()
1294 stmmac_dvr_remove(&pdev->dev); in sun8i_dwmac_probe()
1309 struct sunxi_priv_data *gmac = priv->plat->bsp_priv; in sun8i_dwmac_remove()
1311 if (gmac->variant->soc_has_internal_phy) { in sun8i_dwmac_remove()
1312 mdio_mux_uninit(gmac->mux_handle); in sun8i_dwmac_remove()
1314 reset_control_put(gmac->rst_ephy); in sun8i_dwmac_remove()
1315 clk_put(gmac->ephy_clk); in sun8i_dwmac_remove()
1326 struct sunxi_priv_data *gmac = priv->plat->bsp_priv; in sun8i_dwmac_shutdown()
1332 { .compatible = "allwinner,sun8i-h3-emac",
1334 { .compatible = "allwinner,sun8i-v3s-emac",
1336 { .compatible = "allwinner,sun8i-a83t-emac",
1338 { .compatible = "allwinner,sun8i-r40-gmac",
1340 { .compatible = "allwinner,sun50i-a64-emac",
1342 { .compatible = "allwinner,sun50i-h6-emac",
1353 .name = "dwmac-sun8i",
1361 MODULE_DESCRIPTION("Allwinner sun8i DWMAC specific glue layer");