mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-31 10:26:10 +01:00 
			
		
		
		
	At present ofnode is present in the device even if it is never used. With of-platdata this field is not used, so can be removed. In preparation for this, change the access to go through inline functions. Signed-off-by: Simon Glass <sjg@chromium.org>
		
			
				
	
	
		
			234 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			234 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0+
 | |
| /*
 | |
|  * (C) Copyright 2019
 | |
|  * Alex Marginean, NXP
 | |
|  */
 | |
| 
 | |
| #include <common.h>
 | |
| #include <dm.h>
 | |
| #include <log.h>
 | |
| #include <miiphy.h>
 | |
| #include <dm/device-internal.h>
 | |
| #include <dm/uclass-internal.h>
 | |
| #include <dm/lists.h>
 | |
| 
 | |
| #define MDIO_MUX_CHILD_DRV_NAME	"mdio-mux-bus-drv"
 | |
| 
 | |
| /**
 | |
|  * struct mdio_mux_perdev_priv - Per-device class data for MDIO MUX DM
 | |
|  *
 | |
|  * @parent_mdio: Parent DM MDIO device, this is called for actual MDIO I/O after
 | |
|  *               setting up the mux.  Typically this is a real MDIO device,
 | |
|  *               unless there are cascaded muxes.
 | |
|  * @selected:    Current child bus selection.  Defaults to -1
 | |
|  */
 | |
