150 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Adaptrum Anarion DWMAC glue layer4 *5 * Copyright (C) 2017, Adaptrum, Inc.6 * (Written by Alexandru Gagniuc <alex.g at adaptrum.com> for Adaptrum, Inc.)7 */8 9#include <linux/io.h>10#include <linux/of.h>11#include <linux/of_net.h>12#include <linux/stmmac.h>13 14#include "stmmac.h"15#include "stmmac_platform.h"16 17#define GMAC_RESET_CONTROL_REG 018#define GMAC_SW_CONFIG_REG 419#define GMAC_CONFIG_INTF_SEL_MASK (0x7 << 0)20#define GMAC_CONFIG_INTF_RGMII (0x1 << 0)21 22struct anarion_gmac {23 void __iomem *ctl_block;24 uint32_t phy_intf_sel;25};26 27static uint32_t gmac_read_reg(struct anarion_gmac *gmac, uint8_t reg)28{29 return readl(gmac->ctl_block + reg);30};31 32static void gmac_write_reg(struct anarion_gmac *gmac, uint8_t reg, uint32_t val)33{34 writel(val, gmac->ctl_block + reg);35}36 37static int anarion_gmac_init(struct platform_device *pdev, void *priv)38{39 uint32_t sw_config;40 struct anarion_gmac *gmac = priv;41 42 /* Reset logic, configure interface mode, then release reset. SIMPLE! */43 gmac_write_reg(gmac, GMAC_RESET_CONTROL_REG, 1);44 45 sw_config = gmac_read_reg(gmac, GMAC_SW_CONFIG_REG);46 sw_config &= ~GMAC_CONFIG_INTF_SEL_MASK;47 sw_config |= (gmac->phy_intf_sel & GMAC_CONFIG_INTF_SEL_MASK);48 gmac_write_reg(gmac, GMAC_SW_CONFIG_REG, sw_config);49 50 gmac_write_reg(gmac, GMAC_RESET_CONTROL_REG, 0);51 52 return 0;53}54 55static void anarion_gmac_exit(struct platform_device *pdev, void *priv)56{57 struct anarion_gmac *gmac = priv;58 59 gmac_write_reg(gmac, GMAC_RESET_CONTROL_REG, 1);60}61 62static struct anarion_gmac *anarion_config_dt(struct platform_device *pdev)63{64 struct anarion_gmac *gmac;65 phy_interface_t phy_mode;66 void __iomem *ctl_block;67 int err;68 69 ctl_block = devm_platform_ioremap_resource(pdev, 1);70 if (IS_ERR(ctl_block)) {71 err = PTR_ERR(ctl_block);72 dev_err(&pdev->dev, "Cannot get reset region (%d)!\n", err);73 return ERR_PTR(err);74 }75 76 gmac = devm_kzalloc(&pdev->dev, sizeof(*gmac), GFP_KERNEL);77 if (!gmac)78 return ERR_PTR(-ENOMEM);79 80 gmac->ctl_block = ctl_block;81 82 err = of_get_phy_mode(pdev->dev.of_node, &phy_mode);83 if (err)84 return ERR_PTR(err);85 86 switch (phy_mode) {87 case PHY_INTERFACE_MODE_RGMII:88 fallthrough;89 case PHY_INTERFACE_MODE_RGMII_ID:90 case PHY_INTERFACE_MODE_RGMII_RXID:91 case PHY_INTERFACE_MODE_RGMII_TXID:92 gmac->phy_intf_sel = GMAC_CONFIG_INTF_RGMII;93 break;94 default:95 dev_err(&pdev->dev, "Unsupported phy-mode (%d)\n",96 phy_mode);97 return ERR_PTR(-ENOTSUPP);98 }99 100 return gmac;101}102 103static int anarion_dwmac_probe(struct platform_device *pdev)104{105 int ret;106 struct anarion_gmac *gmac;107 struct plat_stmmacenet_data *plat_dat;108 struct stmmac_resources stmmac_res;109 110 ret = stmmac_get_platform_resources(pdev, &stmmac_res);111 if (ret)112 return ret;113 114 gmac = anarion_config_dt(pdev);115 if (IS_ERR(gmac))116 return PTR_ERR(gmac);117 118 plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac);119 if (IS_ERR(plat_dat))120 return PTR_ERR(plat_dat);121 122 plat_dat->init = anarion_gmac_init;123 plat_dat->exit = anarion_gmac_exit;124 anarion_gmac_init(pdev, gmac);125 plat_dat->bsp_priv = gmac;126 127 return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);128}129 130static const struct of_device_id anarion_dwmac_match[] = {131 { .compatible = "adaptrum,anarion-gmac" },132 { }133};134MODULE_DEVICE_TABLE(of, anarion_dwmac_match);135 136static struct platform_driver anarion_dwmac_driver = {137 .probe = anarion_dwmac_probe,138 .remove_new = stmmac_pltfr_remove,139 .driver = {140 .name = "anarion-dwmac",141 .pm = &stmmac_pltfr_pm_ops,142 .of_match_table = anarion_dwmac_match,143 },144};145module_platform_driver(anarion_dwmac_driver);146 147MODULE_DESCRIPTION("Adaptrum Anarion DWMAC specific glue layer");148MODULE_AUTHOR("Alexandru Gagniuc <mr.nuke.me@gmail.com>");149MODULE_LICENSE("GPL v2");150