| From 863b5b6941f9f43b924393b6ba2b36647e7dee42 Mon Sep 17 00:00:00 2001 | 
 | From: Russell King <rmk+kernel@armlinux.org.uk> | 
 | Date: Thu, 7 Nov 2019 17:06:08 +0000 | 
 | Subject: [PATCH 615/660] net: sfp: rework upstream interface | 
 |  | 
 | The current upstream interface is an all-or-nothing, which is | 
 | sub-optimal for future changes, as it doesn't allow the upstream driver | 
 | to prepare for the SFP module becoming available, as it is at boot. | 
 |  | 
 | Switch to a find-sfp-bus, add-upstream, del-upstream, put-sfp-bus | 
 | interface structure instead, which allows the upstream driver to | 
 | prepare for a module being available as soon as add-upstream is called. | 
 |  | 
 | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | 
 | --- | 
 |  drivers/net/phy/phylink.c | 10 +++-- | 
 |  drivers/net/phy/sfp-bus.c | 92 +++++++++++++++++++++++++++------------ | 
 |  include/linux/sfp.h       | 25 +++++++---- | 
 |  3 files changed, 88 insertions(+), 39 deletions(-) | 
 |  | 
 | --- a/drivers/net/phy/phylink.c | 
 | +++ b/drivers/net/phy/phylink.c | 
 | @@ -568,7 +568,7 @@ static int phylink_register_sfp(struct p | 
 |  	struct sfp_bus *bus; | 
 |  	int ret; | 
 |   | 
 | -	bus = sfp_register_upstream_node(fwnode, pl, &sfp_phylink_ops); | 
 | +	bus = sfp_bus_find_fwnode(fwnode); | 
 |  	if (IS_ERR(bus)) { | 
 |  		ret = PTR_ERR(bus); | 
 |  		phylink_err(pl, "unable to attach SFP bus: %d\n", ret); | 
 | @@ -577,7 +577,10 @@ static int phylink_register_sfp(struct p | 
 |   | 
 |  	pl->sfp_bus = bus; | 
 |   | 
 | -	return 0; | 
 | +	ret = sfp_bus_add_upstream(bus, pl, &sfp_phylink_ops); | 
 | +	sfp_bus_put(bus); | 
 | + | 
 | +	return ret; | 
 |  } | 
 |   | 
 |  /** | 
 | @@ -675,8 +678,7 @@ EXPORT_SYMBOL_GPL(phylink_create); | 
 |   */ | 
 |  void phylink_destroy(struct phylink *pl) | 
 |  { | 
 | -	if (pl->sfp_bus) | 
 | -		sfp_unregister_upstream(pl->sfp_bus); | 
 | +	sfp_bus_del_upstream(pl->sfp_bus); | 
 |  	if (pl->link_gpio) | 
 |  		gpiod_put(pl->link_gpio); | 
 |   | 
 | --- a/drivers/net/phy/sfp-bus.c | 
 | +++ b/drivers/net/phy/sfp-bus.c | 
 | @@ -404,10 +404,19 @@ static void sfp_bus_release(struct kref | 
 |  	kfree(bus); | 
 |  } | 
 |   | 
 | -static void sfp_bus_put(struct sfp_bus *bus) | 
 | +/** | 
 | + * sfp_bus_put() - put a reference on the &struct sfp_bus | 
 | + * bus: the &struct sfp_bus found via sfp_bus_find_fwnode() | 
 | + * | 
 | + * Put a reference on the &struct sfp_bus and free the underlying structure | 
 | + * if this was the last reference. | 
 | + */ | 
 | +void sfp_bus_put(struct sfp_bus *bus) | 
 |  { | 
 | -	kref_put_mutex(&bus->kref, sfp_bus_release, &sfp_mutex); | 
 | +	if (bus) | 
 | +		kref_put_mutex(&bus->kref, sfp_bus_release, &sfp_mutex); | 
 |  } | 
 | +EXPORT_SYMBOL_GPL(sfp_bus_put); | 
 |   | 
 |  static int sfp_register_bus(struct sfp_bus *bus) | 
 |  { | 
 | @@ -423,11 +432,11 @@ static int sfp_register_bus(struct sfp_b | 
 |  				return ret; | 
 |  		} | 
 |  	} | 
 | +	bus->registered = true; | 
 |  	bus->socket_ops->attach(bus->sfp); | 
 |  	if (bus->started) | 
 |  		bus->socket_ops->start(bus->sfp); | 
 |  	bus->upstream_ops->attach(bus->upstream, bus); | 
 | -	bus->registered = true; | 
 |  	return 0; | 
 |  } | 
 |   | 
 | @@ -521,13 +530,12 @@ static void sfp_upstream_clear(struct sf | 
 |  } | 
 |   | 
 |  /** | 
 | - * sfp_register_upstream_node() - parse and register the neighbouring device | 
 | + * sfp_bus_find_fwnode() - parse and locate the SFP bus from fwnode | 
 |   * @fwnode: firmware node for the parent device (MAC or PHY) | 
 | - * @upstream: the upstream private data | 
 | - * @ops: the upstream's &struct sfp_upstream_ops | 
 |   * | 
 | - * Parse the parent device's firmware node for a SFP bus, and register the | 
 | - * SFP bus using sfp_register_upstream(). | 
 | + * Parse the parent device's firmware node for a SFP bus, and locate | 
 | + * the sfp_bus structure, incrementing its reference count.  This must | 
 | + * be put via sfp_bus_put() when done. | 
 |   * | 
 |   * Returns: on success, a pointer to the sfp_bus structure, | 
 |   *	    %NULL if no SFP is specified, | 
 | @@ -537,9 +545,7 @@ static void sfp_upstream_clear(struct sf | 
 |   * 	        %-ENOMEM if we failed to allocate the bus. | 
 |   *		an error from the upstream's connect_phy() method. | 
 |   */ | 
 | -struct sfp_bus *sfp_register_upstream_node(struct fwnode_handle *fwnode, | 
 | -					   void *upstream, | 
 | -					   const struct sfp_upstream_ops *ops) | 
 | +struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode) | 
 |  { | 
 |  	struct fwnode_reference_args ref; | 
 |  	struct sfp_bus *bus; | 
 | @@ -562,7 +568,39 @@ struct sfp_bus *sfp_register_upstream_no | 
 |  	if (!bus) | 
 |  		return ERR_PTR(-ENOMEM); | 
 |   | 
 | +	return bus; | 
 | +} | 
 | +EXPORT_SYMBOL_GPL(sfp_bus_find_fwnode); | 
 | + | 
 | +/** | 
 | + * sfp_bus_add_upstream() - parse and register the neighbouring device | 
 | + * @bus: the &struct sfp_bus found via sfp_bus_find_fwnode() | 
 | + * @upstream: the upstream private data | 
 | + * @ops: the upstream's &struct sfp_upstream_ops | 
 | + * | 
 | + * Add upstream driver for the SFP bus, and if the bus is complete, register | 
 | + * the SFP bus using sfp_register_upstream().  This takes a reference on the | 
 | + * bus, so it is safe to put the bus after this call. | 
 | + * | 
 | + * Returns: on success, a pointer to the sfp_bus structure, | 
 | + *	    %NULL if no SFP is specified, | 
 | + * 	    on failure, an error pointer value: | 
 | + * 		corresponding to the errors detailed for | 
 | + * 		fwnode_property_get_reference_args(). | 
 | + * 	        %-ENOMEM if we failed to allocate the bus. | 
 | + *		an error from the upstream's connect_phy() method. | 
 | + */ | 
 | +int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream, | 
 | +			 const struct sfp_upstream_ops *ops) | 
 | +{ | 
 | +	int ret; | 
 | + | 
 | +	/* If no bus, return success */ | 
 | +	if (!bus) | 
 | +		return 0; | 
 | + | 
 |  	rtnl_lock(); | 
 | +	kref_get(&bus->kref); | 
 |  	bus->upstream_ops = ops; | 
 |  	bus->upstream = upstream; | 
 |   | 
 | @@ -575,33 +613,33 @@ struct sfp_bus *sfp_register_upstream_no | 
 |  	} | 
 |  	rtnl_unlock(); | 
 |   | 
 | -	if (ret) { | 
 | +	if (ret) | 
 |  		sfp_bus_put(bus); | 
 | -		bus = ERR_PTR(ret); | 
 | -	} | 
 |   | 
 | -	return bus; | 
 | +	return ret; | 
 |  } | 
 | -EXPORT_SYMBOL_GPL(sfp_register_upstream_node); | 
 | +EXPORT_SYMBOL_GPL(sfp_bus_add_upstream); | 
 |   | 
 |  /** | 
 | - * sfp_unregister_upstream() - Unregister sfp bus | 
 | + * sfp_bus_del_upstream() - Delete a 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(). | 
 | + * Delete a previously registered upstream connection for the SFP | 
 | + * module. @bus should have been added by sfp_bus_add_upstream(). | 
 |   */ | 
 | -void sfp_unregister_upstream(struct sfp_bus *bus) | 
 | +void sfp_bus_del_upstream(struct sfp_bus *bus) | 
 |  { | 
 | -	rtnl_lock(); | 
 | -	if (bus->sfp) | 
 | -		sfp_unregister_bus(bus); | 
 | -	sfp_upstream_clear(bus); | 
 | -	rtnl_unlock(); | 
 | +	if (bus) { | 
 | +		rtnl_lock(); | 
 | +		if (bus->sfp) | 
 | +			sfp_unregister_bus(bus); | 
 | +		sfp_upstream_clear(bus); | 
 | +		rtnl_unlock(); | 
 |   | 
 | -	sfp_bus_put(bus); | 
 | +		sfp_bus_put(bus); | 
 | +	} | 
 |  } | 
 | -EXPORT_SYMBOL_GPL(sfp_unregister_upstream); | 
 | +EXPORT_SYMBOL_GPL(sfp_bus_del_upstream); | 
 |   | 
 |  /* Socket driver entry points */ | 
 |  int sfp_add_phy(struct sfp_bus *bus, struct phy_device *phydev) | 
 | --- a/include/linux/sfp.h | 
 | +++ b/include/linux/sfp.h | 
 | @@ -508,10 +508,11 @@ int sfp_get_module_eeprom(struct sfp_bus | 
 |  			  u8 *data); | 
 |  void sfp_upstream_start(struct sfp_bus *bus); | 
 |  void sfp_upstream_stop(struct sfp_bus *bus); | 
 | -struct sfp_bus *sfp_register_upstream_node(struct fwnode_handle *fwnode, | 
 | -					   void *upstream, | 
 | -					   const struct sfp_upstream_ops *ops); | 
 | -void sfp_unregister_upstream(struct sfp_bus *bus); | 
 | +void sfp_bus_put(struct sfp_bus *bus); | 
 | +struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode); | 
 | +int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream, | 
 | +			 const struct sfp_upstream_ops *ops); | 
 | +void sfp_bus_del_upstream(struct sfp_bus *bus); | 
 |  #else | 
 |  static inline int sfp_parse_port(struct sfp_bus *bus, | 
 |  				 const struct sfp_eeprom_id *id, | 
 | @@ -553,14 +554,22 @@ static inline void sfp_upstream_stop(str | 
 |  { | 
 |  } | 
 |   | 
 | -static inline struct sfp_bus *sfp_register_upstream_node( | 
 | -	struct fwnode_handle *fwnode, void *upstream, | 
 | -	const struct sfp_upstream_ops *ops) | 
 | +static inline void sfp_bus_put(struct sfp_bus *bus) | 
 | +{ | 
 | +} | 
 | + | 
 | +static inline struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode) | 
 |  { | 
 |  	return NULL; | 
 |  } | 
 |   | 
 | -static inline void sfp_unregister_upstream(struct sfp_bus *bus) | 
 | +static int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream, | 
 | +				const struct sfp_upstream_ops *ops) | 
 | +{ | 
 | +	return 0; | 
 | +} | 
 | + | 
 | +static inline void sfp_bus_del_upstream(struct sfp_bus *bus) | 
 |  { | 
 |  } | 
 |  #endif |