Blame view

drivers/net/phy/mdio_device.c 4.93 KB
a2443fd1a   Andrew Lunn   net: phy: Convert...
1
  // SPDX-License-Identifier: GPL-2.0+
a9049e0c5   Andrew Lunn   mdio: Add support...
2
3
4
  /* Framework for MDIO devices, other than PHYs.
   *
   * Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch>
a9049e0c5   Andrew Lunn   mdio: Add support...
5
6
7
   */
  
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1d0018a43   Bartosz Golaszewski   net: phy: arrange...
8
  #include <linux/delay.h>
a9049e0c5   Andrew Lunn   mdio: Add support...
9
  #include <linux/errno.h>
bafbdd527   Sergei Shtylyov   phylib: Add devic...
10
11
  #include <linux/gpio.h>
  #include <linux/gpio/consumer.h>
a9049e0c5   Andrew Lunn   mdio: Add support...
12
13
14
15
16
17
18
  #include <linux/init.h>
  #include <linux/interrupt.h>
  #include <linux/kernel.h>
  #include <linux/mdio.h>
  #include <linux/mii.h>
  #include <linux/module.h>
  #include <linux/phy.h>
71dd6c0df   David Bauer   net: phy: add sup...
19
  #include <linux/reset.h>
a9049e0c5   Andrew Lunn   mdio: Add support...
20
21
22
23
24
25
26
27
28
29
30
31
32
33
  #include <linux/slab.h>
  #include <linux/string.h>
  #include <linux/unistd.h>
  
  void mdio_device_free(struct mdio_device *mdiodev)
  {
  	put_device(&mdiodev->dev);
  }
  EXPORT_SYMBOL(mdio_device_free);
  
  static void mdio_device_release(struct device *dev)
  {
  	kfree(to_mdio_device(dev));
  }
648ea0134   Florian Fainelli   net: phy: Allow p...
34
35
36
37
38
39
40
41
42
43
  int mdio_device_bus_match(struct device *dev, struct device_driver *drv)
  {
  	struct mdio_device *mdiodev = to_mdio_device(dev);
  	struct mdio_driver *mdiodrv = to_mdio_driver(drv);
  
  	if (mdiodrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY)
  		return 0;
  
  	return strcmp(mdiodev->modalias, drv->name) == 0;
  }
