|  | // SPDX-License-Identifier: GPL-2.0-or-later | 
|  | /* | 
|  | * Adaptrum Anarion DWMAC glue layer | 
|  | * | 
|  | * Copyright (C) 2017, Adaptrum, Inc. | 
|  | * (Written by Alexandru Gagniuc <alex.g at adaptrum.com> for Adaptrum, Inc.) | 
|  | */ | 
|  |  | 
|  | #include <linux/io.h> | 
|  | #include <linux/of.h> | 
|  | #include <linux/of_net.h> | 
|  | #include <linux/stmmac.h> | 
|  |  | 
|  | #include "stmmac.h" | 
|  | #include "stmmac_platform.h" | 
|  |  | 
|  | #define GMAC_RESET_CONTROL_REG		0 | 
|  | #define GMAC_SW_CONFIG_REG		4 | 
|  | #define  GMAC_CONFIG_INTF_SEL_MASK	(0x7 << 0) | 
|  | #define  GMAC_CONFIG_INTF_RGMII		(0x1 << 0) | 
|  |  | 
|  | struct anarion_gmac { | 
|  | uintptr_t ctl_block; | 
|  | uint32_t phy_intf_sel; | 
|  | }; | 
|  |  | 
|  | static uint32_t gmac_read_reg(struct anarion_gmac *gmac, uint8_t reg) | 
|  | { | 
|  | return readl((void *)(gmac->ctl_block + reg)); | 
|  | }; | 
|  |  | 
|  | static void gmac_write_reg(struct anarion_gmac *gmac, uint8_t reg, uint32_t val) | 
|  | { | 
|  | writel(val, (void *)(gmac->ctl_block + reg)); | 
|  | } | 
|  |  | 
|  | static int anarion_gmac_init(struct platform_device *pdev, void *priv) | 
|  | { | 
|  | uint32_t sw_config; | 
|  | struct anarion_gmac *gmac = priv; | 
|  |  | 
|  | /* Reset logic, configure interface mode, then release reset. SIMPLE! */ | 
|  | gmac_write_reg(gmac, GMAC_RESET_CONTROL_REG, 1); | 
|  |  | 
|  | sw_config = gmac_read_reg(gmac, GMAC_SW_CONFIG_REG); | 
|  | sw_config &= ~GMAC_CONFIG_INTF_SEL_MASK; | 
|  | sw_config |= (gmac->phy_intf_sel & GMAC_CONFIG_INTF_SEL_MASK); | 
|  | gmac_write_reg(gmac, GMAC_SW_CONFIG_REG, sw_config); | 
|  |  | 
|  | gmac_write_reg(gmac, GMAC_RESET_CONTROL_REG, 0); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void anarion_gmac_exit(struct platform_device *pdev, void *priv) | 
|  | { | 
|  | struct anarion_gmac *gmac = priv; | 
|  |  | 
|  | gmac_write_reg(gmac, GMAC_RESET_CONTROL_REG, 1); | 
|  | } | 
|  |  | 
|  | static struct anarion_gmac *anarion_config_dt(struct platform_device *pdev) | 
|  | { | 
|  | int phy_mode; | 
|  | struct resource *res; | 
|  | void __iomem *ctl_block; | 
|  | struct anarion_gmac *gmac; | 
|  |  | 
|  | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | 
|  | ctl_block = devm_ioremap_resource(&pdev->dev, res); | 
|  | if (IS_ERR(ctl_block)) { | 
|  | dev_err(&pdev->dev, "Cannot get reset region (%ld)!\n", | 
|  | PTR_ERR(ctl_block)); | 
|  | return ctl_block; | 
|  | } | 
|  |  | 
|  | gmac = devm_kzalloc(&pdev->dev, sizeof(*gmac), GFP_KERNEL); | 
|  | if (!gmac) | 
|  | return ERR_PTR(-ENOMEM); | 
|  |  | 
|  | gmac->ctl_block = (uintptr_t)ctl_block; | 
|  |  | 
|  | phy_mode = of_get_phy_mode(pdev->dev.of_node); | 
|  | switch (phy_mode) { | 
|  | case PHY_INTERFACE_MODE_RGMII:		/* Fall through */ | 
|  | case PHY_INTERFACE_MODE_RGMII_ID	/* Fall through */: | 
|  | case PHY_INTERFACE_MODE_RGMII_RXID:	/* Fall through */ | 
|  | case PHY_INTERFACE_MODE_RGMII_TXID: | 
|  | gmac->phy_intf_sel = GMAC_CONFIG_INTF_RGMII; | 
|  | break; | 
|  | default: | 
|  | dev_err(&pdev->dev, "Unsupported phy-mode (%d)\n", | 
|  | phy_mode); | 
|  | return ERR_PTR(-ENOTSUPP); | 
|  | } | 
|  |  | 
|  | return gmac; | 
|  | } | 
|  |  | 
|  | static int anarion_dwmac_probe(struct platform_device *pdev) | 
|  | { | 
|  | int ret; | 
|  | struct anarion_gmac *gmac; | 
|  | struct plat_stmmacenet_data *plat_dat; | 
|  | struct stmmac_resources stmmac_res; | 
|  |  | 
|  | ret = stmmac_get_platform_resources(pdev, &stmmac_res); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | gmac = anarion_config_dt(pdev); | 
|  | if (IS_ERR(gmac)) | 
|  | return PTR_ERR(gmac); | 
|  |  | 
|  | plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); | 
|  | if (IS_ERR(plat_dat)) | 
|  | return PTR_ERR(plat_dat); | 
|  |  | 
|  | plat_dat->init = anarion_gmac_init; | 
|  | plat_dat->exit = anarion_gmac_exit; | 
|  | anarion_gmac_init(pdev, gmac); | 
|  | plat_dat->bsp_priv = gmac; | 
|  |  | 
|  | ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); | 
|  | if (ret) { | 
|  | stmmac_remove_config_dt(pdev, plat_dat); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct of_device_id anarion_dwmac_match[] = { | 
|  | { .compatible = "adaptrum,anarion-gmac" }, | 
|  | { } | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(of, anarion_dwmac_match); | 
|  |  | 
|  | static struct platform_driver anarion_dwmac_driver = { | 
|  | .probe  = anarion_dwmac_probe, | 
|  | .remove = stmmac_pltfr_remove, | 
|  | .driver = { | 
|  | .name           = "anarion-dwmac", | 
|  | .pm		= &stmmac_pltfr_pm_ops, | 
|  | .of_match_table = anarion_dwmac_match, | 
|  | }, | 
|  | }; | 
|  | module_platform_driver(anarion_dwmac_driver); | 
|  |  | 
|  | MODULE_DESCRIPTION("Adaptrum Anarion DWMAC specific glue layer"); | 
|  | MODULE_AUTHOR("Alexandru Gagniuc <[email protected]>"); | 
|  | MODULE_LICENSE("GPL v2"); |