panic: add sysctl to dump all CPUs backtraces on oops event
[sfrench/cifs-2.6.git] / include / linux / phy.h
index 2432ca463ddc080db6cbe81b974409806da8fe95..8c05d0fb5c002d11983089d3a61e7370143370be 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/spinlock.h>
 #include <linux/ethtool.h>
 #include <linux/linkmode.h>
+#include <linux/netlink.h>
 #include <linux/mdio.h>
 #include <linux/mii.h>
 #include <linux/mii_timestamper.h>
@@ -25,6 +26,7 @@
 #include <linux/u64_stats_sync.h>
 #include <linux/irqreturn.h>
 #include <linux/iopoll.h>
+#include <linux/refcount.h>
 
 #include <linux/atomic.h>
 
@@ -77,6 +79,7 @@ extern const int phy_10gbit_features_array[1];
 
 #define PHY_IS_INTERNAL                0x00000001
 #define PHY_RST_AFTER_CLK_EN   0x00000002
+#define PHY_POLL_CABLE_TEST    0x00000004
 #define MDIO_DEVICE_IS_PHY     0x80000000
 
 /* Interface Mode definitions */
@@ -206,12 +209,6 @@ static inline const char *phy_modes(phy_interface_t interface)
 
 #define MII_BUS_ID_SIZE        61
 
-/* Or MII_ADDR_C45 into regnum for read/write on mii_bus to enable the 21 bit
-   IEEE 802.3ae clause 45 addressing mode used by 10GIGE phy chips. */
-#define MII_ADDR_C45 (1<<30)
-#define MII_DEVADDR_C45_SHIFT  16
-#define MII_REGADDR_C45_MASK   GENMASK(15, 0)
-
 struct device;
 struct phylink;
 struct sfp_bus;
@@ -227,6 +224,28 @@ struct mdio_bus_stats {
        struct u64_stats_sync syncp;
 };
 
+/* Represents a shared structure between different phydev's in the same
+ * package, for example a quad PHY. See phy_package_join() and
+ * phy_package_leave().
+ */
+struct phy_package_shared {
+       int addr;
+       refcount_t refcnt;
+       unsigned long flags;
+       size_t priv_size;
+
+       /* private data pointer */
+       /* note that this pointer is shared between different phydevs and
+        * the user has to take care of appropriate locking. It is allocated
+        * and freed automatically by phy_package_join() and
+        * phy_package_leave().
+        */
+       void *priv;
+};
+
+/* used as bit number in atomic bitops */
+#define PHY_SHARED_F_INIT_DONE 0
+
 /*
  * The Bus class for PHYs.  Devices which provide access to
  * PHYs should register using this structure
@@ -241,6 +260,9 @@ struct mii_bus {
        int (*reset)(struct mii_bus *bus);
        struct mdio_bus_stats stats[PHY_MAX_ADDR];
 
+       unsigned int is_managed:1;      /* is device-managed */
+       unsigned int is_managed_registered:1;
+
        /*
         * A lock to ensure that only one thing can read/write
         * the MDIO bus at a time
@@ -275,6 +297,12 @@ struct mii_bus {
        int reset_delay_us;
        /* RESET GPIO descriptor pointer */
        struct gpio_desc *reset_gpiod;
+
+       /* protect access to the shared element */
+       struct mutex shared_lock;
+
+       /* shared state across different PHYs */
+       struct phy_package_shared *shared[PHY_MAX_ADDR];
 };
 #define to_mii_bus(d) container_of(d, struct mii_bus, dev)
 
@@ -286,6 +314,20 @@ static inline struct mii_bus *mdiobus_alloc(void)
 
 int __mdiobus_register(struct mii_bus *bus, struct module *owner);
 #define mdiobus_register(bus) __mdiobus_register(bus, THIS_MODULE)
+static inline int devm_mdiobus_register(struct mii_bus *bus)
+{
+       int ret;
+
+       if (!bus->is_managed)
+               return -EPERM;
+
+       ret = mdiobus_register(bus);
+       if (!ret)
+               bus->is_managed_registered = 1;
+
+       return ret;
+}
+
 void mdiobus_unregister(struct mii_bus *bus);
 void mdiobus_free(struct mii_bus *bus);
 struct mii_bus *devm_mdiobus_alloc_size(struct device *dev, int sizeof_priv);
