diff mbox series

[net-next,3/3] net: phy: microchip_rds_ptp : Add PEROUT feature library for RDS PTP supported phys

Message ID 20250103090731.1355-4-divya.koppera@microchip.com (mailing list archive)
State New
Delegated to: Netdev Maintainers
Headers show
Series Add PEROUT library for RDS PTP supported phys | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 1 this patch: 1
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers success CCed 8 of 8 maintainers
netdev/build_clang success Errors and warnings before: 2 this patch: 2
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 1 this patch: 1
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 354 lines checked
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
netdev/contest success net-next-2025-01-04--18-00 (tests: 887)

Commit Message

Divya Koppera Jan. 3, 2025, 9:07 a.m. UTC
Adds PEROUT feature for RDS PTP supported phys where
we can generate periodic output signal on supported
GPIO pins

Signed-off-by: Divya Koppera <divya.koppera@microchip.com>
---
 drivers/net/phy/microchip_rds_ptp.c | 320 ++++++++++++++++++++++++++++
 1 file changed, 320 insertions(+)
diff mbox series

Patch

diff --git a/drivers/net/phy/microchip_rds_ptp.c b/drivers/net/phy/microchip_rds_ptp.c
index 2936e46531cf..396975be2a31 100644
--- a/drivers/net/phy/microchip_rds_ptp.c
+++ b/drivers/net/phy/microchip_rds_ptp.c
@@ -54,6 +54,288 @@  static int mchp_rds_phy_set_bits_mmd(struct mchp_rds_ptp_clock *clock,
 	return phy_set_bits_mmd(phydev, PTP_MMD(clock), addr, val);
 }
 
