[RFC PATCH 2/6] can: esd_402_pci: Add support to control TDC mode and settings

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add the feature to control the TDC mode and (offset) settings via the
netlink interface.

Signed-off-by: Stefan Mätje <stefan.maetje@xxxxxx>
---
 drivers/net/can/esd/esd_402_pci-core.c |  17 +++-
 drivers/net/can/esd/esdacc.c           | 111 ++++++++++++++++++++++++-
 drivers/net/can/esd/esdacc.h           |  14 ++++
 3 files changed, 138 insertions(+), 4 deletions(-)

diff --git a/drivers/net/can/esd/esd_402_pci-core.c b/drivers/net/can/esd/esd_402_pci-core.c
index 0c0fc159a1cb..03df04251c03 100644
--- a/drivers/net/can/esd/esd_402_pci-core.c
+++ b/drivers/net/can/esd/esd_402_pci-core.c
@@ -95,6 +95,17 @@ static const struct can_bittiming_const pci402fd_data_bittiming_const = {
 	.brp_inc = 1,
 };
 
+/* Transmitter Delay Compensation constants for esdACC controller
+ */
+static const struct can_tdc_const pci402fd_tdc_const = {
+	.tdcv_min = 0,
+	.tdcv_max = 0,
+	.tdco_min = 0,
+	.tdco_max = 63,
+	.tdcf_min = 2,
+	.tdcf_max = 63,
+};
+
 static const struct net_device_ops pci402_acc_netdev_ops = {
 	.ndo_open = acc_open,
 	.ndo_stop = acc_close,
@@ -396,9 +407,13 @@ static int pci402_init_cores(struct pci_dev *pdev)
 		if (card->ov.features & ACC_OV_REG_FEAT_MASK_DAR)
 			priv->can.ctrlmode_supported |= CAN_CTRLMODE_ONE_SHOT;
 		if (card->ov.features & ACC_OV_REG_FEAT_MASK_CANFD) {
-			priv->can.ctrlmode_supported |= CAN_CTRLMODE_FD;
+			priv->can.ctrlmode_supported |= CAN_CTRLMODE_FD |
+				CAN_CTRLMODE_TDC_AUTO |
+				CAN_CTRLMODE_TDC_MANUAL;
 			priv->can.bittiming_const = &pci402fd_nom_bittiming_const;
 			priv->can.data_bittiming_const = &pci402fd_data_bittiming_const;
+			priv->can.tdc_const = &pci402fd_tdc_const;
+			priv->can.do_get_auto_tdcv = acc_get_auto_tdcv;
 		} else {
 			priv->can.bittiming_const = &pci402_bittiming_const;
 		}
diff --git a/drivers/net/can/esd/esdacc.c b/drivers/net/can/esd/esdacc.c
index faad38e23251..0f1d32a7f812 100644
--- a/drivers/net/can/esd/esdacc.c
+++ b/drivers/net/can/esd/esdacc.c
@@ -418,6 +418,106 @@ int acc_set_mode(struct net_device *netdev, enum can_mode mode)
 	return 0;
 }
 
