Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

In Linux, “PHY Framework” usually means the Generic PHY Framework, documented in the kernel as the PHY subsystem. It is an in-kernel interface for separately managed physical-layer hardware used by controllers such as USB, Ethernet, SATA, PCIe, and wireless devices. A PHY provider driver owns clocks, resets, regulators, calibration, and hardware sequencing; a consumer controller obtains a struct phy and uses common lifecycle APIs.

The framework is most useful when the physical-layer block is a distinct device or reusable hardware resource. If the PHY logic is inseparably integrated into a controller, a separate generic PHY driver may add no value. “PHY” is also overloaded in Linux: Ethernet transceivers and some SerDes or wireless components can use subsystem-specific abstractions instead.

What a PHY does

PHY means physical layer. It performs the electrical and low-level protocol work needed to connect a digital controller to a transmission medium, including serialization and deserialization, encoding and decoding, rate generation, lane configuration, calibration, and link-related analog operation. USB, Ethernet, SATA, PCIe, and wireless hardware are common examples of systems that may depend on a PHY.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

An external PHY is a separately managed chip or hardware block. An integrated PHY is built into the controller. The generic framework is primarily aimed at the former: it creates a clean boundary between the controller driver and physical-layer implementation.

#1 Best Overall
Sale
Linux Device Drivers, 3rd Edition
  • Used Book in Good Condition

The provider/consumer architecture

Peripheral controller driver
        |
        | generic PHY consumer API
        v
     struct phy
        |
        v
   PHY provider driver
        |
        v
      PHY hardware

The provider creates one or more PHY instances and implements hardware-specific operations. The consumer discovers a PHY, initializes it, powers it, selects a mode when required, and later unwinds those actions. The framework standardizes discovery and lifecycle; it does not make unrelated PHYs interchangeable or remove the need for vendor-specific register programming.

Historically, PHY drivers were spread among controller or subsystem code. The generic interface improves reuse and maintainability by allowing a PHY implementation to serve different consumers while keeping analog details, power sequencing, and quirks in one provider driver. See the Linux PHY subsystem documentation.

When to use it—and when not to

  • Use the generic framework when a distinct PHY block has its own clocks, supplies, reset, calibration, mode, or power lifecycle.
  • It is especially useful when multiple controllers can consume the block or when the controller and PHY should be maintained independently.
  • A separate framework may be unnecessary when the physical layer is inseparable from the controller and no provider/consumer boundary exists.
  • Do not assume every Ethernet transceiver belongs here: Ethernet PHY management commonly uses an Ethernet-specific subsystem. Check the hardware’s existing kernel abstraction and binding.

Writing a provider driver

Define operations and create the PHY

A provider supplies a struct phy_ops. Common callbacks include init, exit, power_on, power_off, set_mode, and, where supported, set_mode_ext. A callback is optional in the implementation; consumers should nevertheless use the standard initialization and power APIs so the same controller works with PHYs that implement them.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
static const struct phy_ops my_phy_ops = {
        .init      = my_phy_init,
        .exit      = my_phy_exit,
        .power_on  = my_phy_power_on,
        .power_off = my_phy_power_off,
        .set_mode  = my_phy_set_mode,
        .owner     = THIS_MODULE,
};

phy = devm_phy_create(dev, child_np, &my_phy_ops);
if (IS_ERR(phy))
        return PTR_ERR(phy);

phy_set_drvdata(phy, priv);

The documented constructors are phy_create() and devm_phy_create(). Device-managed creation is normally preferable when the PHY lifetime follows the provider device. Store private state with phy_set_drvdata() and retrieve it in callbacks with phy_get_drvdata().

Register the provider

For Device Tree consumers, register a provider after creating its PHY instances:

devm_of_phy_provider_register(dev, my_of_xlate);

The non-managed form is of_phy_provider_register(). Current kernels also provide of_phy_provider_register_full() and its managed variant for bindings whose PHY child nodes are nested below additional levels.

A single- PHY provider can often use of_phy_simple_xlate. A provider with several instances generally needs an of_xlate function that validates the firmware specifier and returns the correct PHY. Reject invalid indices rather than silently returning another lane.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Multiple instances

Multiple PHYs make firmware translation part of the driver contract. Validate the binding’s #phy-cells, phandle arguments, child-node layout, and index range. The provider’s compatible string and binding schema—not the generic framework—define the exact representation.

Writing a consumer driver

Acquire the reference

Consumers can use name-based, Device Tree, or index-based helpers:

struct phy *phy_get(struct device *dev, const char *string);
struct phy *devm_phy_get(struct device *dev, const char *string);
struct phy *devm_phy_optional_get(struct device *dev, const char *string);
struct phy *devm_of_phy_get_by_index(struct device *dev,
                                     struct device_node *np, int index);

Other documented helpers include devm_of_phy_get() and devm_of_phy_optional_get(). The devm_ variants release references automatically at device teardown. Name-based calls use a connection identifier; index-based calls are convenient for controllers with several PHYs. In Device Tree, names normally correspond to phy-names.

Rank #3
Mastering Linux Device Driver Development: Write custom device drivers to support computer peripherals in Linux operating systems
  • Mastering Linux Device Driver Development: Write custom device drivers to support computer peripherals in Linux operating systems
  • ABIS BOOK
  • Packt Publishing

Required versus optional PHYs

