mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

ls1b_dwmac_syscon_init() was getting the stmmac iomem resource to detect which GMAC block is being used. Move this to a separate setup() function that only runs at probe time, so it can sensibly behave with an unrecognised resource adress. Use this to set a MAC index (id) which is then used in place of testing the base address. Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> Reviewed-by: Simon Horman <horms@kernel.org> Reviewed-by: Keguang Zhang <keguang.zhang@gmail.com> Tested-by: Keguang Zhang <keguang.zhang@gmail.com> # on LS1B & LS1C Link: https://patch.msgid.link/E1uRqEE-004c7M-Go@rmk-PC.armlinux.org.uk Signed-off-by: Jakub Kicinski <kuba@kernel.org>
246 lines
6 KiB
C
246 lines
6 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Loongson-1 DWMAC glue layer
|
|
*
|
|
* Copyright (C) 2011-2023 Keguang Zhang <keguang.zhang@gmail.com>
|
|
*/
|
|
|
|
#include <linux/mfd/syscon.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/phy.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/regmap.h>
|
|
|
|
#include "stmmac.h"
|
|
#include "stmmac_platform.h"
|
|
|
|
#define LS1B_GMAC0_BASE (0x1fe10000)
|
|
#define LS1B_GMAC1_BASE (0x1fe20000)
|
|
|
|
/* Loongson-1 SYSCON Registers */
|
|
#define LS1X_SYSCON0 (0x0)
|
|
#define LS1X_SYSCON1 (0x4)
|
|
|
|
/* Loongson-1B SYSCON Register Bits */
|
|
#define GMAC1_USE_UART1 BIT(4)
|
|
#define GMAC1_USE_UART0 BIT(3)
|
|
|
|
#define GMAC1_SHUT BIT(13)
|
|
#define GMAC0_SHUT BIT(12)
|
|
|
|
#define GMAC1_USE_TXCLK BIT(3)
|
|
#define GMAC0_USE_TXCLK BIT(2)
|
|
#define GMAC1_USE_PWM23 BIT(1)
|
|
#define GMAC0_USE_PWM01 BIT(0)
|
|
|
|
/* Loongson-1C SYSCON Register Bits */
|
|
#define GMAC_SHUT BIT(6)
|
|
|
|
#define PHY_INTF_SELI GENMASK(30, 28)
|
|
#define PHY_INTF_MII FIELD_PREP(PHY_INTF_SELI, 0)
|
|
#define PHY_INTF_RMII FIELD_PREP(PHY_INTF_SELI, 4)
|
|
|
|
struct ls1x_dwmac {
|
|
struct plat_stmmacenet_data *plat_dat;
|
|
struct regmap *regmap;
|
|
unsigned int id;
|
|
};
|
|
|
|
struct ls1x_data {
|
|
int (*setup)(struct platform_device *pdev,
|
|
struct plat_stmmacenet_data *plat_dat);
|
|
int (*init)(struct platform_device *pdev, void *bsp_priv);
|
|
};
|
|
|
|
static int ls1b_dwmac_setup(struct platform_device *pdev,
|
|
struct plat_stmmacenet_data *plat_dat)
|
|
{
|
|
struct ls1x_dwmac *dwmac = plat_dat->bsp_priv;
|
|
struct resource *res;
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!res) {
|
|
/* This shouldn't fail - stmmac_get_platform_resources()
|
|
* already mapped this resource.
|
|
*/
|
|
dev_err(&pdev->dev, "Could not get IO_MEM resources\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (res->start == LS1B_GMAC0_BASE) {
|
|
dwmac->id = 0;
|
|
} else if (res->start == LS1B_GMAC1_BASE) {
|
|
dwmac->id = 1;
|
|
} else {
|
|
dev_err(&pdev->dev, "Invalid Ethernet MAC base address %pR",
|
|
res);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ls1b_dwmac_syscon_init(struct platform_device *pdev, void *priv)
|
|
{
|
|
struct ls1x_dwmac *dwmac = priv;
|
|
struct plat_stmmacenet_data *plat = dwmac->plat_dat;
|
|
struct regmap *regmap = dwmac->regmap;
|
|
|
|
if (dwmac->id == 0) {
|
|
switch (plat->phy_interface) {
|
|
case PHY_INTERFACE_MODE_RGMII_ID:
|
|
regmap_update_bits(regmap, LS1X_SYSCON0,
|
|
GMAC0_USE_TXCLK | GMAC0_USE_PWM01,
|
|
0);
|
|
break;
|
|
case PHY_INTERFACE_MODE_MII:
|
|
regmap_update_bits(regmap, LS1X_SYSCON0,
|
|
GMAC0_USE_TXCLK | GMAC0_USE_PWM01,
|
|
GMAC0_USE_TXCLK | GMAC0_USE_PWM01);
|
|
break;
|
|
default:
|
|
dev_err(&pdev->dev, "Unsupported PHY mode %u\n",
|
|
plat->phy_interface);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
regmap_update_bits(regmap, LS1X_SYSCON0, GMAC0_SHUT, 0);
|
|
} else if (dwmac->id == 1) {
|
|
regmap_update_bits(regmap, LS1X_SYSCON0,
|
|
GMAC1_USE_UART1 | GMAC1_USE_UART0,
|
|
GMAC1_USE_UART1 | GMAC1_USE_UART0);
|
|
|
|
switch (plat->phy_interface) {
|
|
case PHY_INTERFACE_MODE_RGMII_ID:
|
|
regmap_update_bits(regmap, LS1X_SYSCON1,
|
|
GMAC1_USE_TXCLK | GMAC1_USE_PWM23,
|
|
0);
|
|
|
|
break;
|
|
case PHY_INTERFACE_MODE_MII:
|
|
regmap_update_bits(regmap, LS1X_SYSCON1,
|
|
GMAC1_USE_TXCLK | GMAC1_USE_PWM23,
|
|
GMAC1_USE_TXCLK | GMAC1_USE_PWM23);
|
|
break;
|
|
default:
|
|
dev_err(&pdev->dev, "Unsupported PHY mode %u\n",
|
|
plat->phy_interface);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
regmap_update_bits(regmap, LS1X_SYSCON1, GMAC1_SHUT, 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ls1c_dwmac_syscon_init(struct platform_device *pdev, void *priv)
|
|
{
|
|
struct ls1x_dwmac *dwmac = priv;
|
|
struct plat_stmmacenet_data *plat = dwmac->plat_dat;
|
|
struct regmap *regmap = dwmac->regmap;
|
|
|
|
switch (plat->phy_interface) {
|
|
case PHY_INTERFACE_MODE_MII:
|
|
regmap_update_bits(regmap, LS1X_SYSCON1, PHY_INTF_SELI,
|
|
PHY_INTF_MII);
|
|
break;
|
|
case PHY_INTERFACE_MODE_RMII:
|
|
regmap_update_bits(regmap, LS1X_SYSCON1, PHY_INTF_SELI,
|
|
PHY_INTF_RMII);
|
|
break;
|
|
default:
|
|
dev_err(&pdev->dev, "Unsupported PHY-mode %u\n",
|
|
plat->phy_interface);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
regmap_update_bits(regmap, LS1X_SYSCON0, GMAC0_SHUT, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ls1x_dwmac_probe(struct platform_device *pdev)
|
|
{
|
|
struct plat_stmmacenet_data *plat_dat;
|
|
struct stmmac_resources stmmac_res;
|
|
const struct ls1x_data *data;
|
|
struct regmap *regmap;
|
|
struct ls1x_dwmac *dwmac;
|
|
int ret;
|
|
|
|
ret = stmmac_get_platform_resources(pdev, &stmmac_res);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Probe syscon */
|
|
regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
|
|
"loongson,ls1-syscon");
|
|
if (IS_ERR(regmap))
|
|
return dev_err_probe(&pdev->dev, PTR_ERR(regmap),
|
|
"Unable to find syscon\n");
|
|
|
|
data = of_device_get_match_data(&pdev->dev);
|
|
if (!data) {
|
|
dev_err(&pdev->dev, "No of match data provided\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
|
|
if (!dwmac)
|
|
return -ENOMEM;
|
|
|
|
plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac);
|
|
if (IS_ERR(plat_dat))
|
|
return dev_err_probe(&pdev->dev, PTR_ERR(plat_dat),
|
|
"dt configuration failed\n");
|
|
|
|
plat_dat->bsp_priv = dwmac;
|
|
plat_dat->init = data->init;
|
|
dwmac->plat_dat = plat_dat;
|
|
dwmac->regmap = regmap;
|
|
|
|
if (data->setup) {
|
|
ret = data->setup(pdev, plat_dat);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res);
|
|
}
|
|
|
|
static const struct ls1x_data ls1b_dwmac_data = {
|
|
.setup = ls1b_dwmac_setup,
|
|
.init = ls1b_dwmac_syscon_init,
|
|
};
|
|
|
|
static const struct ls1x_data ls1c_dwmac_data = {
|
|
.init = ls1c_dwmac_syscon_init,
|
|
};
|
|
|
|
static const struct of_device_id ls1x_dwmac_match[] = {
|
|
{
|
|
.compatible = "loongson,ls1b-gmac",
|
|
.data = &ls1b_dwmac_data,
|
|
},
|
|
{
|
|
.compatible = "loongson,ls1c-emac",
|
|
.data = &ls1c_dwmac_data,
|
|
},
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, ls1x_dwmac_match);
|
|
|
|
static struct platform_driver ls1x_dwmac_driver = {
|
|
.probe = ls1x_dwmac_probe,
|
|
.driver = {
|
|
.name = "loongson1-dwmac",
|
|
.of_match_table = ls1x_dwmac_match,
|
|
},
|
|
};
|
|
module_platform_driver(ls1x_dwmac_driver);
|
|
|
|
MODULE_AUTHOR("Keguang Zhang <keguang.zhang@gmail.com>");
|
|
MODULE_DESCRIPTION("Loongson-1 DWMAC glue layer");
|
|
MODULE_LICENSE("GPL");
|