193 lines
5.2 KiB
C
193 lines
5.2 KiB
C
|
#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;
|
||
|
}
|