*
* Based loosely off of Linux's PHY Lib
*/
-#include <common.h>
#include <console.h>
#include <dm.h>
#include <log.h>
#include <phy.h>
#include <errno.h>
#include <asm/global_data.h>
+#include <asm-generic/gpio.h>
+#include <dm/device_compat.h>
#include <dm/of_extra.h>
#include <linux/bitops.h>
#include <linux/delay.h>
/*
* Timeout reached ?
*/
- if (i > (PHY_ANEG_TIMEOUT / 50)) {
+ if (i > (CONFIG_PHY_ANEG_TIMEOUT / 50)) {
printf(" TIMEOUT !\n");
phydev->link = 0;
return -ETIMEDOUT;
.shutdown = genphy_shutdown,
};
-#ifdef CONFIG_NEEDS_MANUAL_RELOC
-int phy_init(void)
-{
- const int ll_n_ents = ll_entry_count(struct phy_driver, phy_driver);
- struct phy_driver *drv, *ll_entry;
-
- /* Perform manual relocation on linker list based PHY drivers */
- ll_entry = ll_entry_start(struct phy_driver, phy_driver);
- for (drv = ll_entry; drv != ll_entry + ll_n_ents; drv++) {
- if (drv->probe)
- drv->probe += gd->reloc_off;
- if (drv->config)
- drv->config += gd->reloc_off;
- if (drv->startup)
- drv->startup += gd->reloc_off;
- if (drv->shutdown)
- drv->shutdown += gd->reloc_off;
- if (drv->readext)
- drv->readext += gd->reloc_off;
- if (drv->writeext)
- drv->writeext += gd->reloc_off;
- if (drv->read_mmd)
- drv->read_mmd += gd->reloc_off;
- if (drv->write_mmd)
- drv->write_mmd += gd->reloc_off;
- }
-
- return 0;
-}
-#endif
-
int phy_set_supported(struct phy_device *phydev, u32 max_speed)
{
/* The default values for phydev->supported are provided by the PHY
return NULL;
}
- if (addr >= 0 && addr < PHY_MAX_ADDR && phy_id != PHY_FIXED_ID)
+ if (addr >= 0 && addr < PHY_MAX_ADDR && phy_id != PHY_FIXED_ID &&
+ phy_id != PHY_NCSI_ID)
bus->phymap[addr] = dev;
return dev;
{
/* If we have one, return the existing device, with new interface */
while (phy_mask) {
- int addr = ffs(phy_mask) - 1;
+ unsigned int addr = ffs(phy_mask) - 1;
if (bus->phymap[addr])
return bus->phymap[addr];
- phy_mask &= ~(1 << addr);
+ phy_mask &= ~(1U << addr);
}
return NULL;
}
return phy_reset(phydev);
}
+#if CONFIG_IS_ENABLED(DM_GPIO) && CONFIG_IS_ENABLED(OF_REAL) && \
+ !IS_ENABLED(CONFIG_DM_ETH_PHY)
+int phy_gpio_reset(struct udevice *dev)
+{
+ struct ofnode_phandle_args phandle_args;
+ struct gpio_desc gpio;
+ u32 assert, deassert;
+ ofnode node;
+ int ret;
+
+ ret = dev_read_phandle_with_args(dev, "phy-handle", NULL, 0, 0,
+ &phandle_args);
+ /* No PHY handle is OK */
+ if (ret)
+ return 0;
+
+ node = phandle_args.node;
+ if (!ofnode_valid(node))
+ return -EINVAL;
+
+ ret = gpio_request_by_name_nodev(node, "reset-gpios", 0, &gpio,
+ GPIOD_IS_OUT | GPIOD_ACTIVE_LOW);
+ /* No PHY reset GPIO is OK */
+ if (ret)
+ return 0;
+
+ assert = ofnode_read_u32_default(node, "reset-assert-us", 20000);
+ deassert = ofnode_read_u32_default(node, "reset-deassert-us", 1000);
+ ret = dm_gpio_set_value(&gpio, 1);
+ if (ret) {
+ dev_err(dev, "Failed assert gpio, err: %d\n", ret);
+ return ret;
+ }
+
+ udelay(assert);
+
+ ret = dm_gpio_set_value(&gpio, 0);
+ if (ret) {
+ dev_err(dev, "Failed deassert gpio, err: %d\n", ret);
+ return ret;
+ }
+
+ udelay(deassert);
+
+ return 0;
+}
+#else
+int phy_gpio_reset(struct udevice *dev)
+{
+ return 0;
+}
+#endif
+
struct phy_device *phy_find_by_mask(struct mii_dev *bus, uint phy_mask)
{
/* Reset the bus */
return get_phy_device_by_mask(bus, phy_mask);
}
-void phy_connect_dev(struct phy_device *phydev, struct udevice *dev,
- phy_interface_t interface)
+static void phy_connect_dev(struct phy_device *phydev, struct udevice *dev,
+ phy_interface_t interface)
{
/* Soft Reset the PHY */
phy_reset(phydev);
ofnode_for_each_subnode(node, dev_ofnode(dev)) {
node = ofnode_by_compatible(node, "xlnx,gmii-to-rgmii-1.0");
if (ofnode_valid(node)) {
- phydev = phy_device_create(bus, 0,
+ int gmiirgmii_phyaddr;
+
+ gmiirgmii_phyaddr = ofnode_read_u32_default(node, "reg", 0);
+ phydev = phy_device_create(bus, gmiirgmii_phyaddr,
PHY_GMII2RGMII_ID, false);
if (phydev)
phydev->node = node;
return 0;
}
+/**
+ * phy_modify_mmd_changed - Function for modifying a register on MMD
+ * @phydev: the phy_device struct
+ * @devad: the MMD containing register to modify
+ * @regnum: register number to modify
+ * @mask: bit mask of bits to clear
+ * @set: new value of bits set in mask to write to @regnum
+ *
+ * NOTE: MUST NOT be called from interrupt context,
+ * because the bus read/write functions may wait for an interrupt
+ * to conclude the operation.
+ *
+ * Returns negative errno, 0 if there was no change, and 1 in case of change
+ */
+int phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum,
+ u16 mask, u16 set)
+{
+ int new, ret;
+
+ ret = phy_read_mmd(phydev, devad, regnum);
+ if (ret < 0)
+ return ret;
+
+ new = (ret & ~mask) | set;
+ if (new == ret)
+ return 0;
+
+ ret = phy_write_mmd(phydev, devad, regnum, new);
+
+ return ret < 0 ? ret : 1;
+}
+
+/**
+ * phy_modify_mmd - Convenience function for modifying a register on MMD
+ * @phydev: the phy_device struct
+ * @devad: the MMD containing register to modify
+ * @regnum: register number to modify
+ * @mask: bit mask of bits to clear
+ * @set: new value of bits set in mask to write to @regnum
+ *
+ * NOTE: MUST NOT be called from interrupt context,
+ * because the bus read/write functions may wait for an interrupt
+ * to conclude the operation.
+ */
+int phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum,
+ u16 mask, u16 set)
+{
+ int ret;
+
+ ret = phy_modify_mmd_changed(phydev, devad, regnum, mask, set);
+
+ return ret < 0 ? ret : 0;
+}
+
bool phy_interface_is_ncsi(void)
{
#ifdef CONFIG_PHY_NCSI