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