u-boot-brain/drivers/net/altera_tse.c
Simon Glass 41575d8e4c dm: treewide: Rename auto_alloc_size members to be shorter
This construct is quite long-winded. In earlier days it made some sense
since auto-allocation was a strange concept. But with driver model now
used pretty universally, we can shorten this to 'auto'. This reduces
verbosity and makes it easier to read.

Coincidentally it also ensures that every declaration is on one line,
thus making dtoc's job easier.

Signed-off-by: Simon Glass <sjg@chromium.org>
2020-12-13 08:00:25 -07:00

718 lines
18 KiB
C

/*
* Altera 10/100/1000 triple speed ethernet mac driver
*
* Copyright (C) 2008 Altera Corporation.
* Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <common.h>
#include <cpu_func.h>
#include <dm.h>
#include <errno.h>
#include <fdt_support.h>
#include <log.h>
#include <memalign.h>
#include <miiphy.h>
#include <net.h>
#include <asm/cache.h>
#include <linux/dma-mapping.h>
#include <asm/io.h>
#include "altera_tse.h"
DECLARE_GLOBAL_DATA_PTR;
static inline void alt_sgdma_construct_descriptor(
struct alt_sgdma_descriptor *desc,
struct alt_sgdma_descriptor *next,
void *read_addr,
void *write_addr,
u16 length_or_eop,
int generate_eop,
int read_fixed,
int write_fixed_or_sop)
{
u8 val;
/*
* Mark the "next" descriptor as "not" owned by hardware. This prevents
* The SGDMA controller from continuing to process the chain.
*/
next->descriptor_control = next->descriptor_control &
~ALT_SGDMA_DESCRIPTOR_CONTROL_OWNED_BY_HW_MSK;
memset(desc, 0, sizeof(struct alt_sgdma_descriptor));
desc->source = virt_to_phys(read_addr);
desc->destination = virt_to_phys(write_addr);
desc->next = virt_to_phys(next);
desc->bytes_to_transfer = length_or_eop;
/*
* Set the descriptor control block as follows:
* - Set "owned by hardware" bit
* - Optionally set "generate EOP" bit
* - Optionally set the "read from fixed address" bit
* - Optionally set the "write to fixed address bit (which serves
* serves as a "generate SOP" control bit in memory-to-stream mode).
* - Set the 4-bit atlantic channel, if specified
*
* Note this step is performed after all other descriptor information
* has been filled out so that, if the controller already happens to be
* pointing at this descriptor, it will not run (via the "owned by
* hardware" bit) until all other descriptor has been set up.
*/
val = ALT_SGDMA_DESCRIPTOR_CONTROL_OWNED_BY_HW_MSK;
if (generate_eop)
val |= ALT_SGDMA_DESCRIPTOR_CONTROL_GENERATE_EOP_MSK;
if (read_fixed)
val |= ALT_SGDMA_DESCRIPTOR_CONTROL_READ_FIXED_ADDRESS_MSK;
if (write_fixed_or_sop)
val |= ALT_SGDMA_DESCRIPTOR_CONTROL_WRITE_FIXED_ADDRESS_MSK;
desc->descriptor_control = val;
}
static int alt_sgdma_wait_transfer(struct alt_sgdma_registers *regs)
{
int status;
ulong ctime;
/* Wait for the descriptor (chain) to complete */
ctime = get_timer(0);
while (1) {
status = readl(&regs->status);
if (!(status & ALT_SGDMA_STATUS_BUSY_MSK))
break;
if (get_timer(ctime) > ALT_TSE_SGDMA_BUSY_TIMEOUT) {
status = -ETIMEDOUT;
debug("sgdma timeout\n");
break;
}
}
/* Clear Run */
writel(0, &regs->control);
/* Clear status */
writel(0xff, &regs->status);
return status;
}
static int alt_sgdma_start_transfer(struct alt_sgdma_registers *regs,
struct alt_sgdma_descriptor *desc)
{
u32 val;
/* Point the controller at the descriptor */
writel(virt_to_phys(desc), &regs->next_descriptor_pointer);
/*
* Set up SGDMA controller to:
* - Disable interrupt generation
* - Run once a valid descriptor is written to controller
* - Stop on an error with any particular descriptor
*/
val = ALT_SGDMA_CONTROL_RUN_MSK | ALT_SGDMA_CONTROL_STOP_DMA_ER_MSK;
writel(val, &regs->control);
return 0;
}
static void tse_adjust_link(struct altera_tse_priv *priv,
struct phy_device *phydev)
{
struct alt_tse_mac *mac_dev = priv->mac_dev;
u32 refvar;
if (!phydev->link) {
debug("%s: No link.\n", phydev->dev->name);
return;
}
refvar = readl(&mac_dev->command_config);
if (phydev->duplex)
refvar |= ALTERA_TSE_CMD_HD_ENA_MSK;
else
refvar &= ~ALTERA_TSE_CMD_HD_ENA_MSK;
switch (phydev->speed) {
case 1000:
refvar |= ALTERA_TSE_CMD_ETH_SPEED_MSK;
refvar &= ~ALTERA_TSE_CMD_ENA_10_MSK;
break;
case 100:
refvar &= ~ALTERA_TSE_CMD_ETH_SPEED_MSK;
refvar &= ~ALTERA_TSE_CMD_ENA_10_MSK;
break;
case 10:
refvar &= ~ALTERA_TSE_CMD_ETH_SPEED_MSK;
refvar |= ALTERA_TSE_CMD_ENA_10_MSK;
break;
}
writel(refvar, &mac_dev->command_config);
}
static int altera_tse_send_sgdma(struct udevice *dev, void *packet, int length)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
struct alt_sgdma_descriptor *tx_desc = priv->tx_desc;
alt_sgdma_construct_descriptor(
tx_desc,
tx_desc + 1,
packet, /* read addr */
NULL, /* write addr */
length, /* length or EOP ,will change for each tx */
1, /* gen eop */
0, /* read fixed */
1 /* write fixed or sop */
);
/* send the packet */
alt_sgdma_start_transfer(priv->sgdma_tx, tx_desc);
alt_sgdma_wait_transfer(priv->sgdma_tx);
debug("sent %d bytes\n", tx_desc->actual_bytes_transferred);
return tx_desc->actual_bytes_transferred;
}
static int altera_tse_recv_sgdma(struct udevice *dev, int flags,
uchar **packetp)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
struct alt_sgdma_descriptor *rx_desc = priv->rx_desc;
int packet_length;
if (rx_desc->descriptor_status &
ALT_SGDMA_DESCRIPTOR_STATUS_TERMINATED_BY_EOP_MSK) {
alt_sgdma_wait_transfer(priv->sgdma_rx);
packet_length = rx_desc->actual_bytes_transferred;
debug("recv %d bytes\n", packet_length);
*packetp = priv->rx_buf;
return packet_length;
}
return -EAGAIN;
}
static int altera_tse_free_pkt_sgdma(struct udevice *dev, uchar *packet,
int length)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
struct alt_sgdma_descriptor *rx_desc = priv->rx_desc;
alt_sgdma_construct_descriptor(
rx_desc,
rx_desc + 1,
NULL, /* read addr */
priv->rx_buf, /* write addr */
0, /* length or EOP */
0, /* gen eop */
0, /* read fixed */
0 /* write fixed or sop */
);
/* setup the sgdma */
alt_sgdma_start_transfer(priv->sgdma_rx, rx_desc);
debug("recv setup\n");
return 0;
}
static void altera_tse_stop_mac(struct altera_tse_priv *priv)
{
struct alt_tse_mac *mac_dev = priv->mac_dev;
u32 status;
ulong ctime;
/* reset the mac */
writel(ALTERA_TSE_CMD_SW_RESET_MSK, &mac_dev->command_config);
ctime = get_timer(0);
while (1) {
status = readl(&mac_dev->command_config);
if (!(status & ALTERA_TSE_CMD_SW_RESET_MSK))
break;
if (get_timer(ctime) > ALT_TSE_SW_RESET_TIMEOUT) {
debug("Reset mac timeout\n");
break;
}
}
}
static void altera_tse_stop_sgdma(struct udevice *dev)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
struct alt_sgdma_registers *rx_sgdma = priv->sgdma_rx;
struct alt_sgdma_registers *tx_sgdma = priv->sgdma_tx;
struct alt_sgdma_descriptor *rx_desc = priv->rx_desc;
int ret;
/* clear rx desc & wait for sgdma to complete */
rx_desc->descriptor_control = 0;
writel(0, &rx_sgdma->control);
ret = alt_sgdma_wait_transfer(rx_sgdma);
if (ret == -ETIMEDOUT)
writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK,
&rx_sgdma->control);
writel(0, &tx_sgdma->control);
ret = alt_sgdma_wait_transfer(tx_sgdma);
if (ret == -ETIMEDOUT)
writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK,
&tx_sgdma->control);
}
static void msgdma_reset(struct msgdma_csr *csr)
{
u32 status;
ulong ctime;
/* Reset mSGDMA */
writel(MSGDMA_CSR_STAT_MASK, &csr->status);
writel(MSGDMA_CSR_CTL_RESET, &csr->control);
ctime = get_timer(0);
while (1) {
status = readl(&csr->status);
if (!(status & MSGDMA_CSR_STAT_RESETTING))
break;
if (get_timer(ctime) > ALT_TSE_SW_RESET_TIMEOUT) {
debug("Reset msgdma timeout\n");
break;
}
}
/* Clear status */
writel(MSGDMA_CSR_STAT_MASK, &csr->status);
}
static u32 msgdma_wait(struct msgdma_csr *csr)
{
u32 status;
ulong ctime;
/* Wait for the descriptor to complete */
ctime = get_timer(0);
while (1) {
status = readl(&csr->status);
if (!(status & MSGDMA_CSR_STAT_BUSY))
break;
if (get_timer(ctime) > ALT_TSE_SGDMA_BUSY_TIMEOUT) {
debug("sgdma timeout\n");
break;
}
}
/* Clear status */
writel(MSGDMA_CSR_STAT_MASK, &csr->status);
return status;
}
static int altera_tse_send_msgdma(struct udevice *dev, void *packet,
int length)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
struct msgdma_extended_desc *desc = priv->tx_desc;
u32 tx_buf = virt_to_phys(packet);
u32 status;
writel(tx_buf, &desc->read_addr_lo);
writel(0, &desc->read_addr_hi);
writel(0, &desc->write_addr_lo);
writel(0, &desc->write_addr_hi);
writel(length, &desc->len);
writel(0, &desc->burst_seq_num);
writel(MSGDMA_DESC_TX_STRIDE, &desc->stride);
writel(MSGDMA_DESC_CTL_TX_SINGLE, &desc->control);
status = msgdma_wait(priv->sgdma_tx);
debug("sent %d bytes, status %08x\n", length, status);
return 0;
}
static int altera_tse_recv_msgdma(struct udevice *dev, int flags,
uchar **packetp)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
struct msgdma_csr *csr = priv->sgdma_rx;
struct msgdma_response *resp = priv->rx_resp;
u32 level, length, status;
level = readl(&csr->resp_fill_level);
if (level & 0xffff) {
length = readl(&resp->bytes_transferred);
status = readl(&resp->status);
debug("recv %d bytes, status %08x\n", length, status);
*packetp = priv->rx_buf;
return length;
}
return -EAGAIN;
}
static int altera_tse_free_pkt_msgdma(struct udevice *dev, uchar *packet,
int length)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
struct msgdma_extended_desc *desc = priv->rx_desc;
u32 rx_buf = virt_to_phys(priv->rx_buf);
writel(0, &desc->read_addr_lo);
writel(0, &desc->read_addr_hi);
writel(rx_buf, &desc->write_addr_lo);
writel(0, &desc->write_addr_hi);
writel(PKTSIZE_ALIGN, &desc->len);
writel(0, &desc->burst_seq_num);
writel(MSGDMA_DESC_RX_STRIDE, &desc->stride);
writel(MSGDMA_DESC_CTL_RX_SINGLE, &desc->control);
debug("recv setup\n");
return 0;
}
static void altera_tse_stop_msgdma(struct udevice *dev)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
msgdma_reset(priv->sgdma_rx);
msgdma_reset(priv->sgdma_tx);
}
static int tse_mdio_read(struct mii_dev *bus, int addr, int devad, int reg)
{
struct altera_tse_priv *priv = bus->priv;
struct alt_tse_mac *mac_dev = priv->mac_dev;
u32 value;
/* set mdio address */
writel(addr, &mac_dev->mdio_phy1_addr);
/* get the data */
value = readl(&mac_dev->mdio_phy1[reg]);
return value & 0xffff;
}
static int tse_mdio_write(struct mii_dev *bus, int addr, int devad, int reg,
u16 val)
{
struct altera_tse_priv *priv = bus->priv;
struct alt_tse_mac *mac_dev = priv->mac_dev;
/* set mdio address */
writel(addr, &mac_dev->mdio_phy1_addr);
/* set the data */
writel(val, &mac_dev->mdio_phy1[reg]);
return 0;
}
static int tse_mdio_init(const char *name, struct altera_tse_priv *priv)
{
struct mii_dev *bus = mdio_alloc();
if (!bus) {
printf("Failed to allocate MDIO bus\n");
return -ENOMEM;
}
bus->read = tse_mdio_read;
bus->write = tse_mdio_write;
snprintf(bus->name, sizeof(bus->name), "%s", name);
bus->priv = (void *)priv;
return mdio_register(bus);
}
static int tse_phy_init(struct altera_tse_priv *priv, void *dev)
{
struct phy_device *phydev;
unsigned int mask = 0xffffffff;
if (priv->phyaddr)
mask = 1 << priv->phyaddr;
phydev = phy_find_by_mask(priv->bus, mask, priv->interface);
if (!phydev)
return -ENODEV;
phy_connect_dev(phydev, dev);
phydev->supported &= PHY_GBIT_FEATURES;
phydev->advertising = phydev->supported;
priv->phydev = phydev;
phy_config(phydev);
return 0;
}
static int altera_tse_write_hwaddr(struct udevice *dev)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
struct alt_tse_mac *mac_dev = priv->mac_dev;
struct eth_pdata *pdata = dev_get_platdata(dev);
u8 *hwaddr = pdata->enetaddr;
u32 mac_lo, mac_hi;
mac_lo = (hwaddr[3] << 24) | (hwaddr[2] << 16) |
(hwaddr[1] << 8) | hwaddr[0];
mac_hi = (hwaddr[5] << 8) | hwaddr[4];
debug("Set MAC address to 0x%04x%08x\n", mac_hi, mac_lo);
writel(mac_lo, &mac_dev->mac_addr_0);
writel(mac_hi, &mac_dev->mac_addr_1);
writel(mac_lo, &mac_dev->supp_mac_addr_0_0);
writel(mac_hi, &mac_dev->supp_mac_addr_0_1);
writel(mac_lo, &mac_dev->supp_mac_addr_1_0);
writel(mac_hi, &mac_dev->supp_mac_addr_1_1);
writel(mac_lo, &mac_dev->supp_mac_addr_2_0);
writel(mac_hi, &mac_dev->supp_mac_addr_2_1);
writel(mac_lo, &mac_dev->supp_mac_addr_3_0);
writel(mac_hi, &mac_dev->supp_mac_addr_3_1);
return 0;
}
static int altera_tse_send(struct udevice *dev, void *packet, int length)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
unsigned long tx_buf = (unsigned long)packet;
flush_dcache_range(tx_buf, tx_buf + length);
return priv->ops->send(dev, packet, length);
}
static int altera_tse_recv(struct udevice *dev, int flags, uchar **packetp)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
return priv->ops->recv(dev, flags, packetp);
}
static int altera_tse_free_pkt(struct udevice *dev, uchar *packet,
int length)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
unsigned long rx_buf = (unsigned long)priv->rx_buf;
invalidate_dcache_range(rx_buf, rx_buf + PKTSIZE_ALIGN);
return priv->ops->free_pkt(dev, packet, length);
}
static void altera_tse_stop(struct udevice *dev)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
priv->ops->stop(dev);
altera_tse_stop_mac(priv);
}
static int altera_tse_start(struct udevice *dev)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
struct alt_tse_mac *mac_dev = priv->mac_dev;
u32 val;
int ret;
/* need to create sgdma */
debug("Configuring rx desc\n");
altera_tse_free_pkt(dev, priv->rx_buf, PKTSIZE_ALIGN);
/* start TSE */
debug("Configuring TSE Mac\n");
/* Initialize MAC registers */
writel(PKTSIZE_ALIGN, &mac_dev->max_frame_length);
writel(priv->rx_fifo_depth - 16, &mac_dev->rx_sel_empty_threshold);
writel(0, &mac_dev->rx_sel_full_threshold);
writel(priv->tx_fifo_depth - 16, &mac_dev->tx_sel_empty_threshold);
writel(0, &mac_dev->tx_sel_full_threshold);
writel(8, &mac_dev->rx_almost_empty_threshold);
writel(8, &mac_dev->rx_almost_full_threshold);
writel(8, &mac_dev->tx_almost_empty_threshold);
writel(3, &mac_dev->tx_almost_full_threshold);
/* NO Shift */
writel(0, &mac_dev->rx_cmd_stat);
writel(0, &mac_dev->tx_cmd_stat);
/* enable MAC */
val = ALTERA_TSE_CMD_TX_ENA_MSK | ALTERA_TSE_CMD_RX_ENA_MSK;
writel(val, &mac_dev->command_config);
/* Start up the PHY */
ret = phy_startup(priv->phydev);
if (ret) {
debug("Could not initialize PHY %s\n",
priv->phydev->dev->name);
return ret;
}
tse_adjust_link(priv, priv->phydev);
if (!priv->phydev->link)
return -EIO;
return 0;
}
static const struct tse_ops tse_sgdma_ops = {
.send = altera_tse_send_sgdma,
.recv = altera_tse_recv_sgdma,
.free_pkt = altera_tse_free_pkt_sgdma,
.stop = altera_tse_stop_sgdma,
};
static const struct tse_ops tse_msgdma_ops = {
.send = altera_tse_send_msgdma,
.recv = altera_tse_recv_msgdma,
.free_pkt = altera_tse_free_pkt_msgdma,
.stop = altera_tse_stop_msgdma,
};
static int altera_tse_probe(struct udevice *dev)
{
struct eth_pdata *pdata = dev_get_platdata(dev);
struct altera_tse_priv *priv = dev_get_priv(dev);
void *blob = (void *)gd->fdt_blob;
int node = dev_of_offset(dev);
const char *list, *end;
const fdt32_t *cell;
void *base, *desc_mem = NULL;
unsigned long addr, size;
int parent, addrc, sizec;
int len, idx;
int ret;
priv->dma_type = dev_get_driver_data(dev);
if (priv->dma_type == ALT_SGDMA)
priv->ops = &tse_sgdma_ops;
else
priv->ops = &tse_msgdma_ops;
/*
* decode regs. there are multiple reg tuples, and they need to
* match with reg-names.
*/
parent = fdt_parent_offset(blob, node);
fdt_support_default_count_cells(blob, parent, &addrc, &sizec);
list = fdt_getprop(blob, node, "reg-names", &len);
if (!list)
return -ENOENT;
end = list + len;
cell = fdt_getprop(blob, node, "reg", &len);
if (!cell)
return -ENOENT;
idx = 0;
while (list < end) {
addr = fdt_translate_address((void *)blob,
node, cell + idx);
size = fdt_addr_to_cpu(cell[idx + addrc]);
base = map_physmem(addr, size, MAP_NOCACHE);
len = strlen(list);
if (strcmp(list, "control_port") == 0)
priv->mac_dev = base;
else if (strcmp(list, "rx_csr") == 0)
priv->sgdma_rx = base;
else if (strcmp(list, "rx_desc") == 0)
priv->rx_desc = base;
else if (strcmp(list, "rx_resp") == 0)
priv->rx_resp = base;
else if (strcmp(list, "tx_csr") == 0)
priv->sgdma_tx = base;
else if (strcmp(list, "tx_desc") == 0)
priv->tx_desc = base;
else if (strcmp(list, "s1") == 0)
desc_mem = base;
idx += addrc + sizec;
list += (len + 1);
}
/* decode fifo depth */
priv->rx_fifo_depth = fdtdec_get_int(blob, node,
"rx-fifo-depth", 0);
priv->tx_fifo_depth = fdtdec_get_int(blob, node,
"tx-fifo-depth", 0);
/* decode phy */
addr = fdtdec_get_int(blob, node,
"phy-handle", 0);
addr = fdt_node_offset_by_phandle(blob, addr);
priv->phyaddr = fdtdec_get_int(blob, addr,
"reg", 0);
/* init desc */
if (priv->dma_type == ALT_SGDMA) {
len = sizeof(struct alt_sgdma_descriptor) * 4;
if (!desc_mem) {
desc_mem = dma_alloc_coherent(len, &addr);
if (!desc_mem)
return -ENOMEM;
}
memset(desc_mem, 0, len);
priv->tx_desc = desc_mem;
priv->rx_desc = priv->tx_desc +
2 * sizeof(struct alt_sgdma_descriptor);
}
/* allocate recv packet buffer */
priv->rx_buf = malloc_cache_aligned(PKTSIZE_ALIGN);
if (!priv->rx_buf)
return -ENOMEM;
/* stop controller */
debug("Reset TSE & SGDMAs\n");
altera_tse_stop(dev);
/* start the phy */
priv->interface = pdata->phy_interface;
tse_mdio_init(dev->name, priv);
priv->bus = miiphy_get_dev_by_name(dev->name);
ret = tse_phy_init(priv, dev);
return ret;
}
static int altera_tse_ofdata_to_platdata(struct udevice *dev)
{
struct eth_pdata *pdata = dev_get_platdata(dev);
const char *phy_mode;
pdata->phy_interface = -1;
phy_mode = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "phy-mode",
NULL);
if (phy_mode)
pdata->phy_interface = phy_get_interface_by_name(phy_mode);
if (pdata->phy_interface == -1) {
debug("%s: Invalid PHY interface '%s'\n", __func__, phy_mode);
return -EINVAL;
}
return 0;
}
static const struct eth_ops altera_tse_ops = {
.start = altera_tse_start,
.send = altera_tse_send,
.recv = altera_tse_recv,
.free_pkt = altera_tse_free_pkt,
.stop = altera_tse_stop,
.write_hwaddr = altera_tse_write_hwaddr,
};
static const struct udevice_id altera_tse_ids[] = {
{ .compatible = "altr,tse-msgdma-1.0", .data = ALT_MSGDMA },
{ .compatible = "altr,tse-1.0", .data = ALT_SGDMA },
{}
};
U_BOOT_DRIVER(altera_tse) = {
.name = "altera_tse",
.id = UCLASS_ETH,
.of_match = altera_tse_ids,
.ops = &altera_tse_ops,
.ofdata_to_platdata = altera_tse_ofdata_to_platdata,
.platdata_auto = sizeof(struct eth_pdata),
.priv_auto = sizeof(struct altera_tse_priv),
.probe = altera_tse_probe,
};