
Hit Stefan,
Sorry for the delay.
On Tue, Mar 15, 2016 at 11:35 AM, Stefan Roese sr@denx.de wrote:
This patch adds support for the mvpp2 ethernet controller which is integrated in the Marvell Armada 375 SoC. This port is based on the Linux driver (v4.4), which has been stripped of the in U-Boot unused portions.
Tested on the Marvell Armada 375 eval board db-88f6720.
Signed-off-by: Stefan Roese sr@denx.de Cc: Luka Perkov luka.perkov@sartura.hr Cc: Joe Hershberger joe.hershberger@gmail.com
drivers/net/Kconfig | 8 + drivers/net/Makefile | 1 + drivers/net/mvpp2.c | 4222 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 4231 insertions(+) create mode 100644 drivers/net/mvpp2.c
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index bc2f51d..bcb4c96 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -94,6 +94,14 @@ config ETH_DESIGNWARE 100Mbit and 1 Gbit operation. You must enable CONFIG_PHYLIB to provide the PHY (physical media interface).
+config MVPP2
bool "Marvell Armada 375 network interface support"
depends on ARMADA_375
select PHYLIB
help
This driver supports the network interface units in the
Marvell ARMADA 375 SoC.
config PCH_GBE bool "Intel Platform Controller Hub EG20T GMAC driver" depends on DM_ETH && DM_PCI diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 33a81ee..fbedd04 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_MPC5xxx_FEC) += mpc5xxx_fec.o obj-$(CONFIG_MPC512x_FEC) += mpc512x_fec.o obj-$(CONFIG_MVGBE) += mvgbe.o obj-$(CONFIG_MVNETA) += mvneta.o +obj-$(CONFIG_MVPP2) += mvpp2.o obj-$(CONFIG_NATSEMI) += natsemi.o obj-$(CONFIG_DRIVER_NE2000) += ne2000.o ne2000_base.o obj-$(CONFIG_DRIVER_AX88796L) += ax88796.o ne2000_base.o diff --git a/drivers/net/mvpp2.c b/drivers/net/mvpp2.c new file mode 100644 index 0000000..09dfc7e --- /dev/null +++ b/drivers/net/mvpp2.c @@ -0,0 +1,4222 @@ +/*
- Driver for Marvell PPv2 network controller for Armada 375 SoC.
- Copyright (C) 2014 Marvell
- Marcin Wojtas mw@semihalf.com
- U-Boot version:
- Copyright (C) 2016 Stefan Roese sr@denx.de
- This file is licensed under the terms of the GNU General Public
- License version 2. This program is licensed "as is" without any
- warranty of any kind, whether express or implied.
- */
+#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <net.h> +#include <netdev.h> +#include <config.h> +#include <malloc.h> +#include <asm/io.h> +#include <asm/errno.h> +#include <phy.h> +#include <miiphy.h> +#include <watchdog.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> +#include <linux/compat.h> +#include <linux/mbus.h>
+DECLARE_GLOBAL_DATA_PTR;
+#if !defined(CONFIG_PHYLIB) +# error Marvell mvpp2 requires PHYLIB +#endif
This isn't needed since you "select" PHYLIB in the Kconfig.
+/* Some linux -> U-Boot compatibility stuff */ +#define netdev_err(dev, fmt, args...) \
printf(fmt, ##args)
+#define netdev_warn(dev, fmt, args...) \
printf(fmt, ##args)
+#define netdev_info(dev, fmt, args...) \
printf(fmt, ##args)
+#define netdev_dbg(dev, fmt, args...) \
printf(fmt, ##args)
+#define ETH_ALEN 6 /* Octets in one ethernet addr */
+#define ETH_P_IP 0x0800 /* Internet Protocol packet */
Already available in include/net.h as PROT_IP
+#define ETH_P_PPP_SES 0x8864 /* PPPoE session messages */
Add this to include/net.h as PROT_PPP_SES
+#define ETH_P_ARP 0x0806 /* Address Resolution packet */
Already available in include/net.h as PROT_ARP
+#define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */
Add this to include/net.h as PROT_IPV6
+#define __verify_pcpu_ptr(ptr) \ +do { \
const void __percpu *__vpp_verify = (typeof((ptr) + 0))NULL; \
(void)__vpp_verify; \
+} while (0)
+#define VERIFY_PERCPU_PTR(__p) \ +({ \
__verify_pcpu_ptr(__p); \
(typeof(*(__p)) __kernel __force *)(__p); \
+})
+#define per_cpu_ptr(ptr, cpu) ({ (void)(cpu); VERIFY_PERCPU_PTR(ptr); }) +#define smp_processor_id() 0 +#define num_present_cpus() 1 +#define for_each_present_cpu(cpu) \
for ((cpu) = 0; (cpu) < 1; (cpu)++)
+#define NET_SKB_PAD max(32, MVPP2_CPU_D_CACHE_LINE_SIZE)
+#define CONFIG_NR_CPUS 1 +#define ETH_HLEN 14 /* Total octets in header */
Already available in include/net.h as ETHER_HDR_SIZE.
+/* 2(HW hdr) 14(MAC hdr) 4(CRC) 32(extra for cache prefetch) */ +#define WRAP (2 + ETH_HLEN + 4 + 32) +#define MTU 1500 +#define RX_BUFFER_SIZE (ALIGN(MTU + WRAP, ARCH_DMA_MINALIGN))
<snip - constants>
+/*
- Page table entries are set to 1MB, or multiples of 1MB
- (not < 1MB). driver uses less bd's so use 1MB bdspace.
- */
+#define BD_SPACE (1 << 20)
It would be great if this long list of constants lived in a header file instead of the top of the source.
+/* Utility/helper methods */
+static void mvpp2_write(struct mvpp2 *priv, u32 offset, u32 data) +{
writel(data, priv->base + offset);
+}
+static u32 mvpp2_read(struct mvpp2 *priv, u32 offset) +{
return readl(priv->base + offset);
+}
<snip>
+/* Set hw internals when starting port */ +static void mvpp2_start_dev(struct mvpp2_port *port) +{
mvpp2_gmac_max_rx_size_set(port);
mvpp2_txp_max_tx_size_set(port);
mvpp2_port_enable(port);
+}
+/* Set hw internals when stopping port */ +static void mvpp2_stop_dev(struct mvpp2_port *port) +{
/* Stop new packets from arriving to RXQs */
mvpp2_ingress_disable(port);
mvpp2_egress_disable(port);
mvpp2_port_disable(port);
+}
+static int mvpp2_phy_connect(struct udevice *dev, struct mvpp2_port *port) +{
struct phy_device *phy_dev;
if (!port->init || port->link == 0) {
phy_dev = phy_connect(port->priv->bus, port->phyaddr, dev,
port->phy_interface);
port->phy_dev = phy_dev;
if (!phy_dev) {
netdev_err(port->dev, "cannot connect to phy\n");
return -ENODEV;
}
phy_dev->supported &= PHY_GBIT_FEATURES;
phy_dev->advertising = phy_dev->supported;
port->phy_dev = phy_dev;
port->link = 0;
port->duplex = 0;
port->speed = 0;
phy_config(phy_dev);
phy_startup(phy_dev);
if (!phy_dev->link) {
printf("%s: No link\n", phy_dev->dev->name);
return -1;
}
port->init = 1;
} else {
mvpp2_egress_enable(port);
mvpp2_ingress_enable(port);
}
return 0;
+}
+static int mvpp2_open(struct udevice *dev, struct mvpp2_port *port) +{
unsigned char mac_bcast[ETH_ALEN] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
int err;
err = mvpp2_prs_mac_da_accept(port->priv, port->id, mac_bcast, true);
if (err) {
netdev_err(dev, "mvpp2_prs_mac_da_accept BC failed\n");
return err;
}
err = mvpp2_prs_mac_da_accept(port->priv, port->id,
port->dev_addr, true);
if (err) {
netdev_err(dev, "mvpp2_prs_mac_da_accept MC failed\n");
return err;
}
err = mvpp2_prs_def_flow(port);
if (err) {
netdev_err(dev, "mvpp2_prs_def_flow failed\n");
return err;
}
/* Allocate the Rx/Tx queues */
err = mvpp2_setup_rxqs(port);
if (err) {
netdev_err(port->dev, "cannot allocate Rx queues\n");
return err;
}
err = mvpp2_setup_txqs(port);
if (err) {
netdev_err(port->dev, "cannot allocate Tx queues\n");
return err;
}
err = mvpp2_phy_connect(dev, port);
if (err < 0)
return err;
mvpp2_link_event(port);
mvpp2_start_dev(port);
return 0;
+}
+/* No Device ops here in U-Boot */
+/* Driver initialization */
+static void mvpp2_port_power_up(struct mvpp2_port *port) +{
mvpp2_port_mii_set(port);
mvpp2_port_periodic_xon_disable(port);
mvpp2_port_fc_adv_enable(port);
mvpp2_port_reset(port);
+}
+/* Initialize port HW */ +static int mvpp2_port_init(struct udevice *dev, struct mvpp2_port *port) +{
struct mvpp2 *priv = port->priv;
struct mvpp2_txq_pcpu *txq_pcpu;
int queue, cpu, err;
if (port->first_rxq + rxq_number > MVPP2_RXQ_TOTAL_NUM)
return -EINVAL;
/* Disable port */
mvpp2_egress_disable(port);
mvpp2_port_disable(port);
port->txqs = devm_kcalloc(dev, txq_number, sizeof(*port->txqs),
GFP_KERNEL);
if (!port->txqs)
return -ENOMEM;
/* Associate physical Tx queues to this port and initialize.
* The mapping is predefined.
*/
for (queue = 0; queue < txq_number; queue++) {
int queue_phy_id = mvpp2_txq_phys(port->id, queue);
struct mvpp2_tx_queue *txq;
txq = devm_kzalloc(dev, sizeof(*txq), GFP_KERNEL);
if (!txq)
return -ENOMEM;
txq->pcpu = devm_kzalloc(dev, sizeof(struct mvpp2_txq_pcpu),
GFP_KERNEL);
if (!txq->pcpu)
return -ENOMEM;
txq->id = queue_phy_id;
txq->log_id = queue;
txq->done_pkts_coal = MVPP2_TXDONE_COAL_PKTS_THRESH;
for_each_present_cpu(cpu) {
txq_pcpu = per_cpu_ptr(txq->pcpu, cpu);
txq_pcpu->cpu = cpu;
}
port->txqs[queue] = txq;
}
port->rxqs = devm_kcalloc(dev, rxq_number, sizeof(*port->rxqs),
GFP_KERNEL);
if (!port->rxqs)
return -ENOMEM;
/* Allocate and initialize Rx queue for this port */
for (queue = 0; queue < rxq_number; queue++) {
struct mvpp2_rx_queue *rxq;
/* Map physical Rx queue to port's logical Rx queue */
rxq = devm_kzalloc(dev, sizeof(*rxq), GFP_KERNEL);
if (!rxq)
return -ENOMEM;
/* Map this Rx queue to a physical queue */
rxq->id = port->first_rxq + queue;
rxq->port = port->id;
rxq->logic_rxq = queue;
port->rxqs[queue] = rxq;
}
/* Configure Rx queue group interrupt for this port */
mvpp2_write(priv, MVPP2_ISR_RXQ_GROUP_REG(port->id), CONFIG_MV_ETH_RXQ);
/* Create Rx descriptor rings */
for (queue = 0; queue < rxq_number; queue++) {
struct mvpp2_rx_queue *rxq = port->rxqs[queue];
rxq->size = port->rx_ring_size;
rxq->pkts_coal = MVPP2_RX_COAL_PKTS;
rxq->time_coal = MVPP2_RX_COAL_USEC;
}
mvpp2_ingress_disable(port);
/* Port default configuration */
mvpp2_defaults_set(port);
/* Port's classifier configuration */
mvpp2_cls_oversize_rxq_set(port);
mvpp2_cls_port_config(port);
/* Provide an initial Rx packet size */
port->pkt_size = MVPP2_RX_PKT_SIZE(PKTSIZE_ALIGN);
/* Initialize pools for swf */
err = mvpp2_swf_bm_pool_init(port);
if (err)
return err;
return 0;
+}
+/* Ports initialization */ +static int mvpp2_port_probe(struct udevice *dev,
struct mvpp2_port *port,
int port_node,
struct mvpp2 *priv,
int *next_first_rxq)
+{
int phy_node;
u32 id;
u32 phyaddr;
const char *phy_mode_str;
int phy_mode = -1;
int priv_common_regs_num = 2;
int err;
phy_node = fdtdec_lookup_phandle(gd->fdt_blob, port_node, "phy");
if (phy_node < 0) {
dev_err(&pdev->dev, "missing phy\n");
return -ENODEV;
}
phy_mode_str = fdt_getprop(gd->fdt_blob, port_node, "phy-mode", NULL);
if (phy_mode_str)
phy_mode = phy_get_interface_by_name(phy_mode_str);
if (phy_mode == -1) {
dev_err(&pdev->dev, "incorrect phy mode\n");
return -EINVAL;
}
id = fdtdec_get_int(gd->fdt_blob, port_node, "port-id", -1);
if (id == -1) {
dev_err(&pdev->dev, "missing port-id value\n");
return -EINVAL;
}
phyaddr = fdtdec_get_int(gd->fdt_blob, phy_node, "reg", 0);
port->priv = priv;
port->id = id;
port->first_rxq = *next_first_rxq;
port->phy_node = phy_node;
port->phy_interface = phy_mode;
port->phyaddr = phyaddr;
port->base = (void __iomem *)dev_get_addr_index(dev->parent,
priv_common_regs_num
+ id);
if (IS_ERR(port->base))
return PTR_ERR(port->base);
port->tx_ring_size = MVPP2_MAX_TXD;
port->rx_ring_size = MVPP2_MAX_RXD;
err = mvpp2_port_init(dev, port);
if (err < 0) {
dev_err(&pdev->dev, "failed to init port %d\n", id);
return err;
}
mvpp2_port_power_up(port);
/* Increment the first Rx queue number to be used by the next port */
*next_first_rxq += CONFIG_MV_ETH_RXQ;
priv->port_list[id] = port;
return 0;
+}
+/* Initialize decoding windows */ +static void mvpp2_conf_mbus_windows(const struct mbus_dram_target_info *dram,
struct mvpp2 *priv)
+{
u32 win_enable;
int i;
for (i = 0; i < 6; i++) {
mvpp2_write(priv, MVPP2_WIN_BASE(i), 0);
mvpp2_write(priv, MVPP2_WIN_SIZE(i), 0);
if (i < 4)
mvpp2_write(priv, MVPP2_WIN_REMAP(i), 0);
}
win_enable = 0;
for (i = 0; i < dram->num_cs; i++) {
const struct mbus_dram_window *cs = dram->cs + i;
mvpp2_write(priv, MVPP2_WIN_BASE(i),
(cs->base & 0xffff0000) | (cs->mbus_attr << 8) |
dram->mbus_dram_target_id);
mvpp2_write(priv, MVPP2_WIN_SIZE(i),
(cs->size - 1) & 0xffff0000);
win_enable |= (1 << i);
}
mvpp2_write(priv, MVPP2_BASE_ADDR_ENABLE, win_enable);
+}
+/* Initialize Rx FIFO's */ +static void mvpp2_rx_fifo_init(struct mvpp2 *priv) +{
int port;
for (port = 0; port < MVPP2_MAX_PORTS; port++) {
mvpp2_write(priv, MVPP2_RX_DATA_FIFO_SIZE_REG(port),
MVPP2_RX_FIFO_PORT_DATA_SIZE);
mvpp2_write(priv, MVPP2_RX_ATTR_FIFO_SIZE_REG(port),
MVPP2_RX_FIFO_PORT_ATTR_SIZE);
}
mvpp2_write(priv, MVPP2_RX_MIN_PKT_SIZE_REG,
MVPP2_RX_FIFO_PORT_MIN_PKT);
mvpp2_write(priv, MVPP2_RX_FIFO_INIT_REG, 0x1);
+}
+/* Initialize network controller common part HW */ +static int mvpp2_init(struct udevice *dev, struct mvpp2 *priv) +{
const struct mbus_dram_target_info *dram_target_info;
int err, i;
u32 val;
/* Checks for hardware constraints (U-Boot uses only one rxq) */
if ((rxq_number > MVPP2_MAX_RXQ) || (txq_number > MVPP2_MAX_TXQ)) {
dev_err(&pdev->dev, "invalid queue size parameter\n");
return -EINVAL;
}
/* MBUS windows configuration */
dram_target_info = mvebu_mbus_dram_info();
if (dram_target_info)
mvpp2_conf_mbus_windows(dram_target_info, priv);
/* Disable HW PHY polling */
val = readl(priv->lms_base + MVPP2_PHY_AN_CFG0_REG);
val |= MVPP2_PHY_AN_STOP_SMI0_MASK;
writel(val, priv->lms_base + MVPP2_PHY_AN_CFG0_REG);
/* Allocate and initialize aggregated TXQs */
priv->aggr_txqs = devm_kcalloc(dev, num_present_cpus(),
sizeof(struct mvpp2_tx_queue),
GFP_KERNEL);
if (!priv->aggr_txqs)
return -ENOMEM;
for_each_present_cpu(i) {
priv->aggr_txqs[i].id = i;
priv->aggr_txqs[i].size = MVPP2_AGGR_TXQ_SIZE;
err = mvpp2_aggr_txq_init(dev, &priv->aggr_txqs[i],
MVPP2_AGGR_TXQ_SIZE, i, priv);
if (err < 0)
return err;
}
/* Rx Fifo Init */
mvpp2_rx_fifo_init(priv);
/* Reset Rx queue group interrupt configuration */
for (i = 0; i < MVPP2_MAX_PORTS; i++)
mvpp2_write(priv, MVPP2_ISR_RXQ_GROUP_REG(i),
CONFIG_MV_ETH_RXQ);
writel(MVPP2_EXT_GLOBAL_CTRL_DEFAULT,
priv->lms_base + MVPP2_MNG_EXTENDED_GLOBAL_CTRL_REG);
/* Allow cache snoop when transmiting packets */
mvpp2_write(priv, MVPP2_TX_SNOOP_REG, 0x1);
/* Buffer Manager initialization */
err = mvpp2_bm_init(dev, priv);
if (err < 0)
return err;
/* Parser default initialization */
err = mvpp2_prs_default_init(dev, priv);
if (err < 0)
return err;
/* Classifier default initialization */
mvpp2_cls_init(priv);
return 0;
+}
+/* SMI / MDIO functions */
+static int smi_wait_ready(struct mvpp2 *priv) +{
u32 timeout = MVPP2_SMI_TIMEOUT;
u32 smi_reg;
/* wait till the SMI is not busy */
do {
/* read smi register */
smi_reg = readl(priv->lms_base + MVPP2_SMI);
if (timeout-- == 0) {
printf("Error: SMI busy timeout\n");
return -EFAULT;
}
} while (smi_reg & MVPP2_SMI_BUSY);
return 0;
+}
+/*
- mpp2_mdio_read - miiphy_read callback function.
- Returns 16bit phy register value, or 0xffff on error
- */
+static int mpp2_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) +{
struct mvpp2 *priv = bus->priv;
u32 smi_reg;
u32 timeout;
/* check parameters */
if (addr > MVPP2_PHY_ADDR_MASK) {
printf("Error: Invalid PHY address %d\n", addr);
return -EFAULT;
}
if (reg > MVPP2_PHY_REG_MASK) {
printf("Err: Invalid register offset %d\n", reg);
return -EFAULT;
}
/* wait till the SMI is not busy */
if (smi_wait_ready(priv) < 0)
return -EFAULT;
/* fill the phy address and regiser offset and read opcode */
smi_reg = (addr << MVPP2_SMI_DEV_ADDR_OFFS)
| (reg << MVPP2_SMI_REG_ADDR_OFFS)
| MVPP2_SMI_OPCODE_READ;
/* write the smi register */
writel(smi_reg, priv->lms_base + MVPP2_SMI);
/* wait till read value is ready */
timeout = MVPP2_SMI_TIMEOUT;
do {
/* read smi register */
smi_reg = readl(priv->lms_base + MVPP2_SMI);
if (timeout-- == 0) {
printf("Err: SMI read ready timeout\n");
return -EFAULT;
}
} while (!(smi_reg & MVPP2_SMI_READ_VALID));
/* Wait for the data to update in the SMI register */
for (timeout = 0; timeout < MVPP2_SMI_TIMEOUT; timeout++)
;
return readl(priv->lms_base + MVPP2_SMI) & MVPP2_SMI_DATA_MASK;
+}
+/*
- mpp2_mdio_write - miiphy_write callback function.
- Returns 0 if write succeed, -EINVAL on bad parameters
- -ETIME on timeout
- */
+static int mpp2_mdio_write(struct mii_dev *bus, int addr, int devad, int reg,
u16 value)
+{
struct mvpp2 *priv = bus->priv;
u32 smi_reg;
/* check parameters */
if (addr > MVPP2_PHY_ADDR_MASK) {
printf("Error: Invalid PHY address %d\n", addr);
return -EFAULT;
}
if (reg > MVPP2_PHY_REG_MASK) {
printf("Err: Invalid register offset %d\n", reg);
return -EFAULT;
}
/* wait till the SMI is not busy */
if (smi_wait_ready(priv) < 0)
return -EFAULT;
/* fill the phy addr and reg offset and write opcode and data */
smi_reg = value << MVPP2_SMI_DATA_OFFS;
smi_reg |= (addr << MVPP2_SMI_DEV_ADDR_OFFS)
| (reg << MVPP2_SMI_REG_ADDR_OFFS);
smi_reg &= ~MVPP2_SMI_OPCODE_READ;
/* write the smi register */
writel(smi_reg, priv->lms_base + MVPP2_SMI);
return 0;
+}
+static int mvpp2_probe(struct udevice *dev) +{
struct mvpp2_port *port = dev_get_priv(dev);
struct mvpp2 *priv;
struct mii_dev *bus;
void *bd_space;
int err;
u32 size = 0;
int i;
/*
* This function is called multiple times. For each controller.
* But we only need to do the main controller probing once. So
* lets only do the port probing here, if called not for the
* first time.
*/
if (buffer_loc.tx_descs) {
priv = buffer_loc.priv;
port->priv = priv;
err = mvpp2_port_probe(dev, port, dev->of_offset, priv,
&buffer_loc.first_rxq);
return 0;
}
/*
* U-Boot special buffer handling:
*
* Allocate buffer area for descs and rx_buffers. This is only
* done once for all interfaces. As only one interface can
* be active. Make this area DMA save by disabling the D-cache
DMA save -> DMA-safe
*/
/* Align buffer area for descs and rx_buffers to 1MiB */
bd_space = memalign(1 << MMU_SECTION_SHIFT, BD_SPACE);
mmu_set_region_dcache_behaviour((u32)bd_space, BD_SPACE, DCACHE_OFF);
buffer_loc.aggr_tx_descs = (struct mvpp2_tx_desc *)bd_space;
size += MVPP2_AGGR_TXQ_SIZE * MVPP2_DESC_ALIGNED_SIZE;
buffer_loc.tx_descs = (struct mvpp2_tx_desc *)((u32)bd_space + size);
size += MVPP2_MAX_TXD * MVPP2_DESC_ALIGNED_SIZE;
buffer_loc.rx_descs = (struct mvpp2_rx_desc *)((u32)bd_space + size);
size += MVPP2_MAX_RXD * MVPP2_DESC_ALIGNED_SIZE;
for (i = 0; i < MVPP2_BM_POOLS_NUM; i++) {
buffer_loc.bm_pool[i] = (u32 *)((u32)bd_space + size);
size += MVPP2_BM_POOL_SIZE_MAX * sizeof(u32);
}
for (i = 0; i < MVPP2_BM_LONG_BUF_NUM; i++) {
buffer_loc.rx_buffer[i] = (u32 *)((u32)bd_space + size);
size += RX_BUFFER_SIZE;
}
/*
* This is done only once, for the first controller that is
* probed. Here the common structures are initialized.
*/
Any reason this isn't in the probe function for the MISC class parent driver?
priv = calloc(1, sizeof(struct mvpp2));
if (!priv)
return -ENOMEM;
/* And save pointer to main mvpp2 struct */
port->priv = priv;
buffer_loc.priv = priv;
priv->base = (void *)dev_get_addr_index(dev->parent, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->lms_base = (void *)dev_get_addr_index(dev->parent, 1);
if (IS_ERR(priv->lms_base))
return PTR_ERR(priv->lms_base);
/* Initialize network controller */
err = mvpp2_init(dev, priv);
if (err < 0) {
dev_err(&pdev->dev, "failed to initialize controller\n");
return err;
}
err = mvpp2_port_probe(dev, port, dev->of_offset, priv,
&buffer_loc.first_rxq);
/*
* Finally create and register the MDIO bus driver
*/
bus = mdio_alloc();
if (!bus) {
printf("Failed to allocate MDIO bus\n");
return -ENOMEM;
}
bus->read = mpp2_mdio_read;
bus->write = mpp2_mdio_write;
snprintf(bus->name, sizeof(bus->name), dev->name);
bus->priv = (void *)priv;
priv->bus = bus;
return mdio_register(bus);
+}
+static int mvpp2_recv(struct udevice *dev, int flags, uchar **packetp) +{
struct mvpp2_port *port = dev_get_priv(dev);
struct mvpp2_rx_desc *rx_desc;
struct mvpp2_bm_pool *bm_pool;
dma_addr_t phys_addr;
u32 bm, rx_status;
int pool, rx_bytes, err;
int rx_received;
struct mvpp2_rx_queue *rxq;
u32 cause_rx_tx, cause_rx, cause_misc;
u8 *data;
cause_rx_tx = mvpp2_read(port->priv,
MVPP2_ISR_RX_TX_CAUSE_REG(port->id));
cause_rx_tx &= ~MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK;
cause_misc = cause_rx_tx & MVPP2_CAUSE_MISC_SUM_MASK;
if (!cause_rx_tx && !cause_misc)
return 0;
cause_rx = cause_rx_tx & MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK;
/* Process RX packets */
cause_rx |= port->pending_cause_rx;
rxq = mvpp2_get_rx_queue(port, cause_rx);
/* Get number of received packets and clamp the to-do */
rx_received = mvpp2_rxq_received(port, rxq->id);
/* Return if no packets are received */
if (!rx_received)
return 0;
rx_desc = mvpp2_rxq_next_desc_get(rxq);
rx_status = rx_desc->status;
rx_bytes = rx_desc->data_size - MVPP2_MH_SIZE;
phys_addr = rx_desc->buf_phys_addr;
bm = mvpp2_bm_cookie_build(rx_desc);
pool = mvpp2_bm_cookie_pool_get(bm);
bm_pool = &port->priv->bm_pools[pool];
/* Check if buffer header is used */
if (rx_status & MVPP2_RXD_BUF_HDR)
return 0;
/* In case of an error, release the requested buffer pointer
* to the Buffer Manager. This request process is controlled
* by the hardware, and the information about the buffer is
* comprised by the RX descriptor.
*/
if (rx_status & MVPP2_RXD_ERR_SUMMARY) {
mvpp2_rx_error(port, rx_desc);
/* Return the buffer to the pool */
mvpp2_pool_refill(port, bm, rx_desc->buf_phys_addr,
rx_desc->buf_cookie);
return 0;
}
err = mvpp2_rx_refill(port, bm_pool, bm, phys_addr);
if (err) {
netdev_err(port->dev, "failed to refill BM pools\n");
return 0;
}
/* Update Rx queue management counters */
mb();
mvpp2_rxq_status_update(port, rxq->id, 1, 1);
/* give packet to stack - skip on first n bytes */
data = (u8 *)phys_addr + 2 + 32;
if (rx_bytes <= 0)
return 0;
/*
* No cache invalidation needed here, since the rx_buffer's are
* located in a uncached memory region
*/
*packetp = data;
return rx_bytes;
+}
+/* Drain Txq */ +static void mvpp2_txq_drain(struct mvpp2_port *port, struct mvpp2_tx_queue *txq,
int enable)
+{
u32 val;
mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id);
val = mvpp2_read(port->priv, MVPP2_TXQ_PREF_BUF_REG);
if (enable)
val |= MVPP2_TXQ_DRAIN_EN_MASK;
else
val &= ~MVPP2_TXQ_DRAIN_EN_MASK;
mvpp2_write(port->priv, MVPP2_TXQ_PREF_BUF_REG, val);
+}
+static int mvpp2_send(struct udevice *dev, void *packet, int length) +{
struct mvpp2_port *port = dev_get_priv(dev);
struct mvpp2_tx_queue *txq, *aggr_txq;
struct mvpp2_tx_desc *tx_desc;
int tx_done;
int timeout;
txq = port->txqs[0];
aggr_txq = &port->priv->aggr_txqs[smp_processor_id()];
/* Get a descriptor for the first part of the packet */
tx_desc = mvpp2_txq_next_desc_get(aggr_txq);
tx_desc->phys_txq = txq->id;
tx_desc->data_size = length;
tx_desc->packet_offset = (u32)packet & MVPP2_TX_DESC_ALIGN;
tx_desc->buf_phys_addr = (u32)packet & ~MVPP2_TX_DESC_ALIGN;
/* First and Last descriptor */
tx_desc->command = MVPP2_TXD_L4_CSUM_NOT | MVPP2_TXD_IP_CSUM_DISABLE
| MVPP2_TXD_F_DESC | MVPP2_TXD_L_DESC;
/* Flush tx data */
flush_dcache_range((u32)packet, (u32)packet + length);
/* Enable transmit */
mb();
mvpp2_aggr_txq_pend_desc_add(port, 1);
mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id);
timeout = 0;
do {
if (timeout++ > 10000) {
printf("timeout: packet not sent from aggregated to phys TXQ\n");
return 0;
}
tx_done = mvpp2_txq_pend_desc_num_get(port, txq);
} while (tx_done);
/* Enable TXQ drain */
mvpp2_txq_drain(port, txq, 1);
timeout = 0;
do {
if (timeout++ > 10000) {
printf("timeout: packet not sent\n");
return 0;
}
tx_done = mvpp2_txq_sent_desc_proc(port, txq);
} while (!tx_done);
/* Disable TXQ drain */
mvpp2_txq_drain(port, txq, 0);
return 0;
+}
+static int mvpp2_start(struct udevice *dev) +{
struct eth_pdata *pdata = dev_get_platdata(dev);
struct mvpp2_port *port = dev_get_priv(dev);
/* Load current MAC address */
memcpy(port->dev_addr, pdata->enetaddr, ETH_ALEN);
/* Reconfigure parser accept the original MAC address */
mvpp2_prs_update_mac_da(port, port->dev_addr);
mvpp2_port_power_up(port);
mvpp2_open(dev, port);
return 0;
+}
+static void mvpp2_stop(struct udevice *dev) +{
struct mvpp2_port *port = dev_get_priv(dev);
mvpp2_stop_dev(port);
mvpp2_cleanup_rxqs(port);
mvpp2_cleanup_txqs(port);
+}
+static const struct eth_ops mvpp2_ops = {
.start = mvpp2_start,
.send = mvpp2_send,
.recv = mvpp2_recv,
.stop = mvpp2_stop,
+};
+static struct driver mvpp2_driver = {
.name = "mvpp2",
.id = UCLASS_ETH,
.probe = mvpp2_probe,
.ops = &mvpp2_ops,
.priv_auto_alloc_size = sizeof(struct mvpp2_port),
.platdata_auto_alloc_size = sizeof(struct eth_pdata),
+};
+/*
- Use a MISC device to bind the n instances (child nodes) of the
- network base controller in UCLASS_ETH.
- */
+static int mvpp2_base_bind(struct udevice *parent) +{
const void *blob = gd->fdt_blob;
int node = parent->of_offset;
struct uclass_driver *drv;
struct udevice *dev;
struct eth_pdata *plat;
char *name;
int subnode;
u32 id;
/* Lookup eth driver */
drv = lists_uclass_lookup(UCLASS_ETH);
if (!drv) {
puts("Cannot find eth driver\n");
return -ENOENT;
}
fdt_for_each_subnode(blob, subnode, node) {
/* Skip disabled ports */
if (!fdtdec_get_is_enabled(blob, subnode))
continue;
plat = calloc(1, sizeof(*plat));
if (!plat)
return -ENOMEM;
id = fdtdec_get_int(blob, subnode, "port-id", -1);
name = calloc(1, 16);
sprintf(name, "mvpp2-%d", id);
/* Create child device UCLASS_ETH and bind it */
device_bind(parent, &mvpp2_driver, name, plat, subnode, &dev);
dev->of_offset = subnode;
}
return 0;
+}
+static const struct udevice_id mvpp2_ids[] = {
{ .compatible = "marvell,armada-375-pp2" },
{ }
+};
+U_BOOT_DRIVER(mvpp2_base) = {
.name = "mvpp2_base",
.id = UCLASS_MISC,
.of_match = mvpp2_ids,
.bind = mvpp2_base_bind,
+};
2.7.3
Mostly looks good to me.
-Joe