| struct mdio_mux_perdev_priv {
 | |
| 	struct udevice *mdio_parent;
 | |
| 	int selected;
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * This source file uses three types of devices, as follows:
 | |
|  * - mux is the hardware MDIO MUX which selects between the existing child MDIO
 | |
|  * buses, this is the device relevant for MDIO MUX class of drivers.
 | |
|  * - ch is a child MDIO bus, this is just a representation of a mux selection,
 | |
|  * not a real piece of hardware.
 | |
|  * - mdio_parent is the actual MDIO bus called to perform reads/writes after
 | |
|  * the MUX is configured.  Typically this is a real MDIO device, unless there
 | |
|  * are cascaded muxes.
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * struct mdio_mux_ch_data - Per-device data for child MDIOs
 | |
|  *
 | |
|  * @sel: Selection value used by the MDIO MUX to access this child MDIO bus
 | |
|  */
 | |
| struct mdio_mux_ch_data {
 | |
| 	int sel;
 | |
| };
 | |
| 
 | |
| static struct udevice *mmux_get_parent_mdio(struct udevice *mux)
 | |
| {
 | |
| 	struct mdio_mux_perdev_priv *pdata = dev_get_uclass_priv(mux);
 | |
| 
 | |
| 	return pdata->mdio_parent;
 | |
| }
 | |
| 
 | |
| static struct mdio_ops *mmux_get_mdio_parent_ops(struct udevice *mux)
 | |
| {
 | |
| 	return mdio_get_ops(mmux_get_parent_mdio(mux));
 | |
| }
 | |
| 
 | |
| /* call driver select function before performing MDIO r/w */
 | |
| static int mmux_change_sel(struct udevice *ch, bool sel)
 | |
| {
 | |
| 	struct udevice *mux = ch->parent;
 | |
| 	struct mdio_mux_perdev_priv *priv = dev_get_uclass_priv(mux);
 | |
| 	struct mdio_mux_ops *ops = mdio_mux_get_ops(mux);
 | |
| 	struct mdio_mux_ch_data *ch_data = dev_get_parent_plat(ch);
 | |
| 	int err = 0;
 | |
| 
 | |
| 	if (sel) {
 | |
| 		err = ops->select(mux, priv->selected, ch_data->sel);
 | |
| 		if (err)
 | |
| 			return err;
 | |
| 
 | |
| 		priv->selected = ch_data->sel;
 | |
| 	} else {
 | |
| 		if (ops->deselect) {
 | |
| 			ops->deselect(mux, ch_data->sel);
 | |
| 			priv->selected = MDIO_MUX_SELECT_NONE;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* Read wrapper, sets up the mux before issuing a read on parent MDIO bus */
 | |
| static int mmux_read(struct udevice *ch, int addr, int devad,
 | |
| 		     int reg)
 | |
| {
 | |
| 	struct udevice *mux = ch->parent;
 | |
| 	struct udevice *parent_mdio = mmux_get_parent_mdio(mux);
 | |
| 	struct mdio_ops *parent_ops = mmux_get_mdio_parent_ops(mux);
 | |
| 	int err;
 | |
| 
 | |
| 	err = mmux_change_sel(ch, true);
 | |
| 	if (err)
 | |
| 		return err;
 | |
| 
 | |
| 	err = parent_ops->read(parent_mdio, addr, devad, reg);
 | |
| 	mmux_change_sel(ch, false);
 | |
| 
 | |
| 	return err;
 | |
| }
 | |
| 
 | |
| /* Write wrapper, sets up the mux before issuing a write on parent MDIO bus */
 | |
| static int mmux_write(struct udevice *ch, int addr, int devad,
 | |
| 		      int reg, u16 val)
 | |
| {
 | |
| 	struct udevice *mux = ch->parent;
 | |
| 	struct udevice *parent_mdio = mmux_get_parent_mdio(mux);
 | |
| 	struct mdio_ops *parent_ops = mmux_get_mdio_parent_ops(mux);
 | |
| 	int err;
 | |
| 
 | |
| 	err = mmux_change_sel(ch, true);
 | |
| 	if (err)
 | |
| 		return err;
 | |
| 
 | |
| 	err = parent_ops->write(parent_mdio, addr, devad, reg, val);
 | |
| 	mmux_change_sel(ch, false);
 | |
| 
 | |
| 	return err;
 | |
| }
 | |
| 
 | |
| /* Reset wrapper, sets up the mux before issuing a reset on parent MDIO bus */
 | |
| static int mmux_reset(struct udevice *ch)
 | |
| {
 | |
| 	struct udevice *mux = ch->parent;
 | |
| 	struct udevice *parent_mdio = mmux_get_parent_mdio(mux);
 | |
| 	struct mdio_ops *parent_ops = mmux_get_mdio_parent_ops(mux);
 | |
| 	int err;
 | |
| 
 | |
| 	/* reset is optional, if it's not implemented just exit */
 | |
| 	if (!parent_ops->reset)
 | |
| 		return 0;
 | |
| 
 | |
| 	err = mmux_change_sel(ch, true);
 | |
| 	if (err)
 | |
| 		return err;
 | |
| 
 | |
| 	err = parent_ops->reset(parent_mdio);
 | |
| 	mmux_change_sel(ch, false);
 | |
| 
 | |
| 	return err;
 | |
| }
 | |
| 
 | |
| /* Picks up the mux selection value for each child */
 | |
| static int dm_mdio_mux_child_post_bind(struct udevice *ch)
 | |
| {
 | |
| 	struct mdio_mux_ch_data *ch_data = dev_get_parent_plat(ch);
 | |
| 
 | |
| 	ch_data->sel = dev_read_u32_default(ch, "reg", MDIO_MUX_SELECT_NONE);
 | |
| 
 | |
| 	if (ch_data->sel == MDIO_MUX_SELECT_NONE)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* Explicitly bind child MDIOs after binding the mux */
 | |
| static int dm_mdio_mux_post_bind(struct udevice *mux)
 | |
| {
 | |
| 	ofnode ch_node;
 | |
| 	int err, first_err = 0;
 | |
| 
 | |
| 	if (!dev_has_ofnode(mux)) {
 | |
| 		debug("%s: no mux node found, no child MDIO busses set up\n",
 | |
| 		      __func__);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * we're going by Linux bindings so the child nodes do not have
 | |
| 	 * compatible strings.  We're going through them here and binding to
 | |
| 	 * them.
 | |
| 	 */
 | |
| 	dev_for_each_subnode(ch_node, mux) {
 | |
| 		struct udevice *ch_dev;
 | |
| 		const char *ch_name;
 | |
| 
 | |
| 		ch_name = ofnode_get_name(ch_node);
 | |
| 
 | |
| 		err = device_bind_driver_to_node(mux, MDIO_MUX_CHILD_DRV_NAME,
 | |
| 						 ch_name, ch_node, &ch_dev);
 | |
| 		/* try to bind all, but keep 1st error */
 | |
| 		if (err && !first_err)
 | |
| 			first_err = err;
 | |
| 	}
 | |
| 
 | |
| 	return first_err;
 | |
| }
 | |
| 
 | |
| /* Get a reference to the parent MDIO bus, it should be bound by now */
 | |
| static int dm_mdio_mux_post_probe(struct udevice *mux)
 | |
| {
 | |
| 	struct mdio_mux_perdev_priv *priv = dev_get_uclass_priv(mux);
 | |
| 	int err;
 | |
| 
 | |
| 	priv->selected = MDIO_MUX_SELECT_NONE;
 | |
| 
 | |
| 	/* pick up mdio parent from device tree */
 | |
| 	err = uclass_get_device_by_phandle(UCLASS_MDIO, mux, "mdio-parent-bus",
 | |
| 					   &priv->mdio_parent);
 | |
| 	if (err) {
 | |
| 		debug("%s: didn't find mdio-parent-bus\n", __func__);
 | |
| 		return err;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| const struct mdio_ops mmux_child_mdio_ops = {
 | |
| 	.read = mmux_read,
 | |
| 	.write = mmux_write,
 | |
| 	.reset = mmux_reset,
 | |
| };
 | |
| 
 | |
| /* MDIO class driver used for MUX child MDIO buses */
 | |
| U_BOOT_DRIVER(mdio_mux_child) = {
 | |
| 	.name		= MDIO_MUX_CHILD_DRV_NAME,
 | |
| 	.id		= UCLASS_MDIO,
 | |
| 	.ops		= &mmux_child_mdio_ops,
 | |
| };
 | |
| 
 | |
| UCLASS_DRIVER(mdio_mux) = {
 | |
| 	.id = UCLASS_MDIO_MUX,
 | |
| 	.name = "mdio-mux",
 | |
| 	.child_post_bind = dm_mdio_mux_child_post_bind,
 | |
| 	.post_bind  = dm_mdio_mux_post_bind,
 | |
| 	.post_probe = dm_mdio_mux_post_probe,
 | |
| 	.per_device_auto	= sizeof(struct mdio_mux_perdev_priv),
 | |
| 	.per_child_plat_auto	= sizeof(struct mdio_mux_ch_data),
 | |
| };
 |