--- /dev/null
+Common MDIO bus properties.
+
+These are generic properties that can apply to any MDIO bus.
+
+Optional properties:
+- reset-gpios: List of one or more GPIOs that control the RESET lines
+ of the PHYs on that MDIO bus.
+- reset-delay-us: RESET pulse width in microseconds as per PHY datasheet.
+
+A list of child nodes, one per device on the bus is expected. These
+should follow the generic phy.txt, or a device specific binding document.
+
+Example :
+This example shows these optional properties, plus other properties
+required for the TI Davinci MDIO driver.
+
+ davinci_mdio: ethernet@0x5c030000 {
+ compatible = "ti,davinci_mdio";
+ reg = <0x5c030000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reset-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>;
+ reset-delay-us = <2>; /* PHY datasheet states 1us min */
+
+ ethphy0: ethernet-phy@1 {
+ reg = <1>;
+ };
+
+ ethphy1: ethernet-phy@3 {
+ reg = <3>;
+ };
+ };
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/of_device.h>
#include <linux/of_mdio.h>
+#include <linux/of_gpio.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
{
struct mdio_device *mdiodev;
int i, err;
+ struct gpio_desc *gpiod;
if (NULL == bus || NULL == bus->name ||
NULL == bus->read || NULL == bus->write)
if (bus->reset)
bus->reset(bus);
+ /* de-assert bus level PHY GPIO resets */
+ if (bus->num_reset_gpios > 0) {
+ bus->reset_gpiod = devm_kcalloc(&bus->dev,
+ bus->num_reset_gpios,
+ sizeof(struct gpio_desc *),
+ GFP_KERNEL);
+ if (!bus->reset_gpiod)
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < bus->num_reset_gpios; i++) {
+ gpiod = devm_gpiod_get_index(&bus->dev, "reset", i,
+ GPIOD_OUT_LOW);
+ if (IS_ERR(gpiod)) {
+ err = PTR_ERR(gpiod);
+ if (err != -ENOENT) {
+ dev_err(&bus->dev,
+ "mii_bus %s couldn't get reset GPIO\n",
+ bus->id);
+ return err;
+ }
+ } else {
+ bus->reset_gpiod[i] = gpiod;
+ gpiod_set_value_cansleep(gpiod, 1);
+ udelay(bus->reset_delay_us);
+ gpiod_set_value_cansleep(gpiod, 0);
+ }
+ }
+
for (i = 0; i < PHY_MAX_ADDR; i++) {
if ((bus->phy_mask & (1 << i)) == 0) {
struct phy_device *phydev;
mdiodev->device_remove(mdiodev);
mdiodev->device_free(mdiodev);
}
+
+ /* Put PHYs in RESET to save power */
+ for (i = 0; i < bus->num_reset_gpios; i++) {
+ if (bus->reset_gpiod[i])
+ gpiod_set_value_cansleep(bus->reset_gpiod[i], 1);
+ }
+
device_del(&bus->dev);
return err;
}
mdiodev->device_remove(mdiodev);
mdiodev->device_free(mdiodev);
}
+
+ /* Put PHYs in RESET to save power */
+ for (i = 0; i < bus->num_reset_gpios; i++) {
+ if (bus->reset_gpiod[i])
+ gpiod_set_value_cansleep(bus->reset_gpiod[i], 1);
+ }
+
device_del(&bus->dev);
}
EXPORT_SYMBOL(mdiobus_unregister);
#include <linux/of_net.h>
#include <linux/module.h>
+#define DEFAULT_GPIO_RESET_DELAY 10 /* in microseconds */
+
MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
MODULE_LICENSE("GPL");
mdio->dev.of_node = np;
+ /* Get bus level PHY reset GPIO details */
+ mdio->reset_delay_us = DEFAULT_GPIO_RESET_DELAY;
+ of_property_read_u32(np, "reset-delay-us", &mdio->reset_delay_us);
+ mdio->num_reset_gpios = of_gpio_named_count(np, "reset-gpios");
+
/* Register the MDIO bus */
rc = mdiobus_register(mdio);
if (rc)