android_kernel_xiaomi_sm8350/include/linux/mdio.h
Vladimir Oltean 2ecca3b282 net: mdio: introduce a shutdown method to mdio device drivers
[ Upstream commit cf9579976f724ad517cc15b7caadea728c7e245c ]

MDIO-attached devices might have interrupts and other things that might
need quiesced when we kexec into a new kernel. Things are even more
creepy when those interrupt lines are shared, and in that case it is
absolutely mandatory to disable all interrupt sources.

Moreover, MDIO devices might be DSA switches, and DSA needs its own
shutdown method to unlink from the DSA master, which is a new
requirement that appeared after commit 2f1e8ea726e9 ("net: dsa: link
interfaces with the DSA master to get rid of lockdep warnings").

So introduce a ->shutdown method in the MDIO device driver structure.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
2021-10-09 14:39:49 +02:00

353 lines
11 KiB
C

/* SPDX-License-Identifier: GPL-2.0-only */
/*
* linux/mdio.h: definitions for MDIO (clause 45) transceivers
* Copyright 2006-2009 Solarflare Communications Inc.
*/
#ifndef __LINUX_MDIO_H__
#define __LINUX_MDIO_H__
#include <uapi/linux/mdio.h>
#include <linux/mod_devicetable.h>
struct gpio_desc;
struct mii_bus;
/* Multiple levels of nesting are possible. However typically this is
* limited to nested DSA like layer, a MUX layer, and the normal
* user. Instead of trying to handle the general case, just define
* these cases.
*/
enum mdio_mutex_lock_class {
MDIO_MUTEX_NORMAL,
MDIO_MUTEX_MUX,
MDIO_MUTEX_NESTED,
};
struct mdio_device {
struct device dev;
struct mii_bus *bus;
char modalias[MDIO_NAME_SIZE];
int (*bus_match)(struct device *dev, struct device_driver *drv);
void (*device_free)(struct mdio_device *mdiodev);
void (*device_remove)(struct mdio_device *mdiodev);
/* Bus address of the MDIO device (0-31) */
int addr;
int flags;
struct gpio_desc *reset_gpio;
struct reset_control *reset_ctrl;
unsigned int reset_assert_delay;
unsigned int reset_deassert_delay;
};
#define to_mdio_device(d) container_of(d, struct mdio_device, dev)
/* struct mdio_driver_common: Common to all MDIO drivers */
struct mdio_driver_common {
struct device_driver driver;
int flags;
};
#define MDIO_DEVICE_FLAG_PHY 1
#define to_mdio_common_driver(d) \
container_of(d, struct mdio_driver_common, driver)
/* struct mdio_driver: Generic MDIO driver */
struct mdio_driver {
struct mdio_driver_common mdiodrv;
/*
* Called during discovery. Used to set
* up device-specific structures, if any
*/
int (*probe)(struct mdio_device *mdiodev);
/* Clears up any memory if needed */
void (*remove)(struct mdio_device *mdiodev);
/* Quiesces the device on system shutdown, turns off interrupts etc */
void (*shutdown)(struct mdio_device *mdiodev);
};
#define to_mdio_driver(d) \
container_of(to_mdio_common_driver(d), struct mdio_driver, mdiodrv)
/* device driver data */
static inline void mdiodev_set_drvdata(struct mdio_device *mdio, void *data)
{
dev_set_drvdata(&mdio->dev, data);
}
static inline void *mdiodev_get_drvdata(struct mdio_device *mdio)
{
return dev_get_drvdata(&mdio->dev);
}
void mdio_device_free(struct mdio_device *mdiodev);
struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr);
int mdio_device_register(struct mdio_device *mdiodev);
void mdio_device_remove(struct mdio_device *mdiodev);
void mdio_device_reset(struct mdio_device *mdiodev, int value);
int mdio_driver_register(struct mdio_driver *drv);
void mdio_driver_unregister(struct mdio_driver *drv);
int mdio_device_bus_match(struct device *dev, struct device_driver *drv);
static inline bool mdio_phy_id_is_c45(int phy_id)
{
return (phy_id & MDIO_PHY_ID_C45) && !(phy_id & ~MDIO_PHY_ID_C45_MASK);
}
static inline __u16 mdio_phy_id_prtad(int phy_id)
{
return (phy_id & MDIO_PHY_ID_PRTAD) >> 5;
}
static inline __u16 mdio_phy_id_devad(int phy_id)
{
return phy_id & MDIO_PHY_ID_DEVAD;
}
/**
* struct mdio_if_info - Ethernet controller MDIO interface
* @prtad: PRTAD of the PHY (%MDIO_PRTAD_NONE if not present/unknown)
* @mmds: Mask of MMDs expected to be present in the PHY. This must be
* non-zero unless @prtad = %MDIO_PRTAD_NONE.
* @mode_support: MDIO modes supported. If %MDIO_SUPPORTS_C22 is set then
* MII register access will be passed through with @devad =
* %MDIO_DEVAD_NONE. If %MDIO_EMULATE_C22 is set then access to
* commonly used clause 22 registers will be translated into
* clause 45 registers.
* @dev: Net device structure
* @mdio_read: Register read function; returns value or negative error code
* @mdio_write: Register write function; returns 0 or negative error code
*/
struct mdio_if_info {
int prtad;
u32 mmds;
unsigned mode_support;
struct net_device *dev;
int (*mdio_read)(struct net_device *dev, int prtad, int devad,
u16 addr);
int (*mdio_write)(struct net_device *dev, int prtad, int devad,
u16 addr, u16 val);
};
#define MDIO_PRTAD_NONE (-1)
#define MDIO_DEVAD_NONE (-1)
#define MDIO_SUPPORTS_C22 1
#define MDIO_SUPPORTS_C45 2
#define MDIO_EMULATE_C22 4
struct ethtool_cmd;
struct ethtool_pauseparam;
extern int mdio45_probe(struct mdio_if_info *mdio, int prtad);
extern int mdio_set_flag(const struct mdio_if_info *mdio,
int prtad, int devad, u16 addr, int mask,
bool sense);
extern int mdio45_links_ok(const struct mdio_if_info *mdio, u32 mmds);
extern int mdio45_nway_restart(const struct mdio_if_info *mdio);
extern void mdio45_ethtool_gset_npage(const struct mdio_if_info *mdio,
struct ethtool_cmd *ecmd,
u32 npage_adv, u32 npage_lpa);
extern void
mdio45_ethtool_ksettings_get_npage(const struct mdio_if_info *mdio,
struct ethtool_link_ksettings *cmd,
u32 npage_adv, u32 npage_lpa);
/**
* mdio45_ethtool_gset - get settings for ETHTOOL_GSET
* @mdio: MDIO interface
* @ecmd: Ethtool request structure
*
* Since the CSRs for auto-negotiation using next pages are not fully
* standardised, this function does not attempt to decode them. Use
* mdio45_ethtool_gset_npage() to specify advertisement bits from next
* pages.
*/
static inline void mdio45_ethtool_gset(const struct mdio_if_info *mdio,
struct ethtool_cmd *ecmd)
{
mdio45_ethtool_gset_npage(mdio, ecmd, 0, 0);
}
/**
* mdio45_ethtool_ksettings_get - get settings for ETHTOOL_GLINKSETTINGS
* @mdio: MDIO interface
* @cmd: Ethtool request structure
*
* Since the CSRs for auto-negotiation using next pages are not fully
* standardised, this function does not attempt to decode them. Use
* mdio45_ethtool_ksettings_get_npage() to specify advertisement bits
* from next pages.
*/
static inline void
mdio45_ethtool_ksettings_get(const struct mdio_if_info *mdio,
struct ethtool_link_ksettings *cmd)
{
mdio45_ethtool_ksettings_get_npage(mdio, cmd, 0, 0);
}
extern int mdio_mii_ioctl(const struct mdio_if_info *mdio,
struct mii_ioctl_data *mii_data, int cmd);
/**
* mmd_eee_cap_to_ethtool_sup_t
* @eee_cap: value of the MMD EEE Capability register
*
* A small helper function that translates MMD EEE Capability (3.20) bits
* to ethtool supported settings.
*/
static inline u32 mmd_eee_cap_to_ethtool_sup_t(u16 eee_cap)
{
u32 supported = 0;
if (eee_cap & MDIO_EEE_100TX)
supported |= SUPPORTED_100baseT_Full;
if (eee_cap & MDIO_EEE_1000T)
supported |= SUPPORTED_1000baseT_Full;
if (eee_cap & MDIO_EEE_10GT)
supported |= SUPPORTED_10000baseT_Full;
if (eee_cap & MDIO_EEE_1000KX)
supported |= SUPPORTED_1000baseKX_Full;
if (eee_cap & MDIO_EEE_10GKX4)
supported |= SUPPORTED_10000baseKX4_Full;
if (eee_cap & MDIO_EEE_10GKR)
supported |= SUPPORTED_10000baseKR_Full;
return supported;
}
/**
* mmd_eee_adv_to_ethtool_adv_t
* @eee_adv: value of the MMD EEE Advertisement/Link Partner Ability registers
*
* A small helper function that translates the MMD EEE Advertisment (7.60)
* and MMD EEE Link Partner Ability (7.61) bits to ethtool advertisement
* settings.
*/
static inline u32 mmd_eee_adv_to_ethtool_adv_t(u16 eee_adv)
{
u32 adv = 0;
if (eee_adv & MDIO_EEE_100TX)
adv |= ADVERTISED_100baseT_Full;
if (eee_adv & MDIO_EEE_1000T)
adv |= ADVERTISED_1000baseT_Full;
if (eee_adv & MDIO_EEE_10GT)
adv |= ADVERTISED_10000baseT_Full;
if (eee_adv & MDIO_EEE_1000KX)
adv |= ADVERTISED_1000baseKX_Full;
if (eee_adv & MDIO_EEE_10GKX4)
adv |= ADVERTISED_10000baseKX4_Full;
if (eee_adv & MDIO_EEE_10GKR)
adv |= ADVERTISED_10000baseKR_Full;
return adv;
}
/**
* ethtool_adv_to_mmd_eee_adv_t
* @adv: the ethtool advertisement settings
*
* A small helper function that translates ethtool advertisement settings
* to EEE advertisements for the MMD EEE Advertisement (7.60) and
* MMD EEE Link Partner Ability (7.61) registers.
*/
static inline u16 ethtool_adv_to_mmd_eee_adv_t(u32 adv)
{
u16 reg = 0;
if (adv & ADVERTISED_100baseT_Full)
reg |= MDIO_EEE_100TX;
if (adv & ADVERTISED_1000baseT_Full)
reg |= MDIO_EEE_1000T;
if (adv & ADVERTISED_10000baseT_Full)
reg |= MDIO_EEE_10GT;
if (adv & ADVERTISED_1000baseKX_Full)
reg |= MDIO_EEE_1000KX;
if (adv & ADVERTISED_10000baseKX4_Full)
reg |= MDIO_EEE_10GKX4;
if (adv & ADVERTISED_10000baseKR_Full)
reg |= MDIO_EEE_10GKR;
return reg;
}
/**
* linkmode_adv_to_mii_10gbt_adv_t
* @advertising: the linkmode advertisement settings
*
* A small helper function that translates linkmode advertisement
* settings to phy autonegotiation advertisements for the C45
* 10GBASE-T AN CONTROL (7.32) register.
*/
static inline u32 linkmode_adv_to_mii_10gbt_adv_t(unsigned long *advertising)
{
u32 result = 0;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
advertising))
result |= MDIO_AN_10GBT_CTRL_ADV2_5G;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
advertising))
result |= MDIO_AN_10GBT_CTRL_ADV5G;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
advertising))
result |= MDIO_AN_10GBT_CTRL_ADV10G;
return result;
}
/**
* mii_10gbt_stat_mod_linkmode_lpa_t
* @advertising: target the linkmode advertisement settings
* @adv: value of the C45 10GBASE-T AN STATUS register
*
* A small helper function that translates C45 10GBASE-T AN STATUS register bits
* to linkmode advertisement settings. Other bits in advertising aren't changed.
*/
static inline void mii_10gbt_stat_mod_linkmode_lpa_t(unsigned long *advertising,
u32 lpa)
{
linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
advertising, lpa & MDIO_AN_10GBT_STAT_LP2_5G);
linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
advertising, lpa & MDIO_AN_10GBT_STAT_LP5G);
linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
advertising, lpa & MDIO_AN_10GBT_STAT_LP10G);
}
int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum);
int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
int mdiobus_write_nested(struct mii_bus *bus, int addr, u32 regnum, u16 val);
int mdiobus_register_device(struct mdio_device *mdiodev);
int mdiobus_unregister_device(struct mdio_device *mdiodev);
bool mdiobus_is_registered_device(struct mii_bus *bus, int addr);
struct phy_device *mdiobus_get_phy(struct mii_bus *bus, int addr);
/**
* mdio_module_driver() - Helper macro for registering mdio drivers
*
* Helper macro for MDIO drivers which do not do anything special in module
* init/exit. Each module may only use this macro once, and calling it
* replaces module_init() and module_exit().
*/
#define mdio_module_driver(_mdio_driver) \
static int __init mdio_module_init(void) \
{ \
return mdio_driver_register(&_mdio_driver); \
} \
module_init(mdio_module_init); \
static void __exit mdio_module_exit(void) \
{ \
mdio_driver_unregister(&_mdio_driver); \
} \
module_exit(mdio_module_exit)
#endif /* __LINUX_MDIO_H__ */