blob: c4eac122c647b36842488729a4dec598649ed2b7 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* dwmac-stm32.c - DWMAC Specific Glue layer for ASR SOC
*
* Copyright (C) ASR Microelectronics 2022
* Author: Fei Lv <feilv@asrmicro.com>.
*/
#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h>
#include <linux/of_net.h>
#include <linux/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_wakeirq.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/stmmac.h>
#include <linux/cputype.h>
#include <soc/asr/regs-addr.h>
#include "stmmac_platform.h"
#define APMU_XGMAC_CLK_RST_CTRL 0x3d4
#define XGMAC_PHY_INTR_EN (0x1 << 12)
#define XGMAC_PHY_LPI_INTR_EN (0x1 << 11)
#define XGMAC_RGMII_TXC_SRC_SEL (0x1 << 8)
#define XGMAC_INTF_SEL_SHIFT 2
#define XGMAC_INTF_SEL_MASK GENMASK(3, XGMAC_INTF_SEL_SHIFT)
#define XGMAC_INTF_SEL_RGMII (0x1 << XGMAC_INTF_SEL_SHIFT)
#define APMU_XGMAC_RGMII_RX_DLINE 0x3d8
#define APMU_XGMAC_RGMII_TX_DLINE 0x3dc
#define APMU_HSIO_RC_SAVE 0x3f8
#define APB_SPARE24_REG 0x15c
/*
* mapping table:
*
* 0x0c_0000 ~ 0x0c_00ac ==> 0xc010_0000 ~ 0xc010_00ac
* 0x0c_1c20 ~ 0x0c_1c40 ==> 0xc010_0120 ~ 0xc010_0140
* 0x0e_0000 ~ 0x0e_0044 ==> 0xc010_0200 ~ 0xc010_0244
* 0x78_0000 ~ 0x78_003c ==> 0xc010_0300 ~ 0xc010_033c
* 0x7c_0000 ~ 0x7c_003c ==> 0xc010_0400 ~ 0xc010_043c
* 0x7e_0000 ~ 0x7e_0028 ==> 0xc010_0500 ~ 0xc010_0528
* 0x80_0800 ~ 0x80_0b00 ==> 0xc010_0800 ~ 0xc010_0b00
* 0xc0_1000 ~ 0xc0_1fff ==> 0xc010_1000 ~ 0xc010_1fff
*/
#define VR_XS_PCS_DIG_CTRL1 0x200
#define VR_XS_PCS_KR_CTRL 0x21c
#define PMA_BASE 0x800
#define SR_MII_CTRL 0x400
#define VR_MII_AN_CTRL 0x504
#define SR_XS_PCS_CTRL2 0x01c
#define SR_XS_PCS_CTRL1 0x000
#define SR_XS_PCS_STS1 0x004
#define VR_XS_PCS_DIG_STS 0x240
#define SR_XS_PCS_TP_CTRL 0x0a8
#define VR_XS_PCS_KR_CTRL 0x21c
#define SR_XS_PCS_TP_ERRCTR 0x0ac
#define VR_XS_PCS_EEE_MCTRL 0x218
struct asr_dwmac {
struct clk *clk_eth_ck;
u32 mode_reg; /* MAC glue-logic mode register */
struct regmap *regmap;
u32 speed;
const struct asr_xgmac_ops *ops;
struct device *dev;
struct plat_stmmacenet_data *plat;
int phy_intr_disable;
struct pinctrl *pinctrl;
struct pinctrl_state *rgmii_pins;
int rst_gpio, ldo_gpio, ldo_gpio2;
/* rgmii clock settings */
int rgmii_tx_clk_soc;
u32 rgmii_tx_delay_code;
u32 rgmii_tx_delay_step;
u32 rgmii_rx_delay_code;
u32 rgmii_rx_delay_step;
};
struct asr_xgmac_ops {
int (*set_mode)(struct plat_stmmacenet_data *plat_dat);
int (*clk_prepare)(struct asr_dwmac *dwmac, bool prepare);
int (*suspend)(struct asr_dwmac *dwmac);
void (*resume)(struct asr_dwmac *dwmac);
int (*parse_data)(struct asr_dwmac *dwmac,
struct device *dev);
};
#if 0
static void asr_xgmac_phy_reg_dump(struct plat_stmmacenet_data *plat_dat)
{
struct asr_dwmac *dwmac = plat_dat->bsp_priv;
int ret, val;
int i;
for (i = 0; i <= 0xac; i = i + 4) {
ret = regmap_read(dwmac->regmap, i, &val);
dev_info(dwmac->dev,"PCS_MMD:0x%x:0x%x\n", 0xC0100000 + i, val);
}
for (i = 0x120; i <= 0x140; i = i + 4) {
ret = regmap_read(dwmac->regmap, i, &val);
dev_info(dwmac->dev,"PCS_MMD:0x%x:0x%x\n", 0xC0100000 + i, val);
}
for (i = 0x200; i <= 0x244; i = i + 4) {
ret = regmap_read(dwmac->regmap, i, &val);
dev_info(dwmac->dev,"PCS_MMD:0x%x:0x%x\n", 0xC0100000 + i, val);
}
for (i = 0x300; i <= 0x33c; i = i + 4) {
ret = regmap_read(dwmac->regmap, i, &val);
dev_info(dwmac->dev,"VS_MMD1:0x%x:0x%x\n", 0xC0100000 + i, val);
}
for (i = 0x400; i <= 0x43c; i = i + 4) {
ret = regmap_read(dwmac->regmap, i, &val);
dev_info(dwmac->dev,"VS_MII_MMD:0x%x:0x%x\n", 0xC0100000 + i, val);
}
for (i = 0x500; i <= 0x528; i = i + 4) {
ret = regmap_read(dwmac->regmap, i, &val);
dev_info(dwmac->dev,"VS_MII_MMD:0x%x:0x%x\n", 0xC0100000 + i, val);
}
for (i = 0x800; i <= 0xb00; i = i + 4) {
ret = regmap_read(dwmac->regmap, i, &val);
dev_info(dwmac->dev,"PMA:0x%x:0x%x\n", 0xC0100000 + i, val);
}
}
#endif
static void asr_dwmac_set_clk_rst(struct plat_stmmacenet_data *plat_dat)
{
void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
u32 val;
val = readl(apmu_base + APMU_XGMAC_CLK_RST_CTRL);
val |= 0x3;
writel(val, apmu_base + APMU_XGMAC_CLK_RST_CTRL);
}
static int asr_dwmac_get_phy_rc(struct plat_stmmacenet_data *plat_dat)
{
void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
u32 val;
(void)plat_dat;
val = readl(apmu_base + APMU_HSIO_RC_SAVE);
return val;
}
static int asr_dwmac_enable_rc_r_cal(struct plat_stmmacenet_data *plat_dat)
{
void __iomem *apb_spare = regs_addr_get_va(REGS_ADDR_APBS);
u32 val;
(void)plat_dat;
val = readl(apb_spare + APB_SPARE24_REG);
if ((val & (0x3 << 14)) == (0x3 << 14))
return 0;
writel(val | BIT(0), apb_spare + APB_SPARE24_REG);
do {
val = readl(apb_spare + APB_SPARE24_REG);
if ((val & (0x3 << 14)) == (0x3 << 14))
break;
} while (1);
return 0;
}
static void asr_dwmac_set_rgmii_rx_dline(struct plat_stmmacenet_data *plat_dat)
{
struct asr_dwmac *dwmac = plat_dat->bsp_priv;
void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
u32 val, dline;
dline = dwmac->rgmii_rx_delay_step;
dline |= (dwmac->rgmii_rx_delay_code << 8);
val = readl(apmu_base + APMU_XGMAC_RGMII_RX_DLINE);
val &= ~(0xff << 8 | 0xff);
val |= dline;
val |= BIT(31);
writel(val, apmu_base + APMU_XGMAC_RGMII_RX_DLINE);
pr_info("==> APMU_XGMAC_RGMII_RX_DLINE: 0x%x rgmii_rx_delay_code=%d\n",
readl(apmu_base + APMU_XGMAC_RGMII_RX_DLINE),
dwmac->rgmii_rx_delay_code);
}
static void asr_dwmac_set_rgmii_tx_dline(struct plat_stmmacenet_data *plat_dat)
{
struct asr_dwmac *dwmac = plat_dat->bsp_priv;
void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
u32 val, dline;
dline = dwmac->rgmii_tx_delay_step;
dline |= (dwmac->rgmii_tx_delay_code << 8);
val = readl(apmu_base + APMU_XGMAC_RGMII_TX_DLINE);
val &= ~(0xff << 8 | 0xff);
val |= dline;
val |= BIT(31);
writel(val, apmu_base + APMU_XGMAC_RGMII_TX_DLINE);
pr_info("==> APMU_XGMAC_RGMII_TX_DLINE: 0x%x rgmii_tx_delay_code=%d\n",
readl(apmu_base + APMU_XGMAC_RGMII_TX_DLINE),
dwmac->rgmii_tx_delay_code);
}
static void asr_dwmac_select_interface(struct plat_stmmacenet_data *plat_dat)
{
struct asr_dwmac *dwmac = plat_dat->bsp_priv;
void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
u32 val;
val = readl(apmu_base + APMU_XGMAC_CLK_RST_CTRL);
val &= ~(XGMAC_INTF_SEL_MASK | XGMAC_RGMII_TXC_SRC_SEL);
/* RGMII settings */
if (plat_dat->interface == PHY_INTERFACE_MODE_RGMII) {
pinctrl_select_state(dwmac->pinctrl, dwmac->rgmii_pins);
val |= XGMAC_INTF_SEL_RGMII;
if (dwmac->rgmii_tx_clk_soc)
val |= XGMAC_RGMII_TXC_SRC_SEL;
asr_dwmac_set_rgmii_rx_dline(plat_dat);
asr_dwmac_set_rgmii_tx_dline(plat_dat);
}
if (dwmac->phy_intr_disable)
val &= ~XGMAC_PHY_INTR_EN;
else
val |= XGMAC_PHY_INTR_EN;
writel(val, apmu_base + APMU_XGMAC_CLK_RST_CTRL);
pr_info("==> APMU_XGMAC_CLK_RST_CTRL: 0x%x\n",
readl(apmu_base + APMU_XGMAC_CLK_RST_CTRL));
}
static int asr_dwmac_init(struct plat_stmmacenet_data *plat_dat)
{
struct asr_dwmac *dwmac = plat_dat->bsp_priv;
int ret;
dwmac->plat = plat_dat;
asr_dwmac_set_clk_rst(plat_dat);
if (dwmac->ops->set_mode) {
ret = dwmac->ops->set_mode(plat_dat);
if (ret)
return ret;
}
if (dwmac->ops->clk_prepare)
ret = dwmac->ops->clk_prepare(dwmac, true);
return ret;
}
static int asr_xgmac_clk_prepare(struct asr_dwmac *dwmac, bool prepare)
{
int ret = 0;
if (!dwmac->clk_eth_ck)
return ret;
if (prepare)
ret = clk_prepare_enable(dwmac->clk_eth_ck);
else
clk_disable_unprepare(dwmac->clk_eth_ck);
return ret;
}
static int asr_xgmac_set_sgmii_2500basex(struct plat_stmmacenet_data *plat_dat)
{
struct asr_dwmac *dwmac = plat_dat->bsp_priv;
int speed = dwmac->speed;
int val, ret;
/* disable USXGMII mode */
ret = regmap_read(dwmac->regmap, VR_XS_PCS_DIG_CTRL1, &val);
val &= ~(0x1 << 9);
ret = regmap_write(dwmac->regmap, VR_XS_PCS_DIG_CTRL1, val);
if (plat_dat->interface == PHY_INTERFACE_MODE_2500BASEX) {
/* Select 10GBASE-X PCS Type */
ret = regmap_read(dwmac->regmap, SR_XS_PCS_CTRL2, &val);
val &= ~0xf;
val |= 0xe;
ret = regmap_write(dwmac->regmap, SR_XS_PCS_CTRL2, val);
} else {
/* Select 10GBASE-X PCS Type */
ret = regmap_read(dwmac->regmap, SR_XS_PCS_CTRL2, &val);
val &= ~0xf;
val |= 0x1;
ret = regmap_write(dwmac->regmap, SR_XS_PCS_CTRL2, val);
/* select SGMII mode */
ret = regmap_read(dwmac->regmap, VR_MII_AN_CTRL, &val);
val &= ~(0x3 << 1);
val |= 0x2 << 1;
ret = regmap_write(dwmac->regmap, VR_MII_AN_CTRL, val);
/* disable rpcs_clk in sgmii mode */
ret = regmap_write(dwmac->regmap, PMA_BASE, 0x15551555);
/* Enable 2.5G GMII Mode if needed */
ret = regmap_read(dwmac->regmap, VR_XS_PCS_DIG_CTRL1, &val);
if (speed == SPEED_2500)
val |= 0x1 << 2;
else
val &= ~(0x1 << 2);
ret = regmap_write(dwmac->regmap, VR_XS_PCS_DIG_CTRL1, val);
}
switch (speed) {
case SPEED_2500:
case SPEED_1000:
/* set 2.5G/1G speed */
ret = regmap_read(dwmac->regmap, SR_MII_CTRL, &val);
val &= ~(0x1 << 13 | 0x1 << 6 | 0x1 << 5);
val |= 0x1 << 6;
ret = regmap_write(dwmac->regmap, SR_MII_CTRL, val);
break;
case SPEED_100:
/* set 100M speed */
ret = regmap_read(dwmac->regmap, SR_MII_CTRL, &val);
val &= ~(0x1 << 13 | 0x1 << 6 | 0x1 << 5);
val |= 0x1 << 13;
ret = regmap_write(dwmac->regmap, SR_MII_CTRL, val);
break;
case SPEED_10:
/* set 10M speed */
ret = regmap_read(dwmac->regmap, SR_MII_CTRL, &val);
val &= ~(0x1 << 13 | 0x1 << 6 | 0x1 << 5);
ret = regmap_write(dwmac->regmap, SR_MII_CTRL, val);
break;
default:
dev_err(dwmac->dev, "unsupported sgmii speed %d\n", speed);
break;
}
/* PCS POWER down */
ret = regmap_read(dwmac->regmap, SR_XS_PCS_CTRL1, &val);
val |= 0x1 << 11;
ret = regmap_write(dwmac->regmap, SR_XS_PCS_CTRL1, val);
if (cpu_is_asr1901()) {
/* get rc value from HSIO RC save register */
val = asr_dwmac_get_phy_rc(plat_dat);
val &= 0xffff;
ret = regmap_write(dwmac->regmap, PMA_BASE + 0x28, val);
} else {
asr_dwmac_enable_rc_r_cal(plat_dat);
}
/* turn off ssc */
ret = regmap_read(dwmac->regmap, PMA_BASE + 0x1c, &val);
val &= ~(0xf << 24);
ret = regmap_write(dwmac->regmap, PMA_BASE + 0x1c, val);
/* wait till PSEQ_STATE are not 3'b100 */
do {
ret = regmap_read(dwmac->regmap, VR_XS_PCS_DIG_STS, &val);
if ((val & (0x7 << 2)) != 0x10)
break;
} while (1);
/* PCS POWER UP */
ret = regmap_read(dwmac->regmap, SR_XS_PCS_CTRL1, &val);
val &= ~(0x1 << 11);
ret = regmap_write(dwmac->regmap, SR_XS_PCS_CTRL1, val);
/* wait PLL lock */
do {
ret = regmap_read(dwmac->regmap, PMA_BASE + 8, &val);
if (val & (0x1 << 24))
break;
} while (1);
dev_info(dwmac->dev,"PLL is locked, phy mode: %s\n",
phy_modes(plat_dat->interface));
/* clear bit15/bit11 to enable pu_tx and rx data */
ret = regmap_write(dwmac->regmap, PMA_BASE + 0x44, 0x0);
return ret;
}
static int asr_xgmac_set_usxgmii(struct plat_stmmacenet_data *plat_dat)
{
struct asr_dwmac *dwmac = plat_dat->bsp_priv;
int speed = dwmac->speed;
int val, ret;
/* disable xgxs_clk for usxgmii mode */
ret = regmap_write(dwmac->regmap, PMA_BASE, 0x55455545);
/* enable USXGMII mode */
ret = regmap_read(dwmac->regmap, VR_XS_PCS_DIG_CTRL1, &val);
val |= 0x1 << 9;
ret = regmap_write(dwmac->regmap, VR_XS_PCS_DIG_CTRL1, val);
switch (speed) {
case SPEED_2500:
/* enable 2.5G-SXGMII */
ret = regmap_read(dwmac->regmap, VR_XS_PCS_KR_CTRL, &val);
val &= ~(0x7 << 10);
val |= 0x2 << 10;
ret = regmap_write(dwmac->regmap, VR_XS_PCS_KR_CTRL, val);
/* set 2.5G speed */
ret = regmap_read(dwmac->regmap, SR_MII_CTRL, &val);
val &= ~(0x1 << 13 | 0x1 << 6 | 0x1 << 5);
val |= 0x1 << 5;
ret = regmap_write(dwmac->regmap, SR_MII_CTRL, val);
break;
case SPEED_5000:
/* enable 5G-SXGMII */
ret = regmap_read(dwmac->regmap, VR_XS_PCS_KR_CTRL, &val);
val &= ~(0x7 << 10);
val |= 0x1 << 10;
ret = regmap_write(dwmac->regmap, VR_XS_PCS_KR_CTRL, val);
/* set 5G speed */
ret = regmap_read(dwmac->regmap, SR_MII_CTRL, &val);
val &= ~(0x1 << 13 | 0x1 << 6 | 0x1 << 5);
val |= 0x1 << 5 | 0x1 << 13;
ret = regmap_write(dwmac->regmap, SR_MII_CTRL, val);
break;
default:
dev_err(dwmac->dev, "unsupported usxgmii speed %d\n", speed);
break;
}
/* force mpu to 0*/
ret = regmap_read(dwmac->regmap, PMA_BASE + 0x44, &val);
val |= 0x1 << 9;
ret = regmap_write(dwmac->regmap, PMA_BASE + 0x44, val);
if (cpu_is_asr1901()) {
/* get rc value from HSIO RC save register */
val = asr_dwmac_get_phy_rc(plat_dat);
val &= 0xffff;
ret = regmap_write(dwmac->regmap, PMA_BASE + 0x28, val);
} else {
asr_dwmac_enable_rc_r_cal(plat_dat);
}
/* release mpu to 1 */
ret = regmap_read(dwmac->regmap, PMA_BASE + 0x44, &val);
val &= ~(0x1 << 9);
ret = regmap_write(dwmac->regmap, PMA_BASE + 0x44, val);
/* do xpcs soft reset to change mode */
ret = regmap_read(dwmac->regmap, VR_XS_PCS_DIG_CTRL1, &val);
val |= 0x1 << 15;
ret = regmap_write(dwmac->regmap, VR_XS_PCS_DIG_CTRL1, val);
/* wait PLL lock */
do {
ret = regmap_read(dwmac->regmap, PMA_BASE + 8, &val);
if (val & (0x1 << 24))
break;
} while (1);
dev_info(dwmac->dev,"USXGMII: pll is locked\n");
/* Enable TX PMA output */
ret = regmap_write(dwmac->regmap, PMA_BASE + 0x44, 0x0);
return ret;
}
static int asr_xgmac_set_mode(struct plat_stmmacenet_data *plat_dat)
{
/* select RGMII or XGMII/GMII */
asr_dwmac_select_interface(plat_dat);
switch (plat_dat->interface) {
case PHY_INTERFACE_MODE_RGMII:
break;
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_2500BASEX:
asr_xgmac_set_sgmii_2500basex(plat_dat);
break;
case PHY_INTERFACE_MODE_USXGMII:
asr_xgmac_set_usxgmii(plat_dat);
break;
default:
pr_debug("SYSCFG init : Do not manage %d interface\n",
plat_dat->interface);
/* Do not manage others interfaces */
return -EINVAL;
}
return 0;
}
static void asr_xgmac_fix_mac_speed(void *priv, unsigned int speed)
{
struct asr_dwmac *dwmac = priv;
struct plat_stmmacenet_data *plat_dat = dwmac->plat;
dwmac->speed = speed;
asr_xgmac_set_mode(plat_dat);
}
static void asr_dwmac_clk_disable(struct asr_dwmac *dwmac)
{
if (dwmac->ops->clk_prepare)
dwmac->ops->clk_prepare(dwmac, false);
}
static int asr_dwmac_parse_data(struct asr_dwmac *dwmac,
struct device *dev)
{
struct device_node *np = dev->of_node;
int err;
if (dwmac->ops->parse_data) {
err = dwmac->ops->parse_data(dwmac, dev);
if (err)
return err;
}
/* Get mode register */
dwmac->regmap = syscon_regmap_lookup_by_phandle(np, "xgmac,phy");
if (IS_ERR(dwmac->regmap))
return PTR_ERR(dwmac->regmap);
err = of_property_read_u32_index(np, "xgmac,phy", 1, &dwmac->mode_reg);
if (err)
dev_err(dev, "Can't get sysconfig mode offset (%d)\n", err);
return err;
}
static int asr_xgmac_parse_data(struct asr_dwmac *dwmac,
struct device *dev)
{
struct device_node *np = dev->of_node;
u32 tx_delay_code, rx_delay_code;
u32 tx_delay_step, rx_delay_step;
dwmac->pinctrl = devm_pinctrl_get(dev);
if (IS_ERR(dwmac->pinctrl))
dev_err(dev, "could not get pinctrl handle\n");
else {
dwmac->rgmii_pins = pinctrl_lookup_state(dwmac->pinctrl, "rgmii");
if (IS_ERR(dwmac->rgmii_pins))
dev_err(dev, "could not get rgmii pinstate\n");
}
dwmac->clk_eth_ck = devm_clk_get(dev, "xgmac-clk");
if (IS_ERR(dwmac->clk_eth_ck)) {
dev_warn(dev, "No phy clock provided...\n");
dwmac->clk_eth_ck = NULL;
}
dwmac->phy_intr_disable =
of_property_read_bool(np, "xgmac,no-phy-intterrupt");
dwmac->rgmii_tx_clk_soc =
of_property_read_bool(np, "xgmac,rgmii-tx-clk-soc");
if (!of_property_read_u32(np, "xgmac,tx-delay", &tx_delay_code)) {
if (tx_delay_code < 255) {
dwmac->rgmii_tx_delay_code = tx_delay_code;
} else {
dev_err(dev, "Invalid TX clock delay: %dps\n",
tx_delay_code);
return -EINVAL;
}
if (!of_property_read_u32(np, "xgmac,tx-delay-step",
&tx_delay_step)) {
if (tx_delay_step < 4) {
dwmac->rgmii_tx_delay_step = tx_delay_step;
} else {
dev_err(dev, "Invalid TX delay step: %dps\n",
tx_delay_step);
return -EINVAL;
}
} else {
dwmac->rgmii_tx_delay_step = 0;
}
} else {
dwmac->rgmii_tx_delay_code = 0;
}
if (!of_property_read_u32(np, "xgmac,rx-delay", &rx_delay_code)) {
if (rx_delay_code < 255) {
dwmac->rgmii_rx_delay_code = rx_delay_code;
} else {
dev_err(dev, "Invalid RX clock delay: %dps\n", rx_delay_code);
return -EINVAL;
}
if (!of_property_read_u32(np, "xgmac,rx-delay-step",
&rx_delay_step)) {
if (rx_delay_step < 4) {
dwmac->rgmii_rx_delay_step = rx_delay_step;
} else {
dev_err(dev, "Invalid RX delay step: %dps\n",
rx_delay_step);
return -EINVAL;
}
} else {
dwmac->rgmii_rx_delay_step = 0;
}
} else {
dwmac->rgmii_rx_delay_code = 0;
}
return 0;
}
static int asr_dwmac_gpio_ctrl(struct asr_dwmac *dwmac,
struct device *dev)
{
struct device_node *np = dev->of_node;
int rst_gpio, ldo_gpio, ldo_gpio2;
int low_active_rst;
u32 delays_rst[3];
int ret;
rst_gpio = of_get_named_gpio(np, "reset-gpio", 0);
if (rst_gpio >= 0) {
low_active_rst = of_property_read_bool(np, "reset-active-low");
of_property_read_u32_array(np, "reset-delays-us", delays_rst, 3);
ret = gpio_request(rst_gpio, "xgmac-reset");
if (ret < 0) {
printk("xgmac: reset-gpio=%d request failed ret=%d\n",
rst_gpio, ret);
return ret;
}
dwmac->rst_gpio = rst_gpio;
}
ldo_gpio = of_get_named_gpio(np, "3v3-ldo-gpio", 0);
if (ldo_gpio >= 0) {
ret = gpio_request(ldo_gpio, "xgmac-ldo-3v3");
if (ret < 0) {
printk("xgmac: xgmac-ldo-3v3=%d request failed ret=%d\n",
ldo_gpio, ret);
return ret;
}
dwmac->ldo_gpio = ldo_gpio;
gpio_direction_output(ldo_gpio, 1);
}
ldo_gpio2 = of_get_named_gpio(np, "0v95-ldo-gpio", 0);
if (ldo_gpio2 >= 0) {
ret = gpio_request(ldo_gpio2, "xgmac-ldo-0v95");
if (ret < 0) {
printk("xgmac: xgmac-ldo-0v95=%d request failed ret=%d\n",
ldo_gpio, ret);
return ret;
}
dwmac->ldo_gpio2 = ldo_gpio2;
gpio_direction_output(ldo_gpio2, 1);
}
/* reset */
if (delays_rst[0]) {
gpio_direction_output(rst_gpio, low_active_rst ? 1 : 0);
msleep(DIV_ROUND_UP(delays_rst[0], 1000));
}
gpio_direction_output(rst_gpio, low_active_rst ? 0 : 1);
if (delays_rst[1])
msleep(DIV_ROUND_UP(delays_rst[1], 1000));
gpio_direction_output(rst_gpio, low_active_rst ? 1 : 0);
if (delays_rst[2])
msleep(DIV_ROUND_UP(delays_rst[2], 1000));
dev_info(dev, "===> gpio init finished\n");
return 0;
}
static int asr_dwmac_probe(struct platform_device *pdev)
{
struct plat_stmmacenet_data *plat_dat;
struct stmmac_resources stmmac_res;
struct asr_dwmac *dwmac;
const struct asr_xgmac_ops *data;
int ret;
ret = stmmac_get_platform_resources(pdev, &stmmac_res);
if (ret)
return ret;
plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
if (IS_ERR(plat_dat))
return PTR_ERR(plat_dat);
dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
if (!dwmac) {
ret = -ENOMEM;
goto err_remove_config_dt;
}
data = of_device_get_match_data(&pdev->dev);
if (!data) {
dev_err(&pdev->dev, "no of match data provided\n");
ret = -EINVAL;
goto err_remove_config_dt;
}
dwmac->ops = data;
dwmac->dev = &pdev->dev;
dwmac->speed = SPEED_1000;
ret = asr_dwmac_parse_data(dwmac, &pdev->dev);
if (ret) {
dev_err(&pdev->dev, "Unable to parse OF data\n");
goto err_remove_config_dt;
}
plat_dat->bsp_priv = dwmac;
plat_dat->fix_mac_speed = asr_xgmac_fix_mac_speed;
ret = asr_dwmac_gpio_ctrl(dwmac, &pdev->dev);
if (ret)
return ret;
ret = asr_dwmac_init(plat_dat);
if (ret)
goto err_remove_config_dt;
#ifdef STMMAC_DIS_SPH
plat_dat->sph_disable = 1;
#endif
ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
if (ret)
goto err_clk_disable;
return 0;
err_clk_disable:
asr_dwmac_clk_disable(dwmac);
err_remove_config_dt:
stmmac_remove_config_dt(pdev, plat_dat);
return ret;
}
static int asr_dwmac_remove(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
struct stmmac_priv *priv = netdev_priv(ndev);
int ret = stmmac_dvr_remove(&pdev->dev);
asr_dwmac_clk_disable(priv->plat->bsp_priv);
return ret;
}
static int asr_xgmac_suspend(struct asr_dwmac *dwmac)
{
int ret = 0;
if (dwmac->clk_eth_ck)
clk_disable_unprepare(dwmac->clk_eth_ck);
return ret;
}
static void asr_xgmac_resume(struct asr_dwmac *dwmac)
{
return;
}
#ifdef CONFIG_PM_SLEEP
static int asr_dwmac_suspend(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev);
struct asr_dwmac *dwmac = priv->plat->bsp_priv;
int ret;
ret = stmmac_suspend(dev);
if (dwmac->ops->suspend)
ret = dwmac->ops->suspend(dwmac);
return ret;
}
static int asr_dwmac_resume(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev);
struct asr_dwmac *dwmac = priv->plat->bsp_priv;
int ret;
if (dwmac->ops->resume)
dwmac->ops->resume(dwmac);
ret = asr_dwmac_init(priv->plat);
if (ret)
return ret;
ret = stmmac_resume(dev);
return ret;
}
#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(asr_dwmac_pm_ops,
asr_dwmac_suspend, asr_dwmac_resume);
static struct asr_xgmac_ops asr_xgmac_dwmac_data = {
.set_mode = asr_xgmac_set_mode,
.clk_prepare = asr_xgmac_clk_prepare,
.suspend = asr_xgmac_suspend,
.resume = asr_xgmac_resume,
.parse_data = asr_xgmac_parse_data,
};
static const struct of_device_id asr_dwmac_match[] = {
{ .compatible = "asr,dwc-xgmac", .data = &asr_xgmac_dwmac_data},
{ }
};
MODULE_DEVICE_TABLE(of, asr_dwmac_match);
static struct platform_driver asr_dwmac_driver = {
.probe = asr_dwmac_probe,
.remove = asr_dwmac_remove,
.driver = {
.name = "asr-xgmac",
.pm = &asr_dwmac_pm_ops,
.of_match_table = asr_dwmac_match,
},
};
module_platform_driver(asr_dwmac_driver);
MODULE_AUTHOR("Fei Lv <feilv@asrmicro.com>");
MODULE_DESCRIPTION("ASR Microelectronics DWMAC Specific Glue layer");
MODULE_LICENSE("GPL v2");