+static int mchp_get_pulsewidth(struct phy_device *phydev,
+			       struct ptp_perout_request *perout_request,
+			       int *pulse_width)
+{
+	struct timespec64 ts_period;
+	s64 ts_on_nsec, period_nsec;
+	struct timespec64 ts_on;
+
+	ts_period.tv_sec = perout_request->period.sec;
+	ts_period.tv_nsec = perout_request->period.nsec;
+
+	ts_on.tv_sec = perout_request->on.sec;
+	ts_on.tv_nsec = perout_request->on.nsec;
+	ts_on_nsec = timespec64_to_ns(&ts_on);
+	period_nsec = timespec64_to_ns(&ts_period);
+
+	if (period_nsec < 200) {
+		phydev_warn(phydev, "perout period too small, minimum is 200ns\n");
+		return -EOPNOTSUPP;
+	}
+
+	switch (ts_on_nsec) {
+	case 200000000:
+		*pulse_width = MCHP_RDS_PTP_GEN_CFG_LTC_EVT_200MS_;
+		break;
+	case 100000000:
+		*pulse_width = MCHP_RDS_PTP_GEN_CFG_LTC_EVT_100MS_;
+		break;
+	case 50000000:
+		*pulse_width = MCHP_RDS_PTP_GEN_CFG_LTC_EVT_50MS_;
+		break;
+	case 10000000:
+		*pulse_width = MCHP_RDS_PTP_GEN_CFG_LTC_EVT_10MS_;
+		break;
+	case 5000000:
+		*pulse_width = MCHP_RDS_PTP_GEN_CFG_LTC_EVT_5MS_;
+		break;
+	case 1000000:
+		*pulse_width = MCHP_RDS_PTP_GEN_CFG_LTC_EVT_1MS_;
+		break;
+	case 500000:
+		*pulse_width = MCHP_RDS_PTP_GEN_CFG_LTC_EVT_500US_;
+		break;
+	case 100000:
+		*pulse_width = MCHP_RDS_PTP_GEN_CFG_LTC_EVT_100US_;
+		break;
+	case 50000:
+		*pulse_width = MCHP_RDS_PTP_GEN_CFG_LTC_EVT_50US_;
+		break;
+	case 10000:
+		*pulse_width = MCHP_RDS_PTP_GEN_CFG_LTC_EVT_10US_;
+		break;
+	case 5000:
+		*pulse_width = MCHP_RDS_PTP_GEN_CFG_LTC_EVT_5US_;
+		break;
+	case 1000:
+		*pulse_width = MCHP_RDS_PTP_GEN_CFG_LTC_EVT_1US_;
+		break;
+	case 500:
+		*pulse_width = MCHP_RDS_PTP_GEN_CFG_LTC_EVT_500NS_;
+		break;
+	case 100:
+		*pulse_width = MCHP_RDS_PTP_GEN_CFG_LTC_EVT_100NS_;
+		break;
+	default:
+		phydev_warn(phydev, "Using default pulse width of 200ms\n");
+		*pulse_width = MCHP_RDS_PTP_GEN_CFG_LTC_EVT_200MS_;
+		break;
+	}
+	return 0;
+}
+
+static int mchp_general_event_config(struct mchp_rds_ptp_clock *clock, s8 event,
+				     int pulse_width)
+{
+	int general_config;
+
+	general_config = mchp_rds_phy_read_mmd(clock, MCHP_RDS_PTP_GEN_CFG,
+					       MCHP_RDS_PTP_CLOCK);
+	if (general_config < 0)
+		return general_config;
+
+	general_config &= ~(MCHP_RDS_PTP_GEN_CFG_LTC_EVT_X_MASK_(event));
+	general_config |= MCHP_RDS_PTP_GEN_CFG_LTC_EVT_X_SET_(event,
+							      pulse_width);
+	general_config &= ~(MCHP_RDS_PTP_GEN_CFG_RELOAD_ADD_X_(event));
+	general_config |= MCHP_RDS_PTP_GEN_CFG_POLARITY_X_(event);
+
+	return mchp_rds_phy_write_mmd(clock, MCHP_RDS_PTP_GEN_CFG,
+				      MCHP_RDS_PTP_CLOCK, general_config);
+}
+
+static int mchp_set_clock_reload(struct mchp_rds_ptp_clock *clock, s8 evt,
+				 s64 period_sec, u32 period_nsec)
+{
+	int rc;
+
+	rc = mchp_rds_phy_write_mmd(clock,
+				    MCHP_RDS_PTP_CLK_TRGT_RELOAD_SEC_LO_X(evt),
+				    MCHP_RDS_PTP_CLOCK,
+				    lower_16_bits(period_sec));
+	if (rc < 0)
+		return rc;
+
+	rc = mchp_rds_phy_write_mmd(clock,
+				    MCHP_RDS_PTP_CLK_TRGT_RELOAD_SEC_HI_X(evt),
+				    MCHP_RDS_PTP_CLOCK,
+				    upper_16_bits(period_sec));
+	if (rc < 0)
+		return rc;
+
+	rc = mchp_rds_phy_write_mmd(clock,
+				    MCHP_RDS_PTP_CLK_TRGT_RELOAD_NS_LO_X(evt),
+				    MCHP_RDS_PTP_CLOCK,
+				    lower_16_bits(period_nsec));
+	if (rc < 0)
+		return rc;
+
+	return mchp_rds_phy_write_mmd(clock,
+				      MCHP_RDS_PTP_CLK_TRGT_RELOAD_NS_HI_X(evt),
+				      MCHP_RDS_PTP_CLOCK,
+				      upper_16_bits(period_nsec) & 0x3fff);
+}
+
+static int mchp_get_event(struct mchp_rds_ptp_clock *clock, int pin)
+{
+	if (clock->mchp_rds_ptp_event_a < 0 && pin == clock->gpio_event_a) {
+		clock->mchp_rds_ptp_event_a = pin;
+		return MCHP_RDS_PTP_EVT_A;
+	}
+
+	if (clock->mchp_rds_ptp_event_b < 0 && pin == clock->gpio_event_b) {
+		clock->mchp_rds_ptp_event_b = pin;
+		return MCHP_RDS_PTP_EVT_B;
+	}
+
+	return -1;
+}
+
+static int mchp_set_clock_target(struct mchp_rds_ptp_clock *clock, s8 evt,
+				 s64 start_sec, u32 start_nsec)
+{
+	int rc;
+
+	if (evt < 0)
+		return -1;
+
+	/* Set the start time */
+	rc = mchp_rds_phy_write_mmd(clock, MCHP_RDS_PTP_CLK_TRGT_SEC_LO_X(evt),
+				    MCHP_RDS_PTP_CLOCK,
+				    lower_16_bits(start_sec));
+	if (rc < 0)
+		return rc;
+
+	rc = mchp_rds_phy_write_mmd(clock, MCHP_RDS_PTP_CLK_TRGT_SEC_HI_X(evt),
+				    MCHP_RDS_PTP_CLOCK,
+				    upper_16_bits(start_sec));
+	if (rc < 0)
+		return rc;
+
+	rc = mchp_rds_phy_write_mmd(clock, MCHP_RDS_PTP_CLK_TRGT_NS_LO_X(evt),
+				    MCHP_RDS_PTP_CLOCK,
+				    lower_16_bits(start_nsec));
+	if (rc < 0)
+		return rc;
+
+	return mchp_rds_phy_write_mmd(clock, MCHP_RDS_PTP_CLK_TRGT_NS_HI_X(evt),
+				      MCHP_RDS_PTP_CLOCK,
+				      upper_16_bits(start_nsec) & 0x3fff);
+}
+
+static int mchp_rds_ptp_perout_off(struct mchp_rds_ptp_clock *clock,
+				   s8 gpio_pin)
+{
+	u16 general_config;
+	int event;
+	int rc;
+
+	if (clock->mchp_rds_ptp_event_a == gpio_pin)
+		event = MCHP_RDS_PTP_EVT_A;
+	else if (clock->mchp_rds_ptp_event_b == gpio_pin)
+		event = MCHP_RDS_PTP_EVT_B;
+
+	/* Set target to too far in the future, effectively disabling it */
+	rc = mchp_set_clock_target(clock, gpio_pin, 0xFFFFFFFF, 0);
+	if (rc < 0)
+		return rc;
+
+	general_config = mchp_rds_phy_read_mmd(clock, MCHP_RDS_PTP_GEN_CFG,
+					       MCHP_RDS_PTP_CLOCK);
+	general_config |= MCHP_RDS_PTP_GEN_CFG_RELOAD_ADD_X_(event);
+	rc = mchp_rds_phy_write_mmd(clock, MCHP_RDS_PTP_GEN_CFG,
+				    MCHP_RDS_PTP_CLOCK, general_config);
+	if (rc < 0)
+		return rc;
+
+	if (event == MCHP_RDS_PTP_EVT_A)
+		clock->mchp_rds_ptp_event_a = -1;
+
+	if (event == MCHP_RDS_PTP_EVT_B)
+		clock->mchp_rds_ptp_event_b = -1;
+
+	return 0;
+}
+
+static int mchp_rds_ptp_perout(struct ptp_clock_info *ptpci,
+			       struct ptp_perout_request *perout, int on)
+{
+	struct mchp_rds_ptp_clock *clock = container_of(ptpci,
+						      struct mchp_rds_ptp_clock,
+						      caps);
+	struct phy_device *phydev = clock->phydev;
+	int ret, event, gpio_pin, pulsewidth;
+
+	/* Reject requests with unsupported flags */
+	if (perout->flags & ~PTP_PEROUT_DUTY_CYCLE)
+		return -EOPNOTSUPP;
+
+	gpio_pin = ptp_find_pin(clock->ptp_clock, PTP_PF_PEROUT, perout->index);
+	if (gpio_pin != clock->gpio_event_a && gpio_pin != clock->gpio_event_b)
+		return -EINVAL;
+
+	if (!on) {
+		ret = mchp_rds_ptp_perout_off(clock, gpio_pin);
+		return ret;
+	}
+
+	ret = mchp_get_pulsewidth(phydev, perout, &pulsewidth);
+	if (ret < 0)
+		return ret;
+
+	event = mchp_get_event(clock, gpio_pin);
+	if (event < 0)
+		return event;
+
+	/* Configure to pulse every period */
+	ret = mchp_general_event_config(clock, event, pulsewidth);
+	if (ret < 0)
+		return ret;
+
+	ret = mchp_set_clock_target(clock, event, perout->start.sec,
+				    perout->start.nsec);
+	if (ret < 0)
+		return ret;
+
+	return mchp_set_clock_reload(clock, event, perout->period.sec,
+				     perout->period.nsec);
+}
+
+static int mchp_rds_ptpci_enable(struct ptp_clock_info *ptpci,
+				 struct ptp_clock_request *request, int on)
+{
+	switch (request->type) {
+	case PTP_CLK_REQ_PEROUT:
+		return mchp_rds_ptp_perout(ptpci, &request->perout, on);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mchp_rds_ptpci_verify(struct ptp_clock_info *ptpci, unsigned int pin,
+				 enum ptp_pin_function func, unsigned int chan)
+{
+	struct mchp_rds_ptp_clock *clock = container_of(ptpci,
+						      struct mchp_rds_ptp_clock,
+						      caps);
+
+	if (!(pin == clock->gpio_event_b && chan == 1) &&
+	    !(pin == clock->gpio_event_a && chan == 0))
+		return -1;
+
+	switch (func) {
+	case PTP_PF_NONE:
+	case PTP_PF_PEROUT:
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
 static int mchp_rds_ptp_flush_fifo(struct mchp_rds_ptp_clock *clock,
 				   enum mchp_rds_ptp_fifo_dir dir)
 {
@@ -479,6 +761,20 @@  static int mchp_rds_ptp_ltc_adjtime(struct ptp_clock_info *info, s64 delta)
 					    MCHP_RDS_PTP_CMD_CTL_LTC_STEP_NSEC);
 	}
 
+	mutex_unlock(&clock->ptp_lock);
+	info->gettime64(info, &ts);
+	mutex_lock(&clock->ptp_lock);
+
+	/* Target update is required for pulse generation on events that
+	 * are enabled
+	 */
+	if (clock->mchp_rds_ptp_event_a >= 0)
+		mchp_set_clock_target(clock, MCHP_RDS_PTP_EVT_A,
+				      ts.tv_sec + MCHP_RDS_PTP_BUFFER_TIME, 0);
+
+	if (clock->mchp_rds_ptp_event_b >= 0)
+		mchp_set_clock_target(clock, MCHP_RDS_PTP_EVT_B,
+				      ts.tv_sec + MCHP_RDS_PTP_BUFFER_TIME, 0);
 out_unlock:
 	mutex_unlock(&clock->ptp_lock);
 
@@ -989,16 +1285,37 @@  struct mchp_rds_ptp_clock *mchp_rds_ptp_probe(struct phy_device *phydev, u8 mmd,
 	clock->mmd		= mmd;
 
 	mutex_init(&clock->ptp_lock);
+	clock->pin_config = devm_kmalloc_array(&phydev->mdio.dev,
+					       MCHP_RDS_PTP_N_GPIO,
+					       sizeof(*clock->pin_config),
+					       GFP_KERNEL);
+	if (!clock->pin_config)
+		return ERR_PTR(-ENOMEM);
+
+	for (int i = 0; i < MCHP_RDS_PTP_N_GPIO; ++i) {
+		struct ptp_pin_desc *p = &clock->pin_config[i];
+
+		memset(p, 0, sizeof(*p));
+		snprintf(p->name, sizeof(p->name), "pin%d", i);
+		p->index = i;
+		p->func = PTP_PF_NONE;
+	}
 	/* Register PTP clock */
 	clock->caps.owner          = THIS_MODULE;
 	snprintf(clock->caps.name, 30, "%s", phydev->drv->name);
 	clock->caps.max_adj        = MCHP_RDS_PTP_MAX_ADJ;
 	clock->caps.n_ext_ts       = 0;
 	clock->caps.pps            = 0;
+	clock->caps.n_pins         = MCHP_RDS_PTP_N_GPIO;
+	clock->caps.n_per_out      = MCHP_RDS_PTP_N_PEROUT;
+	clock->caps.pin_config     = clock->pin_config;
 	clock->caps.adjfine        = mchp_rds_ptp_ltc_adjfine;
 	clock->caps.adjtime        = mchp_rds_ptp_ltc_adjtime;
 	clock->caps.gettime64      = mchp_rds_ptp_ltc_gettime64;
 	clock->caps.settime64      = mchp_rds_ptp_ltc_settime64;
+	clock->caps.enable         = mchp_rds_ptpci_enable;
+	clock->caps.verify         = mchp_rds_ptpci_verify;
+	clock->caps.getcrosststamp = NULL;
 	clock->ptp_clock = ptp_clock_register(&clock->caps,
 					      &phydev->mdio.dev);
 	if (IS_ERR(clock->ptp_clock))
@@ -1021,6 +1338,9 @@  struct mchp_rds_ptp_clock *mchp_rds_ptp_probe(struct phy_device *phydev, u8 mmd,
 
 	phydev->mii_ts = &clock->mii_ts;
 
+	clock->mchp_rds_ptp_event_a = -1;
+	clock->mchp_rds_ptp_event_b = -1;
+
 	/* Timestamp selected by default to keep legacy API */
 	phydev->default_timestamp = true;