Commit 1b1c2e95103ce391c2ea39a9460968fcb73deb30

Authored by Ben Hutchings
Committed by David S. Miller
1 parent 52c94dfae1

mdio: Add generic MDIO (clause 45) support functions

These roughly mirror many of the MII library functions and are based
on code from the sfc driver.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

Showing 4 changed files with 429 additions and 0 deletions Side-by-side Diff

... ... @@ -2443,6 +2443,9 @@
2443 2443  
2444 2444 if NETDEV_10000
2445 2445  
  2446 +config MDIO
  2447 + tristate
  2448 +
2446 2449 config CHELSIO_T1
2447 2450 tristate "Chelsio 10Gb Ethernet support"
2448 2451 depends on PCI
drivers/net/Makefile
... ... @@ -95,6 +95,7 @@
95 95 #
96 96  
97 97 obj-$(CONFIG_MII) += mii.o
  98 +obj-$(CONFIG_MDIO) += mdio.o
98 99 obj-$(CONFIG_PHYLIB) += phy/
99 100  
100 101 obj-$(CONFIG_SUNDANCE) += sundance.o
  1 +/*
  2 + * mdio.c: Generic support for MDIO-compatible transceivers
  3 + * Copyright 2006-2009 Solarflare Communications Inc.
  4 + *
  5 + * This program is free software; you can redistribute it and/or modify it
  6 + * under the terms of the GNU General Public License version 2 as published
  7 + * by the Free Software Foundation, incorporated herein by reference.
  8 + */
  9 +
  10 +#include <linux/kernel.h>
  11 +#include <linux/capability.h>
  12 +#include <linux/errno.h>
  13 +#include <linux/ethtool.h>
  14 +#include <linux/mdio.h>
  15 +#include <linux/module.h>
  16 +
  17 +/**
  18 + * mdio45_probe - probe for an MDIO (clause 45) device
  19 + * @mdio: MDIO interface
  20 + * @prtad: Expected PHY address
  21 + *
  22 + * This sets @prtad and @mmds in the MDIO interface if successful.
  23 + * Returns 0 on success, negative on error.
  24 + */
  25 +int mdio45_probe(struct mdio_if_info *mdio, int prtad)
  26 +{
  27 + int mmd, stat2, devs1, devs2;
  28 +
  29 + /* Assume PHY must have at least one of PMA/PMD, WIS, PCS, PHY
  30 + * XS or DTE XS; give up if none is present. */
  31 + for (mmd = 1; mmd <= 5; mmd++) {
  32 + /* Is this MMD present? */
  33 + stat2 = mdio->mdio_read(mdio->dev, prtad, mmd, MDIO_STAT2);
  34 + if (stat2 < 0 ||
  35 + (stat2 & MDIO_STAT2_DEVPRST) != MDIO_STAT2_DEVPRST_VAL)
  36 + continue;
  37 +
  38 + /* It should tell us about all the other MMDs */
  39 + devs1 = mdio->mdio_read(mdio->dev, prtad, mmd, MDIO_DEVS1);
  40 + devs2 = mdio->mdio_read(mdio->dev, prtad, mmd, MDIO_DEVS2);
  41 + if (devs1 < 0 || devs2 < 0)
  42 + continue;
  43 +
  44 + mdio->prtad = prtad;
  45 + mdio->mmds = devs1 | (devs2 << 16);
  46 + return 0;
  47 + }
  48 +
  49 + return -ENODEV;
  50 +}
  51 +EXPORT_SYMBOL(mdio45_probe);
  52 +
  53 +/**
  54 + * mdio_set_flag - set or clear flag in an MDIO register
  55 + * @mdio: MDIO interface
  56 + * @prtad: PHY address
  57 + * @devad: MMD address
  58 + * @addr: Register address
  59 + * @mask: Mask for flag (single bit set)
  60 + * @sense: New value of flag
  61 + *
  62 + * This debounces changes: it does not write the register if the flag
  63 + * already has the proper value. Returns 0 on success, negative on error.
  64 + */
  65 +int mdio_set_flag(const struct mdio_if_info *mdio,
  66 + int prtad, int devad, u16 addr, int mask,
  67 + bool sense)
  68 +{
  69 + int old_val = mdio->mdio_read(mdio->dev, prtad, devad, addr);
  70 + int new_val;
  71 +
  72 + if (old_val < 0)
  73 + return old_val;
  74 + if (sense)
  75 + new_val = old_val | mask;
  76 + else
  77 + new_val = old_val & ~mask;
  78 + if (old_val == new_val)
  79 + return 0;
  80 + return mdio->mdio_write(mdio->dev, prtad, devad, addr, new_val);
  81 +}
  82 +EXPORT_SYMBOL(mdio_set_flag);
  83 +
  84 +/**
  85 + * mdio_link_ok - is link status up/OK
  86 + * @mdio: MDIO interface
  87 + * @mmd_mask: Mask for MMDs to check
  88 + *
  89 + * Returns 1 if the PHY reports link status up/OK, 0 otherwise.
  90 + * @mmd_mask is normally @mdio->mmds, but if loopback is enabled
  91 + * the MMDs being bypassed should be excluded from the mask.
  92 + */
  93 +int mdio45_links_ok(const struct mdio_if_info *mdio, u32 mmd_mask)
  94 +{
  95 + int devad, reg;
  96 +
  97 + if (!mmd_mask) {
  98 + /* Use absence of XGMII faults in lieu of link state */
  99 + reg = mdio->mdio_read(mdio->dev, mdio->prtad,
  100 + MDIO_MMD_PHYXS, MDIO_STAT2);
  101 + return reg >= 0 && !(reg & MDIO_STAT2_RXFAULT);
  102 + }
  103 +
  104 + for (devad = 0; mmd_mask; devad++) {
  105 + if (mmd_mask & (1 << devad)) {
  106 + mmd_mask &= ~(1 << devad);
  107 +
  108 + /* Read twice because link state is latched and a
  109 + * read moves the current state into the register */
  110 + mdio->mdio_read(mdio->dev, mdio->prtad,
  111 + devad, MDIO_STAT1);
  112 + reg = mdio->mdio_read(mdio->dev, mdio->prtad,
  113 + devad, MDIO_STAT1);
  114 + if (reg < 0 || !(reg & MDIO_STAT1_LSTATUS))
  115 + return false;
  116 + }
  117 + }
  118 +
  119 + return true;
  120 +}
  121 +EXPORT_SYMBOL(mdio45_links_ok);
  122 +
  123 +/**
  124 + * mdio45_nway_restart - restart auto-negotiation for this interface
  125 + * @mdio: MDIO interface
  126 + *
  127 + * Returns 0 on success, negative on error.
  128 + */
  129 +int mdio45_nway_restart(const struct mdio_if_info *mdio)
  130 +{
  131 + if (!(mdio->mmds & MDIO_DEVS_AN))
  132 + return -EOPNOTSUPP;
  133 +
  134 + mdio_set_flag(mdio, mdio->prtad, MDIO_MMD_AN, MDIO_CTRL1,
  135 + MDIO_AN_CTRL1_RESTART, true);
  136 + return 0;
  137 +}
  138 +EXPORT_SYMBOL(mdio45_nway_restart);
  139 +
  140 +static u32 mdio45_get_an(const struct mdio_if_info *mdio, u16 addr)
  141 +{
  142 + u32 result = 0;
  143 + int reg;
  144 +
  145 + reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_AN, addr);
  146 + if (reg & ADVERTISE_10HALF)
  147 + result |= ADVERTISED_10baseT_Half;
  148 + if (reg & ADVERTISE_10FULL)
  149 + result |= ADVERTISED_10baseT_Full;
  150 + if (reg & ADVERTISE_100HALF)
  151 + result |= ADVERTISED_100baseT_Half;
  152 + if (reg & ADVERTISE_100FULL)
  153 + result |= ADVERTISED_100baseT_Full;
  154 + return result;
  155 +}
  156 +
  157 +/**
  158 + * mdio45_ethtool_gset_npage - get settings for ETHTOOL_GSET
  159 + * @mdio: MDIO interface
  160 + * @ecmd: Ethtool request structure
  161 + * @npage_adv: Modes currently advertised on next pages
  162 + * @npage_lpa: Modes advertised by link partner on next pages
  163 + *
  164 + * Since the CSRs for auto-negotiation using next pages are not fully
  165 + * standardised, this function does not attempt to decode them. The
  166 + * caller must pass them in.
  167 + */
  168 +void mdio45_ethtool_gset_npage(const struct mdio_if_info *mdio,
  169 + struct ethtool_cmd *ecmd,
  170 + u32 npage_adv, u32 npage_lpa)
  171 +{
  172 + int reg;
  173 +
  174 + ecmd->transceiver = XCVR_INTERNAL;
  175 + ecmd->phy_address = mdio->prtad;
  176 +
  177 + reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
  178 + MDIO_CTRL2);
  179 + switch (reg & MDIO_PMA_CTRL2_TYPE) {
  180 + case MDIO_PMA_CTRL2_10GBT:
  181 + case MDIO_PMA_CTRL2_1000BT:
  182 + case MDIO_PMA_CTRL2_100BTX:
  183 + case MDIO_PMA_CTRL2_10BT:
  184 + ecmd->port = PORT_TP;
  185 + ecmd->supported = SUPPORTED_TP;
  186 + reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
  187 + MDIO_SPEED);
  188 + if (reg & MDIO_SPEED_10G)
  189 + ecmd->supported |= SUPPORTED_10000baseT_Full;
  190 + if (reg & MDIO_PMA_SPEED_1000)
  191 + ecmd->supported |= (SUPPORTED_1000baseT_Full |
  192 + SUPPORTED_1000baseT_Half);
  193 + if (reg & MDIO_PMA_SPEED_100)
  194 + ecmd->supported |= (SUPPORTED_100baseT_Full |
  195 + SUPPORTED_100baseT_Half);
  196 + if (reg & MDIO_PMA_SPEED_10)
  197 + ecmd->supported |= (SUPPORTED_10baseT_Full |
  198 + SUPPORTED_10baseT_Half);
  199 + ecmd->advertising = ADVERTISED_TP;
  200 + break;
  201 +
  202 + case MDIO_PMA_CTRL2_10GBCX4:
  203 + case MDIO_PMA_CTRL2_10GBKX4:
  204 + case MDIO_PMA_CTRL2_10GBKR:
  205 + case MDIO_PMA_CTRL2_1000BKX:
  206 + ecmd->port = PORT_OTHER;
  207 + ecmd->supported = 0;
  208 + ecmd->advertising = 0;
  209 + break;
  210 +
  211 + /* All the other defined modes are flavours of optical */
  212 + default:
  213 + ecmd->port = PORT_FIBRE;
  214 + ecmd->supported = SUPPORTED_FIBRE;
  215 + ecmd->advertising = ADVERTISED_FIBRE;
  216 + break;
  217 + }
  218 +
  219 + if (mdio->mmds & MDIO_DEVS_AN) {
  220 + ecmd->supported |= SUPPORTED_Autoneg;
  221 + reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_AN,
  222 + MDIO_CTRL1);
  223 + if (reg & MDIO_AN_CTRL1_ENABLE) {
  224 + ecmd->autoneg = AUTONEG_ENABLE;
  225 + ecmd->advertising |=
  226 + ADVERTISED_Autoneg |
  227 + mdio45_get_an(mdio, MDIO_AN_ADVERTISE) |
  228 + npage_adv;
  229 + } else {
  230 + ecmd->autoneg = AUTONEG_DISABLE;
  231 + }
  232 + } else {
  233 + ecmd->autoneg = AUTONEG_DISABLE;
  234 + }
  235 +
  236 + if (ecmd->autoneg) {
  237 + u32 modes = 0;
  238 +
  239 + /* If AN is complete and successful, report best common
  240 + * mode, otherwise report best advertised mode. */
  241 + if (mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_AN,
  242 + MDIO_STAT1) &
  243 + MDIO_AN_STAT1_COMPLETE)
  244 + modes = (ecmd->advertising &
  245 + (mdio45_get_an(mdio, MDIO_AN_LPA) |
  246 + npage_lpa));
  247 + if (modes == 0)
  248 + modes = ecmd->advertising;
  249 +
  250 + if (modes & ADVERTISED_10000baseT_Full) {
  251 + ecmd->speed = SPEED_10000;
  252 + ecmd->duplex = DUPLEX_FULL;
  253 + } else if (modes & (ADVERTISED_1000baseT_Full |
  254 + ADVERTISED_1000baseT_Half)) {
  255 + ecmd->speed = SPEED_1000;
  256 + ecmd->duplex = !!(modes & ADVERTISED_1000baseT_Full);
  257 + } else if (modes & (ADVERTISED_100baseT_Full |
  258 + ADVERTISED_100baseT_Half)) {
  259 + ecmd->speed = SPEED_100;
  260 + ecmd->duplex = !!(modes & ADVERTISED_100baseT_Full);
  261 + } else {
  262 + ecmd->speed = SPEED_10;
  263 + ecmd->duplex = !!(modes & ADVERTISED_10baseT_Full);
  264 + }
  265 + } else {
  266 + /* Report forced settings */
  267 + reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD,
  268 + MDIO_CTRL1);
  269 + ecmd->speed = (((reg & MDIO_PMA_CTRL1_SPEED1000) ? 100 : 1) *
  270 + ((reg & MDIO_PMA_CTRL1_SPEED100) ? 100 : 10));
  271 + ecmd->duplex = (reg & MDIO_CTRL1_FULLDPLX ||
  272 + ecmd->speed == SPEED_10000);
  273 + }
  274 +}
  275 +EXPORT_SYMBOL(mdio45_ethtool_gset_npage);
  276 +
  277 +/**
  278 + * mdio_mii_ioctl - MII ioctl interface for MDIO (clause 22 or 45) PHYs
  279 + * @mdio: MDIO interface
  280 + * @mii_data: MII ioctl data structure
  281 + * @cmd: MII ioctl command
  282 + *
  283 + * Returns 0 on success, negative on error.
  284 + */
  285 +int mdio_mii_ioctl(const struct mdio_if_info *mdio,
  286 + struct mii_ioctl_data *mii_data, int cmd)
  287 +{
  288 + int prtad, devad;
  289 + u16 addr = mii_data->reg_num;
  290 +
  291 + /* Validate/convert cmd to one of SIOC{G,S}MIIREG */
  292 + switch (cmd) {
  293 + case SIOCGMIIPHY:
  294 + if (mdio->prtad == MDIO_PRTAD_NONE)
  295 + return -EOPNOTSUPP;
  296 + mii_data->phy_id = mdio->prtad;
  297 + cmd = SIOCGMIIREG;
  298 + break;
  299 + case SIOCGMIIREG:
  300 + break;
  301 + case SIOCSMIIREG:
  302 + if (!capable(CAP_NET_ADMIN))
  303 + return -EPERM;
  304 + break;
  305 + default:
  306 + return -EOPNOTSUPP;
  307 + }
  308 +
  309 + /* Validate/convert phy_id */
  310 + if ((mdio->mode_support & MDIO_SUPPORTS_C45) &&
  311 + mdio_phy_id_is_c45(mii_data->phy_id)) {
  312 + prtad = mdio_phy_id_prtad(mii_data->phy_id);
  313 + devad = mdio_phy_id_devad(mii_data->phy_id);
  314 + } else if ((mdio->mode_support & MDIO_SUPPORTS_C22) &&
  315 + mii_data->phy_id < 0x20) {
  316 + prtad = mii_data->phy_id;
  317 + devad = MDIO_DEVAD_NONE;
  318 + addr &= 0x1f;
  319 + } else if ((mdio->mode_support & MDIO_EMULATE_C22) &&
  320 + mdio->prtad != MDIO_PRTAD_NONE &&
  321 + mii_data->phy_id == mdio->prtad) {
  322 + /* Remap commonly-used MII registers. */
  323 + prtad = mdio->prtad;
  324 + switch (addr) {
  325 + case MII_BMCR:
  326 + case MII_BMSR:
  327 + case MII_PHYSID1:
  328 + case MII_PHYSID2:
  329 + devad = __ffs(mdio->mmds);
  330 + break;
  331 + case MII_ADVERTISE:
  332 + case MII_LPA:
  333 + if (!(mdio->mmds & MDIO_DEVS_AN))
  334 + return -EINVAL;
  335 + devad = MDIO_MMD_AN;
  336 + if (addr == MII_ADVERTISE)
  337 + addr = MDIO_AN_ADVERTISE;
  338 + else
  339 + addr = MDIO_AN_LPA;
  340 + break;
  341 + default:
  342 + return -EINVAL;
  343 + }
  344 + } else {
  345 + return -EINVAL;
  346 + }
  347 +
  348 + if (cmd == SIOCGMIIREG) {
  349 + int rc = mdio->mdio_read(mdio->dev, prtad, devad, addr);
  350 + if (rc < 0)
  351 + return rc;
  352 + mii_data->val_out = rc;
  353 + return 0;
  354 + } else {
  355 + return mdio->mdio_write(mdio->dev, prtad, devad, addr,
  356 + mii_data->val_in);
  357 + }
  358 +}
  359 +EXPORT_SYMBOL(mdio_mii_ioctl);