@@ -326,6 +368,12 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr);
  * - irq or timer will set NOLINK if link goes down
  * - phy_stop moves to HALTED
  *
+ * CABLETEST: PHY is performing a cable test. Packet reception/sending
+ * is not expected to work, carrier will be indicated as down. PHY will be
+ * poll once per second, or on interrupt for it current state.
+ * Once complete, move to UP to restart the PHY.
+ * - phy_stop aborts the running test and moves to HALTED
+ *
  * HALTED: PHY is up, but no polling or interrupts are done. Or
  * PHY is in an error state.
  * - phy_start moves to UP
@@ -337,6 +385,7 @@ enum phy_state {
        PHY_UP,
        PHY_RUNNING,
        PHY_NOLINK,
+       PHY_CABLETEST,
 };
 
 /**
@@ -431,6 +480,9 @@ struct phy_device {
        int duplex;
        int pause;
        int asym_pause;
+       u8 master_slave_get;
+       u8 master_slave_set;
+       u8 master_slave_state;
 
        /* Union of PHY and Attached devices' supported link modes */
        /* See ethtool.h for more info */
@@ -461,6 +513,15 @@ struct phy_device {
        /* For use by PHYs to maintain extra state */
        void *priv;
 
+       /* shared data pointer */
+       /* For use by PHYs inside the same package that need a shared state. */
+       struct phy_package_shared *shared;
+
+       /* Reporting cable test results */
+       struct sk_buff *skb;
+       void *ehdr;
+       struct nlattr *nest;
+
        /* Interrupt and Polling infrastructure */
        struct delayed_work state_queue;
 
@@ -476,7 +537,7 @@ struct phy_device {
        u8 mdix;
        u8 mdix_ctrl;
 
-       void (*phy_link_change)(struct phy_device *, bool up, bool do_carrier);
+       void (*phy_link_change)(struct phy_device *phydev, bool up);
        void (*adjust_link)(struct net_device *dev);
 
 #if IS_ENABLED(CONFIG_MACSEC)
@@ -487,6 +548,18 @@ struct phy_device {
 #define to_phy_device(d) container_of(to_mdio_device(d), \
                                      struct phy_device, mdio)
 
+/* A structure containing possible configuration parameters
+ * for a TDR cable test. The driver does not need to implement
+ * all the parameters, but should report what is actually used.
+ */
+struct phy_tdr_config {
+       u32 first;
+       u32 last;
+       u32 step;
+       s8 pair;
+};
+#define PHY_PAIR_ALL -1
+
 /* struct phy_driver: Driver structure for a particular PHY type
  *
  * driver_data: static driver data
@@ -636,6 +709,18 @@ struct phy_driver {
        int (*module_eeprom)(struct phy_device *dev,
                             struct ethtool_eeprom *ee, u8 *data);
 
+       /* Start a cable test */
+       int (*cable_test_start)(struct phy_device *dev);
+
+       /* Start a raw TDR cable test */
+       int (*cable_test_tdr_start)(struct phy_device *dev,
+                                   const struct phy_tdr_config *config);
+
+       /* Once per second, or on interrupt, request the status of the
+        * test.
+        */
+       int (*cable_test_get_status)(struct phy_device *dev, bool *finished);
+
        /* Get statistics from the phy using ethtool */
        int (*get_sset_count)(struct phy_device *dev);
        void (*get_strings)(struct phy_device *dev, u8 *data);
@@ -649,6 +734,8 @@ struct phy_driver {
                            struct ethtool_tunable *tuna,
                            const void *data);
        int (*set_loopback)(struct phy_device *dev, bool enable);
+       int (*get_sqi)(struct phy_device *dev);
+       int (*get_sqi_max)(struct phy_device *dev);
 };
 #define to_phy_driver(d) container_of(to_mdio_common_driver(d),                \
                                      struct phy_driver, mdiodrv)
@@ -993,6 +1080,10 @@ static inline bool phy_interrupt_is_valid(struct phy_device *phydev)
  */
 static inline bool phy_polling_mode(struct phy_device *phydev)
 {
+       if (phydev->state == PHY_CABLETEST)
+               if (phydev->drv->flags & PHY_POLL_CABLE_TEST)
+                       return true;
+
        return phydev->irq == PHY_POLL;
 }
 
