mirror of
https://github.com/u-boot/u-boot.git
synced 2025-04-29 17:55:48 +00:00

ICSSG firmware supports FDB commands. Add support to send FDB commands from driver. Once rx_flow_id is obtained from dma, let firmware know that we are using this rx_flow_id by sending a FDB command. Reviewed-by: Ravi Gunasekaran <r-gunasekaran@ti.com> Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
691 lines
17 KiB
C
691 lines
17 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Texas Instruments K3 AM65 PRU Ethernet Driver
|
|
*
|
|
* Copyright (C) 2018-2024 Texas Instruments Incorporated - https://www.ti.com/
|
|
*
|
|
*/
|
|
|
|
#include <asm/io.h>
|
|
#include <asm/processor.h>
|
|
#include <clk.h>
|
|
#include <dm/lists.h>
|
|
#include <dm/device.h>
|
|
#include <dma-uclass.h>
|
|
#include <dm/of_access.h>
|
|
#include <dm/pinctrl.h>
|
|
#include <fs_loader.h>
|
|
#include <miiphy.h>
|
|
#include <net.h>
|
|
#include <phy.h>
|
|
#include <power-domain.h>
|
|
#include <linux/soc/ti/ti-udma.h>
|
|
#include <regmap.h>
|
|
#include <remoteproc.h>
|
|
#include <syscon.h>
|
|
#include <soc.h>
|
|
#include <linux/pruss_driver.h>
|
|
#include <dm/device_compat.h>
|
|
|
|
#include "icssg_prueth.h"
|
|
#include "icss_mii_rt.h"
|
|
|
|
#define ICSS_SLICE0 0
|
|
#define ICSS_SLICE1 1
|
|
|
|
#ifdef PKTSIZE_ALIGN
|
|
#define UDMA_RX_BUF_SIZE PKTSIZE_ALIGN
|
|
#else
|
|
#define UDMA_RX_BUF_SIZE ALIGN(PKTSIZE, ARCH_DMA_MINALIGN)
|
|
#endif
|
|
|
|
#ifdef PKTBUFSRX
|
|
#define UDMA_RX_DESC_NUM PKTBUFSRX
|
|
#else
|
|
#define UDMA_RX_DESC_NUM 4
|
|
#endif
|
|
|
|
/* Config region lies in shared RAM */
|
|
#define ICSS_CONFIG_OFFSET_SLICE0 0
|
|
#define ICSS_CONFIG_OFFSET_SLICE1 0x8000
|
|
|
|
/* Firmware flags */
|
|
#define ICSS_SET_RUN_FLAG_VLAN_ENABLE BIT(0) /* switch only */
|
|
#define ICSS_SET_RUN_FLAG_FLOOD_UNICAST BIT(1) /* switch only */
|
|
#define ICSS_SET_RUN_FLAG_PROMISC BIT(2) /* MAC only */
|
|
#define ICSS_SET_RUN_FLAG_MULTICAST_PROMISC BIT(3) /* MAC only */
|
|
|
|
/* CTRLMMR_ICSSG_RGMII_CTRL register bits */
|
|
#define ICSSG_CTRL_RGMII_ID_MODE BIT(24)
|
|
|
|
/* Management packet type */
|
|
#define PRUETH_PKT_TYPE_CMD 0x10
|
|
|
|
/* Number of PRU Cores per Slice */
|
|
#define ICSSG_NUM_PRU_CORES 3
|
|
|
|
static int icssg_gmii_select(struct prueth_priv *priv)
|
|
{
|
|
struct phy_device *phydev = priv->phydev;
|
|
|
|
if (phydev->interface != PHY_INTERFACE_MODE_MII &&
|
|
phydev->interface < PHY_INTERFACE_MODE_RGMII &&
|
|
phydev->interface > PHY_INTERFACE_MODE_RGMII_TXID) {
|
|
dev_err(priv->dev, "PHY mode unsupported %s\n",
|
|
phy_string_for_interface(phydev->interface));
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* AM65 SR2.0 has TX Internal delay always enabled by hardware
|
|
* and it is not possible to disable TX Internal delay. The below
|
|
* switch case block describes how we handle different phy modes
|
|
* based on hardware restriction.
|
|
*/
|
|
switch (phydev->interface) {
|
|
case PHY_INTERFACE_MODE_RGMII_ID:
|
|
phydev->interface = PHY_INTERFACE_MODE_RGMII_RXID;
|
|
break;
|
|
case PHY_INTERFACE_MODE_RGMII_TXID:
|
|
phydev->interface = PHY_INTERFACE_MODE_RGMII;
|
|
break;
|
|
case PHY_INTERFACE_MODE_RGMII:
|
|
case PHY_INTERFACE_MODE_RGMII_RXID:
|
|
dev_err(priv->dev, "RGMII mode without TX delay is not supported");
|
|
return -EINVAL;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int icssg_phy_init(struct udevice *dev)
|
|
{
|
|
struct prueth_priv *priv = dev_get_priv(dev);
|
|
struct phy_device *phydev;
|
|
u32 supported = PHY_GBIT_FEATURES;
|
|
int ret;
|
|
|
|
phydev = dm_eth_phy_connect(dev);
|
|
if (!phydev) {
|
|
dev_err(dev, "phy_connect() failed\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* disable unsupported features */
|
|
supported &= ~(PHY_10BT_FEATURES |
|
|
SUPPORTED_100baseT_Half |
|
|
SUPPORTED_1000baseT_Half |
|
|
SUPPORTED_Pause |
|
|
SUPPORTED_Asym_Pause);
|
|
|
|
phydev->supported &= supported;
|
|
phydev->advertising = phydev->supported;
|
|
priv->phydev = phydev;
|
|
|
|
ret = icssg_gmii_select(priv);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = phy_config(phydev);
|
|
if (ret < 0)
|
|
dev_err(dev, "phy_config() failed: %d", ret);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static void icssg_config_set_speed(struct prueth_priv *priv, int speed)
|
|
{
|
|
struct prueth *prueth = priv->prueth;
|
|
u8 fw_speed;
|
|
|
|
switch (speed) {
|
|
case SPEED_1000:
|
|
fw_speed = FW_LINK_SPEED_1G;
|
|
break;
|
|
case SPEED_100:
|
|
fw_speed = FW_LINK_SPEED_100M;
|
|
break;
|
|
case SPEED_10:
|
|
fw_speed = FW_LINK_SPEED_10M;
|
|
break;
|
|
default:
|
|
/* Other links speeds not supported */
|
|
dev_err(priv->dev, "Unsupported link speed\n");
|
|
return;
|
|
}
|
|
|
|
writeb(fw_speed, prueth->dram[priv->port_id].pa + PORT_LINK_SPEED_OFFSET);
|
|
}
|
|
|
|
static int icssg_update_link(struct prueth_priv *priv)
|
|
{
|
|
struct phy_device *phy = priv->phydev;
|
|
struct prueth *prueth = priv->prueth;
|
|
bool gig_en = false, full_duplex = false;
|
|
|
|
if (phy->link) { /* link up */
|
|
if (phy->speed == SPEED_1000)
|
|
gig_en = true;
|
|
if (phy->duplex == DUPLEX_FULL)
|
|
full_duplex = true;
|
|
/* Set the RGMII cfg for gig en and full duplex */
|
|
icssg_update_rgmii_cfg(prueth->miig_rt, phy->speed, full_duplex,
|
|
priv->port_id, priv);
|
|
/* update the Tx IPG based on 100M/1G speed */
|
|
icssg_config_ipg(priv, phy->speed, priv->port_id);
|
|
|
|
/* Send command to firmware to update Speed setting */
|
|
icssg_config_set_speed(priv, phy->speed);
|
|
|
|
/* Enable PORT FORWARDING */
|
|
emac_set_port_state(priv, ICSSG_EMAC_PORT_FORWARD);
|
|
|
|
printf("link up on port %d, speed %d, %s duplex\n",
|
|
priv->port_id, phy->speed,
|
|
(phy->duplex == DUPLEX_FULL) ? "full" : "half");
|
|
} else {
|
|
emac_set_port_state(priv, ICSSG_EMAC_PORT_DISABLE);
|
|
printf("link down on port %d\n", priv->port_id);
|
|
}
|
|
|
|
return phy->link;
|
|
}
|
|
|
|
struct icssg_firmwares {
|
|
char *pru;
|
|
char *rtu;
|
|
char *txpru;
|
|
};
|
|
|
|
static struct icssg_firmwares icssg_emac_firmwares[] = {
|
|
{
|
|
.pru = "/lib/firmware/ti-pruss/am65x-sr2-pru0-prueth-fw.elf",
|
|
.rtu = "/lib/firmware/ti-pruss/am65x-sr2-rtu0-prueth-fw.elf",
|
|
.txpru = "/lib/firmware/ti-pruss/am65x-sr2-txpru0-prueth-fw.elf",
|
|
},
|
|
{
|
|
.pru = "/lib/firmware/ti-pruss/am65x-sr2-pru1-prueth-fw.elf",
|
|
.rtu = "/lib/firmware/ti-pruss/am65x-sr2-rtu1-prueth-fw.elf",
|
|
.txpru = "/lib/firmware/ti-pruss/am65x-sr2-txpru1-prueth-fw.elf",
|
|
}
|
|
};
|
|
|
|
static int icssg_start_pru_cores(struct udevice *dev)
|
|
{
|
|
struct prueth_priv *priv = dev_get_priv(dev);
|
|
struct prueth *prueth = priv->prueth;
|
|
struct icssg_firmwares *firmwares;
|
|
struct udevice *rproc_dev = NULL;
|
|
int ret, slice;
|
|
u32 phandle;
|
|
u8 index;
|
|
|
|
slice = priv->port_id;
|
|
index = slice * ICSSG_NUM_PRU_CORES;
|
|
firmwares = icssg_emac_firmwares;
|
|
|
|
ofnode_read_u32_index(dev_ofnode(prueth->dev), "ti,prus", index, &phandle);
|
|
ret = uclass_get_device_by_phandle_id(UCLASS_REMOTEPROC, phandle, &rproc_dev);
|
|
if (ret) {
|
|
dev_err(dev, "Unknown remote processor with phandle '0x%x' requested(%d)\n",
|
|
phandle, ret);
|
|
return ret;
|
|
}
|
|
|
|
prueth->pru_core_id = dev_seq(rproc_dev);
|
|
ret = rproc_set_firmware(rproc_dev, firmwares[slice].pru);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = rproc_boot(rproc_dev);
|
|
if (ret) {
|
|
dev_err(dev, "failed to boot PRU%d: %d\n", slice, ret);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ofnode_read_u32_index(dev_ofnode(prueth->dev), "ti,prus", index + 1, &phandle);
|
|
ret = uclass_get_device_by_phandle_id(UCLASS_REMOTEPROC, phandle, &rproc_dev);
|
|
if (ret) {
|
|
dev_err(dev, "Unknown remote processor with phandle '0x%x' requested(%d)\n",
|
|
phandle, ret);
|
|
goto halt_pru;
|
|
}
|
|
|
|
prueth->rtu_core_id = dev_seq(rproc_dev);
|
|
ret = rproc_set_firmware(rproc_dev, firmwares[slice].rtu);
|
|
if (ret)
|
|
goto halt_pru;
|
|
|
|
ret = rproc_boot(rproc_dev);
|
|
if (ret) {
|
|
dev_err(dev, "failed to boot RTU%d: %d\n", slice, ret);
|
|
goto halt_pru;
|
|
}
|
|
|
|
ofnode_read_u32_index(dev_ofnode(prueth->dev), "ti,prus", index + 2, &phandle);
|
|
ret = uclass_get_device_by_phandle_id(UCLASS_REMOTEPROC, phandle, &rproc_dev);
|
|
if (ret) {
|
|
dev_err(dev, "Unknown remote processor with phandle '0x%x' requested(%d)\n",
|
|
phandle, ret);
|
|
goto halt_rtu;
|
|
}
|
|
|
|
prueth->txpru_core_id = dev_seq(rproc_dev);
|
|
ret = rproc_set_firmware(rproc_dev, firmwares[slice].txpru);
|
|
if (ret)
|
|
goto halt_rtu;
|
|
|
|
ret = rproc_boot(rproc_dev);
|
|
if (ret) {
|
|
dev_err(dev, "failed to boot TXPRU%d: %d\n", slice, ret);
|
|
goto halt_rtu;
|
|
}
|
|
|
|
return 0;
|
|
|
|
halt_rtu:
|
|
rproc_stop(prueth->rtu_core_id);
|
|
|
|
halt_pru:
|
|
rproc_stop(prueth->pru_core_id);
|
|
return ret;
|
|
}
|
|
|
|
static int icssg_stop_pru_cores(struct udevice *dev)
|
|
{
|
|
struct prueth_priv *priv = dev_get_priv(dev);
|
|
struct prueth *prueth = priv->prueth;
|
|
|
|
rproc_stop(prueth->pru_core_id);
|
|
rproc_stop(prueth->rtu_core_id);
|
|
rproc_stop(prueth->txpru_core_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int prueth_start(struct udevice *dev)
|
|
{
|
|
struct ti_udma_drv_chan_cfg_data *dma_rx_cfg_data;
|
|
struct eth_pdata *pdata = dev_get_plat(dev);
|
|
struct prueth_priv *priv = dev_get_priv(dev);
|
|
struct prueth *prueth = priv->prueth;
|
|
struct icssg_flow_cfg *flow_cfg;
|
|
u8 *hwaddr = pdata->enetaddr;
|
|
char chn_name[16];
|
|
void *config;
|
|
int ret, i;
|
|
|
|
icssg_class_set_mac_addr(prueth->miig_rt, priv->port_id, hwaddr);
|
|
icssg_ft1_set_mac_addr(prueth->miig_rt, priv->port_id, hwaddr);
|
|
icssg_class_default(prueth->miig_rt, priv->port_id, 0);
|
|
|
|
/* Set Load time configuration */
|
|
icssg_config(priv);
|
|
|
|
ret = icssg_start_pru_cores(dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* To differentiate channels for SLICE0 vs SLICE1 */
|
|
snprintf(chn_name, sizeof(chn_name), "tx%d-0", priv->port_id);
|
|
|
|
ret = dma_get_by_name(prueth->dev, chn_name, &prueth->dma_tx);
|
|
if (ret)
|
|
dev_err(dev, "TX dma get failed %d\n", ret);
|
|
|
|
snprintf(chn_name, sizeof(chn_name), "rx%d", priv->port_id);
|
|
ret = dma_get_by_name(prueth->dev, chn_name, &prueth->dma_rx);
|
|
if (ret)
|
|
dev_err(dev, "RX dma get failed %d\n", ret);
|
|
|
|
for (i = 0; i < UDMA_RX_DESC_NUM; i++) {
|
|
ret = dma_prepare_rcv_buf(&prueth->dma_rx,
|
|
net_rx_packets[i],
|
|
UDMA_RX_BUF_SIZE);
|
|
if (ret)
|
|
dev_err(dev, "RX dma add buf failed %d\n", ret);
|
|
}
|
|
|
|
ret = dma_enable(&prueth->dma_tx);
|
|
if (ret) {
|
|
dev_err(dev, "TX dma_enable failed %d\n", ret);
|
|
goto tx_fail;
|
|
}
|
|
|
|
ret = dma_enable(&prueth->dma_rx);
|
|
if (ret) {
|
|
dev_err(dev, "RX dma_enable failed %d\n", ret);
|
|
goto rx_fail;
|
|
}
|
|
|
|
/* check if the rx_flow_id of dma_rx is as expected since
|
|
* driver hardcode that value in config struct to firmware
|
|
* in probe. Just add this sanity check to catch any change
|
|
* to rx channel assignment in the future.
|
|
*/
|
|
dma_get_cfg(&prueth->dma_rx, 0, (void **)&dma_rx_cfg_data);
|
|
config = (void *)(prueth->dram[priv->port_id].pa + ICSSG_CONFIG_OFFSET);
|
|
|
|
flow_cfg = config + PSI_L_REGULAR_FLOW_ID_BASE_OFFSET;
|
|
writew(dma_rx_cfg_data->flow_id_base, &flow_cfg->rx_base_flow);
|
|
writew(0, &flow_cfg->mgm_base_flow);
|
|
|
|
dev_info(dev, "K3 ICSSG: rflow_id_base: %u, chn_name = %s\n",
|
|
dma_rx_cfg_data->flow_id_base, chn_name);
|
|
|
|
ret = emac_fdb_flow_id_updated(priv);
|
|
if (ret) {
|
|
dev_err(dev, "Failed to update Rx Flow ID %d", ret);
|
|
goto phy_fail;
|
|
}
|
|
|
|
ret = phy_startup(priv->phydev);
|
|
if (ret) {
|
|
dev_err(dev, "phy_startup failed\n");
|
|
goto phy_fail;
|
|
}
|
|
|
|
ret = icssg_update_link(priv);
|
|
if (!ret) {
|
|
ret = -ENODEV;
|
|
goto phy_shut;
|
|
}
|
|
|
|
return 0;
|
|
|
|
phy_shut:
|
|
phy_shutdown(priv->phydev);
|
|
phy_fail:
|
|
dma_disable(&prueth->dma_rx);
|
|
dma_free(&prueth->dma_rx);
|
|
rx_fail:
|
|
dma_disable(&prueth->dma_tx);
|
|
dma_free(&prueth->dma_tx);
|
|
|
|
tx_fail:
|
|
icssg_class_disable(prueth->miig_rt, priv->port_id);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int prueth_send(struct udevice *dev, void *packet, int length)
|
|
{
|
|
struct prueth_priv *priv = dev_get_priv(dev);
|
|
struct prueth *prueth = priv->prueth;
|
|
int ret;
|
|
|
|
ret = dma_send(&prueth->dma_tx, packet, length, NULL);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int prueth_recv(struct udevice *dev, int flags, uchar **packetp)
|
|
{
|
|
struct prueth_priv *priv = dev_get_priv(dev);
|
|
struct prueth *prueth = priv->prueth;
|
|
int ret;
|
|
|
|
/* try to receive a new packet */
|
|
ret = dma_receive(&prueth->dma_rx, (void **)packetp, NULL);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int prueth_free_pkt(struct udevice *dev, uchar *packet, int length)
|
|
{
|
|
struct prueth_priv *priv = dev_get_priv(dev);
|
|
struct prueth *prueth = priv->prueth;
|
|
int ret = 0;
|
|
|
|
if (length > 0) {
|
|
u32 pkt = prueth->rx_next % UDMA_RX_DESC_NUM;
|
|
|
|
dev_dbg(dev, "%s length:%d pkt:%u\n", __func__, length, pkt);
|
|
|
|
ret = dma_prepare_rcv_buf(&prueth->dma_rx,
|
|
net_rx_packets[pkt],
|
|
UDMA_RX_BUF_SIZE);
|
|
prueth->rx_next++;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void prueth_stop(struct udevice *dev)
|
|
{
|
|
struct prueth_priv *priv = dev_get_priv(dev);
|
|
struct prueth *prueth = priv->prueth;
|
|
|
|
phy_shutdown(priv->phydev);
|
|
|
|
dma_disable(&prueth->dma_tx);
|
|
dma_disable(&prueth->dma_rx);
|
|
|
|
icssg_stop_pru_cores(dev);
|
|
|
|
dma_free(&prueth->dma_tx);
|
|
dma_free(&prueth->dma_rx);
|
|
}
|
|
|
|
static const struct eth_ops prueth_ops = {
|
|
.start = prueth_start,
|
|
.send = prueth_send,
|
|
.recv = prueth_recv,
|
|
.free_pkt = prueth_free_pkt,
|
|
.stop = prueth_stop,
|
|
};
|
|
|
|
static int icssg_ofdata_parse_phy(struct udevice *dev)
|
|
{
|
|
struct prueth_priv *priv = dev_get_priv(dev);
|
|
|
|
dev_read_u32(dev, "reg", &priv->port_id);
|
|
priv->phy_interface = dev_read_phy_mode(dev);
|
|
if (priv->phy_interface == PHY_INTERFACE_MODE_NA) {
|
|
dev_err(dev, "Invalid PHY mode '%s', port %u\n",
|
|
phy_string_for_interface(priv->phy_interface),
|
|
priv->port_id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int prueth_port_probe(struct udevice *dev)
|
|
{
|
|
struct prueth_priv *priv = dev_get_priv(dev);
|
|
struct prueth *prueth;
|
|
char portname[15];
|
|
int ret;
|
|
|
|
priv->dev = dev;
|
|
prueth = dev_get_priv(dev->parent);
|
|
priv->prueth = prueth;
|
|
|
|
sprintf(portname, "%s-%s", dev->parent->name, dev->name);
|
|
|
|
device_set_name(dev, portname);
|
|
|
|
ret = icssg_ofdata_parse_phy(dev);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = icssg_phy_init(dev);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = pruss_request_mem_region(prueth->pruss,
|
|
priv->port_id ? PRUSS_MEM_DRAM1 : PRUSS_MEM_DRAM0,
|
|
&prueth->dram[priv->port_id]);
|
|
if (ret) {
|
|
dev_err(dev, "could not request DRAM%d region\n", priv->port_id);
|
|
return ret;
|
|
}
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int prueth_probe(struct udevice *dev)
|
|
{
|
|
ofnode node, pruss_node, mdio_node, sram_node, curr_sram_node;
|
|
struct prueth *prueth = dev_get_priv(dev);
|
|
u32 phandle, err, sp, prev_end_addr;
|
|
struct udevice **prussdev = NULL;
|
|
ofnode eth_ports_node, eth_node;
|
|
struct udevice *port_dev;
|
|
int ret = 0;
|
|
|
|
prueth->dev = dev;
|
|
|
|
err = ofnode_read_u32(dev_ofnode(dev), "ti,prus", &phandle);
|
|
if (err)
|
|
return err;
|
|
|
|
node = ofnode_get_by_phandle(phandle);
|
|
if (!ofnode_valid(node))
|
|
return -EINVAL;
|
|
|
|
pruss_node = ofnode_get_parent(node);
|
|
ret = device_get_global_by_ofnode(pruss_node, prussdev);
|
|
if (ret)
|
|
dev_err(dev, "error getting the pruss dev\n");
|
|
prueth->pruss = *prussdev;
|
|
|
|
ret = pruss_request_mem_region(*prussdev, PRUSS_MEM_SHRD_RAM2,
|
|
&prueth->shram);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = pruss_request_tm_region(*prussdev, &prueth->tmaddr);
|
|
if (ret)
|
|
return ret;
|
|
|
|
prueth->miig_rt = syscon_regmap_lookup_by_phandle(dev, "ti,mii-g-rt");
|
|
if (!prueth->miig_rt) {
|
|
dev_err(dev, "couldn't get mii-g-rt syscon regmap\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
prueth->mii_rt = syscon_regmap_lookup_by_phandle(dev, "ti,mii-rt");
|
|
if (!prueth->mii_rt) {
|
|
dev_err(dev, "couldn't get mii-rt syscon regmap\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
ret = ofnode_read_u32(dev_ofnode(dev), "sram", &sp);
|
|
if (ret) {
|
|
dev_err(dev, "sram node fetch failed %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
sram_node = ofnode_get_by_phandle(sp);
|
|
if (!ofnode_valid(sram_node))
|
|
return -EINVAL;
|
|
|
|
prev_end_addr = ofnode_get_addr(sram_node);
|
|
|
|
ofnode_for_each_subnode(curr_sram_node, sram_node) {
|
|
u32 start_addr, size, end_addr, avail;
|
|
const char *name;
|
|
|
|
name = ofnode_get_name(curr_sram_node);
|
|
start_addr = ofnode_get_addr(curr_sram_node);
|
|
size = ofnode_get_size(curr_sram_node);
|
|
end_addr = start_addr + size;
|
|
avail = start_addr - prev_end_addr;
|
|
|
|
if (avail > MSMC_RAM_SIZE)
|
|
break;
|
|
|
|
prev_end_addr = end_addr;
|
|
}
|
|
|
|
prueth->sram_pa = prev_end_addr;
|
|
if (prueth->sram_pa % SZ_64K != 0) {
|
|
/* This is constraint for SR2.0 firmware */
|
|
dev_err(dev, "sram address needs to be 64KB aligned\n");
|
|
return -EINVAL;
|
|
}
|
|
dev_dbg(dev, "sram: addr %x size %x\n", prueth->sram_pa, MSMC_RAM_SIZE);
|
|
|
|
mdio_node = ofnode_find_subnode(pruss_node, "mdio");
|
|
prueth->mdio_base = ofnode_get_addr(mdio_node);
|
|
ofnode_read_u32(mdio_node, "bus_freq", &prueth->mdio_freq);
|
|
|
|
ret = clk_get_by_name_nodev(mdio_node, "fck", &prueth->mdiofck);
|
|
if (ret) {
|
|
dev_err(dev, "failed to get clock %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = clk_enable(&prueth->mdiofck);
|
|
if (ret) {
|
|
dev_err(dev, "clk_enable failed %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
eth_ports_node = dev_read_subnode(dev, "ethernet-ports");
|
|
if (!ofnode_valid(eth_ports_node))
|
|
return -ENOENT;
|
|
|
|
ofnode_for_each_subnode(eth_node, eth_ports_node) {
|
|
const char *node_name;
|
|
u32 port_id;
|
|
bool disabled;
|
|
|
|
node_name = ofnode_get_name(eth_node);
|
|
disabled = !ofnode_is_enabled(eth_node);
|
|
ret = ofnode_read_u32(eth_node, "reg", &port_id);
|
|
if (ret)
|
|
dev_err(dev, "%s: error reading port_id (%d)\n", node_name, ret);
|
|
|
|
if (port_id >= PRUETH_NUM_MACS) {
|
|
dev_err(dev, "%s: invalid port_id (%d)\n", node_name, port_id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (port_id < 0)
|
|
continue;
|
|
if (disabled)
|
|
continue;
|
|
|
|
ret = device_bind_driver_to_node(dev, "prueth_port",
|
|
ofnode_get_name(eth_node),
|
|
eth_node, &port_dev);
|
|
if (ret) {
|
|
dev_err(dev, "Failed to bind to %s node\n", ofnode_get_name(eth_node));
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
out:
|
|
clk_disable(&prueth->mdiofck);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct udevice_id prueth_ids[] = {
|
|
{ .compatible = "ti,am654-icssg-prueth" },
|
|
{ .compatible = "ti,am642-icssg-prueth" },
|
|
{ }
|
|
};
|
|
|
|
U_BOOT_DRIVER(prueth) = {
|
|
.name = "prueth",
|
|
.id = UCLASS_MISC,
|
|
.of_match = prueth_ids,
|
|
.probe = prueth_probe,
|
|
.priv_auto = sizeof(struct prueth),
|
|
};
|
|
|
|
U_BOOT_DRIVER(prueth_port) = {
|
|
.name = "prueth_port",
|
|
.id = UCLASS_ETH,
|
|
.probe = prueth_port_probe,
|
|
.ops = &prueth_ops,
|
|
.priv_auto = sizeof(struct prueth_priv),
|
|
.plat_auto = sizeof(struct eth_pdata),
|
|
.flags = DM_FLAG_ALLOC_PRIV_DMA,
|
|
};
|