Commit 9b89f07f authored by David S. Miller's avatar David S. Miller

Merge branch 'SFP-phylink-updates'

Russell King says:

====================
SFP/phylink updates

This series, which follows on from the fixes posted earlier, improves
the phylink/sfp support.  Changes included here are:

- Merge 802.3z and SGMII modes into one "in-band" mode, using the
  PHY_INTERFACE_MODE_xxx definition to determine which should be used.
  This allows more flexibility as more interface modes become
  available.

- Allow 2500base-X and 10GBASE-KR to be requested from SFP.

- Remove unused and unnecessary phylink_init_eee()

- Restart 802.3z autonegotiation when starting the network device to
  ensure that the negotiated parameters are always correct.  It has
  been observed on mvneta that this is not always the case without
  this change.

- Add kerneldoc documentation for phylink and sfp upstream facing APIs
  and link it in to the networking documentation.

- Resolve a sparse warning in sfp-bus.c

- Convert phylink/sfp to use fwnode rather than DT so that other firmware
  systems can take advantage of this - I have received a request for it
  to be usable with ACPI.  The exception to this is our interactions with
  phylib, as phylib itself does not yet support fwnode.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents b0e9fe1b 8fa7b9b6
......@@ -145,3 +145,27 @@ PHY Support
.. kernel-doc:: drivers/net/phy/mdio_bus.c
:internal:
PHYLINK
-------
PHYLINK interfaces traditional network drivers with PHYLIB, fixed-links,
and SFF modules (eg, hot-pluggable SFP) that may contain PHYs. PHYLINK
provides management of the link state and link modes.
.. kernel-doc:: include/linux/phylink.h
:internal:
.. kernel-doc:: drivers/net/phy/phylink.c
SFP support
-----------
.. kernel-doc:: drivers/net/phy/sfp-bus.c
:internal:
.. kernel-doc:: include/linux/sfp.h
:internal:
.. kernel-doc:: drivers/net/phy/sfp-bus.c
:export:
This diff is collapsed.
......@@ -8,10 +8,14 @@
#include "sfp.h"
/**
* struct sfp_bus - internal representation of a sfp bus
*/
struct sfp_bus {
/* private: */
struct kref kref;
struct list_head node;
struct device_node *device_node;
struct fwnode_handle *fwnode;
const struct sfp_socket_ops *socket_ops;
struct device *sfp_dev;
......@@ -26,6 +30,20 @@ struct sfp_bus {
bool started;
};
/**
* sfp_parse_port() - Parse the EEPROM base ID, setting the port type
* @bus: a pointer to the &struct sfp_bus structure for the sfp module
* @id: a pointer to the module's &struct sfp_eeprom_id
* @support: optional pointer to an array of unsigned long for the
* ethtool support mask
*
* Parse the EEPROM identification given in @id, and return one of
* %PORT_TP, %PORT_FIBRE or %PORT_OTHER. If @support is non-%NULL,
* also set the ethtool %ETHTOOL_LINK_MODE_xxx_BIT corresponding with
* the connector type.
*
* If the port type is not known, returns %PORT_OTHER.
*/
int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
unsigned long *support)
{
......@@ -78,6 +96,24 @@ int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
}
EXPORT_SYMBOL_GPL(sfp_parse_port);
/**
* sfp_parse_interface() - Parse the phy_interface_t
* @bus: a pointer to the &struct sfp_bus structure for the sfp module
* @id: a pointer to the module's &struct sfp_eeprom_id
*
* Derive the phy_interface_t mode for the information found in the
* module's identifying EEPROM. There is no standard or defined way
* to derive this information, so we use some heuristics.
*
* If the encoding is 64b66b, then the module must be >= 10G, so
* return %PHY_INTERFACE_MODE_10GKR.
*
* If it's 8b10b, then it's 1G or slower. If it's definitely a fibre
* module, return %PHY_INTERFACE_MODE_1000BASEX mode, otherwise return
* %PHY_INTERFACE_MODE_SGMII mode.
*
* If the encoding is not known, return %PHY_INTERFACE_MODE_NA.
*/
phy_interface_t sfp_parse_interface(struct sfp_bus *bus,
const struct sfp_eeprom_id *id)
{
......@@ -117,6 +153,15 @@ phy_interface_t sfp_parse_interface(struct sfp_bus *bus,
}
EXPORT_SYMBOL_GPL(sfp_parse_interface);
/**
* sfp_parse_support() - Parse the eeprom id for supported link modes
* @bus: a pointer to the &struct sfp_bus structure for the sfp module
* @id: a pointer to the module's &struct sfp_eeprom_id
* @support: pointer to an array of unsigned long for the ethtool support mask
*
* Parse the EEPROM identification information and derive the supported
* ethtool link modes for the module.
*/
void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
unsigned long *support)
{
......@@ -215,7 +260,7 @@ static const struct sfp_upstream_ops *sfp_get_upstream_ops(struct sfp_bus *bus)
return bus->registered ? bus->upstream_ops : NULL;
}
static struct sfp_bus *sfp_bus_get(struct device_node *np)
static struct sfp_bus *sfp_bus_get(struct fwnode_handle *fwnode)
{
struct sfp_bus *sfp, *new, *found = NULL;
......@@ -224,7 +269,7 @@ static struct sfp_bus *sfp_bus_get(struct device_node *np)
mutex_lock(&sfp_mutex);
list_for_each_entry(sfp, &sfp_buses, node) {
if (sfp->device_node == np) {
if (sfp->fwnode == fwnode) {
kref_get(&sfp->kref);
found = sfp;
break;
......@@ -233,7 +278,7 @@ static struct sfp_bus *sfp_bus_get(struct device_node *np)
if (!found && new) {
kref_init(&new->kref);
new->device_node = np;
new->fwnode = fwnode;
list_add(&new->node, &sfp_buses);
found = new;
new = NULL;
......@@ -246,7 +291,7 @@ static struct sfp_bus *sfp_bus_get(struct device_node *np)
return found;
}
static void sfp_bus_release(struct kref *kref) __releases(sfp_mutex)
static void sfp_bus_release(struct kref *kref)
{
struct sfp_bus *bus = container_of(kref, struct sfp_bus, kref);
......@@ -293,6 +338,16 @@ static void sfp_unregister_bus(struct sfp_bus *bus)
bus->registered = false;
}
/**
* sfp_get_module_info() - Get the ethtool_modinfo for a SFP module
* @bus: a pointer to the &struct sfp_bus structure for the sfp module
* @modinfo: a &struct ethtool_modinfo
*
* Fill in the type and eeprom_len parameters in @modinfo for a module on
* the sfp bus specified by @bus.
*
* Returns 0 on success or a negative errno number.
*/
int sfp_get_module_info(struct sfp_bus *bus, struct ethtool_modinfo *modinfo)
{
if (!bus->registered)
......@@ -301,6 +356,17 @@ int sfp_get_module_info(struct sfp_bus *bus, struct ethtool_modinfo *modinfo)
}
EXPORT_SYMBOL_GPL(sfp_get_module_info);
/**
* sfp_get_module_eeprom() - Read the SFP module EEPROM
* @bus: a pointer to the &struct sfp_bus structure for the sfp module
* @ee: a &struct ethtool_eeprom
* @data: buffer to contain the EEPROM data (must be at least @ee->len bytes)
*
* Read the EEPROM as specified by the supplied @ee. See the documentation
* for &struct ethtool_eeprom for the region to be read.
*
* Returns 0 on success or a negative errno number.
*/
int sfp_get_module_eeprom(struct sfp_bus *bus, struct ethtool_eeprom *ee,
u8 *data)
{
......@@ -310,6 +376,15 @@ int sfp_get_module_eeprom(struct sfp_bus *bus, struct ethtool_eeprom *ee,
}
EXPORT_SYMBOL_GPL(sfp_get_module_eeprom);
/**
* sfp_upstream_start() - Inform the SFP that the network device is up
* @bus: a pointer to the &struct sfp_bus structure for the sfp module
*
* Inform the SFP socket that the network device is now up, so that the
* module can be enabled by allowing TX_DISABLE to be deasserted. This
* should be called from the network device driver's &struct net_device_ops
* ndo_open() method.
*/
void sfp_upstream_start(struct sfp_bus *bus)
{
if (bus->registered)
......@@ -318,6 +393,15 @@ void sfp_upstream_start(struct sfp_bus *bus)
}
EXPORT_SYMBOL_GPL(sfp_upstream_start);
/**
* sfp_upstream_stop() - Inform the SFP that the network device is down
* @bus: a pointer to the &struct sfp_bus structure for the sfp module
*
* Inform the SFP socket that the network device is now up, so that the
* module can be disabled by asserting TX_DISABLE, disabling the laser
* in optical modules. This should be called from the network device
* driver's &struct net_device_ops ndo_stop() method.
*/
void sfp_upstream_stop(struct sfp_bus *bus)
{
if (bus->registered)
......@@ -326,11 +410,24 @@ void sfp_upstream_stop(struct sfp_bus *bus)
}
EXPORT_SYMBOL_GPL(sfp_upstream_stop);
struct sfp_bus *sfp_register_upstream(struct device_node *np,
/**
* sfp_register_upstream() - Register the neighbouring device
* @np: device node for the SFP bus
* @ndev: network device associated with the interface
* @upstream: the upstream private data
* @ops: the upstream's &struct sfp_upstream_ops
*
* Register the upstream device (eg, PHY) with the SFP bus. MAC drivers
* should use phylink, which will call this function for them. Returns
* a pointer to the allocated &struct sfp_bus.
*
* On error, returns %NULL.
*/
struct sfp_bus *sfp_register_upstream(struct fwnode_handle *fwnode,
struct net_device *ndev, void *upstream,
const struct sfp_upstream_ops *ops)
{
struct sfp_bus *bus = sfp_bus_get(np);
struct sfp_bus *bus = sfp_bus_get(fwnode);
int ret = 0;
if (bus) {
......@@ -353,6 +450,13 @@ struct sfp_bus *sfp_register_upstream(struct device_node *np,
}
EXPORT_SYMBOL_GPL(sfp_register_upstream);
/**
* sfp_unregister_upstream() - Unregister sfp bus
* @bus: a pointer to the &struct sfp_bus structure for the sfp module
*
* Unregister a previously registered upstream connection for the SFP
* module. @bus is returned from sfp_register_upstream().
*/
void sfp_unregister_upstream(struct sfp_bus *bus)
{
rtnl_lock();
......@@ -433,7 +537,7 @@ EXPORT_SYMBOL_GPL(sfp_module_remove);
struct sfp_bus *sfp_register_socket(struct device *dev, struct sfp *sfp,
const struct sfp_socket_ops *ops)
{
struct sfp_bus *bus = sfp_bus_get(dev->of_node);
struct sfp_bus *bus = sfp_bus_get(dev->fwnode);
int ret = 0;
if (bus) {
......
......@@ -761,6 +761,20 @@ static inline bool phy_interface_mode_is_rgmii(phy_interface_t mode)
mode <= PHY_INTERFACE_MODE_RGMII_TXID;
};
/**
* phy_interface_mode_is_8023z() - does the phy interface mode use 802.3z
* negotiation
* @mode: one of &enum phy_interface_t
*
* Returns true if the phy interface mode uses the 16-bit negotiation
* word as defined in 802.3z. (See 802.3-2015 37.2.1 Config_Reg encoding)
*/
static inline bool phy_interface_mode_is_8023z(phy_interface_t mode)
{
return mode == PHY_INTERFACE_MODE_1000BASEX ||
mode == PHY_INTERFACE_MODE_2500BASEX;
}
/**
* phy_interface_is_rgmii - Convenience function for testing if a PHY interface
* is RGMII (all variants)
......
......@@ -7,6 +7,7 @@
struct device_node;
struct ethtool_cmd;
struct fwnode_handle;
struct net_device;
enum {
......@@ -20,19 +21,31 @@ enum {
MLO_AN_PHY = 0, /* Conventional PHY */
MLO_AN_FIXED, /* Fixed-link mode */
MLO_AN_SGMII, /* Cisco SGMII protocol */
MLO_AN_8023Z, /* 1000base-X protocol */
MLO_AN_INBAND, /* In-band protocol */
};
static inline bool phylink_autoneg_inband(unsigned int mode)
{
return mode == MLO_AN_SGMII || mode == MLO_AN_8023Z;
return mode == MLO_AN_INBAND;
}
/**
* struct phylink_link_state - link state structure
* @advertising: ethtool bitmask containing advertised link modes
* @lp_advertising: ethtool bitmask containing link partner advertised link
* modes
* @interface: link &typedef phy_interface_t mode
* @speed: link speed, one of the SPEED_* constants.
* @duplex: link duplex mode, one of DUPLEX_* constants.
* @pause: link pause state, described by MLO_PAUSE_* constants.
* @link: true if the link is up.
* @an_enabled: true if autonegotiation is enabled/desired.
* @an_complete: true if autonegotiation has completed.
*/
struct phylink_link_state {
__ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
__ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising);
phy_interface_t interface; /* PHY_INTERFACE_xxx */
phy_interface_t interface;
int speed;
int duplex;
int pause;
......@@ -41,66 +54,136 @@ struct phylink_link_state {
unsigned int an_complete:1;
};
/**
* struct phylink_mac_ops - MAC operations structure.
* @validate: Validate and update the link configuration.
* @mac_link_state: Read the current link state from the hardware.
* @mac_config: configure the MAC for the selected mode and state.
* @mac_an_restart: restart 802.3z BaseX autonegotiation.
* @mac_link_down: take the link down.
* @mac_link_up: allow the link to come up.
*
* The individual methods are described more fully below.
*/
struct phylink_mac_ops {
/**
* validate: validate and update the link configuration
* @ndev: net_device structure associated with MAC
* @config: configuration to validate
*
* Update the %config->supported and %config->advertised masks
* clearing bits that can not be supported.
*
* Note: the PHY may be able to transform from one connection
* technology to another, so, eg, don't clear 1000BaseX just
* because the MAC is unable to support it. This is more about
* clearing unsupported speeds and duplex settings.
*
* If the %config->interface mode is %PHY_INTERFACE_MODE_1000BASEX
* or %PHY_INTERFACE_MODE_2500BASEX, select the appropriate mode
* based on %config->advertised and/or %config->speed.
*/
void (*validate)(struct net_device *ndev, unsigned long *supported,
struct phylink_link_state *state);
/* Read the current link state from the hardware */
int (*mac_link_state)(struct net_device *, struct phylink_link_state *);
/* Configure the MAC */
/**
* mac_config: configure the MAC for the selected mode and state
* @ndev: net_device structure for the MAC
* @mode: one of MLO_AN_FIXED, MLO_AN_PHY, MLO_AN_8023Z, MLO_AN_SGMII
* @state: state structure
*
* The action performed depends on the currently selected mode:
*
* %MLO_AN_FIXED, %MLO_AN_PHY:
* set the specified speed, duplex, pause mode, and phy interface
* mode in the provided @state.
* %MLO_AN_8023Z:
* place the link in 1000base-X mode, advertising the parameters
* given in advertising in @state.
* %MLO_AN_SGMII:
* place the link in Cisco SGMII mode - there is no advertisment
* to make as the PHY communicates the speed and duplex to the
* MAC over the in-band control word. Configuration of the pause
* mode is as per MLO_AN_PHY since this is not included.
*/
int (*mac_link_state)(struct net_device *ndev,
struct phylink_link_state *state);
void (*mac_config)(struct net_device *ndev, unsigned int mode,
const struct phylink_link_state *state);
/**
* mac_an_restart: restart 802.3z BaseX autonegotiation
* @ndev: net_device structure for the MAC
*/
void (*mac_an_restart)(struct net_device *ndev);
void (*mac_link_down)(struct net_device *, unsigned int mode);
void (*mac_link_up)(struct net_device *, unsigned int mode,
struct phy_device *);
void (*mac_link_down)(struct net_device *ndev, unsigned int mode);
void (*mac_link_up)(struct net_device *ndev, unsigned int mode,
struct phy_device *phy);
};
struct phylink *phylink_create(struct net_device *, struct device_node *,
#if 0 /* For kernel-doc purposes only. */
/**
* validate - Validate and update the link configuration
* @ndev: a pointer to a &struct net_device for the MAC.
* @supported: ethtool bitmask for supported link modes.
* @state: a pointer to a &struct phylink_link_state.
*
* Clear bits in the @supported and @state->advertising masks that
* are not supportable by the MAC.
*
* Note that the PHY may be able to transform from one connection
* technology to another, so, eg, don't clear 1000BaseX just
* because the MAC is unable to BaseX mode. This is more about
* clearing unsupported speeds and duplex settings.
*
* If the @state->interface mode is %PHY_INTERFACE_MODE_1000BASEX
* or %PHY_INTERFACE_MODE_2500BASEX, select the appropriate mode
* based on @state->advertising and/or @state->speed and update
* @state->interface accordingly.
*/
void validate(struct net_device *ndev, unsigned long *supported,
struct phylink_link_state *state);
/**
* mac_link_state() - Read the current link state from the hardware
* @ndev: a pointer to a &struct net_device for the MAC.
* @state: a pointer to a &struct phylink_link_state.
*
* Read the current link state from the MAC, reporting the current
* speed in @state->speed, duplex mode in @state->duplex, pause mode
* in @state->pause using the %MLO_PAUSE_RX and %MLO_PAUSE_TX bits,
* negotiation completion state in @state->an_complete, and link
* up state in @state->link.
*/
int mac_link_state(struct net_device *ndev,
struct phylink_link_state *state);
/**
* mac_config() - configure the MAC for the selected mode and state
* @ndev: a pointer to a &struct net_device for the MAC.
* @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
* @state: a pointer to a &struct phylink_link_state.
*
* The action performed depends on the currently selected mode:
*
* %MLO_AN_FIXED, %MLO_AN_PHY:
* Configure the specified @state->speed, @state->duplex and
* @state->pause (%MLO_PAUSE_TX / %MLO_PAUSE_RX) mode.
*
* %MLO_AN_INBAND:
* place the link in an inband negotiation mode (such as 802.3z
* 1000base-X or Cisco SGMII mode depending on the @state->interface
* mode). In both cases, link state management (whether the link
* is up or not) is performed by the MAC, and reported via the
* mac_link_state() callback. Changes in link state must be made
* by calling phylink_mac_change().
*
* If in 802.3z mode, the link speed is fixed, dependent on the
* @state->interface. Duplex is negotiated, and pause is advertised
* according to @state->an_enabled, @state->pause and
* @state->advertising flags. Beware of MACs which only support full
* duplex at gigabit and higher speeds.
*
* If in Cisco SGMII mode, the link speed and duplex mode are passed
* in the serial bitstream 16-bit configuration word, and the MAC
* should be configured to read these bits and acknowledge the
* configuration word. Nothing is advertised by the MAC. The MAC is
* responsible for reading the configuration word and configuring
* itself accordingly.
*/
void mac_config(struct net_device *ndev, unsigned int mode,
const struct phylink_link_state *state);
/**
* mac_an_restart() - restart 802.3z BaseX autonegotiation
* @ndev: a pointer to a &struct net_device for the MAC.
*/
void mac_an_restart(struct net_device *ndev);
/**
* mac_link_down() - take the link down
* @ndev: a pointer to a &struct net_device for the MAC.
* @mode: link autonegotiation mode
*
* If @mode is not an in-band negotiation mode (as defined by
* phylink_autoneg_inband()), force the link down and disable any
* Energy Efficient Ethernet MAC configuration.
*/
void mac_link_down(struct net_device *ndev, unsigned int mode);
/**
* mac_link_up() - allow the link to come up
* @ndev: a pointer to a &struct net_device for the MAC.
* @mode: link autonegotiation mode
* @phy: any attached phy
*
* If @mode is not an in-band negotiation mode (as defined by
* phylink_autoneg_inband()), allow the link to come up. If @phy
* is non-%NULL, configure Energy Efficient Ethernet by calling
* phy_init_eee() and perform appropriate MAC configuration for EEE.
*/
void mac_link_up(struct net_device *ndev, unsigned int mode,
struct phy_device *phy);
#endif
struct phylink *phylink_create(struct net_device *, struct fwnode_handle *,
phy_interface_t iface, const struct phylink_mac_ops *ops);
void phylink_destroy(struct phylink *);
......@@ -128,7 +211,6 @@ int phylink_ethtool_set_pauseparam(struct phylink *,
int phylink_ethtool_get_module_info(struct phylink *, struct ethtool_modinfo *);
int phylink_ethtool_get_module_eeprom(struct phylink *,
struct ethtool_eeprom *, u8 *);
int phylink_init_eee(struct phylink *, bool);
int phylink_get_eee_err(struct phylink *);
int phylink_ethtool_get_eee(struct phylink *, struct ethtool_eee *);
int phylink_ethtool_set_eee(struct phylink *, struct ethtool_eee *);
......
......@@ -3,7 +3,7 @@
#include <linux/phy.h>
struct __packed sfp_eeprom_base {
struct sfp_eeprom_base {
u8 phys_id;
u8 phys_ext_id;
u8 connector;
......@@ -166,12 +166,12 @@ struct __packed sfp_eeprom_base {
union {
__be16 optical_wavelength;
u8 cable_spec;
};
} __packed;
u8 reserved62;
u8 cc_base;
};
} __packed;
struct __packed sfp_eeprom_ext {
struct sfp_eeprom_ext {
__be16 options;
u8 br_max;
u8 br_min;
......@@ -181,12 +181,21 @@ struct __packed sfp_eeprom_ext {
u8 enhopts;
u8 sff8472_compliance;
u8 cc_ext;
};
struct __packed sfp_eeprom_id {
} __packed;
/**
* struct sfp_eeprom_id - raw SFP module identification information
* @base: base SFP module identification structure
* @ext: extended SFP module identification structure
*
* See the SFF-8472 specification and related documents for the definition
* of these structure members. This can be obtained from
* ftp://ftp.seagate.com/sff
*/
struct sfp_eeprom_id {
struct sfp_eeprom_base base;
struct sfp_eeprom_ext ext;
};
} __packed;
/* SFP EEPROM registers */
enum {
......@@ -347,19 +356,32 @@ enum {
SFP_PAGE = 0x7f,
};
struct device_node;
struct fwnode_handle;
struct ethtool_eeprom;
struct ethtool_modinfo;
struct net_device;
struct sfp_bus;
/**
* struct sfp_upstream_ops - upstream operations structure
* @module_insert: called after a module has been detected to determine
* whether the module is supported for the upstream device.
* @module_remove: called after the module has been removed.
* @link_down: called when the link is non-operational for whatever
* reason.
* @link_up: called when the link is operational.
* @connect_phy: called when an I2C accessible PHY has been detected
* on the module.
* @disconnect_phy: called when a module with an I2C accessible PHY has
* been removed.
*/
struct sfp_upstream_ops {
int (*module_insert)(void *, const struct sfp_eeprom_id *id);
void (*module_remove)(void *);
void (*link_down)(void *);
void (*link_up)(void *);
int (*connect_phy)(void *, struct phy_device *);
void (*disconnect_phy)(void *);
int (*module_insert)(void *priv, const struct sfp_eeprom_id *id);
void (*module_remove)(void *priv);
void (*link_down)(void *priv);
void (*link_up)(void *priv);
int (*connect_phy)(void *priv, struct phy_device *);
void (*disconnect_phy)(void *priv);
};
#if IS_ENABLED(CONFIG_SFP)
......@@ -375,7 +397,7 @@ int sfp_get_module_eeprom(struct sfp_bus *bus, struct ethtool_eeprom *ee,
u8 *data);
void sfp_upstream_start(struct sfp_bus *bus);
void sfp_upstream_stop(struct sfp_bus *bus);
struct sfp_bus *sfp_register_upstream(struct device_node *np,
struct sfp_bus *sfp_register_upstream(struct fwnode_handle *fwnode,
struct net_device *ndev, void *upstream,
const struct sfp_upstream_ops *ops);
void sfp_unregister_upstream(struct sfp_bus *bus);
......@@ -419,7 +441,8 @@ static inline void sfp_upstream_stop(struct sfp_bus *bus)
{
}
static inline struct sfp_bus *sfp_register_upstream(struct device_node *np,
static inline struct sfp_bus *sfp_register_upstream(
struct fwnode_handle *fwnode,
struct net_device *ndev, void *upstream,
const struct sfp_upstream_ops *ops)
{
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment