diff mbox series

[RFC,v7,6/8] ptp_ocp: implement DPLL ops

Message ID 20230428002009.2948020-7-vadfed@meta.com (mailing list archive)
State RFC
Delegated to: Netdev Maintainers
Headers show
Series Create common DPLL configuration API | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Guessed tree name to be net-next, async
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 fail Errors and warnings before: 12 this patch: 12
netdev/cc_maintainers warning 2 maintainers not CCed: vadfed@fb.com richardcochran@gmail.com
netdev/build_clang fail Errors and warnings before: 8 this patch: 11
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: 8 this patch: 8
netdev/checkpatch warning CHECK: Please don't use multiple blank lines WARNING: line length of 81 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns WARNING: line length of 87 exceeds 80 columns WARNING: line length of 90 exceeds 80 columns WARNING: line length of 95 exceeds 80 columns WARNING: line length of 96 exceeds 80 columns WARNING: line length of 97 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Vadim Fedorenko April 28, 2023, 12:20 a.m. UTC
From: Vadim Fedorenko <vadim.fedorenko@linux.dev>

Implement basic DPLL operations in ptp_ocp driver as the
simplest example of using new subsystem.

Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
---
 drivers/ptp/Kconfig   |   1 +
 drivers/ptp/ptp_ocp.c | 327 +++++++++++++++++++++++++++++++++++-------
 2 files changed, 276 insertions(+), 52 deletions(-)

Comments

Jiri Pirko May 4, 2023, 9:27 a.m. UTC | #1
Fri, Apr 28, 2023 at 02:20:07AM CEST, vadfed@meta.com wrote:
>From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>
>Implement basic DPLL operations in ptp_ocp driver as the
>simplest example of using new subsystem.
>
>Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>---
> drivers/ptp/Kconfig   |   1 +
> drivers/ptp/ptp_ocp.c | 327 +++++++++++++++++++++++++++++++++++-------
> 2 files changed, 276 insertions(+), 52 deletions(-)
>
>diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
>index b00201d81313..e3575c2e34dc 100644
>--- a/drivers/ptp/Kconfig
>+++ b/drivers/ptp/Kconfig
>@@ -177,6 +177,7 @@ config PTP_1588_CLOCK_OCP
> 	depends on COMMON_CLK
> 	select NET_DEVLINK
> 	select CRC16
>+	select DPLL
> 	help
> 	  This driver adds support for an OpenCompute time card.
> 
>diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
>index 2b63f3487645..100e5da0aeb3 100644
>--- a/drivers/ptp/ptp_ocp.c
>+++ b/drivers/ptp/ptp_ocp.c
>@@ -23,6 +23,7 @@
> #include <linux/mtd/mtd.h>
> #include <linux/nvmem-consumer.h>
> #include <linux/crc16.h>
>+#include <linux/dpll.h>
> 
> #define PCI_VENDOR_ID_FACEBOOK			0x1d9b
> #define PCI_DEVICE_ID_FACEBOOK_TIMECARD		0x0400
>@@ -261,12 +262,21 @@ enum ptp_ocp_sma_mode {
> 	SMA_MODE_OUT,
> };
> 
>+static struct dpll_pin_frequency ptp_ocp_sma_freq[] = {

const

>+	DPLL_PIN_FREQUENCY_1PPS,
>+	DPLL_PIN_FREQUENCY_10MHZ,
>+	DPLL_PIN_FREQUENCY_IRIG_B,
>+	DPLL_PIN_FREQUENCY_DCF77,
>+};
>+
> struct ptp_ocp_sma_connector {
> 	enum	ptp_ocp_sma_mode mode;
> 	bool	fixed_fcn;
> 	bool	fixed_dir;
> 	bool	disabled;
> 	u8	default_fcn;
>+	struct dpll_pin		   *dpll_pin;
>+	struct dpll_pin_properties dpll_prop;
> };
> 
> struct ocp_attr_group {
>@@ -295,6 +305,7 @@ struct ptp_ocp_serial_port {
> 
> #define OCP_BOARD_ID_LEN		13
> #define OCP_SERIAL_LEN			6
>+#define OCP_SMA_NUM			4
> 
> struct ptp_ocp {
> 	struct pci_dev		*pdev;
>@@ -351,8 +362,9 @@ struct ptp_ocp {
> 	u32			ts_window_adjust;
> 	u64			fw_cap;
> 	struct ptp_ocp_signal	signal[4];
>-	struct ptp_ocp_sma_connector sma[4];
>+	struct ptp_ocp_sma_connector sma[OCP_SMA_NUM];
> 	const struct ocp_sma_op *sma_op;
>+	struct dpll_device *dpll;
> };
> 
> #define OCP_REQ_TIMESTAMP	BIT(0)
>@@ -836,6 +848,7 @@ static DEFINE_IDR(ptp_ocp_idr);
> struct ocp_selector {
> 	const char *name;
> 	int value;
>+	u64 frequency;
> };
> 
> static const struct ocp_selector ptp_ocp_clock[] = {
>@@ -856,31 +869,31 @@ static const struct ocp_selector ptp_ocp_clock[] = {
> #define SMA_SELECT_MASK		GENMASK(14, 0)
> 
> static const struct ocp_selector ptp_ocp_sma_in[] = {
>-	{ .name = "10Mhz",	.value = 0x0000 },
>-	{ .name = "PPS1",	.value = 0x0001 },
>-	{ .name = "PPS2",	.value = 0x0002 },
>-	{ .name = "TS1",	.value = 0x0004 },
>-	{ .name = "TS2",	.value = 0x0008 },
>-	{ .name = "IRIG",	.value = 0x0010 },
>-	{ .name = "DCF",	.value = 0x0020 },
>-	{ .name = "TS3",	.value = 0x0040 },
>-	{ .name = "TS4",	.value = 0x0080 },
>-	{ .name = "FREQ1",	.value = 0x0100 },
>-	{ .name = "FREQ2",	.value = 0x0200 },
>-	{ .name = "FREQ3",	.value = 0x0400 },
>-	{ .name = "FREQ4",	.value = 0x0800 },
>-	{ .name = "None",	.value = SMA_DISABLE },
>+	{ .name = "10Mhz",  .value = 0x0000,      .frequency = 10000000 },
>+	{ .name = "PPS1",   .value = 0x0001,      .frequency = 1 },
>+	{ .name = "PPS2",   .value = 0x0002,      .frequency = 1 },
>+	{ .name = "TS1",    .value = 0x0004,      .frequency = 0 },
>+	{ .name = "TS2",    .value = 0x0008,      .frequency = 0 },
>+	{ .name = "IRIG",   .value = 0x0010,      .frequency = 10000 },
>+	{ .name = "DCF",    .value = 0x0020,      .frequency = 77500 },
>+	{ .name = "TS3",    .value = 0x0040,      .frequency = 0 },
>+	{ .name = "TS4",    .value = 0x0080,      .frequency = 0 },
>+	{ .name = "FREQ1",  .value = 0x0100,      .frequency = 0 },
>+	{ .name = "FREQ2",  .value = 0x0200,      .frequency = 0 },
>+	{ .name = "FREQ3",  .value = 0x0400,      .frequency = 0 },
>+	{ .name = "FREQ4",  .value = 0x0800,      .frequency = 0 },
>+	{ .name = "None",   .value = SMA_DISABLE, .frequency = 0 },
> 	{ }
> };
> 
> static const struct ocp_selector ptp_ocp_sma_out[] = {
>-	{ .name = "10Mhz",	.value = 0x0000 },
>-	{ .name = "PHC",	.value = 0x0001 },
>-	{ .name = "MAC",	.value = 0x0002 },
>-	{ .name = "GNSS1",	.value = 0x0004 },
>-	{ .name = "GNSS2",	.value = 0x0008 },
>-	{ .name = "IRIG",	.value = 0x0010 },
>-	{ .name = "DCF",	.value = 0x0020 },
>+	{ .name = "10Mhz",	.value = 0x0000,  .frequency = 10000000 },
>+	{ .name = "PHC",	.value = 0x0001,  .frequency = 1 },
>+	{ .name = "MAC",	.value = 0x0002,  .frequency = 1 },
>+	{ .name = "GNSS1",	.value = 0x0004,  .frequency = 1 },
>+	{ .name = "GNSS2",	.value = 0x0008,  .frequency = 1 },
>+	{ .name = "IRIG",	.value = 0x0010,  .frequency = 10000 },
>+	{ .name = "DCF",	.value = 0x0020,  .frequency = 77000 },
> 	{ .name = "GEN1",	.value = 0x0040 },
> 	{ .name = "GEN2",	.value = 0x0080 },
> 	{ .name = "GEN3",	.value = 0x0100 },
>@@ -891,15 +904,15 @@ static const struct ocp_selector ptp_ocp_sma_out[] = {
> };
> 
> static const struct ocp_selector ptp_ocp_art_sma_in[] = {
>-	{ .name = "PPS1",	.value = 0x0001 },
>-	{ .name = "10Mhz",	.value = 0x0008 },
>+	{ .name = "PPS1",	.value = 0x0001,  .frequency = 1 },
>+	{ .name = "10Mhz",	.value = 0x0008,  .frequency = 1000000 },
> 	{ }
> };
> 
> static const struct ocp_selector ptp_ocp_art_sma_out[] = {
>-	{ .name = "PHC",	.value = 0x0002 },
>-	{ .name = "GNSS",	.value = 0x0004 },
>-	{ .name = "10Mhz",	.value = 0x0010 },
>+	{ .name = "PHC",	.value = 0x0002,  .frequency = 1 },
>+	{ .name = "GNSS",	.value = 0x0004,  .frequency = 1 },
>+	{ .name = "10Mhz",	.value = 0x0010,  .frequency = 10000000 },
> 	{ }
> };
> 
>@@ -2283,22 +2296,34 @@ ptp_ocp_sma_fb_set_inputs(struct ptp_ocp *bp, int sma_nr, u32 val)
> static void
> ptp_ocp_sma_fb_init(struct ptp_ocp *bp)
> {
>+	struct dpll_pin_properties prop = {

Why don't you have this as static const outside the function?


>+		.label = NULL,

Pointless init.


>+		.type = DPLL_PIN_TYPE_EXT,
>+		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>+		.freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
>+		.freq_supported = ptp_ocp_sma_freq,
>+
>+	};
> 	u32 reg;
> 	int i;
> 
> 	/* defaults */
>+	for (i = 0; i < OCP_SMA_NUM; i++) {
>+		bp->sma[i].default_fcn = i & 1;
>+		bp->sma[i].dpll_prop = prop;
>+		bp->sma[i].dpll_prop.label = bp->ptp_info.pin_config[i].name;
>+	}
> 	bp->sma[0].mode = SMA_MODE_IN;
> 	bp->sma[1].mode = SMA_MODE_IN;
> 	bp->sma[2].mode = SMA_MODE_OUT;
> 	bp->sma[3].mode = SMA_MODE_OUT;
>-	for (i = 0; i < 4; i++)
>-		bp->sma[i].default_fcn = i & 1;
>-
> 	/* If no SMA1 map, the pin functions and directions are fixed. */
> 	if (!bp->sma_map1) {
>-		for (i = 0; i < 4; i++) {
>+		for (i = 0; i < OCP_SMA_NUM; i++) {
> 			bp->sma[i].fixed_fcn = true;
> 			bp->sma[i].fixed_dir = true;
>+			bp->sma[1].dpll_prop.capabilities &=
>+				~DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
> 		}
> 		return;
> 	}
>@@ -2308,7 +2333,7 @@ ptp_ocp_sma_fb_init(struct ptp_ocp *bp)
> 	 */
> 	reg = ioread32(&bp->sma_map2->gpio2);
> 	if (reg == 0xffffffff) {
>-		for (i = 0; i < 4; i++)
>+		for (i = 0; i < OCP_SMA_NUM; i++)
> 			bp->sma[i].fixed_dir = true;
> 	} else {
> 		reg = ioread32(&bp->sma_map1->gpio1);
>@@ -2330,7 +2355,7 @@ static const struct ocp_sma_op ocp_fb_sma_op = {
> };
> 
> static int
>-ptp_ocp_fb_set_pins(struct ptp_ocp *bp)
>+ptp_ocp_set_pins(struct ptp_ocp *bp)
> {
> 	struct ptp_pin_desc *config;
> 	int i;
>@@ -2397,16 +2422,16 @@ ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
> 
> 	ptp_ocp_tod_init(bp);
> 	ptp_ocp_nmea_out_init(bp);
>-	ptp_ocp_sma_init(bp);
> 	ptp_ocp_signal_init(bp);
> 
> 	err = ptp_ocp_attr_group_add(bp, fb_timecard_groups);
> 	if (err)
> 		return err;
> 
>-	err = ptp_ocp_fb_set_pins(bp);
>+	err = ptp_ocp_set_pins(bp);
> 	if (err)
> 		return err;
>+	ptp_ocp_sma_init(bp);
> 
> 	return ptp_ocp_init_clock(bp);
> }
>@@ -2446,6 +2471,14 @@ ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data)
> static void
> ptp_ocp_art_sma_init(struct ptp_ocp *bp)
> {
>+	struct dpll_pin_properties prop = {
>+		.label = NULL,
>+		.type = DPLL_PIN_TYPE_EXT,
>+		.capabilities = 0,

Same comment as to the similar prop struct above. Plus another pointless
init here.


>+		.freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
>+		.freq_supported = ptp_ocp_sma_freq,
>+
>+	};
> 	u32 reg;
> 	int i;
> 
>@@ -2460,16 +2493,16 @@ ptp_ocp_art_sma_init(struct ptp_ocp *bp)
> 	bp->sma[2].default_fcn = 0x10;	/* OUT: 10Mhz */
> 	bp->sma[3].default_fcn = 0x02;	/* OUT: PHC */
> 
>-	/* If no SMA map, the pin functions and directions are fixed. */
>-	if (!bp->art_sma) {
>-		for (i = 0; i < 4; i++) {
>+
>+	for (i = 0; i < OCP_SMA_NUM; i++) {
>+		/* If no SMA map, the pin functions and directions are fixed. */
>+		bp->sma[i].dpll_prop = prop;
>+		bp->sma[i].dpll_prop.label = bp->ptp_info.pin_config[i].name;
>+		if (!bp->art_sma) {
> 			bp->sma[i].fixed_fcn = true;
> 			bp->sma[i].fixed_dir = true;
>+			continue;
> 		}
>-		return;
>-	}
>-
>-	for (i = 0; i < 4; i++) {
> 		reg = ioread32(&bp->art_sma->map[i].gpio);
> 
> 		switch (reg & 0xff) {
>@@ -2480,9 +2513,13 @@ ptp_ocp_art_sma_init(struct ptp_ocp *bp)
> 		case 1:
> 		case 8:
> 			bp->sma[i].mode = SMA_MODE_IN;
>+			bp->sma[i].dpll_prop.capabilities =
>+				DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
> 			break;
> 		default:
> 			bp->sma[i].mode = SMA_MODE_OUT;
>+			bp->sma[i].dpll_prop.capabilities =
>+				DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
> 			break;
> 		}
> 	}
>@@ -2549,6 +2586,9 @@ ptp_ocp_art_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
> 	/* Enable MAC serial port during initialisation */
> 	iowrite32(1, &bp->board_config->mro50_serial_activate);
> 
>+	err = ptp_ocp_set_pins(bp);
>+	if (err)
>+		return err;
> 	ptp_ocp_sma_init(bp);
> 
> 	err = ptp_ocp_attr_group_add(bp, art_timecard_groups);
>@@ -2690,16 +2730,9 @@ sma4_show(struct device *dev, struct device_attribute *attr, char *buf)
> }
> 
> static int
>-ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>+ptp_ocp_sma_store_val(struct ptp_ocp *bp, int val, enum ptp_ocp_sma_mode mode, int sma_nr)
> {
> 	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>-	enum ptp_ocp_sma_mode mode;
>-	int val;
>-
>-	mode = sma->mode;
>-	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>-	if (val < 0)
>-		return val;
> 
> 	if (sma->fixed_dir && (mode != sma->mode || val & SMA_DISABLE))
> 		return -EOPNOTSUPP;
>@@ -2734,6 +2767,20 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
> 	return val;
> }
> 
>+static int
>+ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>+{
>+	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>+	enum ptp_ocp_sma_mode mode;
>+	int val;
>+
>+	mode = sma->mode;
>+	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>+	if (val < 0)
>+		return val;
>+	return ptp_ocp_sma_store_val(bp, val, mode, sma_nr);
>+}
>+
> static ssize_t
> sma1_store(struct device *dev, struct device_attribute *attr,
> 	   const char *buf, size_t count)
>@@ -4172,12 +4219,148 @@ ptp_ocp_detach(struct ptp_ocp *bp)
> 	device_unregister(&bp->dev);
> }
> 
>+static int ptp_ocp_dpll_lock_status_get(const struct dpll_device *dpll,
>+					void *priv,
>+					enum dpll_lock_status *status,
>+					struct netlink_ext_ack *extack)
>+{
>+	struct ptp_ocp *bp = priv;
>+	int sync;
>+
>+	sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
>+	*status = sync ? DPLL_LOCK_STATUS_LOCKED : DPLL_LOCK_STATUS_UNLOCKED;

Does your device support event delivery in case of the status change?
ice and mlx5 drivers do poll for changes in this area anyway. It's a
part of this patchset. You should do the same if your device does
not support events.

Could you please implement notifications using
dpll_device_notify() for status change and dpll_pin_notify() for pin
state change?



>+
>+	return 0;
>+}
>+
>+static int ptp_ocp_dpll_source_idx_get(const struct dpll_device *dpll,
>+				       void *priv, u32 *idx,
>+				       struct netlink_ext_ack *extack)
>+{
>+	struct ptp_ocp *bp = priv;
>+
>+	if (bp->pps_select) {
>+		*idx = ioread32(&bp->pps_select->gpio1);
>+		return 0;
>+	}
>+	return -EINVAL;
>+}
>+
>+static int ptp_ocp_dpll_mode_get(const struct dpll_device *dpll, void *priv,
>+				 u32 *mode, struct netlink_ext_ack *extack)
>+{
>+	*mode = DPLL_MODE_AUTOMATIC;
>+	return 0;
>+}
>+
>+static bool ptp_ocp_dpll_mode_supported(const struct dpll_device *dpll,
>+					void *priv, const enum dpll_mode mode,
>+					struct netlink_ext_ack *extack)
>+{
>+	return mode == DPLL_MODE_AUTOMATIC;
>+}
>+
>+static int ptp_ocp_dpll_direction_get(const struct dpll_pin *pin,
>+				      void *pin_priv,
>+				      const struct dpll_device *dpll,
>+				      void *priv,
>+				      enum dpll_pin_direction *direction,
>+				      struct netlink_ext_ack *extack)
>+{
>+	struct ptp_ocp_sma_connector *sma = pin_priv;
>+
>+	*direction = sma->mode == SMA_MODE_IN ?
>+				  DPLL_PIN_DIRECTION_SOURCE :
>+				  DPLL_PIN_DIRECTION_OUTPUT;
>+	return 0;
>+}
>+
>+static int ptp_ocp_dpll_direction_set(const struct dpll_pin *pin,
>+				      void *pin_priv,
>+				      const struct dpll_device *dpll,
>+				      void *dpll_priv,
>+				      enum dpll_pin_direction direction,
>+				      struct netlink_ext_ack *extack)
>+{
>+	struct ptp_ocp_sma_connector *sma = pin_priv;
>+	struct ptp_ocp *bp = dpll_priv;
>+	enum ptp_ocp_sma_mode mode;
>+	int sma_nr = (sma - bp->sma);
>+
>+	if (sma->fixed_dir)

I believe that this is a pointless check as DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE
is not set and therefore the check in dpll_pin_direction_set() will be
true and -EOPNOTSUPP will be returned from there.
Remove this.


>+		return -EOPNOTSUPP;
>+	mode = direction == DPLL_PIN_DIRECTION_SOURCE ?
>+			    SMA_MODE_IN : SMA_MODE_OUT;
>+	return ptp_ocp_sma_store_val(bp, 0, mode, sma_nr);

You need sma_nr just here. Why can't you change ptp_ocp_sma_store_val()
to accept struct ptp_ocp_sma_connector * instead avoiding the need for
tne sma_nr completely?


>+}
>+
>+static int ptp_ocp_dpll_frequency_set(const struct dpll_pin *pin,
>+				      void *pin_priv,
>+				      const struct dpll_device *dpll,
>+				      void *dpll_priv, u64 frequency,
>+				      struct netlink_ext_ack *extack)
>+{
>+	struct ptp_ocp_sma_connector *sma = pin_priv;
>+	struct ptp_ocp *bp = dpll_priv;
>+	const struct ocp_selector *tbl;
>+	int sma_nr = (sma - bp->sma);
>+	int val, i;
>+
>+	if (sma->fixed_fcn)

In that case, just fill up a single frequency in the properties,
avoid this check-fail and let the dpll core handle it.


>+		return -EOPNOTSUPP;
>+
>+	tbl = bp->sma_op->tbl[sma->mode];
>+	for (i = 0; tbl[i].name; i++)
>+		if (tbl[i].frequency == frequency)
>+			return ptp_ocp_sma_store_val(bp, val, sma->mode, sma_nr);
>+	return -EINVAL;
>+}
>+
>+static int ptp_ocp_dpll_frequency_get(const struct dpll_pin *pin,
>+				      void *pin_priv,
>+				      const struct dpll_device *dpll,
>+				      void *dpll_priv, u64 *frequency,
>+				      struct netlink_ext_ack *extack)
>+{
>+	struct ptp_ocp_sma_connector *sma = pin_priv;
>+	struct ptp_ocp *bp = dpll_priv;
>+	const struct ocp_selector *tbl;
>+	int sma_nr = (sma - bp->sma);

1) void "()"s here.
2) why don't you fill the sma_nr in struct ptp_ocp_sma_connector to make
   this easier to follow? IDK, just a suggestion, take or leave.

Same applies to the the rest of similar occurances above.


>+	u32 val;
>+	int i;
>+
>+	val = bp->sma_op->get(bp, sma_nr);
>+	tbl = bp->sma_op->tbl[sma->mode];
>+	for (i = 0; tbl[i].name; i++)
>+		if (val == tbl[i].value) {
>+			*frequency = tbl[i].frequency;
>+			return 0;
>+		}
>+
>+	return -EINVAL;
>+}
>+
>+static const struct dpll_device_ops dpll_ops = {
>+	.lock_status_get = ptp_ocp_dpll_lock_status_get,
>+	.source_pin_idx_get = ptp_ocp_dpll_source_idx_get,

This op is a leftover, in dpll core it is not called. This was removed
and agreed that drivers should implement state_on_dpll_get() op for pins
to see which one is connected.

Please fix here and remove the leftover from DPLL patch #2 as well.


>+	.mode_get = ptp_ocp_dpll_mode_get,
>+	.mode_supported = ptp_ocp_dpll_mode_supported,
>+};
>+
>+static const struct dpll_pin_ops dpll_pins_ops = {
>+	.frequency_get = ptp_ocp_dpll_frequency_get,
>+	.frequency_set = ptp_ocp_dpll_frequency_set,
>+	.direction_get = ptp_ocp_dpll_direction_get,
>+	.direction_set = ptp_ocp_dpll_direction_set,
>+};
>+
> static int
> ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> {
> 	struct devlink *devlink;
> 	struct ptp_ocp *bp;
>-	int err;
>+	int err, i;
>+	u64 clkid;
> 
> 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev->dev);
> 	if (!devlink) {
>@@ -4227,8 +4410,39 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> 
> 	ptp_ocp_info(bp);
> 	devlink_register(devlink);
>-	return 0;
> 
>+	clkid = pci_get_dsn(pdev);
>+	bp->dpll = dpll_device_get(clkid, 0, THIS_MODULE);

I suggested this the last time, but again: Could you please:
1) rename dpll_device_get to __dpll_device_get
2) introduce dpll_device_get as a macro filling up THIS_MODULE

Then drivers will just call always:
bp->dpll = dpll_device_get(clkid, 0);
and the macro will fillup the module automatically.

Please do the same for dpll_pin_get()


>+	if (IS_ERR(bp->dpll)) {
>+		dev_err(&pdev->dev, "dpll_device_alloc failed\n");

You need to fix your error path to call devlink_unregister() in this
case.


>+		goto out;
>+	}
>+
>+	err = dpll_device_register(bp->dpll, DPLL_TYPE_PPS, &dpll_ops, bp, &pdev->dev);
>+	if (err)

You need to fix your error path to call dpll_device_put() in this
case.


>+		goto out;
>+
>+	for (i = 0; i < OCP_SMA_NUM; i++) {
>+		bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &bp->sma[i].dpll_prop);
>+		if (IS_ERR(bp->sma[i].dpll_pin))
>+			goto out_dpll;
>+
>+		err = dpll_pin_register(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops,
>+					&bp->sma[i], NULL);
>+		if (err) {
>+			dpll_pin_put(bp->sma[i].dpll_pin);
>+			goto out_dpll;
>+		}
>+	}
>+
>+	return 0;
>+out_dpll:
>+	while (i) {
>+		--i;

	while (i--) {
	?

>+		dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, &bp->sma[i]);
>+		dpll_pin_put(bp->sma[i].dpll_pin);
>+	}
>+	dpll_device_put(bp->dpll);
> out:
> 	ptp_ocp_detach(bp);
> out_disable:
>@@ -4243,7 +4457,16 @@ ptp_ocp_remove(struct pci_dev *pdev)
> {
> 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
> 	struct devlink *devlink = priv_to_devlink(bp);
>+	int i;
> 
>+	for (i = 0; i < OCP_SMA_NUM; i++) {
>+		if (bp->sma[i].dpll_pin) {

Remove this pointless check. It is always true.


>+			dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, bp);
>+			dpll_pin_put(bp->sma[i].dpll_pin);
>+		}
>+	}
>+	dpll_device_unregister(bp->dpll, &dpll_ops, bp);
>+	dpll_device_put(bp->dpll);
> 	devlink_unregister(devlink);
> 	ptp_ocp_detach(bp);
> 	pci_disable_device(pdev);
>-- 
>2.34.1
>
Vadim Fedorenko May 5, 2023, 1:43 p.m. UTC | #2
On 04/05/2023 10:27, Jiri Pirko wrote:
> Fri, Apr 28, 2023 at 02:20:07AM CEST, vadfed@meta.com wrote:
>> From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>>
>> Implement basic DPLL operations in ptp_ocp driver as the
>> simplest example of using new subsystem.
>>
>> Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>> ---
>> drivers/ptp/Kconfig   |   1 +
>> drivers/ptp/ptp_ocp.c | 327 +++++++++++++++++++++++++++++++++++-------
>> 2 files changed, 276 insertions(+), 52 deletions(-)
>>
>> diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
>> index b00201d81313..e3575c2e34dc 100644
>> --- a/drivers/ptp/Kconfig
>> +++ b/drivers/ptp/Kconfig
>> @@ -177,6 +177,7 @@ config PTP_1588_CLOCK_OCP
>> 	depends on COMMON_CLK
>> 	select NET_DEVLINK
>> 	select CRC16
>> +	select DPLL
>> 	help
>> 	  This driver adds support for an OpenCompute time card.
>>
>> diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
>> index 2b63f3487645..100e5da0aeb3 100644
>> --- a/drivers/ptp/ptp_ocp.c
>> +++ b/drivers/ptp/ptp_ocp.c
>> @@ -23,6 +23,7 @@
>> #include <linux/mtd/mtd.h>
>> #include <linux/nvmem-consumer.h>
>> #include <linux/crc16.h>
>> +#include <linux/dpll.h>
>>
>> #define PCI_VENDOR_ID_FACEBOOK			0x1d9b
>> #define PCI_DEVICE_ID_FACEBOOK_TIMECARD		0x0400
>> @@ -261,12 +262,21 @@ enum ptp_ocp_sma_mode {
>> 	SMA_MODE_OUT,
>> };
>>
>> +static struct dpll_pin_frequency ptp_ocp_sma_freq[] = {
> 
> const

Forgot about this one, will change it.

> 
>> +	DPLL_PIN_FREQUENCY_1PPS,
>> +	DPLL_PIN_FREQUENCY_10MHZ,
>> +	DPLL_PIN_FREQUENCY_IRIG_B,
>> +	DPLL_PIN_FREQUENCY_DCF77,
>> +};
>> +
>> struct ptp_ocp_sma_connector {
>> 	enum	ptp_ocp_sma_mode mode;
>> 	bool	fixed_fcn;
>> 	bool	fixed_dir;
>> 	bool	disabled;
>> 	u8	default_fcn;
>> +	struct dpll_pin		   *dpll_pin;
>> +	struct dpll_pin_properties dpll_prop;
>> };
>>
>> struct ocp_attr_group {
>> @@ -295,6 +305,7 @@ struct ptp_ocp_serial_port {
>>
>> #define OCP_BOARD_ID_LEN		13
>> #define OCP_SERIAL_LEN			6
>> +#define OCP_SMA_NUM			4
>>
>> struct ptp_ocp {
>> 	struct pci_dev		*pdev;
>> @@ -351,8 +362,9 @@ struct ptp_ocp {
>> 	u32			ts_window_adjust;
>> 	u64			fw_cap;
>> 	struct ptp_ocp_signal	signal[4];
>> -	struct ptp_ocp_sma_connector sma[4];
>> +	struct ptp_ocp_sma_connector sma[OCP_SMA_NUM];
>> 	const struct ocp_sma_op *sma_op;
>> +	struct dpll_device *dpll;
>> };
>>
>> #define OCP_REQ_TIMESTAMP	BIT(0)
>> @@ -836,6 +848,7 @@ static DEFINE_IDR(ptp_ocp_idr);
>> struct ocp_selector {
>> 	const char *name;
>> 	int value;
>> +	u64 frequency;
>> };
>>
>> static const struct ocp_selector ptp_ocp_clock[] = {
>> @@ -856,31 +869,31 @@ static const struct ocp_selector ptp_ocp_clock[] = {
>> #define SMA_SELECT_MASK		GENMASK(14, 0)
>>
>> static const struct ocp_selector ptp_ocp_sma_in[] = {
>> -	{ .name = "10Mhz",	.value = 0x0000 },
>> -	{ .name = "PPS1",	.value = 0x0001 },
>> -	{ .name = "PPS2",	.value = 0x0002 },
>> -	{ .name = "TS1",	.value = 0x0004 },
>> -	{ .name = "TS2",	.value = 0x0008 },
>> -	{ .name = "IRIG",	.value = 0x0010 },
>> -	{ .name = "DCF",	.value = 0x0020 },
>> -	{ .name = "TS3",	.value = 0x0040 },
>> -	{ .name = "TS4",	.value = 0x0080 },
>> -	{ .name = "FREQ1",	.value = 0x0100 },
>> -	{ .name = "FREQ2",	.value = 0x0200 },
>> -	{ .name = "FREQ3",	.value = 0x0400 },
>> -	{ .name = "FREQ4",	.value = 0x0800 },
>> -	{ .name = "None",	.value = SMA_DISABLE },
>> +	{ .name = "10Mhz",  .value = 0x0000,      .frequency = 10000000 },
>> +	{ .name = "PPS1",   .value = 0x0001,      .frequency = 1 },
>> +	{ .name = "PPS2",   .value = 0x0002,      .frequency = 1 },
>> +	{ .name = "TS1",    .value = 0x0004,      .frequency = 0 },
>> +	{ .name = "TS2",    .value = 0x0008,      .frequency = 0 },
>> +	{ .name = "IRIG",   .value = 0x0010,      .frequency = 10000 },
>> +	{ .name = "DCF",    .value = 0x0020,      .frequency = 77500 },
>> +	{ .name = "TS3",    .value = 0x0040,      .frequency = 0 },
>> +	{ .name = "TS4",    .value = 0x0080,      .frequency = 0 },
>> +	{ .name = "FREQ1",  .value = 0x0100,      .frequency = 0 },
>> +	{ .name = "FREQ2",  .value = 0x0200,      .frequency = 0 },
>> +	{ .name = "FREQ3",  .value = 0x0400,      .frequency = 0 },
>> +	{ .name = "FREQ4",  .value = 0x0800,      .frequency = 0 },
>> +	{ .name = "None",   .value = SMA_DISABLE, .frequency = 0 },
>> 	{ }
>> };
>>
>> static const struct ocp_selector ptp_ocp_sma_out[] = {
>> -	{ .name = "10Mhz",	.value = 0x0000 },
>> -	{ .name = "PHC",	.value = 0x0001 },
>> -	{ .name = "MAC",	.value = 0x0002 },
>> -	{ .name = "GNSS1",	.value = 0x0004 },
>> -	{ .name = "GNSS2",	.value = 0x0008 },
>> -	{ .name = "IRIG",	.value = 0x0010 },
>> -	{ .name = "DCF",	.value = 0x0020 },
>> +	{ .name = "10Mhz",	.value = 0x0000,  .frequency = 10000000 },
>> +	{ .name = "PHC",	.value = 0x0001,  .frequency = 1 },
>> +	{ .name = "MAC",	.value = 0x0002,  .frequency = 1 },
>> +	{ .name = "GNSS1",	.value = 0x0004,  .frequency = 1 },
>> +	{ .name = "GNSS2",	.value = 0x0008,  .frequency = 1 },
>> +	{ .name = "IRIG",	.value = 0x0010,  .frequency = 10000 },
>> +	{ .name = "DCF",	.value = 0x0020,  .frequency = 77000 },
>> 	{ .name = "GEN1",	.value = 0x0040 },
>> 	{ .name = "GEN2",	.value = 0x0080 },
>> 	{ .name = "GEN3",	.value = 0x0100 },
>> @@ -891,15 +904,15 @@ static const struct ocp_selector ptp_ocp_sma_out[] = {
>> };
>>
>> static const struct ocp_selector ptp_ocp_art_sma_in[] = {
>> -	{ .name = "PPS1",	.value = 0x0001 },
>> -	{ .name = "10Mhz",	.value = 0x0008 },
>> +	{ .name = "PPS1",	.value = 0x0001,  .frequency = 1 },
>> +	{ .name = "10Mhz",	.value = 0x0008,  .frequency = 1000000 },
>> 	{ }
>> };
>>
>> static const struct ocp_selector ptp_ocp_art_sma_out[] = {
>> -	{ .name = "PHC",	.value = 0x0002 },
>> -	{ .name = "GNSS",	.value = 0x0004 },
>> -	{ .name = "10Mhz",	.value = 0x0010 },
>> +	{ .name = "PHC",	.value = 0x0002,  .frequency = 1 },
>> +	{ .name = "GNSS",	.value = 0x0004,  .frequency = 1 },
>> +	{ .name = "10Mhz",	.value = 0x0010,  .frequency = 10000000 },
>> 	{ }
>> };
>>
>> @@ -2283,22 +2296,34 @@ ptp_ocp_sma_fb_set_inputs(struct ptp_ocp *bp, int sma_nr, u32 val)
>> static void
>> ptp_ocp_sma_fb_init(struct ptp_ocp *bp)
>> {
>> +	struct dpll_pin_properties prop = {
> 
> Why don't you have this as static const outside the function?
> 

Because I'm changing label string for every pin. I cannot change it in
the const object.

> 
>> +		.label = NULL,
> 
> Pointless init.

Agree

>> +		.type = DPLL_PIN_TYPE_EXT,
>> +		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>> +		.freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
>> +		.freq_supported = ptp_ocp_sma_freq,
>> +
>> +	};
>> 	u32 reg;
>> 	int i;
>>
>> 	/* defaults */
>> +	for (i = 0; i < OCP_SMA_NUM; i++) {
>> +		bp->sma[i].default_fcn = i & 1;
>> +		bp->sma[i].dpll_prop = prop;
>> +		bp->sma[i].dpll_prop.label = bp->ptp_info.pin_config[i].name;
>> +	}
>> 	bp->sma[0].mode = SMA_MODE_IN;
>> 	bp->sma[1].mode = SMA_MODE_IN;
>> 	bp->sma[2].mode = SMA_MODE_OUT;
>> 	bp->sma[3].mode = SMA_MODE_OUT;
>> -	for (i = 0; i < 4; i++)
>> -		bp->sma[i].default_fcn = i & 1;
>> -
>> 	/* If no SMA1 map, the pin functions and directions are fixed. */
>> 	if (!bp->sma_map1) {
>> -		for (i = 0; i < 4; i++) {
>> +		for (i = 0; i < OCP_SMA_NUM; i++) {
>> 			bp->sma[i].fixed_fcn = true;
>> 			bp->sma[i].fixed_dir = true;
>> +			bp->sma[1].dpll_prop.capabilities &=
>> +				~DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
>> 		}
>> 		return;
>> 	}
>> @@ -2308,7 +2333,7 @@ ptp_ocp_sma_fb_init(struct ptp_ocp *bp)
>> 	 */
>> 	reg = ioread32(&bp->sma_map2->gpio2);
>> 	if (reg == 0xffffffff) {
>> -		for (i = 0; i < 4; i++)
>> +		for (i = 0; i < OCP_SMA_NUM; i++)
>> 			bp->sma[i].fixed_dir = true;
>> 	} else {
>> 		reg = ioread32(&bp->sma_map1->gpio1);
>> @@ -2330,7 +2355,7 @@ static const struct ocp_sma_op ocp_fb_sma_op = {
>> };
>>
>> static int
>> -ptp_ocp_fb_set_pins(struct ptp_ocp *bp)
>> +ptp_ocp_set_pins(struct ptp_ocp *bp)
>> {
>> 	struct ptp_pin_desc *config;
>> 	int i;
>> @@ -2397,16 +2422,16 @@ ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
>>
>> 	ptp_ocp_tod_init(bp);
>> 	ptp_ocp_nmea_out_init(bp);
>> -	ptp_ocp_sma_init(bp);
>> 	ptp_ocp_signal_init(bp);
>>
>> 	err = ptp_ocp_attr_group_add(bp, fb_timecard_groups);
>> 	if (err)
>> 		return err;
>>
>> -	err = ptp_ocp_fb_set_pins(bp);
>> +	err = ptp_ocp_set_pins(bp);
>> 	if (err)
>> 		return err;
>> +	ptp_ocp_sma_init(bp);
>>
>> 	return ptp_ocp_init_clock(bp);
>> }
>> @@ -2446,6 +2471,14 @@ ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data)
>> static void
>> ptp_ocp_art_sma_init(struct ptp_ocp *bp)
>> {
>> +	struct dpll_pin_properties prop = {
>> +		.label = NULL,
>> +		.type = DPLL_PIN_TYPE_EXT,
>> +		.capabilities = 0,
> 
> Same comment as to the similar prop struct above. Plus another pointless
> init here.
> 

Will remove pointless init.

> 
>> +		.freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
>> +		.freq_supported = ptp_ocp_sma_freq,
>> +
>> +	};
>> 	u32 reg;
>> 	int i;
>>
>> @@ -2460,16 +2493,16 @@ ptp_ocp_art_sma_init(struct ptp_ocp *bp)
>> 	bp->sma[2].default_fcn = 0x10;	/* OUT: 10Mhz */
>> 	bp->sma[3].default_fcn = 0x02;	/* OUT: PHC */
>>
>> -	/* If no SMA map, the pin functions and directions are fixed. */
>> -	if (!bp->art_sma) {
>> -		for (i = 0; i < 4; i++) {
>> +
>> +	for (i = 0; i < OCP_SMA_NUM; i++) {
>> +		/* If no SMA map, the pin functions and directions are fixed. */
>> +		bp->sma[i].dpll_prop = prop;
>> +		bp->sma[i].dpll_prop.label = bp->ptp_info.pin_config[i].name;
>> +		if (!bp->art_sma) {
>> 			bp->sma[i].fixed_fcn = true;
>> 			bp->sma[i].fixed_dir = true;
>> +			continue;
>> 		}
>> -		return;
>> -	}
>> -
>> -	for (i = 0; i < 4; i++) {
>> 		reg = ioread32(&bp->art_sma->map[i].gpio);
>>
>> 		switch (reg & 0xff) {
>> @@ -2480,9 +2513,13 @@ ptp_ocp_art_sma_init(struct ptp_ocp *bp)
>> 		case 1:
>> 		case 8:
>> 			bp->sma[i].mode = SMA_MODE_IN;
>> +			bp->sma[i].dpll_prop.capabilities =
>> +				DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
>> 			break;
>> 		default:
>> 			bp->sma[i].mode = SMA_MODE_OUT;
>> +			bp->sma[i].dpll_prop.capabilities =
>> +				DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
>> 			break;
>> 		}
>> 	}
>> @@ -2549,6 +2586,9 @@ ptp_ocp_art_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
>> 	/* Enable MAC serial port during initialisation */
>> 	iowrite32(1, &bp->board_config->mro50_serial_activate);
>>
>> +	err = ptp_ocp_set_pins(bp);
>> +	if (err)
>> +		return err;
>> 	ptp_ocp_sma_init(bp);
>>
>> 	err = ptp_ocp_attr_group_add(bp, art_timecard_groups);
>> @@ -2690,16 +2730,9 @@ sma4_show(struct device *dev, struct device_attribute *attr, char *buf)
>> }
>>
>> static int
>> -ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>> +ptp_ocp_sma_store_val(struct ptp_ocp *bp, int val, enum ptp_ocp_sma_mode mode, int sma_nr)
>> {
>> 	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>> -	enum ptp_ocp_sma_mode mode;
>> -	int val;
>> -
>> -	mode = sma->mode;
>> -	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>> -	if (val < 0)
>> -		return val;
>>
>> 	if (sma->fixed_dir && (mode != sma->mode || val & SMA_DISABLE))
>> 		return -EOPNOTSUPP;
>> @@ -2734,6 +2767,20 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>> 	return val;
>> }
>>
>> +static int
>> +ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>> +{
>> +	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>> +	enum ptp_ocp_sma_mode mode;
>> +	int val;
>> +
>> +	mode = sma->mode;
>> +	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>> +	if (val < 0)
>> +		return val;
>> +	return ptp_ocp_sma_store_val(bp, val, mode, sma_nr);
>> +}
>> +
>> static ssize_t
>> sma1_store(struct device *dev, struct device_attribute *attr,
>> 	   const char *buf, size_t count)
>> @@ -4172,12 +4219,148 @@ ptp_ocp_detach(struct ptp_ocp *bp)
>> 	device_unregister(&bp->dev);
>> }
>>
>> +static int ptp_ocp_dpll_lock_status_get(const struct dpll_device *dpll,
>> +					void *priv,
>> +					enum dpll_lock_status *status,
>> +					struct netlink_ext_ack *extack)
>> +{
>> +	struct ptp_ocp *bp = priv;
>> +	int sync;
>> +
>> +	sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
>> +	*status = sync ? DPLL_LOCK_STATUS_LOCKED : DPLL_LOCK_STATUS_UNLOCKED;
> 
> Does your device support event delivery in case of the status change?
> ice and mlx5 drivers do poll for changes in this area anyway. It's a
> part of this patchset. You should do the same if your device does
> not support events.
> 
> Could you please implement notifications using
> dpll_device_notify() for status change and dpll_pin_notify() for pin
> state change?
> 

We are working on implementation of interrupt-based notifications, 
that's why I didn't implement polling code. Hopefully it will be ready 
before the non-RFC patchset submission.

> 
>> +
>> +	return 0;
>> +}
>> +
>> +static int ptp_ocp_dpll_source_idx_get(const struct dpll_device *dpll,
>> +				       void *priv, u32 *idx,
>> +				       struct netlink_ext_ack *extack)
>> +{
>> +	struct ptp_ocp *bp = priv;
>> +
>> +	if (bp->pps_select) {
>> +		*idx = ioread32(&bp->pps_select->gpio1);
>> +		return 0;
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +static int ptp_ocp_dpll_mode_get(const struct dpll_device *dpll, void *priv,
>> +				 u32 *mode, struct netlink_ext_ack *extack)
>> +{
>> +	*mode = DPLL_MODE_AUTOMATIC;
>> +	return 0;
>> +}
>> +
>> +static bool ptp_ocp_dpll_mode_supported(const struct dpll_device *dpll,
>> +					void *priv, const enum dpll_mode mode,
>> +					struct netlink_ext_ack *extack)
>> +{
>> +	return mode == DPLL_MODE_AUTOMATIC;
>> +}
>> +
>> +static int ptp_ocp_dpll_direction_get(const struct dpll_pin *pin,
>> +				      void *pin_priv,
>> +				      const struct dpll_device *dpll,
>> +				      void *priv,
>> +				      enum dpll_pin_direction *direction,
>> +				      struct netlink_ext_ack *extack)
>> +{
>> +	struct ptp_ocp_sma_connector *sma = pin_priv;
>> +
>> +	*direction = sma->mode == SMA_MODE_IN ?
>> +				  DPLL_PIN_DIRECTION_SOURCE :
>> +				  DPLL_PIN_DIRECTION_OUTPUT;
>> +	return 0;
>> +}
>> +
>> +static int ptp_ocp_dpll_direction_set(const struct dpll_pin *pin,
>> +				      void *pin_priv,
>> +				      const struct dpll_device *dpll,
>> +				      void *dpll_priv,
>> +				      enum dpll_pin_direction direction,
>> +				      struct netlink_ext_ack *extack)
>> +{
>> +	struct ptp_ocp_sma_connector *sma = pin_priv;
>> +	struct ptp_ocp *bp = dpll_priv;
>> +	enum ptp_ocp_sma_mode mode;
>> +	int sma_nr = (sma - bp->sma);
>> +
>> +	if (sma->fixed_dir)
> 
> I believe that this is a pointless check as DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE
> is not set and therefore the check in dpll_pin_direction_set() will be
> true and -EOPNOTSUPP will be returned from there.
> Remove this.

Yep, will do it.

> 
>> +		return -EOPNOTSUPP;
>> +	mode = direction == DPLL_PIN_DIRECTION_SOURCE ?
>> +			    SMA_MODE_IN : SMA_MODE_OUT;
>> +	return ptp_ocp_sma_store_val(bp, 0, mode, sma_nr);
> 
> You need sma_nr just here. Why can't you change ptp_ocp_sma_store_val()
> to accept struct ptp_ocp_sma_connector * instead avoiding the need for
> tne sma_nr completely?
>

I wanted to add minimal changes to the driver, I will consider changing 
this as a separate net-next patch.

> 
>> +}
>> +
>> +static int ptp_ocp_dpll_frequency_set(const struct dpll_pin *pin,
>> +				      void *pin_priv,
>> +				      const struct dpll_device *dpll,
>> +				      void *dpll_priv, u64 frequency,
>> +				      struct netlink_ext_ack *extack)
>> +{
>> +	struct ptp_ocp_sma_connector *sma = pin_priv;
>> +	struct ptp_ocp *bp = dpll_priv;
>> +	const struct ocp_selector *tbl;
>> +	int sma_nr = (sma - bp->sma);
>> +	int val, i;
>> +
>> +	if (sma->fixed_fcn)
> 
> In that case, just fill up a single frequency in the properties,
> avoid this check-fail and let the dpll core handle it.
> 

Makes sense.

> 
>> +		return -EOPNOTSUPP;
>> +
>> +	tbl = bp->sma_op->tbl[sma->mode];
>> +	for (i = 0; tbl[i].name; i++)
>> +		if (tbl[i].frequency == frequency)
>> +			return ptp_ocp_sma_store_val(bp, val, sma->mode, sma_nr);
>> +	return -EINVAL;
>> +}
>> +
>> +static int ptp_ocp_dpll_frequency_get(const struct dpll_pin *pin,
>> +				      void *pin_priv,
>> +				      const struct dpll_device *dpll,
>> +				      void *dpll_priv, u64 *frequency,
>> +				      struct netlink_ext_ack *extack)
>> +{
>> +	struct ptp_ocp_sma_connector *sma = pin_priv;
>> +	struct ptp_ocp *bp = dpll_priv;
>> +	const struct ocp_selector *tbl;
>> +	int sma_nr = (sma - bp->sma);
> 
> 1) void "()"s here.
> 2) why don't you fill the sma_nr in struct ptp_ocp_sma_connector to make
>     this easier to follow? IDK, just a suggestion, take or leave.
> 
> Same applies to the the rest of similar occurances above.
>

Will do it, yes.

> 
>> +	u32 val;
>> +	int i;
>> +
>> +	val = bp->sma_op->get(bp, sma_nr);
>> +	tbl = bp->sma_op->tbl[sma->mode];
>> +	for (i = 0; tbl[i].name; i++)
>> +		if (val == tbl[i].value) {
>> +			*frequency = tbl[i].frequency;
>> +			return 0;
>> +		}
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +static const struct dpll_device_ops dpll_ops = {
>> +	.lock_status_get = ptp_ocp_dpll_lock_status_get,
>> +	.source_pin_idx_get = ptp_ocp_dpll_source_idx_get,
> 
> This op is a leftover, in dpll core it is not called. This was removed
> and agreed that drivers should implement state_on_dpll_get() op for pins
> to see which one is connected.
> 
> Please fix here and remove the leftover from DPLL patch #2 as well.
> 
> 
>> +	.mode_get = ptp_ocp_dpll_mode_get,
>> +	.mode_supported = ptp_ocp_dpll_mode_supported,
>> +};
>> +
>> +static const struct dpll_pin_ops dpll_pins_ops = {
>> +	.frequency_get = ptp_ocp_dpll_frequency_get,
>> +	.frequency_set = ptp_ocp_dpll_frequency_set,
>> +	.direction_get = ptp_ocp_dpll_direction_get,
>> +	.direction_set = ptp_ocp_dpll_direction_set,
>> +};
>> +
>> static int
>> ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>> {
>> 	struct devlink *devlink;
>> 	struct ptp_ocp *bp;
>> -	int err;
>> +	int err, i;
>> +	u64 clkid;
>>
>> 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev->dev);
>> 	if (!devlink) {
>> @@ -4227,8 +4410,39 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>>
>> 	ptp_ocp_info(bp);
>> 	devlink_register(devlink);
>> -	return 0;
>>
>> +	clkid = pci_get_dsn(pdev);
>> +	bp->dpll = dpll_device_get(clkid, 0, THIS_MODULE);
> 
> I suggested this the last time, but again: Could you please:
> 1) rename dpll_device_get to __dpll_device_get
> 2) introduce dpll_device_get as a macro filling up THIS_MODULE
> 
> Then drivers will just call always:
> bp->dpll = dpll_device_get(clkid, 0);
> and the macro will fillup the module automatically.
> 
> Please do the same for dpll_pin_get()
> 

Not sure why I have missed this part, but sure will do it.

> 
>> +	if (IS_ERR(bp->dpll)) {
>> +		dev_err(&pdev->dev, "dpll_device_alloc failed\n");
> 
> You need to fix your error path to call devlink_unregister() in this
> case.
> 
> 
>> +		goto out;
>> +	}
>> +
>> +	err = dpll_device_register(bp->dpll, DPLL_TYPE_PPS, &dpll_ops, bp, &pdev->dev);
>> +	if (err)
> 
> You need to fix your error path to call dpll_device_put() in this
> case.
> 

Ok, I'll re-check and update the error path for the next version.

> 
>> +		goto out;
>> +
>> +	for (i = 0; i < OCP_SMA_NUM; i++) {
>> +		bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &bp->sma[i].dpll_prop);
>> +		if (IS_ERR(bp->sma[i].dpll_pin))
>> +			goto out_dpll;
>> +
>> +		err = dpll_pin_register(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops,
>> +					&bp->sma[i], NULL);
>> +		if (err) {
>> +			dpll_pin_put(bp->sma[i].dpll_pin);
>> +			goto out_dpll;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +out_dpll:
>> +	while (i) {
>> +		--i;
> 
> 	while (i--) {
> 	?
> 
>> +		dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, &bp->sma[i]);
>> +		dpll_pin_put(bp->sma[i].dpll_pin);
>> +	}
>> +	dpll_device_put(bp->dpll);
>> out:
>> 	ptp_ocp_detach(bp);
>> out_disable:
>> @@ -4243,7 +4457,16 @@ ptp_ocp_remove(struct pci_dev *pdev)
>> {
>> 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
>> 	struct devlink *devlink = priv_to_devlink(bp);
>> +	int i;
>>
>> +	for (i = 0; i < OCP_SMA_NUM; i++) {
>> +		if (bp->sma[i].dpll_pin) {
> 
> Remove this pointless check. It is always true.

Agree.

> 
> 
>> +			dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, bp);
>> +			dpll_pin_put(bp->sma[i].dpll_pin);
>> +		}
>> +	}
>> +	dpll_device_unregister(bp->dpll, &dpll_ops, bp);
>> +	dpll_device_put(bp->dpll);
>> 	devlink_unregister(devlink);
>> 	ptp_ocp_detach(bp);
>> 	pci_disable_device(pdev);
>> -- 
>> 2.34.1
>>
Jiri Pirko May 6, 2023, 12:42 p.m. UTC | #3
Fri, May 05, 2023 at 03:43:08PM CEST, vadim.fedorenko@linux.dev wrote:
>On 04/05/2023 10:27, Jiri Pirko wrote:
>> Fri, Apr 28, 2023 at 02:20:07AM CEST, vadfed@meta.com wrote:
>> > From: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>> > 
>> > Implement basic DPLL operations in ptp_ocp driver as the
>> > simplest example of using new subsystem.
>> > 
>> > Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
>> > ---
>> > drivers/ptp/Kconfig   |   1 +
>> > drivers/ptp/ptp_ocp.c | 327 +++++++++++++++++++++++++++++++++++-------
>> > 2 files changed, 276 insertions(+), 52 deletions(-)
>> > 
>> > diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
>> > index b00201d81313..e3575c2e34dc 100644
>> > --- a/drivers/ptp/Kconfig
>> > +++ b/drivers/ptp/Kconfig
>> > @@ -177,6 +177,7 @@ config PTP_1588_CLOCK_OCP
>> > 	depends on COMMON_CLK
>> > 	select NET_DEVLINK
>> > 	select CRC16
>> > +	select DPLL
>> > 	help
>> > 	  This driver adds support for an OpenCompute time card.
>> > 
>> > diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
>> > index 2b63f3487645..100e5da0aeb3 100644
>> > --- a/drivers/ptp/ptp_ocp.c
>> > +++ b/drivers/ptp/ptp_ocp.c
>> > @@ -23,6 +23,7 @@
>> > #include <linux/mtd/mtd.h>
>> > #include <linux/nvmem-consumer.h>
>> > #include <linux/crc16.h>
>> > +#include <linux/dpll.h>
>> > 
>> > #define PCI_VENDOR_ID_FACEBOOK			0x1d9b
>> > #define PCI_DEVICE_ID_FACEBOOK_TIMECARD		0x0400
>> > @@ -261,12 +262,21 @@ enum ptp_ocp_sma_mode {
>> > 	SMA_MODE_OUT,
>> > };
>> > 
>> > +static struct dpll_pin_frequency ptp_ocp_sma_freq[] = {
>> 
>> const
>
>Forgot about this one, will change it.
>
>> 
>> > +	DPLL_PIN_FREQUENCY_1PPS,
>> > +	DPLL_PIN_FREQUENCY_10MHZ,
>> > +	DPLL_PIN_FREQUENCY_IRIG_B,
>> > +	DPLL_PIN_FREQUENCY_DCF77,
>> > +};
>> > +
>> > struct ptp_ocp_sma_connector {
>> > 	enum	ptp_ocp_sma_mode mode;
>> > 	bool	fixed_fcn;
>> > 	bool	fixed_dir;
>> > 	bool	disabled;
>> > 	u8	default_fcn;
>> > +	struct dpll_pin		   *dpll_pin;
>> > +	struct dpll_pin_properties dpll_prop;
>> > };
>> > 
>> > struct ocp_attr_group {
>> > @@ -295,6 +305,7 @@ struct ptp_ocp_serial_port {
>> > 
>> > #define OCP_BOARD_ID_LEN		13
>> > #define OCP_SERIAL_LEN			6
>> > +#define OCP_SMA_NUM			4
>> > 
>> > struct ptp_ocp {
>> > 	struct pci_dev		*pdev;
>> > @@ -351,8 +362,9 @@ struct ptp_ocp {
>> > 	u32			ts_window_adjust;
>> > 	u64			fw_cap;
>> > 	struct ptp_ocp_signal	signal[4];
>> > -	struct ptp_ocp_sma_connector sma[4];
>> > +	struct ptp_ocp_sma_connector sma[OCP_SMA_NUM];
>> > 	const struct ocp_sma_op *sma_op;
>> > +	struct dpll_device *dpll;
>> > };
>> > 
>> > #define OCP_REQ_TIMESTAMP	BIT(0)
>> > @@ -836,6 +848,7 @@ static DEFINE_IDR(ptp_ocp_idr);
>> > struct ocp_selector {
>> > 	const char *name;
>> > 	int value;
>> > +	u64 frequency;
>> > };
>> > 
>> > static const struct ocp_selector ptp_ocp_clock[] = {
>> > @@ -856,31 +869,31 @@ static const struct ocp_selector ptp_ocp_clock[] = {
>> > #define SMA_SELECT_MASK		GENMASK(14, 0)
>> > 
>> > static const struct ocp_selector ptp_ocp_sma_in[] = {
>> > -	{ .name = "10Mhz",	.value = 0x0000 },
>> > -	{ .name = "PPS1",	.value = 0x0001 },
>> > -	{ .name = "PPS2",	.value = 0x0002 },
>> > -	{ .name = "TS1",	.value = 0x0004 },
>> > -	{ .name = "TS2",	.value = 0x0008 },
>> > -	{ .name = "IRIG",	.value = 0x0010 },
>> > -	{ .name = "DCF",	.value = 0x0020 },
>> > -	{ .name = "TS3",	.value = 0x0040 },
>> > -	{ .name = "TS4",	.value = 0x0080 },
>> > -	{ .name = "FREQ1",	.value = 0x0100 },
>> > -	{ .name = "FREQ2",	.value = 0x0200 },
>> > -	{ .name = "FREQ3",	.value = 0x0400 },
>> > -	{ .name = "FREQ4",	.value = 0x0800 },
>> > -	{ .name = "None",	.value = SMA_DISABLE },
>> > +	{ .name = "10Mhz",  .value = 0x0000,      .frequency = 10000000 },
>> > +	{ .name = "PPS1",   .value = 0x0001,      .frequency = 1 },
>> > +	{ .name = "PPS2",   .value = 0x0002,      .frequency = 1 },
>> > +	{ .name = "TS1",    .value = 0x0004,      .frequency = 0 },
>> > +	{ .name = "TS2",    .value = 0x0008,      .frequency = 0 },
>> > +	{ .name = "IRIG",   .value = 0x0010,      .frequency = 10000 },
>> > +	{ .name = "DCF",    .value = 0x0020,      .frequency = 77500 },
>> > +	{ .name = "TS3",    .value = 0x0040,      .frequency = 0 },
>> > +	{ .name = "TS4",    .value = 0x0080,      .frequency = 0 },
>> > +	{ .name = "FREQ1",  .value = 0x0100,      .frequency = 0 },
>> > +	{ .name = "FREQ2",  .value = 0x0200,      .frequency = 0 },
>> > +	{ .name = "FREQ3",  .value = 0x0400,      .frequency = 0 },
>> > +	{ .name = "FREQ4",  .value = 0x0800,      .frequency = 0 },
>> > +	{ .name = "None",   .value = SMA_DISABLE, .frequency = 0 },
>> > 	{ }
>> > };
>> > 
>> > static const struct ocp_selector ptp_ocp_sma_out[] = {
>> > -	{ .name = "10Mhz",	.value = 0x0000 },
>> > -	{ .name = "PHC",	.value = 0x0001 },
>> > -	{ .name = "MAC",	.value = 0x0002 },
>> > -	{ .name = "GNSS1",	.value = 0x0004 },
>> > -	{ .name = "GNSS2",	.value = 0x0008 },
>> > -	{ .name = "IRIG",	.value = 0x0010 },
>> > -	{ .name = "DCF",	.value = 0x0020 },
>> > +	{ .name = "10Mhz",	.value = 0x0000,  .frequency = 10000000 },
>> > +	{ .name = "PHC",	.value = 0x0001,  .frequency = 1 },
>> > +	{ .name = "MAC",	.value = 0x0002,  .frequency = 1 },
>> > +	{ .name = "GNSS1",	.value = 0x0004,  .frequency = 1 },
>> > +	{ .name = "GNSS2",	.value = 0x0008,  .frequency = 1 },
>> > +	{ .name = "IRIG",	.value = 0x0010,  .frequency = 10000 },
>> > +	{ .name = "DCF",	.value = 0x0020,  .frequency = 77000 },
>> > 	{ .name = "GEN1",	.value = 0x0040 },
>> > 	{ .name = "GEN2",	.value = 0x0080 },
>> > 	{ .name = "GEN3",	.value = 0x0100 },
>> > @@ -891,15 +904,15 @@ static const struct ocp_selector ptp_ocp_sma_out[] = {
>> > };
>> > 
>> > static const struct ocp_selector ptp_ocp_art_sma_in[] = {
>> > -	{ .name = "PPS1",	.value = 0x0001 },
>> > -	{ .name = "10Mhz",	.value = 0x0008 },
>> > +	{ .name = "PPS1",	.value = 0x0001,  .frequency = 1 },
>> > +	{ .name = "10Mhz",	.value = 0x0008,  .frequency = 1000000 },
>> > 	{ }
>> > };
>> > 
>> > static const struct ocp_selector ptp_ocp_art_sma_out[] = {
>> > -	{ .name = "PHC",	.value = 0x0002 },
>> > -	{ .name = "GNSS",	.value = 0x0004 },
>> > -	{ .name = "10Mhz",	.value = 0x0010 },
>> > +	{ .name = "PHC",	.value = 0x0002,  .frequency = 1 },
>> > +	{ .name = "GNSS",	.value = 0x0004,  .frequency = 1 },
>> > +	{ .name = "10Mhz",	.value = 0x0010,  .frequency = 10000000 },
>> > 	{ }
>> > };
>> > 
>> > @@ -2283,22 +2296,34 @@ ptp_ocp_sma_fb_set_inputs(struct ptp_ocp *bp, int sma_nr, u32 val)
>> > static void
>> > ptp_ocp_sma_fb_init(struct ptp_ocp *bp)
>> > {
>> > +	struct dpll_pin_properties prop = {
>> 
>> Why don't you have this as static const outside the function?
>> 
>
>Because I'm changing label string for every pin. I cannot change it in
>the const object.

No you don't. You just copy prop to bp->sma[i].dpll_prop. Prop does not
change.


>
>> 
>> > +		.label = NULL,
>> 
>> Pointless init.
>
>Agree
>
>> > +		.type = DPLL_PIN_TYPE_EXT,
>> > +		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>> > +		.freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
>> > +		.freq_supported = ptp_ocp_sma_freq,
>> > +
>> > +	};
>> > 	u32 reg;
>> > 	int i;
>> > 
>> > 	/* defaults */
>> > +	for (i = 0; i < OCP_SMA_NUM; i++) {
>> > +		bp->sma[i].default_fcn = i & 1;
>> > +		bp->sma[i].dpll_prop = prop;
>> > +		bp->sma[i].dpll_prop.label = bp->ptp_info.pin_config[i].name;
>> > +	}
>> > 	bp->sma[0].mode = SMA_MODE_IN;
>> > 	bp->sma[1].mode = SMA_MODE_IN;
>> > 	bp->sma[2].mode = SMA_MODE_OUT;
>> > 	bp->sma[3].mode = SMA_MODE_OUT;
>> > -	for (i = 0; i < 4; i++)
>> > -		bp->sma[i].default_fcn = i & 1;
>> > -
>> > 	/* If no SMA1 map, the pin functions and directions are fixed. */
>> > 	if (!bp->sma_map1) {
>> > -		for (i = 0; i < 4; i++) {
>> > +		for (i = 0; i < OCP_SMA_NUM; i++) {
>> > 			bp->sma[i].fixed_fcn = true;
>> > 			bp->sma[i].fixed_dir = true;
>> > +			bp->sma[1].dpll_prop.capabilities &=
>> > +				~DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
>> > 		}
>> > 		return;
>> > 	}
>> > @@ -2308,7 +2333,7 @@ ptp_ocp_sma_fb_init(struct ptp_ocp *bp)
>> > 	 */
>> > 	reg = ioread32(&bp->sma_map2->gpio2);
>> > 	if (reg == 0xffffffff) {
>> > -		for (i = 0; i < 4; i++)
>> > +		for (i = 0; i < OCP_SMA_NUM; i++)
>> > 			bp->sma[i].fixed_dir = true;
>> > 	} else {
>> > 		reg = ioread32(&bp->sma_map1->gpio1);
>> > @@ -2330,7 +2355,7 @@ static const struct ocp_sma_op ocp_fb_sma_op = {
>> > };
>> > 
>> > static int
>> > -ptp_ocp_fb_set_pins(struct ptp_ocp *bp)
>> > +ptp_ocp_set_pins(struct ptp_ocp *bp)
>> > {
>> > 	struct ptp_pin_desc *config;
>> > 	int i;
>> > @@ -2397,16 +2422,16 @@ ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
>> > 
>> > 	ptp_ocp_tod_init(bp);
>> > 	ptp_ocp_nmea_out_init(bp);
>> > -	ptp_ocp_sma_init(bp);
>> > 	ptp_ocp_signal_init(bp);
>> > 
>> > 	err = ptp_ocp_attr_group_add(bp, fb_timecard_groups);
>> > 	if (err)
>> > 		return err;
>> > 
>> > -	err = ptp_ocp_fb_set_pins(bp);
>> > +	err = ptp_ocp_set_pins(bp);
>> > 	if (err)
>> > 		return err;
>> > +	ptp_ocp_sma_init(bp);
>> > 
>> > 	return ptp_ocp_init_clock(bp);
>> > }
>> > @@ -2446,6 +2471,14 @@ ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data)
>> > static void
>> > ptp_ocp_art_sma_init(struct ptp_ocp *bp)
>> > {
>> > +	struct dpll_pin_properties prop = {
>> > +		.label = NULL,
>> > +		.type = DPLL_PIN_TYPE_EXT,
>> > +		.capabilities = 0,
>> 
>> Same comment as to the similar prop struct above. Plus another pointless
>> init here.
>> 
>
>Will remove pointless init.
>
>> 
>> > +		.freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
>> > +		.freq_supported = ptp_ocp_sma_freq,
>> > +
>> > +	};
>> > 	u32 reg;
>> > 	int i;
>> > 
>> > @@ -2460,16 +2493,16 @@ ptp_ocp_art_sma_init(struct ptp_ocp *bp)
>> > 	bp->sma[2].default_fcn = 0x10;	/* OUT: 10Mhz */
>> > 	bp->sma[3].default_fcn = 0x02;	/* OUT: PHC */
>> > 
>> > -	/* If no SMA map, the pin functions and directions are fixed. */
>> > -	if (!bp->art_sma) {
>> > -		for (i = 0; i < 4; i++) {
>> > +
>> > +	for (i = 0; i < OCP_SMA_NUM; i++) {
>> > +		/* If no SMA map, the pin functions and directions are fixed. */
>> > +		bp->sma[i].dpll_prop = prop;
>> > +		bp->sma[i].dpll_prop.label = bp->ptp_info.pin_config[i].name;
>> > +		if (!bp->art_sma) {
>> > 			bp->sma[i].fixed_fcn = true;
>> > 			bp->sma[i].fixed_dir = true;
>> > +			continue;
>> > 		}
>> > -		return;
>> > -	}
>> > -
>> > -	for (i = 0; i < 4; i++) {
>> > 		reg = ioread32(&bp->art_sma->map[i].gpio);
>> > 
>> > 		switch (reg & 0xff) {
>> > @@ -2480,9 +2513,13 @@ ptp_ocp_art_sma_init(struct ptp_ocp *bp)
>> > 		case 1:
>> > 		case 8:
>> > 			bp->sma[i].mode = SMA_MODE_IN;
>> > +			bp->sma[i].dpll_prop.capabilities =
>> > +				DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
>> > 			break;
>> > 		default:
>> > 			bp->sma[i].mode = SMA_MODE_OUT;
>> > +			bp->sma[i].dpll_prop.capabilities =
>> > +				DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
>> > 			break;
>> > 		}
>> > 	}
>> > @@ -2549,6 +2586,9 @@ ptp_ocp_art_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
>> > 	/* Enable MAC serial port during initialisation */
>> > 	iowrite32(1, &bp->board_config->mro50_serial_activate);
>> > 
>> > +	err = ptp_ocp_set_pins(bp);
>> > +	if (err)
>> > +		return err;
>> > 	ptp_ocp_sma_init(bp);
>> > 
>> > 	err = ptp_ocp_attr_group_add(bp, art_timecard_groups);
>> > @@ -2690,16 +2730,9 @@ sma4_show(struct device *dev, struct device_attribute *attr, char *buf)
>> > }
>> > 
>> > static int
>> > -ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>> > +ptp_ocp_sma_store_val(struct ptp_ocp *bp, int val, enum ptp_ocp_sma_mode mode, int sma_nr)
>> > {
>> > 	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>> > -	enum ptp_ocp_sma_mode mode;
>> > -	int val;
>> > -
>> > -	mode = sma->mode;
>> > -	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>> > -	if (val < 0)
>> > -		return val;
>> > 
>> > 	if (sma->fixed_dir && (mode != sma->mode || val & SMA_DISABLE))
>> > 		return -EOPNOTSUPP;
>> > @@ -2734,6 +2767,20 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>> > 	return val;
>> > }
>> > 
>> > +static int
>> > +ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>> > +{
>> > +	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>> > +	enum ptp_ocp_sma_mode mode;
>> > +	int val;
>> > +
>> > +	mode = sma->mode;
>> > +	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>> > +	if (val < 0)
>> > +		return val;
>> > +	return ptp_ocp_sma_store_val(bp, val, mode, sma_nr);
>> > +}
>> > +
>> > static ssize_t
>> > sma1_store(struct device *dev, struct device_attribute *attr,
>> > 	   const char *buf, size_t count)
>> > @@ -4172,12 +4219,148 @@ ptp_ocp_detach(struct ptp_ocp *bp)
>> > 	device_unregister(&bp->dev);
>> > }
>> > 
>> > +static int ptp_ocp_dpll_lock_status_get(const struct dpll_device *dpll,
>> > +					void *priv,
>> > +					enum dpll_lock_status *status,
>> > +					struct netlink_ext_ack *extack)
>> > +{
>> > +	struct ptp_ocp *bp = priv;
>> > +	int sync;
>> > +
>> > +	sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
>> > +	*status = sync ? DPLL_LOCK_STATUS_LOCKED : DPLL_LOCK_STATUS_UNLOCKED;
>> 
>> Does your device support event delivery in case of the status change?
>> ice and mlx5 drivers do poll for changes in this area anyway. It's a
>> part of this patchset. You should do the same if your device does
>> not support events.
>> 
>> Could you please implement notifications using
>> dpll_device_notify() for status change and dpll_pin_notify() for pin
>> state change?
>> 
>
>We are working on implementation of interrupt-based notifications, that's why
>I didn't implement polling code. Hopefully it will be ready before the
>non-RFC patchset submission.

Do polling implementation if not and replace by interrupt driver later.
Point is, there have to be notifications from day 1.


>
>> 
>> > +
>> > +	return 0;
>> > +}
>> > +
>> > +static int ptp_ocp_dpll_source_idx_get(const struct dpll_device *dpll,
>> > +				       void *priv, u32 *idx,
>> > +				       struct netlink_ext_ack *extack)
>> > +{
>> > +	struct ptp_ocp *bp = priv;
>> > +
>> > +	if (bp->pps_select) {
>> > +		*idx = ioread32(&bp->pps_select->gpio1);
>> > +		return 0;
>> > +	}
>> > +	return -EINVAL;
>> > +}
>> > +
>> > +static int ptp_ocp_dpll_mode_get(const struct dpll_device *dpll, void *priv,
>> > +				 u32 *mode, struct netlink_ext_ack *extack)
>> > +{
>> > +	*mode = DPLL_MODE_AUTOMATIC;
>> > +	return 0;
>> > +}
>> > +
>> > +static bool ptp_ocp_dpll_mode_supported(const struct dpll_device *dpll,
>> > +					void *priv, const enum dpll_mode mode,
>> > +					struct netlink_ext_ack *extack)
>> > +{
>> > +	return mode == DPLL_MODE_AUTOMATIC;
>> > +}
>> > +
>> > +static int ptp_ocp_dpll_direction_get(const struct dpll_pin *pin,
>> > +				      void *pin_priv,
>> > +				      const struct dpll_device *dpll,
>> > +				      void *priv,
>> > +				      enum dpll_pin_direction *direction,
>> > +				      struct netlink_ext_ack *extack)
>> > +{
>> > +	struct ptp_ocp_sma_connector *sma = pin_priv;
>> > +
>> > +	*direction = sma->mode == SMA_MODE_IN ?
>> > +				  DPLL_PIN_DIRECTION_SOURCE :
>> > +				  DPLL_PIN_DIRECTION_OUTPUT;
>> > +	return 0;
>> > +}
>> > +
>> > +static int ptp_ocp_dpll_direction_set(const struct dpll_pin *pin,
>> > +				      void *pin_priv,
>> > +				      const struct dpll_device *dpll,
>> > +				      void *dpll_priv,
>> > +				      enum dpll_pin_direction direction,
>> > +				      struct netlink_ext_ack *extack)
>> > +{
>> > +	struct ptp_ocp_sma_connector *sma = pin_priv;
>> > +	struct ptp_ocp *bp = dpll_priv;
>> > +	enum ptp_ocp_sma_mode mode;
>> > +	int sma_nr = (sma - bp->sma);
>> > +
>> > +	if (sma->fixed_dir)
>> 
>> I believe that this is a pointless check as DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE
>> is not set and therefore the check in dpll_pin_direction_set() will be
>> true and -EOPNOTSUPP will be returned from there.
>> Remove this.
>
>Yep, will do it.
>
>> 
>> > +		return -EOPNOTSUPP;
>> > +	mode = direction == DPLL_PIN_DIRECTION_SOURCE ?
>> > +			    SMA_MODE_IN : SMA_MODE_OUT;
>> > +	return ptp_ocp_sma_store_val(bp, 0, mode, sma_nr);
>> 
>> You need sma_nr just here. Why can't you change ptp_ocp_sma_store_val()
>> to accept struct ptp_ocp_sma_connector * instead avoiding the need for
>> tne sma_nr completely?
>> 
>
>I wanted to add minimal changes to the driver, I will consider changing this
>as a separate net-next patch.

Hmm, you do change ptp_ocp_sma_store_val() here anyway. Could be an
extra patch in this patchset before this one if you want to have it
separate.


>
>> 
>> > +}
>> > +
>> > +static int ptp_ocp_dpll_frequency_set(const struct dpll_pin *pin,
>> > +				      void *pin_priv,
>> > +				      const struct dpll_device *dpll,
>> > +				      void *dpll_priv, u64 frequency,
>> > +				      struct netlink_ext_ack *extack)
>> > +{
>> > +	struct ptp_ocp_sma_connector *sma = pin_priv;
>> > +	struct ptp_ocp *bp = dpll_priv;
>> > +	const struct ocp_selector *tbl;
>> > +	int sma_nr = (sma - bp->sma);
>> > +	int val, i;
>> > +
>> > +	if (sma->fixed_fcn)
>> 
>> In that case, just fill up a single frequency in the properties,
>> avoid this check-fail and let the dpll core handle it.
>> 
>
>Makes sense.
>
>> 
>> > +		return -EOPNOTSUPP;
>> > +
>> > +	tbl = bp->sma_op->tbl[sma->mode];
>> > +	for (i = 0; tbl[i].name; i++)
>> > +		if (tbl[i].frequency == frequency)
>> > +			return ptp_ocp_sma_store_val(bp, val, sma->mode, sma_nr);
>> > +	return -EINVAL;
>> > +}
>> > +
>> > +static int ptp_ocp_dpll_frequency_get(const struct dpll_pin *pin,
>> > +				      void *pin_priv,
>> > +				      const struct dpll_device *dpll,
>> > +				      void *dpll_priv, u64 *frequency,
>> > +				      struct netlink_ext_ack *extack)
>> > +{
>> > +	struct ptp_ocp_sma_connector *sma = pin_priv;
>> > +	struct ptp_ocp *bp = dpll_priv;
>> > +	const struct ocp_selector *tbl;
>> > +	int sma_nr = (sma - bp->sma);
>> 
>> 1) void "()"s here.
>> 2) why don't you fill the sma_nr in struct ptp_ocp_sma_connector to make
>>     this easier to follow? IDK, just a suggestion, take or leave.
>> 
>> Same applies to the the rest of similar occurances above.
>> 
>
>Will do it, yes.
>
>> 
>> > +	u32 val;
>> > +	int i;
>> > +
>> > +	val = bp->sma_op->get(bp, sma_nr);
>> > +	tbl = bp->sma_op->tbl[sma->mode];
>> > +	for (i = 0; tbl[i].name; i++)
>> > +		if (val == tbl[i].value) {
>> > +			*frequency = tbl[i].frequency;
>> > +			return 0;
>> > +		}
>> > +
>> > +	return -EINVAL;
>> > +}
>> > +
>> > +static const struct dpll_device_ops dpll_ops = {
>> > +	.lock_status_get = ptp_ocp_dpll_lock_status_get,
>> > +	.source_pin_idx_get = ptp_ocp_dpll_source_idx_get,
>> 
>> This op is a leftover, in dpll core it is not called. This was removed
>> and agreed that drivers should implement state_on_dpll_get() op for pins
>> to see which one is connected.
>> 
>> Please fix here and remove the leftover from DPLL patch #2 as well.

Did you miss this by any chance?


>> 
>> 
>> > +	.mode_get = ptp_ocp_dpll_mode_get,
>> > +	.mode_supported = ptp_ocp_dpll_mode_supported,
>> > +};
>> > +
>> > +static const struct dpll_pin_ops dpll_pins_ops = {
>> > +	.frequency_get = ptp_ocp_dpll_frequency_get,
>> > +	.frequency_set = ptp_ocp_dpll_frequency_set,
>> > +	.direction_get = ptp_ocp_dpll_direction_get,
>> > +	.direction_set = ptp_ocp_dpll_direction_set,
>> > +};
>> > +
>> > static int
>> > ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>> > {
>> > 	struct devlink *devlink;
>> > 	struct ptp_ocp *bp;
>> > -	int err;
>> > +	int err, i;
>> > +	u64 clkid;
>> > 
>> > 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev->dev);
>> > 	if (!devlink) {
>> > @@ -4227,8 +4410,39 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>> > 
>> > 	ptp_ocp_info(bp);
>> > 	devlink_register(devlink);
>> > -	return 0;
>> > 
>> > +	clkid = pci_get_dsn(pdev);
>> > +	bp->dpll = dpll_device_get(clkid, 0, THIS_MODULE);
>> 
>> I suggested this the last time, but again: Could you please:
>> 1) rename dpll_device_get to __dpll_device_get
>> 2) introduce dpll_device_get as a macro filling up THIS_MODULE
>> 
>> Then drivers will just call always:
>> bp->dpll = dpll_device_get(clkid, 0);
>> and the macro will fillup the module automatically.
>> 
>> Please do the same for dpll_pin_get()
>> 
>
>Not sure why I have missed this part, but sure will do it.

Awesome.


>
>> 
>> > +	if (IS_ERR(bp->dpll)) {
>> > +		dev_err(&pdev->dev, "dpll_device_alloc failed\n");
>> 
>> You need to fix your error path to call devlink_unregister() in this
>> case.
>> 
>> 
>> > +		goto out;
>> > +	}
>> > +
>> > +	err = dpll_device_register(bp->dpll, DPLL_TYPE_PPS, &dpll_ops, bp, &pdev->dev);
>> > +	if (err)
>> 
>> You need to fix your error path to call dpll_device_put() in this
>> case.
>> 
>
>Ok, I'll re-check and update the error path for the next version.
>
>> 
>> > +		goto out;
>> > +
>> > +	for (i = 0; i < OCP_SMA_NUM; i++) {
>> > +		bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &bp->sma[i].dpll_prop);
>> > +		if (IS_ERR(bp->sma[i].dpll_pin))
>> > +			goto out_dpll;
>> > +
>> > +		err = dpll_pin_register(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops,
>> > +					&bp->sma[i], NULL);
>> > +		if (err) {
>> > +			dpll_pin_put(bp->sma[i].dpll_pin);
>> > +			goto out_dpll;
>> > +		}
>> > +	}
>> > +
>> > +	return 0;
>> > +out_dpll:
>> > +	while (i) {
>> > +		--i;
>> 
>> 	while (i--) {
>> 	?
>> 
>> > +		dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, &bp->sma[i]);
>> > +		dpll_pin_put(bp->sma[i].dpll_pin);
>> > +	}
>> > +	dpll_device_put(bp->dpll);
>> > out:
>> > 	ptp_ocp_detach(bp);
>> > out_disable:
>> > @@ -4243,7 +4457,16 @@ ptp_ocp_remove(struct pci_dev *pdev)
>> > {
>> > 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
>> > 	struct devlink *devlink = priv_to_devlink(bp);
>> > +	int i;
>> > 
>> > +	for (i = 0; i < OCP_SMA_NUM; i++) {
>> > +		if (bp->sma[i].dpll_pin) {
>> 
>> Remove this pointless check. It is always true.
>
>Agree.
>
>> 
>> 
>> > +			dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, bp);
>> > +			dpll_pin_put(bp->sma[i].dpll_pin);
>> > +		}
>> > +	}
>> > +	dpll_device_unregister(bp->dpll, &dpll_ops, bp);
>> > +	dpll_device_put(bp->dpll);
>> > 	devlink_unregister(devlink);
>> > 	ptp_ocp_detach(bp);
>> > 	pci_disable_device(pdev);
>> > -- 
>> > 2.34.1
>> > 
>
diff mbox series

Patch

diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index b00201d81313..e3575c2e34dc 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -177,6 +177,7 @@  config PTP_1588_CLOCK_OCP
 	depends on COMMON_CLK
 	select NET_DEVLINK
 	select CRC16
+	select DPLL
 	help
 	  This driver adds support for an OpenCompute time card.
 
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index 2b63f3487645..100e5da0aeb3 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -23,6 +23,7 @@ 
 #include <linux/mtd/mtd.h>
 #include <linux/nvmem-consumer.h>
 #include <linux/crc16.h>
+#include <linux/dpll.h>
 
 #define PCI_VENDOR_ID_FACEBOOK			0x1d9b
 #define PCI_DEVICE_ID_FACEBOOK_TIMECARD		0x0400
@@ -261,12 +262,21 @@  enum ptp_ocp_sma_mode {
 	SMA_MODE_OUT,
 };
 
+static struct dpll_pin_frequency ptp_ocp_sma_freq[] = {
+	DPLL_PIN_FREQUENCY_1PPS,
+	DPLL_PIN_FREQUENCY_10MHZ,
+	DPLL_PIN_FREQUENCY_IRIG_B,
+	DPLL_PIN_FREQUENCY_DCF77,
+};
+
 struct ptp_ocp_sma_connector {
 	enum	ptp_ocp_sma_mode mode;
 	bool	fixed_fcn;
 	bool	fixed_dir;
 	bool	disabled;
 	u8	default_fcn;
+	struct dpll_pin		   *dpll_pin;
+	struct dpll_pin_properties dpll_prop;
 };
 
 struct ocp_attr_group {
@@ -295,6 +305,7 @@  struct ptp_ocp_serial_port {
 
 #define OCP_BOARD_ID_LEN		13
 #define OCP_SERIAL_LEN			6
+#define OCP_SMA_NUM			4
 
 struct ptp_ocp {
 	struct pci_dev		*pdev;
@@ -351,8 +362,9 @@  struct ptp_ocp {
 	u32			ts_window_adjust;
 	u64			fw_cap;
 	struct ptp_ocp_signal	signal[4];
-	struct ptp_ocp_sma_connector sma[4];
+	struct ptp_ocp_sma_connector sma[OCP_SMA_NUM];
 	const struct ocp_sma_op *sma_op;
+	struct dpll_device *dpll;
 };
 
 #define OCP_REQ_TIMESTAMP	BIT(0)
@@ -836,6 +848,7 @@  static DEFINE_IDR(ptp_ocp_idr);
 struct ocp_selector {
 	const char *name;
 	int value;
+	u64 frequency;
 };
 
 static const struct ocp_selector ptp_ocp_clock[] = {
@@ -856,31 +869,31 @@  static const struct ocp_selector ptp_ocp_clock[] = {
 #define SMA_SELECT_MASK		GENMASK(14, 0)
 
 static const struct ocp_selector ptp_ocp_sma_in[] = {
-	{ .name = "10Mhz",	.value = 0x0000 },
-	{ .name = "PPS1",	.value = 0x0001 },
-	{ .name = "PPS2",	.value = 0x0002 },
-	{ .name = "TS1",	.value = 0x0004 },
-	{ .name = "TS2",	.value = 0x0008 },
-	{ .name = "IRIG",	.value = 0x0010 },
-	{ .name = "DCF",	.value = 0x0020 },
-	{ .name = "TS3",	.value = 0x0040 },
-	{ .name = "TS4",	.value = 0x0080 },
-	{ .name = "FREQ1",	.value = 0x0100 },
-	{ .name = "FREQ2",	.value = 0x0200 },
-	{ .name = "FREQ3",	.value = 0x0400 },
-	{ .name = "FREQ4",	.value = 0x0800 },
-	{ .name = "None",	.value = SMA_DISABLE },
+	{ .name = "10Mhz",  .value = 0x0000,      .frequency = 10000000 },
+	{ .name = "PPS1",   .value = 0x0001,      .frequency = 1 },
+	{ .name = "PPS2",   .value = 0x0002,      .frequency = 1 },
+	{ .name = "TS1",    .value = 0x0004,      .frequency = 0 },
+	{ .name = "TS2",    .value = 0x0008,      .frequency = 0 },
+	{ .name = "IRIG",   .value = 0x0010,      .frequency = 10000 },
+	{ .name = "DCF",    .value = 0x0020,      .frequency = 77500 },
+	{ .name = "TS3",    .value = 0x0040,      .frequency = 0 },
+	{ .name = "TS4",    .value = 0x0080,      .frequency = 0 },
+	{ .name = "FREQ1",  .value = 0x0100,      .frequency = 0 },
+	{ .name = "FREQ2",  .value = 0x0200,      .frequency = 0 },
+	{ .name = "FREQ3",  .value = 0x0400,      .frequency = 0 },
+	{ .name = "FREQ4",  .value = 0x0800,      .frequency = 0 },
+	{ .name = "None",   .value = SMA_DISABLE, .frequency = 0 },
 	{ }
 };
 
 static const struct ocp_selector ptp_ocp_sma_out[] = {
-	{ .name = "10Mhz",	.value = 0x0000 },
-	{ .name = "PHC",	.value = 0x0001 },
-	{ .name = "MAC",	.value = 0x0002 },
-	{ .name = "GNSS1",	.value = 0x0004 },
-	{ .name = "GNSS2",	.value = 0x0008 },
-	{ .name = "IRIG",	.value = 0x0010 },
-	{ .name = "DCF",	.value = 0x0020 },
+	{ .name = "10Mhz",	.value = 0x0000,  .frequency = 10000000 },
+	{ .name = "PHC",	.value = 0x0001,  .frequency = 1 },
+	{ .name = "MAC",	.value = 0x0002,  .frequency = 1 },
+	{ .name = "GNSS1",	.value = 0x0004,  .frequency = 1 },
+	{ .name = "GNSS2",	.value = 0x0008,  .frequency = 1 },
+	{ .name = "IRIG",	.value = 0x0010,  .frequency = 10000 },
+	{ .name = "DCF",	.value = 0x0020,  .frequency = 77000 },
 	{ .name = "GEN1",	.value = 0x0040 },
 	{ .name = "GEN2",	.value = 0x0080 },
 	{ .name = "GEN3",	.value = 0x0100 },
@@ -891,15 +904,15 @@  static const struct ocp_selector ptp_ocp_sma_out[] = {
 };
 
 static const struct ocp_selector ptp_ocp_art_sma_in[] = {
-	{ .name = "PPS1",	.value = 0x0001 },
-	{ .name = "10Mhz",	.value = 0x0008 },
+	{ .name = "PPS1",	.value = 0x0001,  .frequency = 1 },
+	{ .name = "10Mhz",	.value = 0x0008,  .frequency = 1000000 },
 	{ }
 };
 
 static const struct ocp_selector ptp_ocp_art_sma_out[] = {
-	{ .name = "PHC",	.value = 0x0002 },
-	{ .name = "GNSS",	.value = 0x0004 },
-	{ .name = "10Mhz",	.value = 0x0010 },
+	{ .name = "PHC",	.value = 0x0002,  .frequency = 1 },
+	{ .name = "GNSS",	.value = 0x0004,  .frequency = 1 },
+	{ .name = "10Mhz",	.value = 0x0010,  .frequency = 10000000 },
 	{ }
 };
 
@@ -2283,22 +2296,34 @@  ptp_ocp_sma_fb_set_inputs(struct ptp_ocp *bp, int sma_nr, u32 val)
 static void
 ptp_ocp_sma_fb_init(struct ptp_ocp *bp)
 {
+	struct dpll_pin_properties prop = {
+		.label = NULL,
+		.type = DPLL_PIN_TYPE_EXT,
+		.capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
+		.freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
+		.freq_supported = ptp_ocp_sma_freq,
+
+	};
 	u32 reg;
 	int i;
 
 	/* defaults */
+	for (i = 0; i < OCP_SMA_NUM; i++) {
+		bp->sma[i].default_fcn = i & 1;
+		bp->sma[i].dpll_prop = prop;
+		bp->sma[i].dpll_prop.label = bp->ptp_info.pin_config[i].name;
+	}
 	bp->sma[0].mode = SMA_MODE_IN;
 	bp->sma[1].mode = SMA_MODE_IN;
 	bp->sma[2].mode = SMA_MODE_OUT;
 	bp->sma[3].mode = SMA_MODE_OUT;
-	for (i = 0; i < 4; i++)
-		bp->sma[i].default_fcn = i & 1;
-
 	/* If no SMA1 map, the pin functions and directions are fixed. */
 	if (!bp->sma_map1) {
-		for (i = 0; i < 4; i++) {
+		for (i = 0; i < OCP_SMA_NUM; i++) {
 			bp->sma[i].fixed_fcn = true;
 			bp->sma[i].fixed_dir = true;
+			bp->sma[1].dpll_prop.capabilities &=
+				~DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
 		}
 		return;
 	}
@@ -2308,7 +2333,7 @@  ptp_ocp_sma_fb_init(struct ptp_ocp *bp)
 	 */
 	reg = ioread32(&bp->sma_map2->gpio2);
 	if (reg == 0xffffffff) {
-		for (i = 0; i < 4; i++)
+		for (i = 0; i < OCP_SMA_NUM; i++)
 			bp->sma[i].fixed_dir = true;
 	} else {
 		reg = ioread32(&bp->sma_map1->gpio1);
@@ -2330,7 +2355,7 @@  static const struct ocp_sma_op ocp_fb_sma_op = {
 };
 
 static int
-ptp_ocp_fb_set_pins(struct ptp_ocp *bp)
+ptp_ocp_set_pins(struct ptp_ocp *bp)
 {
 	struct ptp_pin_desc *config;
 	int i;
@@ -2397,16 +2422,16 @@  ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
 
 	ptp_ocp_tod_init(bp);
 	ptp_ocp_nmea_out_init(bp);
-	ptp_ocp_sma_init(bp);
 	ptp_ocp_signal_init(bp);
 
 	err = ptp_ocp_attr_group_add(bp, fb_timecard_groups);
 	if (err)
 		return err;
 
-	err = ptp_ocp_fb_set_pins(bp);
+	err = ptp_ocp_set_pins(bp);
 	if (err)
 		return err;
+	ptp_ocp_sma_init(bp);
 
 	return ptp_ocp_init_clock(bp);
 }
@@ -2446,6 +2471,14 @@  ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data)
 static void
 ptp_ocp_art_sma_init(struct ptp_ocp *bp)
 {
+	struct dpll_pin_properties prop = {
+		.label = NULL,
+		.type = DPLL_PIN_TYPE_EXT,
+		.capabilities = 0,
+		.freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
+		.freq_supported = ptp_ocp_sma_freq,
+
+	};
 	u32 reg;
 	int i;
 
@@ -2460,16 +2493,16 @@  ptp_ocp_art_sma_init(struct ptp_ocp *bp)
 	bp->sma[2].default_fcn = 0x10;	/* OUT: 10Mhz */
 	bp->sma[3].default_fcn = 0x02;	/* OUT: PHC */
 
-	/* If no SMA map, the pin functions and directions are fixed. */
-	if (!bp->art_sma) {
-		for (i = 0; i < 4; i++) {
+
+	for (i = 0; i < OCP_SMA_NUM; i++) {
+		/* If no SMA map, the pin functions and directions are fixed. */
+		bp->sma[i].dpll_prop = prop;
+		bp->sma[i].dpll_prop.label = bp->ptp_info.pin_config[i].name;
+		if (!bp->art_sma) {
 			bp->sma[i].fixed_fcn = true;
 			bp->sma[i].fixed_dir = true;
+			continue;
 		}
-		return;
-	}
-
-	for (i = 0; i < 4; i++) {
 		reg = ioread32(&bp->art_sma->map[i].gpio);
 
 		switch (reg & 0xff) {
@@ -2480,9 +2513,13 @@  ptp_ocp_art_sma_init(struct ptp_ocp *bp)
 		case 1:
 		case 8:
 			bp->sma[i].mode = SMA_MODE_IN;
+			bp->sma[i].dpll_prop.capabilities =
+				DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
 			break;
 		default:
 			bp->sma[i].mode = SMA_MODE_OUT;
+			bp->sma[i].dpll_prop.capabilities =
+				DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
 			break;
 		}
 	}
@@ -2549,6 +2586,9 @@  ptp_ocp_art_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
 	/* Enable MAC serial port during initialisation */
 	iowrite32(1, &bp->board_config->mro50_serial_activate);
 
+	err = ptp_ocp_set_pins(bp);
+	if (err)
+		return err;
 	ptp_ocp_sma_init(bp);
 
 	err = ptp_ocp_attr_group_add(bp, art_timecard_groups);
@@ -2690,16 +2730,9 @@  sma4_show(struct device *dev, struct device_attribute *attr, char *buf)
 }
 
 static int
-ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
+ptp_ocp_sma_store_val(struct ptp_ocp *bp, int val, enum ptp_ocp_sma_mode mode, int sma_nr)
 {
 	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
-	enum ptp_ocp_sma_mode mode;
-	int val;
-
-	mode = sma->mode;
-	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
-	if (val < 0)
-		return val;
 
 	if (sma->fixed_dir && (mode != sma->mode || val & SMA_DISABLE))
 		return -EOPNOTSUPP;
@@ -2734,6 +2767,20 @@  ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
 	return val;
 }
 
+static int
+ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
+{
+	struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
+	enum ptp_ocp_sma_mode mode;
+	int val;
+
+	mode = sma->mode;
+	val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
+	if (val < 0)
+		return val;
+	return ptp_ocp_sma_store_val(bp, val, mode, sma_nr);
+}
+
 static ssize_t
 sma1_store(struct device *dev, struct device_attribute *attr,
 	   const char *buf, size_t count)
@@ -4172,12 +4219,148 @@  ptp_ocp_detach(struct ptp_ocp *bp)
 	device_unregister(&bp->dev);
 }
 
+static int ptp_ocp_dpll_lock_status_get(const struct dpll_device *dpll,
+					void *priv,
+					enum dpll_lock_status *status,
+					struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp *bp = priv;
+	int sync;
+
+	sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
+	*status = sync ? DPLL_LOCK_STATUS_LOCKED : DPLL_LOCK_STATUS_UNLOCKED;
+
+	return 0;
+}
+
+static int ptp_ocp_dpll_source_idx_get(const struct dpll_device *dpll,
+				       void *priv, u32 *idx,
+				       struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp *bp = priv;
+
+	if (bp->pps_select) {
+		*idx = ioread32(&bp->pps_select->gpio1);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int ptp_ocp_dpll_mode_get(const struct dpll_device *dpll, void *priv,
+				 u32 *mode, struct netlink_ext_ack *extack)
+{
+	*mode = DPLL_MODE_AUTOMATIC;
+	return 0;
+}
+
+static bool ptp_ocp_dpll_mode_supported(const struct dpll_device *dpll,
+					void *priv, const enum dpll_mode mode,
+					struct netlink_ext_ack *extack)
+{
+	return mode == DPLL_MODE_AUTOMATIC;
+}
+
+static int ptp_ocp_dpll_direction_get(const struct dpll_pin *pin,
+				      void *pin_priv,
+				      const struct dpll_device *dpll,
+				      void *priv,
+				      enum dpll_pin_direction *direction,
+				      struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp_sma_connector *sma = pin_priv;
+
+	*direction = sma->mode == SMA_MODE_IN ?
+				  DPLL_PIN_DIRECTION_SOURCE :
+				  DPLL_PIN_DIRECTION_OUTPUT;
+	return 0;
+}
+
+static int ptp_ocp_dpll_direction_set(const struct dpll_pin *pin,
+				      void *pin_priv,
+				      const struct dpll_device *dpll,
+				      void *dpll_priv,
+				      enum dpll_pin_direction direction,
+				      struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp_sma_connector *sma = pin_priv;
+	struct ptp_ocp *bp = dpll_priv;
+	enum ptp_ocp_sma_mode mode;
+	int sma_nr = (sma - bp->sma);
+
+	if (sma->fixed_dir)
+		return -EOPNOTSUPP;
+	mode = direction == DPLL_PIN_DIRECTION_SOURCE ?
+			    SMA_MODE_IN : SMA_MODE_OUT;
+	return ptp_ocp_sma_store_val(bp, 0, mode, sma_nr);
+}
+
+static int ptp_ocp_dpll_frequency_set(const struct dpll_pin *pin,
+				      void *pin_priv,
+				      const struct dpll_device *dpll,
+				      void *dpll_priv, u64 frequency,
+				      struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp_sma_connector *sma = pin_priv;
+	struct ptp_ocp *bp = dpll_priv;
+	const struct ocp_selector *tbl;
+	int sma_nr = (sma - bp->sma);
+	int val, i;
+
+	if (sma->fixed_fcn)
+		return -EOPNOTSUPP;
+
+	tbl = bp->sma_op->tbl[sma->mode];
+	for (i = 0; tbl[i].name; i++)
+		if (tbl[i].frequency == frequency)
+			return ptp_ocp_sma_store_val(bp, val, sma->mode, sma_nr);
+	return -EINVAL;
+}
+
+static int ptp_ocp_dpll_frequency_get(const struct dpll_pin *pin,
+				      void *pin_priv,
+				      const struct dpll_device *dpll,
+				      void *dpll_priv, u64 *frequency,
+				      struct netlink_ext_ack *extack)
+{
+	struct ptp_ocp_sma_connector *sma = pin_priv;
+	struct ptp_ocp *bp = dpll_priv;
+	const struct ocp_selector *tbl;
+	int sma_nr = (sma - bp->sma);
+	u32 val;
+	int i;
+
+	val = bp->sma_op->get(bp, sma_nr);
+	tbl = bp->sma_op->tbl[sma->mode];
+	for (i = 0; tbl[i].name; i++)
+		if (val == tbl[i].value) {
+			*frequency = tbl[i].frequency;
+			return 0;
+		}
+
+	return -EINVAL;
+}
+
+static const struct dpll_device_ops dpll_ops = {
+	.lock_status_get = ptp_ocp_dpll_lock_status_get,
+	.source_pin_idx_get = ptp_ocp_dpll_source_idx_get,
+	.mode_get = ptp_ocp_dpll_mode_get,
+	.mode_supported = ptp_ocp_dpll_mode_supported,
+};
+
+static const struct dpll_pin_ops dpll_pins_ops = {
+	.frequency_get = ptp_ocp_dpll_frequency_get,
+	.frequency_set = ptp_ocp_dpll_frequency_set,
+	.direction_get = ptp_ocp_dpll_direction_get,
+	.direction_set = ptp_ocp_dpll_direction_set,
+};
+
 static int
 ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
 	struct devlink *devlink;
 	struct ptp_ocp *bp;
-	int err;
+	int err, i;
+	u64 clkid;
 
 	devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev->dev);
 	if (!devlink) {
@@ -4227,8 +4410,39 @@  ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
 	ptp_ocp_info(bp);
 	devlink_register(devlink);
-	return 0;
 
+	clkid = pci_get_dsn(pdev);
+	bp->dpll = dpll_device_get(clkid, 0, THIS_MODULE);
+	if (IS_ERR(bp->dpll)) {
+		dev_err(&pdev->dev, "dpll_device_alloc failed\n");
+		goto out;
+	}
+
+	err = dpll_device_register(bp->dpll, DPLL_TYPE_PPS, &dpll_ops, bp, &pdev->dev);
+	if (err)
+		goto out;
+
+	for (i = 0; i < OCP_SMA_NUM; i++) {
+		bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &bp->sma[i].dpll_prop);
+		if (IS_ERR(bp->sma[i].dpll_pin))
+			goto out_dpll;
+
+		err = dpll_pin_register(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops,
+					&bp->sma[i], NULL);
+		if (err) {
+			dpll_pin_put(bp->sma[i].dpll_pin);
+			goto out_dpll;
+		}
+	}
+
+	return 0;
+out_dpll:
+	while (i) {
+		--i;
+		dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, &bp->sma[i]);
+		dpll_pin_put(bp->sma[i].dpll_pin);
+	}
+	dpll_device_put(bp->dpll);
 out:
 	ptp_ocp_detach(bp);
 out_disable:
@@ -4243,7 +4457,16 @@  ptp_ocp_remove(struct pci_dev *pdev)
 {
 	struct ptp_ocp *bp = pci_get_drvdata(pdev);
 	struct devlink *devlink = priv_to_devlink(bp);
+	int i;
 
+	for (i = 0; i < OCP_SMA_NUM; i++) {
+		if (bp->sma[i].dpll_pin) {
+			dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, bp);
+			dpll_pin_put(bp->sma[i].dpll_pin);
+		}
+	}
+	dpll_device_unregister(bp->dpll, &dpll_ops, bp);
+	dpll_device_put(bp->dpll);
 	devlink_unregister(devlink);
 	ptp_ocp_detach(bp);
 	pci_disable_device(pdev);