@@ -339,6 +365,16 @@ static bool nxp_c45_match_ts(struct ptp_header *header,
header->domain_number == hwts->domain_number;
}
+static void nxp_c45_get_extts(struct nxp_c45_phy *priv,
+ struct timespec64 *extts)
+{
+ extts->tv_nsec = phy_read_mmd(priv->phydev, MDIO_MMD_VEND1, VEND1_EXT_TRG_TS_DATA_0);
+ extts->tv_nsec |= phy_read_mmd(priv->phydev, MDIO_MMD_VEND1, VEND1_EXT_TRG_TS_DATA_1) << 16;
+ extts->tv_sec = phy_read_mmd(priv->phydev, MDIO_MMD_VEND1, VEND1_EXT_TRG_TS_DATA_2);
+ extts->tv_sec |= phy_read_mmd(priv->phydev, MDIO_MMD_VEND1, VEND1_EXT_TRG_TS_DATA_3) << 16;
+ phy_write_mmd(priv->phydev, MDIO_MMD_VEND1, VEND1_EXT_TRG_TS_CTRL, RING_DONE);
+}
+
static bool nxp_c45_get_hwtxts(struct nxp_c45_phy *priv,
struct nxp_c45_hwts *hwts)
{@@ -439,9 +477,167 @@ static long nxp_c45_do_aux_work(struct ptp_clock_info *ptp)
netif_rx_ni(skb);
}
+ if (priv->extts) {
+ nxp_c45_get_extts(priv, &ts);
+ if (timespec64_compare(&ts, &priv->extts_ts) > 0) {
+ priv->extts_ts = ts;
+ event.index = priv->extts_index;
+ event.type = PTP_CLOCK_EXTTS;
+ event.timestamp = ns_to_ktime(timespec64_to_ns(&ts));
+ ptp_clock_event(priv->ptp_clock, &event);
+ }
+ reschedule = true;
+ }
+
return reschedule ? 1 : -1;
}
+static void nxp_c45_gpio_config(struct nxp_c45_phy *priv,
+ int pin, u16 pin_cfg)
+{
+ struct phy_device *phydev = priv->phydev;
+
+ phy_write_mmd(phydev, MDIO_MMD_VEND1,
+ VEND1_GPIO_FUNC_CONFIG_BASE + pin, pin_cfg);
+}
+
+static int nxp_c45_perout_enable(struct nxp_c45_phy *priv,
+ struct ptp_perout_request *perout, int on)
+{
+ bool rev_pol = false;
+ int pin;
+
+ if (perout->flags & ~PTP_PEROUT_REVERSE_POLARITY)
+ return -EOPNOTSUPP;
+
+ if (perout->flags & PTP_PEROUT_REVERSE_POLARITY)
+ rev_pol = true;
+
+ pin = ptp_find_pin(priv->ptp_clock, PTP_PF_PEROUT, perout->index);
+ if (pin < 0)
+ return pin;
+
+ if (!on) {
+ phy_clear_bits_mmd(priv->phydev, MDIO_MMD_VEND1,
+ VEND1_PTP_CONFIG, PPS_OUT_EN);
+
+ nxp_c45_gpio_config(priv, pin, GPIO_DISABLE);
+
+ return 0;
+ }
+
+ if (perout->start.sec != 0 || perout->start.nsec != 0)
+ return -EINVAL;
+
+ if (perout->period.sec != 1 || perout->period.nsec != 0)
+ return -EINVAL;
+
+ nxp_c45_gpio_config(priv, pin, GPIO_PPS_OUT_CFG);
+
+ if (rev_pol)
+ phy_set_bits_mmd(priv->phydev, MDIO_MMD_VEND1,
+ VEND1_PTP_CONFIG, PPS_OUT_POL);
+ else
+ phy_clear_bits_mmd(priv->phydev, MDIO_MMD_VEND1,
+ VEND1_PTP_CONFIG, PPS_OUT_POL);
+
+ phy_set_bits_mmd(priv->phydev, MDIO_MMD_VEND1, VEND1_PTP_CONFIG,
+ PPS_OUT_EN);
+
+ return 0;
+}
+
+static int nxp_c45_extts_enable(struct nxp_c45_phy *priv,
+ struct ptp_extts_request *extts, int on)
+{
+ int pin;
+
+ if (extts->flags & ~(PTP_ENABLE_FEATURE |
+ PTP_RISING_EDGE |
+ PTP_FALLING_EDGE |
+ PTP_STRICT_FLAGS))
+ return -EOPNOTSUPP;
+
+ /* Sampling on both edges is not supported */
+ if ((extts->flags & PTP_RISING_EDGE) &&
+ (extts->flags & PTP_FALLING_EDGE))
+ return -EOPNOTSUPP;
+
+ pin = ptp_find_pin(priv->ptp_clock, PTP_PF_EXTTS, extts->index);
+ if (pin < 0)
+ return pin;
+
+ if (!on) {
+ nxp_c45_gpio_config(priv, pin, GPIO_DISABLE);
+ priv->extts = false;
+
+ return 0;
+ }
+
+ if (extts->flags & PTP_RISING_EDGE)
+ phy_clear_bits_mmd(priv->phydev, MDIO_MMD_VEND1,
+ VEND1_PTP_CONFIG, EXT_TRG_EDGE);
+
+ if (extts->flags & PTP_FALLING_EDGE)
+ phy_set_bits_mmd(priv->phydev, MDIO_MMD_VEND1,
+ VEND1_PTP_CONFIG, EXT_TRG_EDGE);
+
+ nxp_c45_gpio_config(priv, pin, GPIO_EXTTS_OUT_CFG);
+ priv->extts = true;
+ priv->extts_index = extts->index;
+ ptp_schedule_worker(priv->ptp_clock, 0);
+
+ return 0;
+}
+
+static int nxp_c45_ptp_enable(struct ptp_clock_info *ptp,
+ struct ptp_clock_request *req, int on)
+{
+ struct nxp_c45_phy *priv = container_of(ptp, struct nxp_c45_phy, caps);
+
+ switch (req->type) {
+ case PTP_CLK_REQ_EXTTS:
+ return nxp_c45_extts_enable(priv, &req->extts, on);
+ case PTP_CLK_REQ_PEROUT:
+ return nxp_c45_perout_enable(priv, &req->perout, on);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static struct ptp_pin_desc nxp_c45_ptp_pins[] = {
+ { "nxp_c45_gpio0", 0, PTP_PF_NONE},
+ { "nxp_c45_gpio1", 1, PTP_PF_NONE},
+ { "nxp_c45_gpio2", 2, PTP_PF_NONE},
+ { "nxp_c45_gpio3", 3, PTP_PF_NONE},
+ { "nxp_c45_gpio4", 4, PTP_PF_NONE},
+ { "nxp_c45_gpio5", 5, PTP_PF_NONE},
+ { "nxp_c45_gpio6", 6, PTP_PF_NONE},
+ { "nxp_c45_gpio7", 7, PTP_PF_NONE},
+ { "nxp_c45_gpio8", 8, PTP_PF_NONE},
+ { "nxp_c45_gpio9", 9, PTP_PF_NONE},
+ { "nxp_c45_gpio10", 10, PTP_PF_NONE},
+ { "nxp_c45_gpio11", 11, PTP_PF_NONE},
+};
+
+static int nxp_c45_ptp_verify_pin(struct ptp_clock_info *ptp, unsigned int pin,
+ enum ptp_pin_function func, unsigned int chan)
+{
+ if (pin >= ARRAY_SIZE(nxp_c45_ptp_pins))
+ return -EINVAL;
+
+ switch (func) {
+ case PTP_PF_NONE:
+ case PTP_PF_PEROUT:
+ case PTP_PF_EXTTS:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
static int nxp_c45_init_ptp_clock(struct nxp_c45_phy *priv)
{
priv->caps = (struct ptp_clock_info) {@@ -452,7 +648,13 @@ static int nxp_c45_init_ptp_clock(struct nxp_c45_phy *priv)
.adjtime = nxp_c45_ptp_adjtime,
.gettimex64 = nxp_c45_ptp_gettimex64,
.settime64 = nxp_c45_ptp_settime64,
+ .enable = nxp_c45_ptp_enable,
+ .verify = nxp_c45_ptp_verify_pin,
.do_aux_work = nxp_c45_do_aux_work,
+ .pin_config = nxp_c45_ptp_pins,
+ .n_pins = ARRAY_SIZE(nxp_c45_ptp_pins),
+ .n_ext_ts = 1,
+ .n_per_out = 1,
};
priv->ptp_clock = ptp_clock_register(&priv->caps,