include/linux/mdio.h
... ... @@ -234,5 +234,71 @@
234 234 return phy_id & MDIO_PHY_ID_DEVAD;
235 235 }
236 236  
  237 +#ifdef __KERNEL__
  238 +
  239 +/**
  240 + * struct mdio_if_info - Ethernet controller MDIO interface
  241 + * @prtad: PRTAD of the PHY (%MDIO_PRTAD_NONE if not present/unknown)
  242 + * @mmds: Mask of MMDs expected to be present in the PHY. This must be
  243 + * non-zero unless @prtad = %MDIO_PRTAD_NONE.
  244 + * @mode_support: MDIO modes supported. If %MDIO_SUPPORTS_C22 is set then
  245 + * MII register access will be passed through with @devad =
  246 + * %MDIO_DEVAD_NONE. If %MDIO_EMULATE_C22 is set then access to
  247 + * commonly used clause 22 registers will be translated into
  248 + * clause 45 registers.
  249 + * @dev: Net device structure
  250 + * @mdio_read: Register read function; returns value or negative error code
  251 + * @mdio_write: Register write function; returns 0 or negative error code
  252 + */
  253 +struct mdio_if_info {
  254 + int prtad;
  255 + u32 __bitwise mmds;
  256 + unsigned mode_support;
  257 +
  258 + struct net_device *dev;
  259 + int (*mdio_read)(struct net_device *dev, int prtad, int devad,
  260 + u16 addr);
  261 + int (*mdio_write)(struct net_device *dev, int prtad, int devad,
  262 + u16 addr, u16 val);
  263 +};
  264 +
  265 +#define MDIO_PRTAD_NONE (-1)
  266 +#define MDIO_DEVAD_NONE (-1)
  267 +#define MDIO_SUPPORTS_C22 1
  268 +#define MDIO_SUPPORTS_C45 2
  269 +#define MDIO_EMULATE_C22 4
  270 +
  271 +struct ethtool_cmd;
  272 +struct ethtool_pauseparam;
  273 +extern int mdio45_probe(struct mdio_if_info *mdio, int prtad);
  274 +extern int mdio_set_flag(const struct mdio_if_info *mdio,
  275 + int prtad, int devad, u16 addr, int mask,
  276 + bool sense);
  277 +extern int mdio45_links_ok(const struct mdio_if_info *mdio, u32 mmds);
  278 +extern int mdio45_nway_restart(const struct mdio_if_info *mdio);
  279 +extern void mdio45_ethtool_gset_npage(const struct mdio_if_info *mdio,
  280 + struct ethtool_cmd *ecmd,
  281 + u32 npage_adv, u32 npage_lpa);
  282 +
  283 +/**
  284 + * mdio45_ethtool_gset - get settings for ETHTOOL_GSET
  285 + * @mdio: MDIO interface
  286 + * @ecmd: Ethtool request structure
  287 + *
  288 + * Since the CSRs for auto-negotiation using next pages are not fully
  289 + * standardised, this function does not attempt to decode them. Use
  290 + * mdio45_ethtool_gset_npage() to specify advertisement bits from next
  291 + * pages.
  292 + */
  293 +static inline void mdio45_ethtool_gset(const struct mdio_if_info *mdio,
  294 + struct ethtool_cmd *ecmd)
  295 +{
  296 + mdio45_ethtool_gset_npage(mdio, ecmd, 0, 0);
  297 +}
  298 +
  299 +extern int mdio_mii_ioctl(const struct mdio_if_info *mdio,
  300 + struct mii_ioctl_data *mii_data, int cmd);
  301 +
  302 +#endif /* __KERNEL__ */
237 303 #endif /* __LINUX_MDIO_H__ */