Add include/pcs.h, a interface for Ethernet PCS (Physical Coding
Sublayer) devices. A PCS driver provides a struct pcs_ops, and a MAC
driver calls it through the pcs_set_config(), pcs_get_state() and
pcs_disable() wrappers:

- set_config selects the interface mode, the auto-negotiation type
  (none, Clause 37 or Clause 73) and the expected speed
- get_state reports link, auto-negotiation status and speed
- disable stops auto-negotiation and powers the PCS down

A PCS is a UCLASS_MISC device.
The MAC driver is expected to find it by phandle.

Signed-off-by: Akash Nair <[email protected]>
---
 include/pcs.h | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 140 insertions(+)
 create mode 100644 include/pcs.h

diff --git a/include/pcs.h b/include/pcs.h
new file mode 100644
index 00000000000..710b8136d04
--- /dev/null
+++ b/include/pcs.h
@@ -0,0 +1,140 @@
+/*
+ * Generic interface of an Ethernet PCS (Physical Coding Sublayer) device
+ */
+
+#ifndef __PCS_H
+#define __PCS_H
+
+#include <dm/device.h>
+#include <phy_interface.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+
+/**
+ * enum pcs_an_mode - auto-negotiation used by a PCS
+ * @PCS_AN_FORCED: no auto-negotiation, the mode and speed are fixed
+ * @PCS_AN_C73: Clause 73 auto-negotiation (backplane, for example 10GBASE-KR)
+ * @PCS_AN_C37: Clause 37 auto-negotiation (1000BASE-X)
+ */
+enum pcs_an_mode {
+       PCS_AN_FORCED = 0,
+       PCS_AN_C73,
+       PCS_AN_C37,
+};
+
+/**
+ * struct pcs_config - requested configuration of a PCS
+ * @iface: interface mode to run. This selects the PCS mode.
+ * @an_mode: auto-negotiation to use with @iface. It must be one the PCS
+ *     supports for that interface, otherwise the config is rejected.
+ * @speed_mbps: speed the caller expects for @iface, in Mbit/s.
+ *     0 means not specified, which is accepted only when @an_mode has
+ *     auto-negotiation, and a mismatch is rejected. A mode without
+ *     auto-negotiation needs the speed given.
+ */
+struct pcs_config {
+       phy_interface_t         iface;
+       enum pcs_an_mode        an_mode;
+       int                     speed_mbps;
+};
+
+/**
+ * struct pcs_state - state reported by a PCS
+ * @link: the link is up
+ * @an_complete: auto-negotiation has completed. Always false when the mode
+ *     has no auto-negotiation. It does not mean the link is up, so check
+ *     @link as well.
+ * @speed_mbps: speed of the configured mode, 0 if the PCS is not configured
+ *     or is powered down. It is set as soon as the PCS is configured, not
+ *     when the link comes up, so a non-zero value does not mean the link
+ *     is up.
+ *
+ * If the PCS is powered down or not configured all fields stay 0.
+ * If pcs_get_state() returns an error, @link and @an_complete
+ * are not valid and should not be used.
+ */
+struct pcs_state {
+       bool    link;
+       bool    an_complete;
+       int     speed_mbps;
+};
+
+/**
+ * struct pcs_ops - operations provided by a PCS driver
+ *
+ * @set_config: Configure the PCS for the requested interface mode and
+ *     auto-negotiation type, and start auto-negotiation if the mode uses it.
+ *     Returns 0 on success or a negative error. On error the PCS is left
+ *     unconfigured. It can be called again later, including after @disable.
+ * @get_state: Read the link status, auto-negotiation status and speed.
+ *     A negative return value means auto-negotiation failed.
+ * @disable: Stop auto-negotiation and power the PCS down. Calling it again
+ *     has no effect.
+ */
+struct pcs_ops {
+       int (*set_config)(struct udevice *dev, const struct pcs_config *cfg);
+       int (*get_state)(struct udevice *dev, struct pcs_state *state);
+       int (*disable)(struct udevice *dev);
+};
+
+#define pcs_get_ops(dev)       ((struct pcs_ops *)(dev)->driver->ops)
+
+/**
+ * pcs_set_config() - configure a PCS via its driver's set_config op
+ * @dev: PCS device
+ * @cfg: requested interface mode, auto-negotiation type and speed
+ *
+ * Return: 0 on success, -ENOSYS if the driver has no set_config op,
+ * or a negative error from the driver.
+ */
+static inline int pcs_set_config(struct udevice *dev,
+                                const struct pcs_config *cfg)
+{
+       const struct pcs_ops *ops = pcs_get_ops(dev);
+
+       if (!ops || !ops->set_config)
+               return -ENOSYS;
+
+       return ops->set_config(dev, cfg);
+}
+
+/**
+ * pcs_get_state() - read the link and auto-negotiation state of a PCS
+ * @dev: PCS device
+ * @state: filled in by the driver, zeroed first
+ *
+ * Return: 0 on success, -ENOSYS if the driver has no get_state op, or a
+ * negative error from the driver. @state is not valid on error.
+ */
+static inline int pcs_get_state(struct udevice *dev, struct pcs_state *state)
+{
+       const struct pcs_ops *ops = pcs_get_ops(dev);
+
+       state->link = false;
+       state->an_complete = false;
+       state->speed_mbps = 0;
+
+       if (!ops || !ops->get_state)
+               return -ENOSYS;
+
+       return ops->get_state(dev, state);
+}
+
+/**
+ * pcs_disable() - stop auto-negotiation and power a PCS down
+ * @dev: PCS device
+ *
+ * Return: 0 on success (also if already disabled), -ENOSYS if the driver has
+ * no disable op, or a negative error from the driver.
+ */
+static inline int pcs_disable(struct udevice *dev)
+{
+       const struct pcs_ops *ops = pcs_get_ops(dev);
+
+       if (!ops || !ops->disable)
+               return -ENOSYS;
+
+       return ops->disable(dev);
+}
+
+#endif /* __PCS_H */
-- 
2.43.0

Reply via email to