Merge branch 'FEC-MDIO-speedups'
Andrew Lunn says: ==================== FEC MDIO speedups This patchset gives a number of speedups for MDIO with the FEC. Replacing interrupt driven with polled IO brings a big speedup due to the overheads of interrupts compared to the short time interval. Clocking the bus faster, when the MDIO targets supports it, can double the transfer rate. And suppressing the preamble, if devices support it, makes each transaction faster. By default the MDIO clock remains 2.5MHz and preables are used. But these can now be controlled from the device tree. Since these are generic properties applicable to more than just FEC, these have been added to the generic MDIO binding documentation. v2: readl_poll_timeout() Add patches to set bus frequency and preamble disable v3: Add Reviewed tags uS->us readl_poll_timeout_atomic() Extend DT binding documentation ==================== Acked-by: Fugang Duan <fugang.duan@nxp.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
749d22e8f6
|
@ -82,6 +82,7 @@ ethernet@83fec000 {
|
|||
phy-supply = <®_fec_supply>;
|
||||
phy-handle = <ðphy>;
|
||||
mdio {
|
||||
clock-frequency = <5000000>;
|
||||
ethphy: ethernet-phy@6 {
|
||||
compatible = "ethernet-phy-ieee802.3-c22";
|
||||
reg = <6>;
|
||||
|
|
|
@ -39,6 +39,18 @@ properties:
|
|||
and must therefore be appropriately determined based on all PHY
|
||||
requirements (maximum value of all per-PHY RESET pulse widths).
|
||||
|
||||
clock-frequency:
|
||||
description:
|
||||
Desired MDIO bus clock frequency in Hz. Values greater than IEEE 802.3
|
||||
defined 2.5MHz should only be used when all devices on the bus support
|
||||
the given clock speed.
|
||||
|
||||
suppress-preamble:
|
||||
description:
|
||||
The 32 bit preamble should be suppressed. In order for this to
|
||||
work, all devices on the bus must support suppressed preamble.
|
||||
type: boolean
|
||||
|
||||
patternProperties:
|
||||
"^ethernet-phy@[0-9a-f]+$":
|
||||
type: object
|
||||
|
|
|
@ -376,8 +376,7 @@ struct bufdesc_ex {
|
|||
#define FEC_ENET_TS_AVAIL ((uint)0x00010000)
|
||||
#define FEC_ENET_TS_TIMER ((uint)0x00008000)
|
||||
|
||||
#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII)
|
||||
#define FEC_NAPI_IMASK FEC_ENET_MII
|
||||
#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF)
|
||||
#define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF))
|
||||
|
||||
/* ENET interrupt coalescing macro define */
|
||||
|
@ -543,7 +542,6 @@ struct fec_enet_private {
|
|||
int link;
|
||||
int full_duplex;
|
||||
int speed;
|
||||
struct completion mdio_done;
|
||||
int irq[FEC_IRQ_NUM];
|
||||
bool bufdesc_ex;
|
||||
int pause_flag;
|
||||
|
|
|
@ -976,8 +976,8 @@ fec_restart(struct net_device *ndev)
|
|||
writel((__force u32)cpu_to_be32(temp_mac[1]),
|
||||
fep->hwp + FEC_ADDR_HIGH);
|
||||
|
||||
/* Clear any outstanding interrupt. */
|
||||
writel(0xffffffff, fep->hwp + FEC_IEVENT);
|
||||
/* Clear any outstanding interrupt, except MDIO. */
|
||||
writel((0xffffffff & ~FEC_ENET_MII), fep->hwp + FEC_IEVENT);
|
||||
|
||||
fec_enet_bd_init(ndev);
|
||||
|
||||
|
@ -1123,7 +1123,7 @@ fec_restart(struct net_device *ndev)
|
|||
if (fep->link)
|
||||
writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
|
||||
else
|
||||
writel(FEC_ENET_MII, fep->hwp + FEC_IMASK);
|
||||
writel(0, fep->hwp + FEC_IMASK);
|
||||
|
||||
/* Init the interrupt coalescing */
|
||||
fec_enet_itr_coal_init(ndev);
|
||||
|
@ -1652,6 +1652,10 @@ fec_enet_interrupt(int irq, void *dev_id)
|
|||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
int_events = readl(fep->hwp + FEC_IEVENT);
|
||||
|
||||
/* Don't clear MDIO events, we poll for those */
|
||||
int_events &= ~FEC_ENET_MII;
|
||||
|
||||
writel(int_events, fep->hwp + FEC_IEVENT);
|
||||
fec_enet_collect_events(fep, int_events);
|
||||
|
||||
|
@ -1659,16 +1663,12 @@ fec_enet_interrupt(int irq, void *dev_id)
|
|||
ret = IRQ_HANDLED;
|
||||
|
||||
if (napi_schedule_prep(&fep->napi)) {
|
||||
/* Disable the NAPI interrupts */
|
||||
writel(FEC_NAPI_IMASK, fep->hwp + FEC_IMASK);
|
||||
/* Disable interrupts */
|
||||
writel(0, fep->hwp + FEC_IMASK);
|
||||
__napi_schedule(&fep->napi);
|
||||
}
|
||||
}
|
||||
|
||||
if (int_events & FEC_ENET_MII) {
|
||||
ret = IRQ_HANDLED;
|
||||
complete(&fep->mdio_done);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1818,11 +1818,24 @@ static void fec_enet_adjust_link(struct net_device *ndev)
|
|||
phy_print_status(phy_dev);
|
||||
}
|
||||
|
||||
static int fec_enet_mdio_wait(struct fec_enet_private *fep)
|
||||
{
|
||||
uint ievent;
|
||||
int ret;
|
||||
|
||||
ret = readl_poll_timeout_atomic(fep->hwp + FEC_IEVENT, ievent,
|
||||
ievent & FEC_ENET_MII, 2, 30000);
|
||||
|
||||
if (!ret)
|
||||
writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
|
||||
{
|
||||
struct fec_enet_private *fep = bus->priv;
|
||||
struct device *dev = &fep->pdev->dev;
|
||||
unsigned long time_left;
|
||||
int ret = 0, frame_start, frame_addr, frame_op;
|
||||
bool is_c45 = !!(regnum & MII_ADDR_C45);
|
||||
|
||||
|
@ -1830,8 +1843,6 @@ static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
reinit_completion(&fep->mdio_done);
|
||||
|
||||
if (is_c45) {
|
||||
frame_start = FEC_MMFR_ST_C45;
|
||||
|
||||
|
@ -1843,11 +1854,9 @@ static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
|
|||
fep->hwp + FEC_MII_DATA);
|
||||
|
||||
/* wait for end of transfer */
|
||||
time_left = wait_for_completion_timeout(&fep->mdio_done,
|
||||
usecs_to_jiffies(FEC_MII_TIMEOUT));
|
||||
if (time_left == 0) {
|
||||
ret = fec_enet_mdio_wait(fep);
|
||||
if (ret) {
|
||||
netdev_err(fep->netdev, "MDIO address write timeout\n");
|
||||
ret = -ETIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -1866,11 +1875,9 @@ static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
|
|||
FEC_MMFR_TA, fep->hwp + FEC_MII_DATA);
|
||||
|
||||
/* wait for end of transfer */
|
||||
time_left = wait_for_completion_timeout(&fep->mdio_done,
|
||||
usecs_to_jiffies(FEC_MII_TIMEOUT));
|
||||
if (time_left == 0) {
|
||||
ret = fec_enet_mdio_wait(fep);
|
||||
if (ret) {
|
||||
netdev_err(fep->netdev, "MDIO read timeout\n");
|
||||
ret = -ETIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -1888,7 +1895,6 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
|
|||
{
|
||||
struct fec_enet_private *fep = bus->priv;
|
||||
struct device *dev = &fep->pdev->dev;
|
||||
unsigned long time_left;
|
||||
int ret, frame_start, frame_addr;
|
||||
bool is_c45 = !!(regnum & MII_ADDR_C45);
|
||||
|
||||
|
@ -1898,8 +1904,6 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
|
|||
else
|
||||
ret = 0;
|
||||
|
||||
reinit_completion(&fep->mdio_done);
|
||||
|
||||
if (is_c45) {
|
||||
frame_start = FEC_MMFR_ST_C45;
|
||||
|
||||
|
@ -1911,11 +1915,9 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
|
|||
fep->hwp + FEC_MII_DATA);
|
||||
|
||||
/* wait for end of transfer */
|
||||
time_left = wait_for_completion_timeout(&fep->mdio_done,
|
||||
usecs_to_jiffies(FEC_MII_TIMEOUT));
|
||||
if (time_left == 0) {
|
||||
ret = fec_enet_mdio_wait(fep);
|
||||
if (ret) {
|
||||
netdev_err(fep->netdev, "MDIO address write timeout\n");
|
||||
ret = -ETIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
|
@ -1931,12 +1933,9 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
|
|||
fep->hwp + FEC_MII_DATA);
|
||||
|
||||
/* wait for end of transfer */
|
||||
time_left = wait_for_completion_timeout(&fep->mdio_done,
|
||||
usecs_to_jiffies(FEC_MII_TIMEOUT));
|
||||
if (time_left == 0) {
|
||||
ret = fec_enet_mdio_wait(fep);
|
||||
if (ret)
|
||||
netdev_err(fep->netdev, "MDIO write timeout\n");
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
out:
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
|
@ -2065,9 +2064,11 @@ static int fec_enet_mii_init(struct platform_device *pdev)
|
|||
static struct mii_bus *fec0_mii_bus;
|
||||
struct net_device *ndev = platform_get_drvdata(pdev);
|
||||
struct fec_enet_private *fep = netdev_priv(ndev);
|
||||
bool suppress_preamble = false;
|
||||
struct device_node *node;
|
||||
int err = -ENXIO;
|
||||
u32 mii_speed, holdtime;
|
||||
u32 bus_freq;
|
||||
|
||||
/*
|
||||
* The i.MX28 dual fec interfaces are not equal.
|
||||
|
@ -2095,15 +2096,23 @@ static int fec_enet_mii_init(struct platform_device *pdev)
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
bus_freq = 2500000; /* 2.5MHz by default */
|
||||
node = of_get_child_by_name(pdev->dev.of_node, "mdio");
|
||||
if (node) {
|
||||
of_property_read_u32(node, "clock-frequency", &bus_freq);
|
||||
suppress_preamble = of_property_read_bool(node,
|
||||
"suppress-preamble");
|
||||
}
|
||||
|
||||
/*
|
||||
* Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed)
|
||||
* Set MII speed (= clk_get_rate() / 2 * phy_speed)
|
||||
*
|
||||
* The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while
|
||||
* for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28
|
||||
* Reference Manual has an error on this, and gets fixed on i.MX6Q
|
||||
* document.
|
||||
*/
|
||||
mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000);
|
||||
mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), bus_freq * 2);
|
||||
if (fep->quirks & FEC_QUIRK_ENET_MAC)
|
||||
mii_speed--;
|
||||
if (mii_speed > 63) {
|
||||
|
@ -2130,8 +2139,14 @@ static int fec_enet_mii_init(struct platform_device *pdev)
|
|||
|
||||
fep->phy_speed = mii_speed << 1 | holdtime << 8;
|
||||
|
||||
if (suppress_preamble)
|
||||
fep->phy_speed |= BIT(7);
|
||||
|
||||
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
|
||||
|
||||
/* Clear any pending transaction complete indication */
|
||||
writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
|
||||
|
||||
fep->mii_bus = mdiobus_alloc();
|
||||
if (fep->mii_bus == NULL) {
|
||||
err = -ENOMEM;
|
||||
|
@ -2146,7 +2161,6 @@ static int fec_enet_mii_init(struct platform_device *pdev)
|
|||
fep->mii_bus->priv = fep;
|
||||
fep->mii_bus->parent = &pdev->dev;
|
||||
|
||||
node = of_get_child_by_name(pdev->dev.of_node, "mdio");
|
||||
err = of_mdiobus_register(fep->mii_bus, node);
|
||||
of_node_put(node);
|
||||
if (err)
|
||||
|
@ -3674,7 +3688,6 @@ fec_probe(struct platform_device *pdev)
|
|||
fep->irq[i] = irq;
|
||||
}
|
||||
|
||||
init_completion(&fep->mdio_done);
|
||||
ret = fec_enet_mii_init(pdev);
|
||||
if (ret)
|
||||
goto failed_mii_init;
|
||||
|
|
Loading…
Reference in New Issue
Block a user