a9049e0c5   Andrew Lunn   mdio: Add support...
44
45
46
47
48
49
50
51
52
53
54
55
  struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr)
  {
  	struct mdio_device *mdiodev;
  
  	/* We allocate the device, and initialize the default values */
  	mdiodev = kzalloc(sizeof(*mdiodev), GFP_KERNEL);
  	if (!mdiodev)
  		return ERR_PTR(-ENOMEM);
  
  	mdiodev->dev.release = mdio_device_release;
  	mdiodev->dev.parent = &bus->dev;
  	mdiodev->dev.bus = &mdio_bus_type;
711fdba37   Andrew Lunn   mdio: Abstract de...
56
57
  	mdiodev->device_free = mdio_device_free;
  	mdiodev->device_remove = mdio_device_remove;
a9049e0c5   Andrew Lunn   mdio: Add support...
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
  	mdiodev->bus = bus;
  	mdiodev->addr = addr;
  
  	dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr);
  
  	device_initialize(&mdiodev->dev);
  
  	return mdiodev;
  }
  EXPORT_SYMBOL(mdio_device_create);
  
  /**
   * mdio_device_register - Register the mdio device on the MDIO bus
   * @mdiodev: mdio_device structure to be added to the MDIO bus
   */
  int mdio_device_register(struct mdio_device *mdiodev)
  {
  	int err;
29b84f20e   Florian Fainelli   mdio: Demote prin...
76
77
  	dev_dbg(&mdiodev->dev, "mdio_device_register
  ");
a9049e0c5   Andrew Lunn   mdio: Add support...
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
  
  	err = mdiobus_register_device(mdiodev);
  	if (err)
  		return err;
  
  	err = device_add(&mdiodev->dev);
  	if (err) {
  		pr_err("MDIO %d failed to add
  ", mdiodev->addr);
  		goto out;
  	}
  
  	return 0;
  
   out:
  	mdiobus_unregister_device(mdiodev);
  	return err;
  }
  EXPORT_SYMBOL(mdio_device_register);
  
  /**
   * mdio_device_remove - Remove a previously registered mdio device from the
   *			MDIO bus
   * @mdiodev: mdio_device structure to remove
   *
   * This doesn't free the mdio_device itself, it merely reverses the effects
   * of mdio_device_register(). Use mdio_device_free() to free the device
   * after calling this function.
   */
  void mdio_device_remove(struct mdio_device *mdiodev)
  {
  	device_del(&mdiodev->dev);
  	mdiobus_unregister_device(mdiodev);
  }
  EXPORT_SYMBOL(mdio_device_remove);
bafbdd527   Sergei Shtylyov   phylib: Add devic...
113
114
  void mdio_device_reset(struct mdio_device *mdiodev, int value)
  {
3a30ae6ef   Richard Leitner   phylib: Add devic...
115
  	unsigned int d;
6110ed2db   David Bauer   net: mdio: rename...
116
  	if (!mdiodev->reset_gpio && !mdiodev->reset_ctrl)
3a30ae6ef   Richard Leitner   phylib: Add devic...
117
  		return;
6110ed2db   David Bauer   net: mdio: rename...
118
  	if (mdiodev->reset_gpio)
ea977d19d   Andrea Merello   net: phy: allow f...
119
  		gpiod_set_value_cansleep(mdiodev->reset_gpio, value);
71dd6c0df   David Bauer   net: phy: add sup...
120
121
122
123
124
125
126
  
  	if (mdiodev->reset_ctrl) {
  		if (value)
  			reset_control_assert(mdiodev->reset_ctrl);
  		else
  			reset_control_deassert(mdiodev->reset_ctrl);
  	}
3a30ae6ef   Richard Leitner   phylib: Add devic...
127

04f629f73   Richard Leitner   phylib: rename re...
128
  	d = value ? mdiodev->reset_assert_delay : mdiodev->reset_deassert_delay;
3a30ae6ef   Richard Leitner   phylib: Add devic...
129
  	if (d)
e4d5efdd0   Bruno Thomsen   net: mdio device:...
130
  		fsleep(d);
bafbdd527   Sergei Shtylyov   phylib: Add devic...
131
132
  }
  EXPORT_SYMBOL(mdio_device_reset);
a9049e0c5   Andrew Lunn   mdio: Add support...
133
134
135
136
137
138
139
140
141
142
143
144
145
  /**
   * mdio_probe - probe an MDIO device
   * @dev: device to probe
   *
   * Description: Take care of setting up the mdio_device structure
   * and calling the driver to probe the device.
   */
  static int mdio_probe(struct device *dev)
  {
  	struct mdio_device *mdiodev = to_mdio_device(dev);
  	struct device_driver *drv = mdiodev->dev.driver;
  	struct mdio_driver *mdiodrv = to_mdio_driver(drv);
  	int err = 0;
96e263592   Bartosz Golaszewski   net: phy: mdio: r...
146
147
  	/* Deassert the reset signal */
  	mdio_device_reset(mdiodev, 0);
bafbdd527   Sergei Shtylyov   phylib: Add devic...
148

96e263592   Bartosz Golaszewski   net: phy: mdio: r...
149
  	if (mdiodrv->probe) {
a9049e0c5   Andrew Lunn   mdio: Add support...
150
  		err = mdiodrv->probe(mdiodev);
bafbdd527   Sergei Shtylyov   phylib: Add devic...
151
152
153
154
155
  		if (err) {
  			/* Assert the reset signal */
  			mdio_device_reset(mdiodev, 1);
  		}
  	}
a9049e0c5   Andrew Lunn   mdio: Add support...
156
157
158
159
160
161
162
163
164
  
  	return err;
  }
  
  static int mdio_remove(struct device *dev)
  {
  	struct mdio_device *mdiodev = to_mdio_device(dev);
  	struct device_driver *drv = mdiodev->dev.driver;
  	struct mdio_driver *mdiodrv = to_mdio_driver(drv);
96e263592   Bartosz Golaszewski   net: phy: mdio: r...
165
  	if (mdiodrv->remove)
a9049e0c5   Andrew Lunn   mdio: Add support...
166
  		mdiodrv->remove(mdiodev);
96e263592   Bartosz Golaszewski   net: phy: mdio: r...
167
168
  	/* Assert the reset signal */
  	mdio_device_reset(mdiodev, 1);
bafbdd527   Sergei Shtylyov   phylib: Add devic...
169

a9049e0c5   Andrew Lunn   mdio: Add support...
170
171
172
173
174
  	return 0;
  }
  
  /**
   * mdio_driver_register - register an mdio_driver with the MDIO layer
19c5a5fec   Andrew Lunn   net: phy: Fixup p...
175
   * @drv: new mdio_driver to register
a9049e0c5   Andrew Lunn   mdio: Add support...
176
177
178
179
180
   */
  int mdio_driver_register(struct mdio_driver *drv)
  {
  	struct mdio_driver_common *mdiodrv = &drv->mdiodrv;
  	int retval;
eb2ca35f1   Florian Fainelli   mdio: Demote prin...
181
182
  	pr_debug("mdio_driver_register: %s
  ", mdiodrv->driver.name);
a9049e0c5   Andrew Lunn   mdio: Add support...
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
  
  	mdiodrv->driver.bus = &mdio_bus_type;
  	mdiodrv->driver.probe = mdio_probe;
  	mdiodrv->driver.remove = mdio_remove;
  
  	retval = driver_register(&mdiodrv->driver);
  	if (retval) {
  		pr_err("%s: Error %d in registering driver
  ",
  		       mdiodrv->driver.name, retval);
  
  		return retval;
  	}
  
  	return 0;
  }
  EXPORT_SYMBOL(mdio_driver_register);
  
  void mdio_driver_unregister(struct mdio_driver *drv)
  {
  	struct mdio_driver_common *mdiodrv = &drv->mdiodrv;
  
  	driver_unregister(&mdiodrv->driver);
  }
  EXPORT_SYMBOL(mdio_driver_unregister);