mv643xx_eth: use the SMI done interrupt to wait for SMI access completion
If the platform code has passed us the IRQ number of the mv643xx_eth top-level error interrupt, use the error interrupt to wait for SMI access completion instead of polling the SMI busy bit, since SMI bus accesses can take up to tens of milliseconds. Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
This commit is contained in:
parent
2b3ba0e3ea
commit
45c5d3bc1e
1 changed files with 135 additions and 58 deletions
|
@ -72,6 +72,13 @@ static char mv643xx_eth_driver_version[] = "1.3";
|
|||
*/
|
||||
#define PHY_ADDR 0x0000
|
||||
#define SMI_REG 0x0004
|
||||
#define SMI_BUSY 0x10000000
|
||||
#define SMI_READ_VALID 0x08000000
|
||||
#define SMI_OPCODE_READ 0x04000000
|
||||
#define SMI_OPCODE_WRITE 0x00000000
|
||||
#define ERR_INT_CAUSE 0x0080
|
||||
#define ERR_INT_SMI_DONE 0x00000010
|
||||
#define ERR_INT_MASK 0x0084
|
||||
#define WINDOW_BASE(w) (0x0200 + ((w) << 3))
|
||||
#define WINDOW_SIZE(w) (0x0204 + ((w) << 3))
|
||||
#define WINDOW_REMAP_HIGH(w) (0x0280 + ((w) << 2))
|
||||
|
@ -253,6 +260,15 @@ struct mv643xx_eth_shared_private {
|
|||
*/
|
||||
struct mutex phy_lock;
|
||||
|
||||
/*
|
||||
* If we have access to the error interrupt pin (which is
|
||||
* somewhat misnamed as it not only reflects internal errors
|
||||
* but also reflects SMI completion), use that to wait for
|
||||
* SMI access completion instead of polling the SMI busy bit.
|
||||
*/
|
||||
int err_interrupt;
|
||||
wait_queue_head_t smi_busy_wait;
|
||||
|
||||
/*
|
||||
* Per-port MBUS window access register value.
|
||||
*/
|
||||
|
@ -979,68 +995,103 @@ static void txq_set_wrr(struct tx_queue *txq, int weight)
|
|||
|
||||
|
||||
/* mii management interface *************************************************/
|
||||
#define SMI_BUSY 0x10000000
|
||||
#define SMI_READ_VALID 0x08000000
|
||||
#define SMI_OPCODE_READ 0x04000000
|
||||
#define SMI_OPCODE_WRITE 0x00000000
|
||||
|
||||
static void smi_reg_read(struct mv643xx_eth_private *mp, unsigned int addr,
|
||||
unsigned int reg, unsigned int *value)
|
||||
static irqreturn_t mv643xx_eth_err_irq(int irq, void *dev_id)
|
||||
{
|
||||
void __iomem *smi_reg = mp->shared_smi->base + SMI_REG;
|
||||
int i;
|
||||
struct mv643xx_eth_shared_private *msp = dev_id;
|
||||
|
||||
/* the SMI register is a shared resource */
|
||||
mutex_lock(&mp->shared_smi->phy_lock);
|
||||
if (readl(msp->base + ERR_INT_CAUSE) & ERR_INT_SMI_DONE) {
|
||||
writel(~ERR_INT_SMI_DONE, msp->base + ERR_INT_CAUSE);
|
||||
wake_up(&msp->smi_busy_wait);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* wait for the SMI register to become available */
|
||||
for (i = 0; readl(smi_reg) & SMI_BUSY; i++) {
|
||||
if (i == 1000) {
|
||||
printk("%s: PHY busy timeout\n", mp->dev->name);
|
||||
goto out;
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int smi_is_done(struct mv643xx_eth_shared_private *msp)
|
||||
{
|
||||
return !(readl(msp->base + SMI_REG) & SMI_BUSY);
|
||||
}
|
||||
|
||||
static int smi_wait_ready(struct mv643xx_eth_shared_private *msp)
|
||||
{
|
||||
if (msp->err_interrupt == NO_IRQ) {
|
||||
int i;
|
||||
|
||||
for (i = 0; !smi_is_done(msp); i++) {
|
||||
if (i == 10)
|
||||
return -ETIMEDOUT;
|
||||
msleep(10);
|
||||
}
|
||||
udelay(10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!wait_event_timeout(msp->smi_busy_wait, smi_is_done(msp),
|
||||
msecs_to_jiffies(100)))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smi_reg_read(struct mv643xx_eth_private *mp,
|
||||
unsigned int addr, unsigned int reg)
|
||||
{
|
||||
struct mv643xx_eth_shared_private *msp = mp->shared_smi;
|
||||
void __iomem *smi_reg = msp->base + SMI_REG;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&msp->phy_lock);
|
||||
|
||||
if (smi_wait_ready(msp)) {
|
||||
printk("%s: SMI bus busy timeout\n", mp->dev->name);
|
||||
ret = -ETIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
writel(SMI_OPCODE_READ | (reg << 21) | (addr << 16), smi_reg);
|
||||
|
||||
/* now wait for the data to be valid */
|
||||
for (i = 0; !(readl(smi_reg) & SMI_READ_VALID); i++) {
|
||||
if (i == 1000) {
|
||||
printk("%s: PHY read timeout\n", mp->dev->name);
|
||||
goto out;
|
||||
}
|
||||
udelay(10);
|
||||
if (smi_wait_ready(msp)) {
|
||||
printk("%s: SMI bus busy timeout\n", mp->dev->name);
|
||||
ret = -ETIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*value = readl(smi_reg) & 0xffff;
|
||||
ret = readl(smi_reg);
|
||||
if (!(ret & SMI_READ_VALID)) {
|
||||
printk("%s: SMI bus read not valid\n", mp->dev->name);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret &= 0xffff;
|
||||
|
||||
out:
|
||||
mutex_unlock(&mp->shared_smi->phy_lock);
|
||||
mutex_unlock(&msp->phy_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void smi_reg_write(struct mv643xx_eth_private *mp,
|
||||
unsigned int addr,
|
||||
unsigned int reg, unsigned int value)
|
||||
static int smi_reg_write(struct mv643xx_eth_private *mp, unsigned int addr,
|
||||
unsigned int reg, unsigned int value)
|
||||
{
|
||||
void __iomem *smi_reg = mp->shared_smi->base + SMI_REG;
|
||||
int i;
|
||||
struct mv643xx_eth_shared_private *msp = mp->shared_smi;
|
||||
void __iomem *smi_reg = msp->base + SMI_REG;
|
||||
|
||||
/* the SMI register is a shared resource */
|
||||
mutex_lock(&mp->shared_smi->phy_lock);
|
||||
mutex_lock(&msp->phy_lock);
|
||||
|
||||
/* wait for the SMI register to become available */
|
||||
for (i = 0; readl(smi_reg) & SMI_BUSY; i++) {
|
||||
if (i == 1000) {
|
||||
printk("%s: PHY busy timeout\n", mp->dev->name);
|
||||
goto out;
|
||||
}
|
||||
udelay(10);
|
||||
if (smi_wait_ready(msp)) {
|
||||
printk("%s: SMI bus busy timeout\n", mp->dev->name);
|
||||
mutex_unlock(&msp->phy_lock);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
writel(SMI_OPCODE_WRITE | (reg << 21) |
|
||||
(addr << 16) | (value & 0xffff), smi_reg);
|
||||
out:
|
||||
mutex_unlock(&mp->shared_smi->phy_lock);
|
||||
|
||||
mutex_unlock(&msp->phy_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1877,16 +1928,19 @@ static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id)
|
|||
|
||||
static void phy_reset(struct mv643xx_eth_private *mp)
|
||||
{
|
||||
unsigned int data;
|
||||
int data;
|
||||
|
||||
data = smi_reg_read(mp, mp->phy_addr, MII_BMCR);
|
||||
if (data < 0)
|
||||
return;
|
||||
|
||||
smi_reg_read(mp, mp->phy_addr, MII_BMCR, &data);
|
||||
data |= BMCR_RESET;
|
||||
smi_reg_write(mp, mp->phy_addr, MII_BMCR, data);
|
||||
if (smi_reg_write(mp, mp->phy_addr, MII_BMCR, data) < 0)
|
||||
return;
|
||||
|
||||
do {
|
||||
udelay(1);
|
||||
smi_reg_read(mp, mp->phy_addr, MII_BMCR, &data);
|
||||
} while (data & BMCR_RESET);
|
||||
data = smi_reg_read(mp, mp->phy_addr, MII_BMCR);
|
||||
} while (data >= 0 && data & BMCR_RESET);
|
||||
}
|
||||
|
||||
static void port_start(struct mv643xx_eth_private *mp)
|
||||
|
@ -2214,11 +2268,7 @@ static void mv643xx_eth_netpoll(struct net_device *dev)
|
|||
static int mv643xx_eth_mdio_read(struct net_device *dev, int addr, int reg)
|
||||
{
|
||||
struct mv643xx_eth_private *mp = netdev_priv(dev);
|
||||
int val;
|
||||
|
||||
smi_reg_read(mp, addr, reg, &val);
|
||||
|
||||
return val;
|
||||
return smi_reg_read(mp, addr, reg);
|
||||
}
|
||||
|
||||
static void mv643xx_eth_mdio_write(struct net_device *dev, int addr, int reg, int val)
|
||||
|
@ -2317,6 +2367,24 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev)
|
|||
|
||||
mutex_init(&msp->phy_lock);
|
||||
|
||||
msp->err_interrupt = NO_IRQ;
|
||||
init_waitqueue_head(&msp->smi_busy_wait);
|
||||
|
||||
/*
|
||||
* Check whether the error interrupt is hooked up.
|
||||
*/
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (res != NULL) {
|
||||
int err;
|
||||
|
||||
err = request_irq(res->start, mv643xx_eth_err_irq,
|
||||
IRQF_SHARED, "mv643xx_eth", msp);
|
||||
if (!err) {
|
||||
writel(ERR_INT_SMI_DONE, msp->base + ERR_INT_MASK);
|
||||
msp->err_interrupt = res->start;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (Re-)program MBUS remapping windows if we are asked to.
|
||||
*/
|
||||
|
@ -2343,6 +2411,8 @@ static int mv643xx_eth_shared_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct mv643xx_eth_shared_private *msp = platform_get_drvdata(pdev);
|
||||
|
||||
if (msp->err_interrupt != NO_IRQ)
|
||||
free_irq(msp->err_interrupt, msp);
|
||||
iounmap(msp->base);
|
||||
kfree(msp);
|
||||
|
||||
|
@ -2431,13 +2501,20 @@ static void set_params(struct mv643xx_eth_private *mp,
|
|||
|
||||
static int phy_detect(struct mv643xx_eth_private *mp)
|
||||
{
|
||||
unsigned int data;
|
||||
unsigned int data2;
|
||||
int data;
|
||||
int data2;
|
||||
|
||||
smi_reg_read(mp, mp->phy_addr, MII_BMCR, &data);
|
||||
smi_reg_write(mp, mp->phy_addr, MII_BMCR, data ^ BMCR_ANENABLE);
|
||||
data = smi_reg_read(mp, mp->phy_addr, MII_BMCR);
|
||||
if (data < 0)
|
||||
return -ENODEV;
|
||||
|
||||
if (smi_reg_write(mp, mp->phy_addr, MII_BMCR, data ^ BMCR_ANENABLE) < 0)
|
||||
return -ENODEV;
|
||||
|
||||
data2 = smi_reg_read(mp, mp->phy_addr, MII_BMCR);
|
||||
if (data2 < 0)
|
||||
return -ENODEV;
|
||||
|
||||
smi_reg_read(mp, mp->phy_addr, MII_BMCR, &data2);
|
||||
if (((data ^ data2) & BMCR_ANENABLE) == 0)
|
||||
return -ENODEV;
|
||||
|
||||
|
|
Loading…
Reference in a new issue