+/**
+ * acc_get_auto_tdcv() - get the TD measurement
+ *
+ * @netdev: CAN network device.
+ * @tdcv: pointer to TD measurement output
+ *
+ * This function returns the value of the last Transmitter Delay (TD) measurement
+ * if available.
+ *
+ * Return:
+ * * %0		- a valid TD measurement is returned
+ * * %-EINVAL	- no TD measurement available yet
+ */
+int acc_get_auto_tdcv(const struct net_device *netdev, u32 *tdcv)
+{
+	struct acc_net_priv *priv = netdev_priv(netdev);
+	int err;
+
+	*tdcv = FIELD_GET(ACC_REG_TDCR_TDC_MEAS, acc_read32(priv->core, ACC_CORE_OF_TDCR));
+
+	err = *tdcv > 0 ? 0 : -EINVAL;
+
+	return err;
+}
+
+/**
+ * acc_build_tdcr() - build TDC control register value from struct can_priv data
+ *
+ * @can: can_priv data structure
+ *
+ * This function builds the TDC control register value according to the three
+ * supported TDC modes:
+ *	- CAN_CTRLMODE_TDC_AUTO
+ *	- CAN_CTRLMODE_TDC_MANUAL
+ *	- none of them i. e. TDC off
+ *
+ * The measured transmitter delay is named here TD to distinguish it from
+ * the TDCV value possibly set via the netlink interface.
+ *
+ * The 6-bit value written to the ACC_REG_TDCR_TDC_OFFS field of the TDCR is
+ * named here TDC_REG_x. This field is a signed value TDC_REG_s in automatic mode
+ * and a unsigned value TDC_REG_u in manual mode.
+ *
+ * The esdACC controller is always aware of the SP setting and adds this amount of
+ * clocks to the TDC_REG_x contents.
+ *
+ * Automatic mode
+ *	SSP = TD + SP + TDC_REG_s = TD + SP + (absolute TDCO - SP)
+ *
+ *	Therefore TDC_REG_s is (absolute TDCO - SP) which is calculated by
+ *	can_get_relative_tdco().
+ *	This function provides access to a "full automatic mode" that is
+ *	selected with "tdc-mode auto tdco 0" which works always for all
+ *	bitrates (SSP will then be SSP = TD + SP + 0).
+ *
+ * Manual mode
+ *	SSP = SP + TDC_REG_u = SP + (absolute TDCO - SP)
+ *
+ *	Because there is no TDCV register in the esdACC core the
+ *	struct can_tdc_const::tdcv_* values specify that only
+ *	TDCV == 0 is supported.
+ *	Derived from the formula without TDCV TDC_REG_u is (absolute TDCO - SP)
+ *	which is calculated by can_get_relative_tdco(). "Negative" results
+ *	trigger a warning and are clamped to zero.
+ *
+ * TDC off
+ *	Set TDC control mode to ACC_TDC_MODE_OFF and clear all other fields.
+ *
+ * Return: TDC control values in esdACC TDCR format
+ */
+static u32 acc_build_tdcr(struct can_priv *can)
+{
+	u32 tdcr = FIELD_PREP(ACC_REG_TDCR_TDC_MIN, can->tdc.tdcf);
+	s32 tdc_rel_offset = 0;
+
+	tdc_rel_offset = can_get_relative_tdco(can);
+	if (can->ctrlmode & CAN_CTRLMODE_TDC_AUTO) {
+		if (can->tdc.tdco == 0U) {
+			/* Special case: Use "full automatic mode" feature. */
+			tdc_rel_offset = 0;
+		}
+		tdcr |= FIELD_PREP(ACC_REG_TDCR_TDC_MODE, ACC_TDC_MODE_AUTO);
+		tdcr |= FIELD_PREP(ACC_REG_TDCR_TDC_OFFS, tdc_rel_offset);
+
+	} else if (can->ctrlmode & CAN_CTRLMODE_TDC_MANUAL) {
+		if (tdc_rel_offset < 0) {
+			netdev_warn(can->dev, "TDC manual offset underflow: %d\n", tdc_rel_offset);
+			tdc_rel_offset = 0;
+		}
+		tdcr |= FIELD_PREP(ACC_REG_TDCR_TDC_MODE, ACC_TDC_MODE_MANUAL);
+		tdcr |= FIELD_PREP(ACC_REG_TDCR_TDC_OFFS, tdc_rel_offset);
+
+	} else {
+		/* Overwrite tdcr to clear ACC_REG_TDCR_TDC_MIN again. */
+		tdcr = FIELD_PREP(ACC_REG_TDCR_TDC_MODE, ACC_TDC_MODE_OFF);
+	}
+
+	return tdcr;
+}
+
 static void acc_set_bittiming(struct net_device *netdev)
 {
 	struct acc_net_priv *priv = netdev_priv(netdev);
@@ -431,7 +531,9 @@ static void acc_set_bittiming(struct net_device *netdev)
 		   "OAM?"[FIELD_GET(CAN_CTRLMODE_TDC_MASK, priv->can.ctrlmode)]);
 
 	if (priv->ov->features & ACC_OV_REG_FEAT_MASK_CANFD) {
+		/* Initialize fbtr and tdcr with reset state values. */
 		u32 fbtr = 0;
+		u32 tdcr = 0;
 
 		netdev_dbg(netdev, "nbit timing: brp %u, prop %u, ph1 %u ph2 %u, sjw %u\n",
 			   bt->brp, bt->prop_seg,
@@ -456,12 +558,15 @@ static void acc_set_bittiming(struct net_device *netdev)
 					  dbt->phase_seg1 + dbt->prop_seg - 1);
 			fbtr |= FIELD_PREP(ACC_REG_FBTR_FD_MASK_TSEG2, dbt->phase_seg2 - 1);
 			fbtr |= FIELD_PREP(ACC_REG_FBTR_FD_MASK_SJW, dbt->sjw - 1);
+
+			tdcr = acc_build_tdcr(&priv->can);
 		}
