315 lines · 7.3 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0+/* Framework for MDIO devices, other than PHYs. * * Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch> */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/delay.h>#include <linux/errno.h>#include <linux/gpio/consumer.h>#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>#include <linux/reset.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/unistd.h>#include <linux/property.h>#include "phylib-internal.h" /** * mdio_device_register_reset - Read and initialize the reset properties of *				an mdio device * @mdiodev: mdio_device structure * * Return: Zero if successful, negative error code on failure */static int mdio_device_register_reset(struct mdio_device *mdiodev){	struct reset_control *reset; 	/* Deassert the optional reset signal */	mdiodev->reset_gpio = gpiod_get_optional(&mdiodev->dev,						 "reset", GPIOD_OUT_LOW);	if (IS_ERR(mdiodev->reset_gpio))		return PTR_ERR(mdiodev->reset_gpio); 	if (mdiodev->reset_gpio)		gpiod_set_consumer_name(mdiodev->reset_gpio, "PHY reset"); 	reset = reset_control_get_optional_exclusive(&mdiodev->dev, "phy");	if (IS_ERR(reset)) {		gpiod_put(mdiodev->reset_gpio);		mdiodev->reset_gpio = NULL;		return PTR_ERR(reset);	} 	mdiodev->reset_ctrl = reset; 	/* Read optional firmware properties */	device_property_read_u32(&mdiodev->dev, "reset-assert-us",				 &mdiodev->reset_assert_delay);	device_property_read_u32(&mdiodev->dev, "reset-deassert-us",				 &mdiodev->reset_deassert_delay); 	return 0;} /** * mdio_device_unregister_reset - uninitialize the reset properties of *				  an mdio device * @mdiodev: mdio_device structure */static void mdio_device_unregister_reset(struct mdio_device *mdiodev){	gpiod_put(mdiodev->reset_gpio);	mdiodev->reset_gpio = NULL;	reset_control_put(mdiodev->reset_ctrl);	mdiodev->reset_ctrl = NULL;	mdiodev->reset_assert_delay = 0;	mdiodev->reset_deassert_delay = 0;} void mdio_device_reset(struct mdio_device *mdiodev, int value){	unsigned int d; 	if (!mdiodev->reset_gpio && !mdiodev->reset_ctrl)		return; 	if (mdiodev->reset_state == value)		return; 	if (mdiodev->reset_gpio)		gpiod_set_value_cansleep(mdiodev->reset_gpio, value); 	if (mdiodev->reset_ctrl) {		if (value)			reset_control_assert(mdiodev->reset_ctrl);		else			reset_control_deassert(mdiodev->reset_ctrl);	} 	d = value ? mdiodev->reset_assert_delay : mdiodev->reset_deassert_delay;	if (d)		fsleep(d); 	mdiodev->reset_state = value;}EXPORT_SYMBOL(mdio_device_reset); 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){	fwnode_handle_put(dev->fwnode);	kfree(to_mdio_device(dev));} 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_obj(*mdiodev);	if (!mdiodev)		return ERR_PTR(-ENOMEM); 	mdiodev->dev.release = mdio_device_release;	mdiodev->dev.parent = &bus->dev;	mdiodev->dev.bus = &mdio_bus_type;	mdiodev->device_free = mdio_device_free;	mdiodev->device_remove = mdio_device_remove;	mdiodev->bus = bus;	mdiodev->addr = addr;	mdiodev->reset_state = -1; 	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 * * Return: Zero if successful, negative error code on failure */int mdio_device_register(struct mdio_device *mdiodev){	int err; 	dev_dbg(&mdiodev->dev, "%s\n", __func__); 	err = mdiobus_register_device(mdiodev);	if (err)		return err; 	err = device_add(&mdiodev->dev);	if (err) {		pr_err("MDIO %d failed to add\n", 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); int mdiobus_register_device(struct mdio_device *mdiodev){	int err; 	if (mdiodev->bus->mdio_map[mdiodev->addr])		return -EBUSY; 	if (mdiodev->flags & MDIO_DEVICE_FLAG_PHY) {		err = mdio_device_register_reset(mdiodev);		if (err)			return err; 		/* Assert the reset signal */		mdio_device_reset(mdiodev, 1);	} 	mdiodev->bus->mdio_map[mdiodev->addr] = mdiodev; 	return 0;} int mdiobus_unregister_device(struct mdio_device *mdiodev){	if (mdiodev->bus->mdio_map[mdiodev->addr] != mdiodev)		return -EINVAL; 	mdio_device_unregister_reset(mdiodev); 	mdiodev->bus->mdio_map[mdiodev->addr] = NULL; 	return 0;} /** * 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. * * Return: Zero if successful, negative error code on failure */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; 	/* Deassert the reset signal */	mdio_device_reset(mdiodev, 0); 	if (mdiodrv->probe) {		err = mdiodrv->probe(mdiodev);		if (err) {			/* Assert the reset signal */			mdio_device_reset(mdiodev, 1);		}	} 	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); 	if (mdiodrv->remove)		mdiodrv->remove(mdiodev); 	/* Assert the reset signal */	mdio_device_reset(mdiodev, 1); 	return 0;} static void mdio_shutdown(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); 	if (mdiodrv->shutdown)		mdiodrv->shutdown(mdiodev);} /** * mdio_driver_register - register an mdio_driver with the MDIO layer * @drv: new mdio_driver to register * * Return: Zero if successful, negative error code on failure */int mdio_driver_register(struct mdio_driver *drv){	struct mdio_driver_common *mdiodrv = &drv->mdiodrv;	int retval; 	pr_debug("%s: %s\n", __func__, mdiodrv->driver.name); 	mdiodrv->driver.bus = &mdio_bus_type;	mdiodrv->driver.probe = mdio_probe;	mdiodrv->driver.remove = mdio_remove;	mdiodrv->driver.shutdown = mdio_shutdown; 	retval = driver_register(&mdiodrv->driver);	if (retval) {		pr_err("%s: Error %d in registering driver\n",		       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);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t