Use an optional-get function only when the hardware genuinely supports operation without that PHY:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
phy = devm_phy_optional_get(dev, "usb");
if (IS_ERR(phy))
        return PTR_ERR(phy);
/* phy == NULL is valid for an absent optional PHY. */

Do not convert NULL into -ENODEV unconditionally. The optional APIs return NULL for an absent PHY, while real lookup failures remain encoded errors checked with IS_ERR(). The documented consumer operations—including initialization, exit, power, and release—are no-ops for a NULL PHY.

Follow the lifecycle order

The documented sequence is:

[devm_][of_]phy_get()
phy_init()
phy_power_on()
[phy_set_mode[_ext]()]
...
phy_power_off()
phy_exit()
[[of_]phy_put()]

A representative probe path is:

phy = devm_phy_get(dev, "usb");
if (IS_ERR(phy))
        return PTR_ERR(phy);

ret = phy_init(phy);
if (ret)
        return ret;

ret = phy_power_on(phy);
if (ret) {
        phy_exit(phy);
        return ret;
}

ret = phy_set_mode(phy, PHY_MODE_USB_HOST);
if (ret) {
        phy_power_off(phy);
        phy_exit(phy);
        return ret;
}

/* Configure and start the controller. */

On shutdown, runtime suspend, or an equivalent stop path, stop controller traffic first, then call phy_power_off() and phy_exit(). If references were acquired manually, finish with phy_put(); managed references are released automatically. The exact mode constant depends on the consumer and kernel headers, and not every PHY accepts every enum phy_mode.

Device Tree integration

A consumer usually references a provider with phys and names connections with phy-names:

usb@... {
        phys = <&usb2_phy>;
        phy-names = "usb2-phy";
};

controller@... {
        phys = <&phy_provider 0>, <&phy_provider 1>;
        phy-names = "usb2", "usb3";
};

These snippets are illustrative, not universal bindings. The individual device schema defines the compatible string, node hierarchy, #phy-cells, phandle arguments, and whether child nodes are required. Validate the binding with the kernel’s schema tooling and match the provider’s translation logic to it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Non-Device-Tree lookup mappings

Platforms without a Device Tree phandle model can associate a consumer with a PHY explicitly:

int phy_create_lookup(struct phy *phy,
                      const char *con_id,
                      const char *dev_id);
void phy_remove_lookup(struct phy *phy,
                       const char *con_id,
                       const char *dev_id);

This is relevant to ACPI or platform descriptions, legacy board files, and statically described devices. When a standard firmware binding exists, use it rather than adding new ad-hoc lookup tables.

Runtime power management and hardware sequencing

Creating a PHY enables runtime PM for its device; destroying it disables runtime PM. The PHY device is a child of the provider device, allowing parent-child runtime-PM relationships to propagate. phy_power_on() and phy_power_off() participate in that lifecycle, but they do not replace provider-specific sequencing.

A real provider may need ordered regulators, reference and functional clocks, reset deassertion, power-domain transitions, calibration, PLL-lock polling, lane selection, firmware tables, and silicon-revision workarounds. Coordinate runtime and system suspend with the controller: powering down a PHY while its controller still accesses it can cause link loss, USB errors, PCIe instability, or resume failures. Resume may require restoring registers and repeating initialization before the controller touches the link.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

API quick reference

Purpose Representative APIs
Create a PHY phy_create(), devm_phy_create()
Attach private data phy_set_drvdata(), phy_get_drvdata()
Register provider of_phy_provider_register(), devm_of_phy_provider_register()
Acquire consumer reference phy_get(), devm_phy_get(), optional and Device Tree helpers
Lifecycle phy_init(), phy_power_on(), phy_power_off(), phy_exit()
Mode phy_set_mode(), phy_set_mode_ext()
Non-DT mapping phy_create_lookup(), phy_remove_lookup()
Destroy or release phy_destroy(), devm_phy_destroy(), phy_put()

Troubleshooting by symptom

Probe returns -EPROBE_DEFER

  1. Confirm the provider node is enabled and its compatible value matches a loaded driver.
  2. Check the consumer’s phys, phy-names, and phandle arguments.
  3. Inspect provider probe logs and verify that PHY creation and provider registration complete.
  4. Check regulator, clock, reset, power-domain, and firmware dependencies.

Missing PHY or -ENODEV

Decide whether the PHY is required. For an optional design, use an optional-get API and accept NULL. Otherwise inspect the connection name, index ordering, provider node, and binding.

Power-on succeeds but the link fails

Check mode selection, reference-clock rate, reset order, supply stability, lane/protocol agreement, calibration, PLL-lock waits, and unexpected runtime suspension. A successful API return does not prove that the analog link is correctly configured.

Suspend or resume fails

Ensure the controller stops using the link before phy_power_off(), and that resume restores provider state before controller access. Verify parent-child runtime-PM ordering and any registers lost during power collapse.

Provider removal or module unload fails

Stop active transfers, ensure consumers have released references, and never destroy a PHY that is still in use. Avoid mixing manual and device-managed cleanup in a way that causes double release or ordering conflicts.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Bottom line

Linux’s Generic PHY Framework standardizes how a controller discovers and operates a separately managed physical-layer block. A robust implementation gets the provider/consumer boundary, firmware translation, required-versus-optional handling, lifecycle order, and runtime-PM dependencies right. The framework supplies the common plumbing; the provider remains responsible for the electrical and hardware-specific details.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.