diff options
author | Claudiu Manoil <claudiu.manoil@freescale.com> | 2014-04-30 07:27:21 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-04-30 16:12:23 -0400 |
commit | 6ce29b0e2a04ea85617cd21099af67449a76f589 (patch) | |
tree | 646809dbccfa024bccc007e5d4449fff7cefaed0 /drivers/net | |
parent | 0cda345d1b2201dd15591b163e3c92bad5191745 (diff) |
gianfar: Avoid unnecessary reg accesses in adjust_link()
For phy devices that don't issue interrupts upon link
state changes, phylib polls the link state resulting in
repeated calls to adjust_link(), even if the link state
didn't change. As a result, some mac registers are
repeatedly read and written with the same values, which
is not ok.
To fix this, adjust_link() has been refactored to check
first whether the link state has changed and to take action
only if needed, updating mac registers and local state
variables. The 'new_state' local flag, set if one of the
link params changed (link, speed or duplex), has been
rendered useless and removed by this refactoring.
Signed-off-by: Claudiu Manoil <claudiu.manoil@freescale.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/ethernet/freescale/gianfar.c | 223 |
1 files changed, 113 insertions, 110 deletions
diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 9125d9abf099..e2d42475b006 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c | |||
@@ -121,6 +121,7 @@ static irqreturn_t gfar_error(int irq, void *dev_id); | |||
121 | static irqreturn_t gfar_transmit(int irq, void *dev_id); | 121 | static irqreturn_t gfar_transmit(int irq, void *dev_id); |
122 | static irqreturn_t gfar_interrupt(int irq, void *dev_id); | 122 | static irqreturn_t gfar_interrupt(int irq, void *dev_id); |
123 | static void adjust_link(struct net_device *dev); | 123 | static void adjust_link(struct net_device *dev); |
124 | static noinline void gfar_update_link_state(struct gfar_private *priv); | ||
124 | static int init_phy(struct net_device *dev); | 125 | static int init_phy(struct net_device *dev); |
125 | static int gfar_probe(struct platform_device *ofdev); | 126 | static int gfar_probe(struct platform_device *ofdev); |
126 | static int gfar_remove(struct platform_device *ofdev); | 127 | static int gfar_remove(struct platform_device *ofdev); |
@@ -3076,41 +3077,6 @@ static irqreturn_t gfar_interrupt(int irq, void *grp_id) | |||
3076 | return IRQ_HANDLED; | 3077 | return IRQ_HANDLED; |
3077 | } | 3078 | } |
3078 | 3079 | ||
3079 | static u32 gfar_get_flowctrl_cfg(struct gfar_private *priv) | ||
3080 | { | ||
3081 | struct phy_device *phydev = priv->phydev; | ||
3082 | u32 val = 0; | ||
3083 | |||
3084 | if (!phydev->duplex) | ||
3085 | return val; | ||
3086 | |||
3087 | if (!priv->pause_aneg_en) { | ||
3088 | if (priv->tx_pause_en) | ||
3089 | val |= MACCFG1_TX_FLOW; | ||
3090 | if (priv->rx_pause_en) | ||
3091 | val |= MACCFG1_RX_FLOW; | ||
3092 | } else { | ||
3093 | u16 lcl_adv, rmt_adv; | ||
3094 | u8 flowctrl; | ||
3095 | /* get link partner capabilities */ | ||
3096 | rmt_adv = 0; | ||
3097 | if (phydev->pause) | ||
3098 | rmt_adv = LPA_PAUSE_CAP; | ||
3099 | if (phydev->asym_pause) | ||
3100 | rmt_adv |= LPA_PAUSE_ASYM; | ||
3101 | |||
3102 | lcl_adv = mii_advertise_flowctrl(phydev->advertising); | ||
3103 | |||
3104 | flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv); | ||
3105 | if (flowctrl & FLOW_CTRL_TX) | ||
3106 | val |= MACCFG1_TX_FLOW; | ||
3107 | if (flowctrl & FLOW_CTRL_RX) | ||
3108 | val |= MACCFG1_RX_FLOW; | ||
3109 | } | ||
3110 | |||
3111 | return val; | ||
3112 | } | ||
3113 | |||
3114 | /* Called every time the controller might need to be made | 3080 | /* Called every time the controller might need to be made |
3115 | * aware of new link state. The PHY code conveys this | 3081 | * aware of new link state. The PHY code conveys this |
3116 | * information through variables in the phydev structure, and this | 3082 | * information through variables in the phydev structure, and this |
@@ -3120,83 +3086,12 @@ static u32 gfar_get_flowctrl_cfg(struct gfar_private *priv) | |||
3120 | static void adjust_link(struct net_device *dev) | 3086 | static void adjust_link(struct net_device *dev) |
3121 | { | 3087 | { |
3122 | struct gfar_private *priv = netdev_priv(dev); | 3088 | struct gfar_private *priv = netdev_priv(dev); |
3123 | struct gfar __iomem *regs = priv->gfargrp[0].regs; | ||
3124 | struct phy_device *phydev = priv->phydev; | 3089 | struct phy_device *phydev = priv->phydev; |
3125 | int new_state = 0; | ||
3126 | 3090 | ||
3127 | if (test_bit(GFAR_RESETTING, &priv->state)) | 3091 | if (unlikely(phydev->link != priv->oldlink || |
3128 | return; | 3092 | phydev->duplex != priv->oldduplex || |
3129 | 3093 | phydev->speed != priv->oldspeed)) | |
3130 | if (phydev->link) { | 3094 | gfar_update_link_state(priv); |
3131 | u32 tempval1 = gfar_read(®s->maccfg1); | ||
3132 | u32 tempval = gfar_read(®s->maccfg2); | ||
3133 | u32 ecntrl = gfar_read(®s->ecntrl); | ||
3134 | |||
3135 | /* Now we make sure that we can be in full duplex mode. | ||
3136 | * If not, we operate in half-duplex mode. | ||
3137 | */ | ||
3138 | if (phydev->duplex != priv->oldduplex) { | ||
3139 | new_state = 1; | ||
3140 | if (!(phydev->duplex)) | ||
3141 | tempval &= ~(MACCFG2_FULL_DUPLEX); | ||
3142 | else | ||
3143 | tempval |= MACCFG2_FULL_DUPLEX; | ||
3144 | |||
3145 | priv->oldduplex = phydev->duplex; | ||
3146 | } | ||
3147 | |||
3148 | if (phydev->speed != priv->oldspeed) { | ||
3149 | new_state = 1; | ||
3150 | switch (phydev->speed) { | ||
3151 | case 1000: | ||
3152 | tempval = | ||
3153 | ((tempval & ~(MACCFG2_IF)) | MACCFG2_GMII); | ||
3154 | |||
3155 | ecntrl &= ~(ECNTRL_R100); | ||
3156 | break; | ||
3157 | case 100: | ||
3158 | case 10: | ||
3159 | tempval = | ||
3160 | ((tempval & ~(MACCFG2_IF)) | MACCFG2_MII); | ||
3161 | |||
3162 | /* Reduced mode distinguishes | ||
3163 | * between 10 and 100 | ||
3164 | */ | ||
3165 | if (phydev->speed == SPEED_100) | ||
3166 | ecntrl |= ECNTRL_R100; | ||
3167 | else | ||
3168 | ecntrl &= ~(ECNTRL_R100); | ||
3169 | break; | ||
3170 | default: | ||
3171 | netif_warn(priv, link, dev, | ||
3172 | "Ack! Speed (%d) is not 10/100/1000!\n", | ||
3173 | phydev->speed); | ||
3174 | break; | ||
3175 | } | ||
3176 | |||
3177 | priv->oldspeed = phydev->speed; | ||
3178 | } | ||
3179 | |||
3180 | tempval1 &= ~(MACCFG1_TX_FLOW | MACCFG1_RX_FLOW); | ||
3181 | tempval1 |= gfar_get_flowctrl_cfg(priv); | ||
3182 | |||
3183 | gfar_write(®s->maccfg1, tempval1); | ||
3184 | gfar_write(®s->maccfg2, tempval); | ||
3185 | gfar_write(®s->ecntrl, ecntrl); | ||
3186 | |||
3187 | if (!priv->oldlink) { | ||
3188 | new_state = 1; | ||
3189 | priv->oldlink = 1; | ||
3190 | } | ||
3191 | } else if (priv->oldlink) { | ||
3192 | new_state = 1; | ||
3193 | priv->oldlink = 0; | ||
3194 | priv->oldspeed = 0; | ||
3195 | priv->oldduplex = -1; | ||
3196 | } | ||
3197 | |||
3198 | if (new_state && netif_msg_link(priv)) | ||
3199 | phy_print_status(phydev); | ||
3200 | } | 3095 | } |
3201 | 3096 | ||
3202 | /* Update the hash table based on the current list of multicast | 3097 | /* Update the hash table based on the current list of multicast |
@@ -3442,6 +3337,114 @@ static irqreturn_t gfar_error(int irq, void *grp_id) | |||
3442 | return IRQ_HANDLED; | 3337 | return IRQ_HANDLED; |
3443 | } | 3338 | } |
3444 | 3339 | ||
3340 | static u32 gfar_get_flowctrl_cfg(struct gfar_private *priv) | ||
3341 | { | ||
3342 | struct phy_device *phydev = priv->phydev; | ||
3343 | u32 val = 0; | ||
3344 | |||
3345 | if (!phydev->duplex) | ||
3346 | return val; | ||
3347 | |||
3348 | if (!priv->pause_aneg_en) { | ||
3349 | if (priv->tx_pause_en) | ||
3350 | val |= MACCFG1_TX_FLOW; | ||
3351 | if (priv->rx_pause_en) | ||
3352 | val |= MACCFG1_RX_FLOW; | ||
3353 | } else { | ||
3354 | u16 lcl_adv, rmt_adv; | ||
3355 | u8 flowctrl; | ||
3356 | /* get link partner capabilities */ | ||
3357 | rmt_adv = 0; | ||
3358 | if (phydev->pause) | ||
3359 | rmt_adv = LPA_PAUSE_CAP; | ||
3360 | if (phydev->asym_pause) | ||
3361 | rmt_adv |= LPA_PAUSE_ASYM; | ||
3362 | |||
3363 | lcl_adv = mii_advertise_flowctrl(phydev->advertising); | ||
3364 | |||
3365 | flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv); | ||
3366 | if (flowctrl & FLOW_CTRL_TX) | ||
3367 | val |= MACCFG1_TX_FLOW; | ||
3368 | if (flowctrl & FLOW_CTRL_RX) | ||
3369 | val |= MACCFG1_RX_FLOW; | ||
3370 | } | ||
3371 | |||
3372 | return val; | ||
3373 | } | ||
3374 | |||
3375 | static noinline void gfar_update_link_state(struct gfar_private *priv) | ||
3376 | { | ||
3377 | struct gfar __iomem *regs = priv->gfargrp[0].regs; | ||
3378 | struct phy_device *phydev = priv->phydev; | ||
3379 | |||
3380 | if (unlikely(test_bit(GFAR_RESETTING, &priv->state))) | ||
3381 | return; | ||
3382 | |||
3383 | if (phydev->link) { | ||
3384 | u32 tempval1 = gfar_read(®s->maccfg1); | ||
3385 | u32 tempval = gfar_read(®s->maccfg2); | ||
3386 | u32 ecntrl = gfar_read(®s->ecntrl); | ||
3387 | |||
3388 | if (phydev->duplex != priv->oldduplex) { | ||
3389 | if (!(phydev->duplex)) | ||
3390 | tempval &= ~(MACCFG2_FULL_DUPLEX); | ||
3391 | else | ||
3392 | tempval |= MACCFG2_FULL_DUPLEX; | ||
3393 | |||
3394 | priv->oldduplex = phydev->duplex; | ||
3395 | } | ||
3396 | |||
3397 | if (phydev->speed != priv->oldspeed) { | ||
3398 | switch (phydev->speed) { | ||
3399 | case 1000: | ||
3400 | tempval = | ||
3401 | ((tempval & ~(MACCFG2_IF)) | MACCFG2_GMII); | ||
3402 | |||
3403 | ecntrl &= ~(ECNTRL_R100); | ||
3404 | break; | ||
3405 | case 100: | ||
3406 | case 10: | ||
3407 | tempval = | ||
3408 | ((tempval & ~(MACCFG2_IF)) | MACCFG2_MII); | ||
3409 | |||
3410 | /* Reduced mode distinguishes | ||
3411 | * between 10 and 100 | ||
3412 | */ | ||
3413 | if (phydev->speed == SPEED_100) | ||
3414 | ecntrl |= ECNTRL_R100; | ||
3415 | else | ||
3416 | ecntrl &= ~(ECNTRL_R100); | ||
3417 | break; | ||
3418 | default: | ||
3419 | netif_warn(priv, link, priv->ndev, | ||
3420 | "Ack! Speed (%d) is not 10/100/1000!\n", | ||
3421 | phydev->speed); | ||
3422 | break; | ||
3423 | } | ||
3424 | |||
3425 | priv->oldspeed = phydev->speed; | ||
3426 | } | ||
3427 | |||
3428 | tempval1 &= ~(MACCFG1_TX_FLOW | MACCFG1_RX_FLOW); | ||
3429 | tempval1 |= gfar_get_flowctrl_cfg(priv); | ||
3430 | |||
3431 | gfar_write(®s->maccfg1, tempval1); | ||
3432 | gfar_write(®s->maccfg2, tempval); | ||
3433 | gfar_write(®s->ecntrl, ecntrl); | ||
3434 | |||
3435 | if (!priv->oldlink) | ||
3436 | priv->oldlink = 1; | ||
3437 | |||
3438 | } else if (priv->oldlink) { | ||
3439 | priv->oldlink = 0; | ||
3440 | priv->oldspeed = 0; | ||
3441 | priv->oldduplex = -1; | ||
3442 | } | ||
3443 | |||
3444 | if (netif_msg_link(priv)) | ||
3445 | phy_print_status(phydev); | ||
3446 | } | ||
3447 | |||
3445 | static struct of_device_id gfar_match[] = | 3448 | static struct of_device_id gfar_match[] = |
3446 | { | 3449 | { |
3447 | { | 3450 | { |