ckfwq/linux-3.0.4/drivers/net/gsc3280mac/gsc3280mac_mdio.c

193 lines
5.2 KiB
C
Raw Normal View History

2024-12-30 10:53:50 +08:00
#include <linux/mii.h>
#include <linux/phy.h>
#include <linux/slab.h>
#include "gsc3280mac.h"
#define MII_BUSY 0x00000001
#define MII_WRITE 0x00000002
/**
* gsc3280_mdio_read
* @bus: points to the mii_bus structure
* @phyaddr: MII addr reg bits 15-11
* @phyreg: MII addr reg bits 10-6
* Description: it reads data from the MII register from within the phy device.
* For the GSC3280MAC, we must set the bit 0 in the MII address register while
* accessing the PHY registers.
* Fortunately, it seems this has no drawback for the MAC.
*/
static int gsc3280_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg)
{
struct net_device *ndev = bus->priv;
struct gsc3280mac_priv *priv = netdev_priv(ndev);
unsigned int mii_address = priv->hw->mii.addr;
unsigned int mii_data = priv->hw->mii.data;
int data;
u16 regValue = (((phyaddr << 11) & (0x0000F800)) |
((phyreg << 6) & (0x000007C0)));
regValue |= MII_BUSY | ((priv->plat->clk_csr & 7) << 2);
do {} while (((readl(priv->ioaddr + mii_address)) & MII_BUSY) == 1);
writel(regValue, priv->ioaddr + mii_address);
do {} while (((readl(priv->ioaddr + mii_address)) & MII_BUSY) == 1);
/* Read the data from the MII data register */
data = (int)readl(priv->ioaddr + mii_data);
return data;
}
/**
* gsc3280_mdio_write
* @bus: points to the mii_bus structure
* @phyaddr: MII addr reg bits 15-11
* @phyreg: MII addr reg bits 10-6
* @phydata: phy data
* Description: it writes the data into the MII register from within the device.
*/
static int gsc3280_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg,
u16 phydata)
{
struct net_device *ndev = bus->priv;
struct gsc3280mac_priv *priv = netdev_priv(ndev);
unsigned int mii_address = priv->hw->mii.addr;
unsigned int mii_data = priv->hw->mii.data;
u16 value =
(((phyaddr << 11) & (0x0000F800)) | ((phyreg << 6) & (0x000007C0)))
| MII_WRITE;
value |= MII_BUSY | ((priv->plat->clk_csr & 7) << 2);
/* Wait until any existing MII operation is complete */
do {} while (((readl(priv->ioaddr + mii_address)) & MII_BUSY) == 1);
/* Set the MII address register to write */
writel(phydata, priv->ioaddr + mii_data);
writel(value, priv->ioaddr + mii_address);
/* Wait until any existing MII operation is complete */
do {} while (((readl(priv->ioaddr + mii_address)) & MII_BUSY) == 1);
return 0;
}
/**
* gsc3280_mdio_reset
* @bus: points to the mii_bus structure
* Description: reset the MII bus
*/
static int gsc3280_mdio_reset(struct mii_bus *bus)
{
struct net_device *ndev = bus->priv;
struct gsc3280mac_priv *priv = netdev_priv(ndev);
unsigned int mii_address = priv->hw->mii.addr;
if (priv->phy_reset) {
pr_debug("gsc3280_mdio_reset: calling phy_reset\n");
priv->phy_reset(priv->plat->bsp_priv);
}
/* This is a workaround for problems with the STE101P PHY.
* It doesn't complete its reset until at least one clock cycle
* on MDC, so perform a dummy mdio read.
*/
writel(0, priv->ioaddr + mii_address);
return 0;
}
/**
* gsc3280_mdio_register
* @ndev: net device structure
* Description: it registers the MII bus
*/
int gsc3280mac_mdio_register(struct net_device *ndev)
{
int err = 0;
struct mii_bus *new_bus;
int *irqlist;
struct gsc3280mac_priv *priv = netdev_priv(ndev);
int addr, found;
new_bus = mdiobus_alloc();
if (new_bus == NULL)
return -ENOMEM;
irqlist = kzalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
if (irqlist == NULL) {
err = -ENOMEM;
goto irqlist_alloc_fail;
}
/* Assign IRQ to phy at address phy_addr */
if (priv->phy_addr != -1)
irqlist[priv->phy_addr] = priv->phy_irq;
new_bus->name = "GSC3280 MII Bus";
new_bus->read = &gsc3280_mdio_read;
new_bus->write = &gsc3280_mdio_write;
new_bus->reset = &gsc3280_mdio_reset;
snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", priv->plat->bus_id);
new_bus->priv = ndev;
new_bus->irq = irqlist;
new_bus->phy_mask = priv->phy_mask;
new_bus->parent = priv->device;
err = mdiobus_register(new_bus);
if (err != 0) {
pr_err("%s: Cannot register as MDIO bus\n", new_bus->name);
goto bus_register_fail;
}
priv->mii = new_bus;
found = 0;
for (addr = 0; addr < 32; addr++) {
struct phy_device *phydev = new_bus->phy_map[addr];
if (phydev) {
if (priv->phy_addr == -1) {
priv->phy_addr = addr;
phydev->irq = priv->phy_irq;
irqlist[addr] = priv->phy_irq;
}
pr_info("%s: PHY ID %08x at %d IRQ %d (%s)%s\n",
ndev->name, phydev->phy_id, addr,
phydev->irq, dev_name(&phydev->dev),
(addr == priv->phy_addr) ? " active" : "");
found = 1;
if(found)
goto found_bus;
}
}
if (!found)
pr_warning("%s: No PHY found\n", ndev->name);
found_bus:
return 0;
bus_register_fail:
kfree(irqlist);
irqlist_alloc_fail:
kfree(new_bus);
return err;
}
/**
* gsc3280_mdio_unregister
* @ndev: net device structure
* Description: it unregisters the MII bus
*/
int gsc3280mac_mdio_unregister(struct net_device *ndev)
{
struct gsc3280mac_priv *priv = netdev_priv(ndev);
mdiobus_unregister(priv->mii);
priv->mii->priv = NULL;
kfree(priv->mii);
return 0;
}