blob: c0b6ef499422cb4360356aad1df46aed76b8ed67 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2021 BayLibre, SAS
*/
#include <common.h>
#include <asm/io.h>
#include <dm.h>
#include <phy.h>
#include "designware.h"
#include <dm/device_compat.h>
#include <linux/err.h>
#define ETH_REG_0 0x0
#define ETH_REG_1 0x4
#define ETH_REG_2 0x18
#define ETH_REG_3 0x1c
#define GX_ETH_REG_0_PHY_INTF BIT(0)
#define GX_ETH_REG_0_TX_PHASE(x) (((x) & 3) << 5)
#define GX_ETH_REG_0_TX_RATIO(x) (((x) & 7) << 7)
#define GX_ETH_REG_0_PHY_CLK_EN BIT(10)
#define GX_ETH_REG_0_INVERT_RMII_CLK BIT(11)
#define GX_ETH_REG_0_CLK_EN BIT(12)
#define AXG_ETH_REG_0_PHY_INTF_RGMII BIT(0)
#define AXG_ETH_REG_0_PHY_INTF_RMII BIT(2)
#define AXG_ETH_REG_0_TX_PHASE(x) (((x) & 3) << 5)
#define AXG_ETH_REG_0_TX_RATIO(x) (((x) & 7) << 7)
#define AXG_ETH_REG_0_PHY_CLK_EN BIT(10)
#define AXG_ETH_REG_0_INVERT_RMII_CLK BIT(11)
#define AXG_ETH_REG_0_CLK_EN BIT(12)
struct dwmac_meson8b_plat {
struct dw_eth_pdata dw_eth_pdata;
int (*dwmac_setup)(struct udevice *dev, struct eth_pdata *edata);
void *regs;
};
static int dwmac_meson8b_of_to_plat(struct udevice *dev)
{
struct dwmac_meson8b_plat *pdata = dev_get_plat(dev);
pdata->regs = (void *)dev_read_addr_index(dev, 1);
if ((fdt_addr_t)pdata->regs == FDT_ADDR_T_NONE)
return -EINVAL;
pdata->dwmac_setup = (void *)dev_get_driver_data(dev);
if (!pdata->dwmac_setup)
return -EINVAL;
return designware_eth_of_to_plat(dev);
}
static int dwmac_setup_axg(struct udevice *dev, struct eth_pdata *edata)
{
struct dwmac_meson8b_plat *plat = dev_get_plat(dev);
switch (edata->phy_interface) {
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_TXID:
/* Set RGMII mode */
setbits_le32(plat->regs + ETH_REG_0, AXG_ETH_REG_0_PHY_INTF_RGMII |
AXG_ETH_REG_0_TX_PHASE(1) |
AXG_ETH_REG_0_TX_RATIO(4) |
AXG_ETH_REG_0_PHY_CLK_EN |
AXG_ETH_REG_0_CLK_EN);
break;
case PHY_INTERFACE_MODE_RMII:
/* Set RMII mode */
out_le32(plat->regs + ETH_REG_0, AXG_ETH_REG_0_PHY_INTF_RMII |
AXG_ETH_REG_0_INVERT_RMII_CLK |
AXG_ETH_REG_0_CLK_EN);
break;
default:
dev_err(dev, "Unsupported PHY mode\n");
return -EINVAL;
}
return 0;
}
static int dwmac_setup_gx(struct udevice *dev, struct eth_pdata *edata)
{
struct dwmac_meson8b_plat *plat = dev_get_plat(dev);
switch (edata->phy_interface) {
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_TXID:
/* Set RGMII mode */
setbits_le32(plat->regs + ETH_REG_0, GX_ETH_REG_0_PHY_INTF |
GX_ETH_REG_0_TX_PHASE(1) |
GX_ETH_REG_0_TX_RATIO(4) |
GX_ETH_REG_0_PHY_CLK_EN |
GX_ETH_REG_0_CLK_EN);
break;
case PHY_INTERFACE_MODE_RMII:
/* Set RMII mode */
out_le32(plat->regs + ETH_REG_0, GX_ETH_REG_0_INVERT_RMII_CLK |
GX_ETH_REG_0_CLK_EN);
if (!IS_ENABLED(CONFIG_MESON_GXBB))
writel(0x10110181, plat->regs + ETH_REG_2);
break;
default:
dev_err(dev, "Unsupported PHY mode\n");
return -EINVAL;
}
return 0;
}
static int dwmac_meson8b_probe(struct udevice *dev)
{
struct dwmac_meson8b_plat *pdata = dev_get_plat(dev);
struct eth_pdata *edata = &pdata->dw_eth_pdata.eth_pdata;
int ret;
ret = pdata->dwmac_setup(dev, edata);
if (ret)
return ret;
return designware_eth_probe(dev);
}
static const struct udevice_id dwmac_meson8b_ids[] = {
{ .compatible = "amlogic,meson-gxbb-dwmac", .data = (ulong)dwmac_setup_gx },
{ .compatible = "amlogic,meson-axg-dwmac", .data = (ulong)dwmac_setup_axg },
{ }
};
U_BOOT_DRIVER(dwmac_meson8b) = {
.name = "dwmac_meson8b",
.id = UCLASS_ETH,
.of_match = dwmac_meson8b_ids,
.of_to_plat = dwmac_meson8b_of_to_plat,
.probe = dwmac_meson8b_probe,
.ops = &designware_eth_ops,
.priv_auto = sizeof(struct dw_eth_dev),
.plat_auto = sizeof(struct dwmac_meson8b_plat),
.flags = DM_FLAG_ALLOC_PRIV_DMA,
};