-		/* Write ACC_CORE_OF_FBTR last. */
+		/* Write ACC_CORE_OF_FBTR as last of bit timing registers. */
 		acc_write32(priv->core, ACC_CORE_OF_FBTR, fbtr);
+		acc_write32(priv->core, ACC_CORE_OF_TDCR, tdcr);
 
-		netdev_dbg(netdev, "esdACC: BRP %u, NBTR 0x%08x, DBTR 0x%08x",
-			   brp, btr, fbtr);
+		netdev_dbg(netdev, "esdACC: BRP %u, NBTR 0x%08x, DBTR 0x%08x, TDCR 0x%08x",
+			   brp, btr, fbtr, tdcr);
 	} else {
 		netdev_dbg(netdev, "bit timing: brp %u, prop %u, ph1 %u ph2 %u, sjw %u\n",
 			   bt->brp, bt->prop_seg,
diff --git a/drivers/net/can/esd/esdacc.h b/drivers/net/can/esd/esdacc.h
index fdbb017b80a9..33c6dbf60f6e 100644
--- a/drivers/net/can/esd/esdacc.h
+++ b/drivers/net/can/esd/esdacc.h
@@ -61,6 +61,7 @@
 #define ACC_CORE_OF_TXFIFO_STATUS 0x004c
 #define ACC_CORE_OF_TX_STATUS_IRQ 0x0050
 #define ACC_CORE_OF_TX_ABORT_MASK 0x0054
+#define ACC_CORE_OF_TDCR 0x5c
 #define ACC_CORE_OF_BM_IRQ_COUNTER 0x0070
 #define ACC_CORE_OF_TXFIFO_ID 0x00c0
 #define ACC_CORE_OF_TXFIFO_DLC 0x00c4
@@ -114,6 +115,18 @@
 #define ACC_REG_FBTR_FD_MASK_TSEG2 GENMASK(19, 16)
 #define ACC_REG_FBTR_FD_MASK_SJW GENMASK(27, 24)
 
+/* TDC register layout (TDCR) */
+#define ACC_REG_TDCR_TDC_MEAS GENMASK(5, 0)	/* RO: also known as TDCV on other controllers */
+#define ACC_REG_TDCR_TDC_MIN GENMASK(13, 8)	/* RW: also known as TDCF on other controllers */
+#define ACC_REG_TDCR_TDC_OFFS GENMASK(21, 16)	/* RW: mode dependent signed or unsigned offset */
+#define ACC_REG_TDCR_TDC_MODE GENMASK(31, 30)	/* RW: TDC operating mode */
+
+/* TDC operating modes for ACC_REG_TDCR_TDC_MODE field */
+#define ACC_TDC_MODE_AUTO 0
+#define ACC_TDC_MODE_MANUAL 1
+#define ACC_TDC_MODE_OFF 3
+
+
 /* 256 BM_MSGs of 32 byte size */
 #define ACC_CORE_DMAMSG_SIZE 32U
 #define ACC_CORE_DMABUF_SIZE (256U * ACC_CORE_DMAMSG_SIZE)
@@ -390,4 +403,5 @@ netdev_tx_t acc_start_xmit(struct sk_buff *skb, struct net_device *netdev);
 int acc_get_berr_counter(const struct net_device *netdev,
 			 struct can_berr_counter *bec);
 int acc_set_mode(struct net_device *netdev, enum can_mode mode);
+int acc_get_auto_tdcv(const struct net_device *netdev, u32 *tdcv);
 irqreturn_t acc_card_interrupt(struct acc_ov *ov, struct acc_core *cores);
-- 
2.34.1





[Index of Archives]     [Automotive Discussions]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]     [CAN Bus]

  Powered by Linux