summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2021-06-16 10:54:37 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2021-06-16 10:54:37 +0200
commit226938796e525f42e002769f21dc66d16ac26f9b (patch)
treed7ef539c99a0da2885c65bdb0885f5570405eb27 /drivers
parent0e062b8296a22afff4157f56c5b6b64e7fac5aac (diff)
parente148f954c3eb025075f0cbf69eab1f3f62459b83 (diff)
downloadbarebox-226938796e525f42e002769f21dc66d16ac26f9b.tar.gz
barebox-226938796e525f42e002769f21dc66d16ac26f9b.tar.xz
Merge branch 'for-next/net'
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpio/gpiolib.c30
-rw-r--r--drivers/net/Kconfig6
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/designware_eqos.c29
-rw-r--r--drivers/net/designware_rockchip.c317
-rw-r--r--drivers/of/of_gpio.c7
6 files changed, 381 insertions, 9 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 7b7261d01f..387b410f12 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -555,27 +555,39 @@ static int of_gpiochip_scan_hogs(struct gpio_chip *chip)
return 0;
}
+static const char *gpio_suffixes[] = {
+ "gpios",
+ "gpio",
+};
+
/* Linux compatibility helper: Get a GPIO descriptor from device tree */
int gpiod_get(struct device_d *dev, const char *_con_id, enum gpiod_flags flags)
{
struct device_node *np = dev->device_node;
enum of_gpio_flags of_flags;
- const char *con_id = "gpios", *label = dev_name(dev);
- char *buf = NULL;
+ const char *label = dev_name(dev);
+ char *buf = NULL, *con_id;
int gpio;
- int ret;
+ int ret, i;
if (!IS_ENABLED(CONFIG_OFDEVICE) || !dev->device_node)
return -ENODEV;
- if (_con_id) {
- con_id = buf = basprintf("%s-gpios", _con_id);
- if (!buf)
+ for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
+ if (_con_id)
+ con_id = basprintf("%s-%s", _con_id, gpio_suffixes[i]);
+ else
+ con_id = basprintf("%s", gpio_suffixes[i]);
+
+ if (!con_id)
return -ENOMEM;
- }
- gpio = of_get_named_gpio_flags(np, con_id, 0, &of_flags);
- free(buf);
+ gpio = of_get_named_gpio_flags(np, con_id, 0, &of_flags);
+ free(con_id);
+
+ if (gpio_is_valid(gpio))
+ break;
+ }
if (!gpio_is_valid(gpio))
return gpio < 0 ? gpio : -EINVAL;
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 0d55ea7a3b..18931211b5 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -108,6 +108,12 @@ config DRIVER_NET_DESIGNWARE_TEGRA186
help
This option enables support for the ethernet MAC on the Tegra186 & 194.
+config DRIVER_NET_DESIGNWARE_ROCKCHIP
+ bool "Designware Universal MAC ethernet driver for Rockchip platforms"
+ select MFD_SYSCON
+ help
+ This option enables support for the ethernet MAC on different Rockchip SoCs
+
endif
config DRIVER_NET_DM9K
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 656d45a868..1674d53dff 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_DRIVER_NET_DESIGNWARE_SOCFPGA) += designware_socfpga.o
obj-$(CONFIG_DRIVER_NET_DESIGNWARE_EQOS) += designware_eqos.o
obj-$(CONFIG_DRIVER_NET_DESIGNWARE_STM32) += designware_stm32.o
obj-$(CONFIG_DRIVER_NET_DESIGNWARE_TEGRA186) += designware_tegra186.o
+obj-$(CONFIG_DRIVER_NET_DESIGNWARE_ROCKCHIP) += designware_rockchip.o
obj-$(CONFIG_DRIVER_NET_DM9K) += dm9k.o
obj-$(CONFIG_DRIVER_NET_E1000) += e1000/regio.o e1000/main.o e1000/eeprom.o
obj-$(CONFIG_DRIVER_NET_ENC28J60) += enc28j60.o
diff --git a/drivers/net/designware_eqos.c b/drivers/net/designware_eqos.c
index d2baaeaf63..f83ec12714 100644
--- a/drivers/net/designware_eqos.c
+++ b/drivers/net/designware_eqos.c
@@ -8,9 +8,12 @@
#include <common.h>
#include <init.h>
+#include <gpio.h>
+#include <gpiod.h>
#include <dma.h>
#include <net.h>
#include <of_net.h>
+#include <of_gpio.h>
#include <linux/iopoll.h>
#include <linux/time.h>
#include <linux/sizes.h>
@@ -189,6 +192,27 @@ struct eqos_desc {
#define MII_BUSY (1 << 0)
+static int eqos_phy_reset(struct device_d *dev, struct eqos *eqos)
+{
+ int phy_reset;
+ u32 delays[3] = { 0, 0, 0 };
+
+ phy_reset = gpiod_get(dev, "snps,reset", GPIOF_OUT_INIT_ACTIVE);
+
+ if (!gpio_is_valid(phy_reset))
+ return 0;
+
+ of_property_read_u32_array(dev->device_node,
+ "snps,reset-delays-us",
+ delays, ARRAY_SIZE(delays));
+
+ udelay(delays[1]);
+ gpio_set_active(phy_reset, false);
+ udelay(delays[2]);
+
+ return 0;
+}
+
static int eqos_mdio_wait_idle(struct eqos *eqos)
{
u32 idle;
@@ -838,11 +862,16 @@ int eqos_probe(struct device_d *dev, const struct eqos_ops *ops, void *priv)
miibus->read = eqos_mdio_read;
miibus->write = eqos_mdio_write;
miibus->priv = eqos;
+ miibus->dev.device_node = of_get_child_by_name(dev->device_node, "mdio");
ret = eqos_init(dev, eqos);
if (ret)
return ret;
+ ret = eqos_phy_reset(dev, eqos);
+ if (ret)
+ return ret;
+
ret = mdiobus_register(miibus);
if (ret)
return ret;
diff --git a/drivers/net/designware_rockchip.c b/drivers/net/designware_rockchip.c
new file mode 100644
index 0000000000..e4f74f646f
--- /dev/null
+++ b/drivers/net/designware_rockchip.c
@@ -0,0 +1,317 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <common.h>
+#include <init.h>
+#include <dma.h>
+#include <net.h>
+#include <regmap.h>
+#include <of_net.h>
+#include <mfd/syscon.h>
+#include <linux/iopoll.h>
+#include <linux/sizes.h>
+#include <linux/time.h>
+#include <linux/clk.h>
+
+#include "designware_eqos.h"
+
+struct rk_gmac_ops {
+ void (*set_to_rgmii)(struct eqos *eqos,
+ int tx_delay, int rx_delay);
+ void (*set_to_rmii)(struct eqos *eqos);
+ void (*set_rmii_speed)(struct eqos *eqos, int speed);
+ void (*set_rgmii_speed)(struct eqos *eqos, int speed);
+ void (*integrated_phy_powerup)(struct eqos *eqos);
+};
+
+struct eqos_rk_gmac {
+ struct clk_bulk_data *clks;
+ int num_clks;
+ bool clock_input;
+ const struct rk_gmac_ops *ops;
+ struct regmap *grf;
+ int bus_id;
+ u32 tx_delay;
+ u32 rx_delay;
+ struct device_d *dev;
+};
+
+enum {
+ CLK_STMMACETH,
+ CLK_MAC_RX,
+ CLK_MAC_TX,
+ CLK_MAC_REFOUT,
+ CLK_MAC_ACLK,
+ CLK_MAC_PCLK,
+ CLK_MAC_SPEED,
+ CLK_PTP_REF,
+ CLK_XPCS_PCLK,
+};
+
+static const struct clk_bulk_data rk_gmac_clks[] = {
+ [CLK_STMMACETH] = { .id = "stmmaceth" },
+ [CLK_MAC_RX] = { .id = "mac_clk_rx" },
+ [CLK_MAC_TX] = { .id = "mac_clk_tx" },
+ [CLK_MAC_REFOUT] = { .id = "clk_mac_refout" },
+ [CLK_MAC_ACLK] = { .id = "aclk_mac" },
+ [CLK_MAC_PCLK] = { .id = "pclk_mac" },
+ [CLK_MAC_SPEED] = { .id = "clk_mac_speed" },
+ [CLK_PTP_REF] = { .id = "ptp_ref" },
+ [CLK_XPCS_PCLK] = { .id = "pclk_xpcs" },
+};
+
+static inline struct eqos_rk_gmac *to_rk_gmac(struct eqos *eqos)
+{
+ return eqos->priv;
+}
+
+#define HIWORD_UPDATE(val, mask, shift) \
+ ((val) << (shift) | (mask) << ((shift) + 16))
+
+#define GRF_BIT(nr) (BIT(nr) | BIT((nr) + 16))
+#define GRF_CLR_BIT(nr) (BIT((nr) + 16))
+
+#define RK3568_GRF_GMAC0_CON0 0x0380
+#define RK3568_GRF_GMAC0_CON1 0x0384
+#define RK3568_GRF_GMAC1_CON0 0x0388
+#define RK3568_GRF_GMAC1_CON1 0x038c
+
+/* RK3568_GRF_GMAC0_CON1 && RK3568_GRF_GMAC1_CON1 */
+#define RK3568_GMAC_PHY_INTF_SEL_RGMII \
+ (GRF_BIT(4) | GRF_CLR_BIT(5) | GRF_CLR_BIT(6))
+#define RK3568_GMAC_PHY_INTF_SEL_RMII \
+ (GRF_CLR_BIT(4) | GRF_CLR_BIT(5) | GRF_BIT(6))
+#define RK3568_GMAC_FLOW_CTRL GRF_BIT(3)
+#define RK3568_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(3)
+#define RK3568_GMAC_RXCLK_DLY_ENABLE GRF_BIT(1)
+#define RK3568_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(1)
+#define RK3568_GMAC_TXCLK_DLY_ENABLE GRF_BIT(0)
+#define RK3568_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(0)
+
+/* RK3568_GRF_GMAC0_CON0 && RK3568_GRF_GMAC1_CON0 */
+#define RK3568_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 8)
+#define RK3568_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0)
+
+static unsigned long eqos_get_csr_clk_rate_rk_gmac(struct eqos *eqos)
+{
+ struct eqos_rk_gmac *priv = to_rk_gmac(eqos);
+
+ return clk_get_rate(priv->clks[CLK_STMMACETH].clk);
+}
+
+static void rk3568_set_to_rgmii(struct eqos *eqos,
+ int tx_delay, int rx_delay)
+{
+ struct eqos_rk_gmac *priv = to_rk_gmac(eqos);
+ struct device_d *dev = priv->dev;
+ u32 offset_con0, offset_con1;
+
+ if (IS_ERR(priv->grf)) {
+ dev_err(dev, "Missing rockchip,grf property\n");
+ return;
+ }
+
+ offset_con0 = (priv->bus_id == 1)
+ ? RK3568_GRF_GMAC1_CON0 : RK3568_GRF_GMAC0_CON0;
+ offset_con1 = (priv->bus_id == 1)
+ ? RK3568_GRF_GMAC1_CON1 : RK3568_GRF_GMAC0_CON1;
+
+ regmap_write(priv->grf, offset_con1,
+ RK3568_GMAC_PHY_INTF_SEL_RGMII |
+ RK3568_GMAC_RXCLK_DLY_ENABLE |
+ RK3568_GMAC_TXCLK_DLY_ENABLE);
+
+ regmap_write(priv->grf, offset_con0,
+ RK3568_GMAC_CLK_RX_DL_CFG(rx_delay) |
+ RK3568_GMAC_CLK_TX_DL_CFG(tx_delay));
+}
+
+static void rk3568_set_to_rmii(struct eqos *eqos)
+{
+ struct eqos_rk_gmac *priv = to_rk_gmac(eqos);
+ struct device_d *dev = priv->dev;
+ u32 offset_con1;
+
+ if (IS_ERR(priv->grf)) {
+ dev_err(dev, "%s: Missing rockchip,grf property\n", __func__);
+ return;
+ }
+
+ offset_con1 = (priv->bus_id == 1)
+ ? RK3568_GRF_GMAC1_CON1 : RK3568_GRF_GMAC0_CON1;
+
+ regmap_write(priv->grf, offset_con1,
+ RK3568_GMAC_PHY_INTF_SEL_RMII);
+}
+
+static void rk3568_set_gmac_speed(struct eqos *eqos, int speed)
+{
+ struct eqos_rk_gmac *priv = to_rk_gmac(eqos);
+ struct device_d *dev = priv->dev;
+ unsigned long rate;
+ int ret;
+
+ switch (speed) {
+ case SPEED_10:
+ rate = 2500000;
+ break;
+ case SPEED_100:
+ rate = 25000000;
+ break;
+ case SPEED_1000:
+ rate = 125000000;
+ break;
+ default:
+ dev_err(dev, "unknown speed value for GMAC speed=%d", speed);
+ return;
+ }
+
+ ret = clk_set_rate(priv->clks[CLK_MAC_SPEED].clk, rate);
+ if (ret)
+ dev_err(dev, "%s: set clk_mac_speed rate %ld failed %d\n",
+ __func__, rate, ret);
+}
+
+static const struct rk_gmac_ops rk3568_ops = {
+ .set_to_rgmii = rk3568_set_to_rgmii,
+ .set_to_rmii = rk3568_set_to_rmii,
+ .set_rmii_speed = rk3568_set_gmac_speed,
+ .set_rgmii_speed = rk3568_set_gmac_speed,
+};
+
+static int rk_gmac_powerup(struct eqos *eqos)
+{
+ struct eqos_rk_gmac *priv = to_rk_gmac(eqos);
+ struct device_d *dev = priv->dev;
+
+ /*rmii or rgmii*/
+ switch (eqos->interface) {
+ case PHY_INTERFACE_MODE_RGMII:
+ dev_dbg(dev, "init for RGMII\n");
+ priv->ops->set_to_rgmii(eqos, priv->tx_delay,
+ priv->rx_delay);
+ break;
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ dev_dbg(dev, "init for RGMII_ID\n");
+ priv->ops->set_to_rgmii(eqos, 0, 0);
+ break;
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ dev_dbg(dev, "init for RGMII_RXID\n");
+ priv->ops->set_to_rgmii(eqos, priv->tx_delay, 0);
+ break;
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ dev_dbg(dev, "init for RGMII_TXID\n");
+ priv->ops->set_to_rgmii(eqos, 0, priv->rx_delay);
+ break;
+ case PHY_INTERFACE_MODE_RMII:
+ dev_dbg(dev, "init for RMII\n");
+ priv->ops->set_to_rmii(eqos);
+ break;
+ default:
+ dev_err(dev, "NO interface defined!\n");
+ }
+
+ return 0;
+}
+
+static void eqos_rk_adjust_link(struct eth_device *edev)
+{
+ struct eqos *eqos = edev->priv;
+ struct eqos_rk_gmac *priv = to_rk_gmac(eqos);
+
+ if (phy_interface_mode_is_rgmii(eqos->interface))
+ priv->ops->set_rgmii_speed(eqos, edev->phydev->speed);
+ else
+ priv->ops->set_rmii_speed(eqos, edev->phydev->speed);
+
+ eqos_adjust_link(edev);
+}
+
+static int eqos_init_rk_gmac(struct device_d *dev, struct eqos *eqos)
+{
+ struct device_node *np = dev->device_node;
+ struct eqos_rk_gmac *priv = to_rk_gmac(eqos);
+ int ret;
+ const char *strings;
+
+ priv->dev = dev;
+
+ ret = of_property_read_string(np, "clock_in_out", &strings);
+ if (ret) {
+ dev_err(dev, "Can not read property: clock_in_out.\n");
+ priv->clock_input = true;
+ } else {
+ dev_dbg(dev, "clock is %s\n", strings);
+ if (!strcmp(strings, "input"))
+ priv->clock_input = true;
+ else
+ priv->clock_input = false;
+ }
+
+ priv->ops = device_get_match_data(dev);
+
+ priv->bus_id = of_alias_get_id(np, "ethernet");
+
+ priv->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+ if (IS_ERR(priv->grf)) {
+ dev_err(dev, "unable to get grf");
+ return PTR_ERR(priv->grf);
+ }
+
+ priv->tx_delay = 0x30;
+ of_property_read_u32(np, "tx_delay", &priv->tx_delay);
+ priv->rx_delay = 0x10;
+ of_property_read_u32(np, "rx_delay", &priv->rx_delay);
+
+ priv->num_clks = ARRAY_SIZE(rk_gmac_clks);
+ priv->clks = xmalloc(priv->num_clks * sizeof(*priv->clks));
+ memcpy(priv->clks, rk_gmac_clks, sizeof rk_gmac_clks);
+
+ ret = clk_bulk_get(dev, priv->num_clks, priv->clks);
+ if (ret) {
+ dev_err(dev, "Failed to get clks: %s\n", strerror(-ret));
+ return ret;
+ }
+
+ ret = clk_bulk_enable(priv->num_clks, priv->clks);
+ if (ret) {
+ dev_err(dev, "Failed to enable clks: %s\n", strerror(-ret));
+ return ret;
+ }
+
+ rk_gmac_powerup(eqos);
+
+ return 0;
+}
+
+static struct eqos_ops rk_gmac_ops = {
+ .init = eqos_init_rk_gmac,
+ .get_ethaddr = eqos_get_ethaddr,
+ .set_ethaddr = eqos_set_ethaddr,
+ .adjust_link = eqos_rk_adjust_link,
+ .get_csr_clk_rate = eqos_get_csr_clk_rate_rk_gmac,
+
+ .clk_csr = EQOS_MDIO_ADDR_CR_250_300,
+ .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_AV,
+};
+
+static int rk_gmac_probe(struct device_d *dev)
+{
+ return eqos_probe(dev, &rk_gmac_ops, xzalloc(sizeof(struct eqos_rk_gmac)));
+}
+
+static __maybe_unused struct of_device_id rk_gmac_compatible[] = {
+ {
+ .compatible = "rockchip,rk3568-gmac",
+ .data = &rk3568_ops,
+ }, {
+ /* sentinel */
+ }
+};
+
+static struct driver_d rk_gmac_driver = {
+ .name = "eqos-rockchip",
+ .probe = rk_gmac_probe,
+ .remove = eqos_remove,
+ .of_compatible = DRV_OF_COMPAT(rk_gmac_compatible),
+};
+device_platform_driver(rk_gmac_driver);
diff --git a/drivers/of/of_gpio.c b/drivers/of/of_gpio.c
index 7cbeeaf69e..e1cafdc848 100644
--- a/drivers/of/of_gpio.c
+++ b/drivers/of/of_gpio.c
@@ -35,6 +35,13 @@ static void of_gpio_flags_quirks(struct device_node *np,
if (active_low)
*flags |= OF_GPIO_ACTIVE_LOW;
}
+
+ /* Legacy handling of stmmac's active-low PHY reset line */
+ if (IS_ENABLED(CONFIG_DRIVER_NET_DESIGNWARE_EQOS) &&
+ !strcmp(propname, "snps,reset-gpio") &&
+ of_property_read_bool(np, "snps,reset-active-low"))
+ *flags |= OF_GPIO_ACTIVE_LOW;
+
}
/**