/* * Altera 10/100/1000 triple speed ethernet mac driver * * Copyright (C) 2008 Altera Corporation. * Copyright (C) 2010 Thomas Chou * * 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 #include #include #include #include #include #include #include #include #include #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(®s->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, ®s->control); /* Clear status */ writel(0xff, ®s->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), ®s->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, ®s->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; 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); of_bus_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, "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_alloc_size = sizeof(struct eth_pdata), .priv_auto_alloc_size = sizeof(struct altera_tse_priv), .probe = altera_tse_probe, };