blob: c64d3c52b8067f222f39a7e4533508f25ee04b42 [file] [log] [blame]
/*
* (C) Copyright 2011
* Author : Mindspeed Technologes
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
* */
#include <config.h>
#include <common.h>
#include <init.h>
#include <net.h>
#include <miidev.h>
#include <malloc.h>
#include <driver.h>
#include <asm/io.h>
#include <c2000_eth_pdata.h>
#include <mach/clkcore.h>
#include <mach/gpio.h>
#include "c2000_eth.h"
#ifdef CONFIG_AR8328_SWITCH
#include "ar8328.h"
#endif
struct gemac_s gem_info[] = {
/* PORT_0 configuration */
{
/* GEMAC config */
.gemac_mode = RGMII,
.gemac_speed = SPEED_1000M,
.gemac_duplex = DUPLEX_FULL,
.flags = EMAC0_FLAGS,
/* phy iface */
.phy_reg_index = EMAC_PORT_0,
},
/* PORT_1 configuration */
{
/* GEMAC config */
.gemac_mode = RGMII,
.gemac_speed = SPEED_1000M,
.gemac_duplex = DUPLEX_FULL,
.flags = EMAC1_FLAGS,
/* phy iface */
.phy_reg_index = EMAC_PORT_0,
},
/* PORT_2 configuration */
{
/* GEMAC config */
.gemac_mode = RGMII,
.gemac_speed = SPEED_1000M,
.gemac_duplex = DUPLEX_FULL,
.flags = EMAC2_FLAGS,
/* phy iface */
.phy_reg_index = EMAC_PORT_0,
},
};
/********************************************************************
* Helper functions for phy read/write
*******************************************************************/
/* Max MII register/address (we support) */
#define MII_REGISTER_MAX 31
#define MII_ADDRESS_MAX 31
#define MDIO_TIMEOUT 5000
/********************************************************************
* gem_phy_man_rd :
* Performs phy management read operation.
*******************************************************************/
static int gem_phy_man_rd(struct c2000_eth_dev *priv, u32 phy_addr, u32 phy_reg)
{
u32 write_data;
write_data = 0x60020000 | ( (phy_addr & (u32) 0x1f) << 23) | ( (phy_reg & (u32) 0x1f) << 18); // read_op
writel(write_data, priv->phyregisters + EMAC_PHY_MANAGEMENT);
return 0;
}
static int gem_phy_man_wr(struct c2000_eth_dev *priv, u32 phy_addr, u32 phy_reg, u32 val)
{
u32 write_data;
write_data = 0x50020000 | ( (phy_addr & (u32) 0x1f) << 23) | ( (phy_reg & (u32) 0x1f) << 18) | (val & (u32) 0xffff); // write_op
writel(write_data, priv->phyregisters + EMAC_PHY_MANAGEMENT);
return 0;
}
/** gem_phy_man_data
* Read the data section of phy management register.
* After a successful read opeeration the data will be stored in
* in this register in lower 16bits.
*/
static u32 gem_phy_man_data(struct c2000_eth_dev *priv)
{
u32 value;
value = readl(priv->phyregisters + EMAC_PHY_MANAGEMENT) & 0xFFFF;
return value;
}
#define EMAC_PHY_IDLE (1 << 2)
static int gem_phy_man_idle(struct c2000_eth_dev *priv)
{
u32 value;
value = readl(priv->phyregisters + EMAC_NETWORK_STATUS);
return ((value & EMAC_PHY_IDLE) == EMAC_PHY_IDLE);
}
static int gem_phy_timeout(struct c2000_eth_dev *priv, int timeout)
{
while(!gem_phy_man_idle(priv)) {
if (timeout-- <= 0) {
printf("Phy MDIO read/write timeout\n");
return -1;
}
//udelay(1);
}
return 0;
}
/** PHY read function
* Reads a 16bit value from a MII register
*
* @param[in] mdev Pointer to MII device structure
* @param[in] phy_addr
* @param[in] phy_reg
*
* @return 16bit value on success, a negivie value (-1) on error
*/
static int c2000_phy_read(struct mii_device *mdev, int phy_addr, int phy_reg)
{
struct eth_device *edev = mdev->edev;
struct c2000_eth_dev *priv = (struct c2000_eth_dev *)edev->priv;
int value;
if ((phy_addr > MII_ADDRESS_MAX) || (phy_reg > MII_REGISTER_MAX))
return -1;
gem_phy_man_rd(priv, phy_addr, phy_reg);
if (gem_phy_timeout(priv, MDIO_TIMEOUT))
return -1;
value = gem_phy_man_data(priv);
// dprint("%s: Addr: %d, Reg: %d, Val: %d\n",
// __func__, phy_addr, phy_reg, value);
return value;
}
/** PHY write function
* Writes a 16bit value to a MII register
*
* @param[in] mdev Pointer to MII device structure
* @param[in] phy_addr
* @param[in] phy_reg
* @param[in] value Value to be written to Phy
*
* @return On success returns 0, a negative value (-1) on error
*/
static int c2000_phy_write(struct mii_device *mdev, int phy_addr,
int phy_reg, int value)
{
struct eth_device *edev = mdev->edev;
struct c2000_eth_dev *priv = (struct c2000_eth_dev *)edev->priv;
if ((phy_addr > MII_ADDRESS_MAX) || (phy_reg > MII_REGISTER_MAX))
return -1;
gem_phy_man_wr(priv, phy_addr, phy_reg, value);
if (gem_phy_timeout(priv, MDIO_TIMEOUT))
return -1;
// dprint("%s: Addr: %d, Reg: %d, Val: %d\n",
// __func__, phy_addr, phy_reg, value);
return 0;
}
/** MAC Address converter
* Convert standard byte style ethernet address to format compatible with MAC.
*
* @param[in] enet_byte_addr Pointer to the mac address in byte format
* @param[out] Pointer to MAC_ADDR structure
*
* @return 0 on success, -1 on failure
*/
int gemac_enet_addr_byte_mac(u8 *enet_byte_addr, MAC_ADDR *enet_addr)
{
if ((enet_byte_addr == NULL) || (enet_addr == NULL))
{
return -1;
}
else
{
enet_addr->bottom = enet_byte_addr[0] |
(enet_byte_addr[1] << 8) |
(enet_byte_addr[2] << 16) |
(enet_byte_addr[3] << 24);
enet_addr->top = enet_byte_addr[4] |
(enet_byte_addr[5] << 8);
return 0;
}
}
/** Configures ethernet address to GEMAC ADDR1 location.
*
* @param[in] edev Pointer to the eth device structure
* @param[in] addr Pointer to ethernet address in byte order
*
* @return 0 on success (always success)
*/
static int c2000_set_ethaddr(struct eth_device *edev, unsigned char *addr)
{
struct c2000_eth_dev *priv = (struct c2000_eth_dev *)edev->priv;
MAC_ADDR enet_address = {0x0, 0x0};
gemac_enet_addr_byte_mac(addr, &enet_address);
gemac_set_laddr1(priv->gem->gemac_base, &enet_address);
return 0;
}
/** Get/Read configured ethernet mac address from GEMAC.
*
* @param[in] edev Pointer to eth device structure
* @param[out] addr Pointer to ethernet address to be copied
*
* @return 0 on success, always success.
*/
static int c2000_get_ethaddr(struct eth_device *edev, unsigned char *addr)
{
struct c2000_eth_dev *priv = (struct c2000_eth_dev *)edev->priv;
MAC_ADDR enet_address = {0x0, 0x0};
u16 *p = (u16 *)addr;
enet_address = gem_get_laddr1(priv->gem->gemac_base);
p[0] = enet_address.bottom & 0xffff;
p[1] = (enet_address.bottom >> 16) & 0xffff;
p[2] = enet_address.top & 0xffff;
return 0;
}
static int c2000_get_gemac_stats_len(struct eth_device *edev)
{
return sizeof(GEM_STATS);
}
static void c2000_get_gemac_stats(struct eth_device *edev, u32 *buf)
{
struct c2000_eth_dev *priv = (struct c2000_eth_dev *)edev->priv;
u32 *pstat = (u32*)gemac_get_stats(priv->gem->gemac_base);
int i;
for (i=0; i<(sizeof(GEM_STATS)/4); i++)
*buf++ = *pstat++;
}
/** SBL ethernet pkt recv function.
* - This funciton is called by network stack
*/
static int c2000_eth_recv(struct eth_device *edev)
{
struct c2000_eth_dev *priv = (struct c2000_eth_dev *)edev->priv;
u32 pkt_buf;
int len;
int phy_port;
len = pfe_recv(&pkt_buf, &phy_port);
if (len < 0)
return 0; //no packet in rx
dprint("Rx pkt: pkt_buf(%08x), phy_port(%d), len(%d)\n", pkt_buf, phy_port, len);
if (phy_port != priv->gemac_port) {
printf("Rx pkt not on expected port\n");
return 0;
}
/* send pkt to stack */
net_receive((unsigned char *)pkt_buf, len);
return 0;
}
/** SBL ethernet driver xmit function.
* This function is called by Network stack to send a packet
*
* @param[in] edev Pointer to ethernet driver control block
* @param[in] data Pointer to packet to send
* @param[in] length Length the packet
*
*/
static int c2000_eth_send(struct eth_device *edev, void *data, int length)
{
struct c2000_eth_dev *priv = (struct c2000_eth_dev *)edev->priv;
int rc;
uint64_t tx_tmo;
dprint("%s: iface %s:%d\n", __func__, edev->dev.name, edev->dev.id);
rc = pfe_send(priv->gemac_port, data, length);
if (rc < 0) {
pr_err("Tx Q full\n");
return 0;
}
/* check tx done */
tx_tmo = get_time_ns();
while (1) {
rc = pfe_tx_done();
if (rc == 0)
break;
if (is_timeout(tx_tmo, 3 * SECOND)) {
pr_err("Tx timeout, send failed\n");
break;
}
}
return 0;
}
/** Enables the PFE/GEMAC of specific interface.
*
* @param[in] edev Pointer to eth device structure.
*
* @return 0, always success.
*/
static int c2000_eth_open(struct eth_device *edev)
{
struct c2000_eth_dev *priv = (struct c2000_eth_dev *)edev->priv;
struct gemac_s *gem = priv->gem;
dprint("%s\n", __func__);
/* Re-negotiate speed and duplex */
if (!(gem->flags & GEMAC_NO_PHY))
{
int speed = gem->gemac_speed;
int duplex = gem->gemac_duplex;
miidev_speed_duplex(&priv->miidev, &speed, &duplex);
switch (speed)
{
case MII_SPEED_10M:
speed = SPEED_10M;
break;
case MII_SPEED_100M:
speed = SPEED_100M;
break;
case MII_SPEED_1000M:
speed = SPEED_1000M;
break;
case MII_SPEED_1000M_PCS:
speed = SPEED_1000M_PCS;
break;
}
duplex = (duplex == MII_DUPLEX_HALF) ? DUPLEX_HALF:DUPLEX_FULL;
gemac_set_speed(gem->gemac_base, speed);
gemac_set_duplex(gem->gemac_base, duplex);
}
/* Enable GPI */
gpi_enable(priv->gem->egpi_base);
/* Enable GEMAC for tx and rx */
gemac_enable(priv->gem->gemac_base);
return 0;
}
/** eth interface init
* - Most of the pfe init will be done in pfe_probe().
* - This function initializes specific GEMAC interface.
*
* @param[in] edev Pointer to eth device structure.
*
* @return 0, always success.
*/
static int c2000_eth_init(struct eth_device *edev)
{
struct c2000_eth_dev *priv = (struct c2000_eth_dev *)edev->priv;
struct gemac_s *gem = priv->gem;
dprint("%s:Interface: %s-%d\n", __func__, edev->dev.name, edev->dev.id);
/* GEMAC init */
pfe_gemac_init(gem->gemac_base, gem->gemac_mode, gem->gemac_speed, gem->gemac_duplex);
/* set ethernet mac address */
c2000_set_ethaddr(edev, priv->einfo->mac_addr);
if (!(gem->flags & GEMAC_NO_PHY) && (gem->gemac_mode == RGMII) )
{
miidev_enable_rgmii_rx_delay(&priv->miidev);
miidev_enable_rgmii_tx_delay(&priv->miidev);
miidev_restart_aneg(&priv->miidev);
}
return 0;
}
/** Stops or Disables GEMAC pointing to this eth iface.
*
* @param[in] edev Pointer to eth device structure.
*
* @return none
*/
static void c2000_eth_halt(struct eth_device *edev)
{
struct c2000_eth_dev *priv = (struct c2000_eth_dev *)edev->priv;
pr_info("%s:\n",__func__);
gemac_disable(priv->gem->gemac_base);
gpi_disable(priv->gem->egpi_base);
return;
}
/** Driver probe function called during board initialization sequence.
*
* @param[in] dev Pointer device data structure(devined in BSP).
*
* @return 0 on success, any negative value indicates failure.
*/
static int c2000_eth_probe(struct device_d *dev)
{
struct eth_device *edev;
struct c2000_eth_dev *priv;
struct pfe *pfe;
dprint("%s: dev: %s-%d\n",__func__, dev->name, dev->id);
//Bring HFE and GEMTX out of reset
writel(0x0, HFE_RESET);
writel(0x0, GEMTX_RESET);
if (dev->platform_data == NULL) {
pr_err("C2000: No platform data\n");
return -ENODEV;
}
edev = (struct eth_device *)xzalloc(sizeof(struct eth_device) + sizeof(struct c2000_eth_dev));
if (edev == NULL) {
pr_err("C2000: No memory\n");
return -ENOMEM;
}
dev->type_data = edev;
edev->priv = (void *)(edev + 1);
priv = (struct c2000_eth_dev *)edev->priv;
priv->einfo = (struct c2000_eth_platform_data *)dev->platform_data;
pfe = &priv->pfe;
pfe->cbus_baseaddr = (void *)COMCERTO_AXI_HFE_CFG_BASE;
pfe->ddr_baseaddr = (void *)CONFIG_DDR_BASEADDR;
pfe->ddr_phys_baseaddr = (unsigned long)CONFIG_DDR_PHYS_BASEADDR;
edev->init = c2000_eth_init;
edev->open = c2000_eth_open;
edev->send = c2000_eth_send;
edev->recv = c2000_eth_recv;
edev->halt = c2000_eth_halt;
edev->set_ethaddr = c2000_set_ethaddr;
edev->get_ethaddr = c2000_get_ethaddr;
edev->get_gemac_stats = c2000_get_gemac_stats;
edev->get_gemac_stats_len = c2000_get_gemac_stats_len;
priv->gemac_port = priv->einfo->gemac_port;
priv->gem = &gem_info[priv->gemac_port];
switch(priv->gemac_port) {
case EMAC_PORT_0:
default:
priv->gem->gemac_base = EMAC1_BASE_ADDR;
priv->gem->egpi_base = EGPI1_BASE_ADDR;
break;
case EMAC_PORT_1:
priv->gem->gemac_base = EMAC2_BASE_ADDR;
priv->gem->egpi_base = EGPI2_BASE_ADDR;
break;
case EMAC_PORT_2:
priv->gem->gemac_base = EMAC3_BASE_ADDR;
priv->gem->egpi_base = EGPI3_BASE_ADDR;
break;
}
pfe_probe(pfe);
priv->phyregisters = (void *)gem_info[gem_info[priv->gemac_port].phy_reg_index].gemac_base;
priv->miidev.read = c2000_phy_read;
priv->miidev.write = c2000_phy_write;
priv->miidev.address = priv->einfo->phy_addr;
priv->miidev.flags = 0;
priv->miidev.edev = edev;
if(priv->gemac_port == 0)
{
gemac_set_mdc_div(priv->gem->gemac_base, MDC_DIV_96);
gemac_enable_mdio(priv->gem->gemac_base);
mii_register(&priv->miidev);
}
c2000_eth_board_init(priv->gemac_port);
eth_register(edev);
return 0;
}
/** Driver remove function, called during shutdown
*
* @param dev Poninter to device data structure.
*
*/
static void c2000_eth_remove(struct device_d *dev)
{
struct eth_device *edev = dev->type_data;
struct c2000_eth_dev *priv = (struct c2000_eth_dev *)edev->priv;
struct pfe *pfe = &priv->pfe;
pfe_remove(pfe);
free(edev);
}
/* Driver description data to register */
static struct driver_d c2000_eth_driver = {
.name = "c2000_eth",
.probe = c2000_eth_probe,
.remove = c2000_eth_remove,
};
/** Register the ethernet driver to the OS
*/
static int c2000_register(void)
{
register_driver(&c2000_eth_driver);
#if 0
printk(KERN_INFO "\nPFE DDR map --\n");
printk(KERN_INFO " BMU2: 0x%7x size=0x%x\n", CONFIG_DDR_PHYS_BASEADDR+BMU2_DDR_BASEADDR, BMU2_DDR_SIZE);
printk(KERN_INFO "TMU queues: 0x%7x size=0x%x\n", CONFIG_DDR_PHYS_BASEADDR+TMU_LLM_BASEADDR, TMU_LLM_SIZE);
printk(KERN_INFO "HIF RxPkts: 0x%7x size=0x%x\n", CONFIG_DDR_PHYS_BASEADDR+HIF_RX_PKT_DDR_BASEADDR, HIF_RX_PKT_DDR_SIZE);
printk(KERN_INFO "HIF TxPkts: 0x%7x size=0x%x\n", CONFIG_DDR_PHYS_BASEADDR+HIF_TX_PKT_DDR_BASEADDR, HIF_TX_PKT_DDR_SIZE);
printk(KERN_INFO "HIF RxDesc: 0x%7x size=0x%x\n", CONFIG_DDR_PHYS_BASEADDR+HIF_DESC_BASEADDR, HIF_RX_DESC_SIZE);
printk(KERN_INFO "HIF TxDesc: 0x%7x size=0x%x\n", CONFIG_DDR_PHYS_BASEADDR+HIF_DESC_BASEADDR+HIF_RX_DESC_SIZE, HIF_TX_DESC_SIZE);
printk(KERN_INFO "FPPDiagCtl: 0x%7x size=0x%x\n", CONFIG_DDR_PHYS_BASEADDR+FPPDIAG_CTL_BASE_ADDR, FPPDIAG_CTL_SIZE);
printk(KERN_INFO "FPPDiag Pg: 0x%7x size=0x%x\n", CONFIG_DDR_PHYS_BASEADDR+FPPDIAG_PAGE_BASE_ADDR, FPPDIAG_PAGE_TOTAL_SIZE);
printk(KERN_INFO " Util code: 0x%7x size=0x%x\n", CONFIG_DDR_PHYS_BASEADDR+UTIL_CODE_BASEADDR, UTIL_CODE_SIZE);
printk(KERN_INFO " Util data: 0x%7x size=0x%x\n", CONFIG_DDR_PHYS_BASEADDR+UTIL_DDR_DATA_BASEADDR, UTIL_DDR_DATA_SIZE);
printk(KERN_INFO "Class data: 0x%7x size=0x%x\n", CONFIG_DDR_PHYS_BASEADDR+CLASS_DDR_DATA_BASEADDR, CLASS_DDR_DATA_SIZE);
printk(KERN_INFO " TMU data: 0x%7x size=0x%x\n", CONFIG_DDR_PHYS_BASEADDR+TMU_DDR_DATA_BASEADDR, TMU_DDR_DATA_SIZE);
printk(KERN_INFO " Route tbl: 0x%7x size=0x%x\n", CONFIG_DDR_PHYS_BASEADDR+ROUTE_TABLE_BASEADDR, ROUTE_TABLE_SIZE);
printk(KERN_INFO "Total Data Size = 0x%x\n\n", PFE_TOTAL_DATA_SIZE);
#endif
return 0;
}
device_initcall(c2000_register);