1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2019
4  * Alex Marginean, NXP
5  */
6 
7 #include <dm.h>
8 #include <errno.h>
9 #include <log.h>
10 #include <miiphy.h>
11 #include <i2c.h>
12 
13 /*
14  * This driver is used for MDIO muxes driven by writing to a register of an I2C
15  * chip.  The board it was developed for uses a mux controlled by on-board FPGA
16  * which in turn is accessed as a chip over I2C.
17  */
18 
19 struct mdio_mux_i2creg_priv {
20 	struct udevice *chip;
21 	int reg;
22 	int mask;
23 };
24 
mdio_mux_i2creg_select(struct udevice * mux,int cur,int sel)25 static int mdio_mux_i2creg_select(struct udevice *mux, int cur, int sel)
26 {
27 	struct mdio_mux_i2creg_priv *priv = dev_get_priv(mux);
28 	u8 val, val_old;
29 
30 	/* if last selection didn't change we're good to go */
31 	if (cur == sel)
32 		return 0;
33 
34 	val_old = dm_i2c_reg_read(priv->chip, priv->reg);
35 	val = (val_old & ~priv->mask) | (sel & priv->mask);
36 	debug("%s: chip %s, reg %x, val %x => %x\n", __func__, priv->chip->name,
37 	      priv->reg, val_old, val);
38 	dm_i2c_reg_write(priv->chip, priv->reg, val);
39 
40 	return 0;
41 }
42 
43 static const struct mdio_mux_ops mdio_mux_i2creg_ops = {
44 	.select = mdio_mux_i2creg_select,
45 };
46 
mdio_mux_i2creg_probe(struct udevice * dev)47 static int mdio_mux_i2creg_probe(struct udevice *dev)
48 {
49 	struct mdio_mux_i2creg_priv *priv = dev_get_priv(dev);
50 	ofnode chip_node, bus_node;
51 	struct udevice *i2c_bus;
52 	u32 reg_mask[2];
53 	u32 chip_addr;
54 	int err;
55 
56 	/* read the register addr/mask pair */
57 	err = dev_read_u32_array(dev, "mux-reg-masks", reg_mask, 2);
58 	if (err) {
59 		debug("%s: error reading mux-reg-masks property\n", __func__);
60 		return err;
61 	}
62 
63 	/* parent should be an I2C chip, grandparent should be an I2C bus */
64 	chip_node = ofnode_get_parent(dev_ofnode(dev));
65 	bus_node = ofnode_get_parent(chip_node);
66 
67 	err = uclass_get_device_by_ofnode(UCLASS_I2C, bus_node, &i2c_bus);
68 	if (err) {
69 		debug("%s: can't find I2C bus for node %s\n", __func__,
70 		      ofnode_get_name(bus_node));
71 		return err;
72 	}
73 
74 	err = ofnode_read_u32(chip_node, "reg", &chip_addr);
75 	if (err) {
76 		debug("%s: can't read chip address in %s\n", __func__,
77 		      ofnode_get_name(chip_node));
78 		return err;
79 	}
80 
81 	err = i2c_get_chip(i2c_bus, (uint)chip_addr, 1, &priv->chip);
82 	if (err) {
83 		debug("%s: can't find i2c chip device for addr %x\n", __func__,
84 		      chip_addr);
85 		return err;
86 	}
87 
88 	priv->reg = (int)reg_mask[0];
89 	priv->mask = (int)reg_mask[1];
90 
91 	debug("%s: chip %s, reg %x, mask %x\n", __func__, priv->chip->name,
92 	      priv->reg, priv->mask);
93 
94 	return 0;
95 }
96 
97 static const struct udevice_id mdio_mux_i2creg_ids[] = {
98 	{ .compatible = "mdio-mux-i2creg" },
99 	{ }
100 };
101 
102 U_BOOT_DRIVER(mdio_mux_i2creg) = {
103 	.name		= "mdio_mux_i2creg",
104 	.id		= UCLASS_MDIO_MUX,
105 	.of_match	= mdio_mux_i2creg_ids,
106 	.probe		= mdio_mux_i2creg_probe,
107 	.ops		= &mdio_mux_i2creg_ops,
108 	.priv_auto	= sizeof(struct mdio_mux_i2creg_priv),
109 };
110