@@ -1174,6 +1265,34 @@ int phy_speed_up(struct phy_device *phydev);
 int phy_restart_aneg(struct phy_device *phydev);
 int phy_reset_after_clk_enable(struct phy_device *phydev);
 
+#if IS_ENABLED(CONFIG_PHYLIB)
+int phy_start_cable_test(struct phy_device *phydev,
+                        struct netlink_ext_ack *extack);
+int phy_start_cable_test_tdr(struct phy_device *phydev,
+                            struct netlink_ext_ack *extack,
+                            const struct phy_tdr_config *config);
+#else
+static inline
+int phy_start_cable_test(struct phy_device *phydev,
+                        struct netlink_ext_ack *extack)
+{
+       NL_SET_ERR_MSG(extack, "Kernel not compiled with PHYLIB support");
+       return -EOPNOTSUPP;
+}
+static inline
+int phy_start_cable_test_tdr(struct phy_device *phydev,
+                            struct netlink_ext_ack *extack,
+                            const struct phy_tdr_config *config)
+{
+       NL_SET_ERR_MSG(extack, "Kernel not compiled with PHYLIB support");
+       return -EOPNOTSUPP;
+}
+#endif
+
+int phy_cable_test_result(struct phy_device *phydev, u8 pair, u16 result);
+int phy_cable_test_fault_length(struct phy_device *phydev, u8 pair,
+                               u16 cm);
+
 static inline void phy_device_reset(struct phy_device *phydev, int value)
 {
        mdio_device_reset(&phydev->mdio, value);
@@ -1234,10 +1353,6 @@ static inline int genphy_config_aneg(struct phy_device *phydev)
        return __genphy_config_aneg(phydev, false);
 }
 
-static inline int genphy_no_soft_reset(struct phy_device *phydev)
-{
-       return 0;
-}
 static inline int genphy_no_ack_interrupt(struct phy_device *phydev)
 {
        return 0;
@@ -1341,6 +1456,10 @@ int phy_ethtool_get_link_ksettings(struct net_device *ndev,
 int phy_ethtool_set_link_ksettings(struct net_device *ndev,
                                   const struct ethtool_link_ksettings *cmd);
 int phy_ethtool_nway_reset(struct net_device *ndev);
+int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size);
+void phy_package_leave(struct phy_device *phydev);
+int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
+                         int addr, size_t priv_size);
 
 #if IS_ENABLED(CONFIG_PHYLIB)
 int __init mdio_bus_init(void);
@@ -1393,6 +1512,58 @@ static inline int phy_ethtool_get_stats(struct phy_device *phydev,
        return 0;
 }
 
+static inline int phy_package_read(struct phy_device *phydev, u32 regnum)
+{
+       struct phy_package_shared *shared = phydev->shared;
+
+       if (!shared)
+               return -EIO;
+
+       return mdiobus_read(phydev->mdio.bus, shared->addr, regnum);
+}
+
+static inline int __phy_package_read(struct phy_device *phydev, u32 regnum)
+{
+       struct phy_package_shared *shared = phydev->shared;
+
+       if (!shared)
+               return -EIO;
+
+       return __mdiobus_read(phydev->mdio.bus, shared->addr, regnum);
+}
+
+static inline int phy_package_write(struct phy_device *phydev,
+                                   u32 regnum, u16 val)
+{
+       struct phy_package_shared *shared = phydev->shared;
+
+       if (!shared)
+               return -EIO;
+
+       return mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val);
+}
+
+static inline int __phy_package_write(struct phy_device *phydev,
+                                     u32 regnum, u16 val)
+{
+       struct phy_package_shared *shared = phydev->shared;
+
+       if (!shared)
+               return -EIO;
+
+       return __mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val);
+}
+
+static inline bool phy_package_init_once(struct phy_device *phydev)
+{
+       struct phy_package_shared *shared = phydev->shared;
+
+       if (!shared)
+               return false;
+
+       return !test_and_set_bit(PHY_SHARED_F_INIT_DONE, &shared->flags);
+}
+
 extern struct bus_type mdio_bus_type;
 
 struct mdio_board_info {