net: lan743x: Add support for PTP-IO Event Output (Periodic Output)
authorRaju Lakkaraju <Raju.Lakkaraju@microchip.com>
Thu, 17 Mar 2022 10:43:10 +0000 (16:13 +0530)
committerDavid S. Miller <davem@davemloft.net>
Fri, 18 Mar 2022 12:53:23 +0000 (12:53 +0000)
Add support for PTP-IO Event Output (Periodic Output - perout) for
PCI11010/PCI11414 chips

Signed-off-by: Raju Lakkaraju <Raju.Lakkaraju@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/microchip/lan743x_main.h
drivers/net/ethernet/microchip/lan743x_ptp.c
drivers/net/ethernet/microchip/lan743x_ptp.h

index 9c52870..1ca5f32 100644 (file)
 #define INT_MOD_CFG9                   (0x7E4)
 
 #define PTP_CMD_CTL                                    (0x0A00)
+#define PTP_CMD_CTL_PTP_LTC_TARGET_READ_               BIT(13)
 #define PTP_CMD_CTL_PTP_CLK_STP_NSEC_                  BIT(6)
 #define PTP_CMD_CTL_PTP_CLOCK_STEP_SEC_                        BIT(5)
 #define PTP_CMD_CTL_PTP_CLOCK_LOAD_                    BIT(4)
        (((value) & 0x7) << (1 + ((channel) << 2)))
 #define PTP_GENERAL_CONFIG_RELOAD_ADD_X_(channel)      (BIT((channel) << 2))
 
+#define HS_PTP_GENERAL_CONFIG                          (0x0A04)
+#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_X_MASK_(channel) \
+       (0xf << (4 + ((channel) << 2)))
+#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_100NS_       (0)
+#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_500NS_       (1)
+#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_1US_         (2)
+#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_5US_         (3)
+#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_10US_                (4)
+#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_50US_                (5)
+#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_100US_       (6)
+#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_500US_       (7)
+#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_1MS_         (8)
+#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_5MS_         (9)
+#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_10MS_                (10)
+#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_50MS_                (11)
+#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_100MS_       (12)
+#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_200MS_       (13)
+#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_TOGG_                (14)
+#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_INT_         (15)
+#define HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_X_SET_(channel, value) \
+       (((value) & 0xf) << (4 + ((channel) << 2)))
+#define HS_PTP_GENERAL_CONFIG_EVENT_POL_X_(channel)    (BIT(1 + ((channel) * 2)))
+#define HS_PTP_GENERAL_CONFIG_RELOAD_ADD_X_(channel)   (BIT((channel) * 2))
+
 #define PTP_INT_STS                            (0x0A08)
 #define PTP_INT_IO_FE_MASK_                    GENMASK(31, 24)
 #define PTP_INT_IO_FE_SHIFT_                   (24)
 #define PTP_INT_IO_RE_MASK_                    GENMASK(23, 16)
 #define PTP_INT_IO_RE_SHIFT_                   (16)
 #define PTP_INT_IO_RE_SET_(channel)            BIT(16 + (channel))
+#define PTP_INT_TX_TS_OVRFL_INT_               BIT(14)
+#define PTP_INT_TX_SWTS_ERR_INT_               BIT(13)
+#define PTP_INT_TX_TS_INT_                     BIT(12)
+#define PTP_INT_RX_TS_OVRFL_INT_               BIT(9)
+#define PTP_INT_RX_TS_INT_                     BIT(8)
+#define PTP_INT_TIMER_INT_B_                   BIT(1)
+#define PTP_INT_TIMER_INT_A_                   BIT(0)
 #define PTP_INT_EN_SET                         (0x0A0C)
 #define PTP_INT_EN_FE_EN_SET_(channel)         BIT(24 + (channel))
 #define PTP_INT_EN_RE_EN_SET_(channel)         BIT(16 + (channel))
+#define PTP_INT_EN_TIMER_SET_(channel)         BIT(channel)
 #define PTP_INT_EN_CLR                         (0x0A10)
 #define PTP_INT_EN_FE_EN_CLR_(channel)         BIT(24 + (channel))
 #define PTP_INT_EN_RE_EN_CLR_(channel)         BIT(16 + (channel))
index 470d905..6a11e2c 100644 (file)
@@ -689,6 +689,215 @@ failed:
        return ret;
 }
 
+static void lan743x_ptp_io_perout_off(struct lan743x_adapter *adapter,
+                                     u32 index)
+{
+       struct lan743x_ptp *ptp = &adapter->ptp;
+       int perout_pin;
+       int event_ch;
+       u32 gen_cfg;
+       int val;
+
+       event_ch = ptp->ptp_io_perout[index];
+       if (event_ch >= 0) {
+               /* set target to far in the future, effectively disabling it */
+               lan743x_csr_write(adapter,
+                                 PTP_CLOCK_TARGET_SEC_X(event_ch),
+                                 0xFFFF0000);
+               lan743x_csr_write(adapter,
+                                 PTP_CLOCK_TARGET_NS_X(event_ch),
+                                 0);
+
+               gen_cfg = lan743x_csr_read(adapter, HS_PTP_GENERAL_CONFIG);
+               gen_cfg &= ~(HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_X_MASK_
+                                   (event_ch));
+               gen_cfg &= ~(HS_PTP_GENERAL_CONFIG_EVENT_POL_X_(event_ch));
+               gen_cfg |= HS_PTP_GENERAL_CONFIG_RELOAD_ADD_X_(event_ch);
+               lan743x_csr_write(adapter, HS_PTP_GENERAL_CONFIG, gen_cfg);
+               if (event_ch)
+                       lan743x_csr_write(adapter, PTP_INT_STS,
+                                         PTP_INT_TIMER_INT_B_);
+               else
+                       lan743x_csr_write(adapter, PTP_INT_STS,
+                                         PTP_INT_TIMER_INT_A_);
+               lan743x_ptp_release_event_ch(adapter, event_ch);
+               ptp->ptp_io_perout[index] = -1;
+       }
+
+       perout_pin = ptp_find_pin(ptp->ptp_clock, PTP_PF_PEROUT, index);
+
+       /* Deselect Event output */
+       val = lan743x_csr_read(adapter, PTP_IO_EVENT_OUTPUT_CFG);
+
+       /* Disables the output of Local Time Target compare events */
+       val &= ~PTP_IO_EVENT_OUTPUT_CFG_EN_(perout_pin);
+       lan743x_csr_write(adapter, PTP_IO_EVENT_OUTPUT_CFG, val);
+
+       /* Configured as an opendrain driver*/
+       val = lan743x_csr_read(adapter, PTP_IO_PIN_CFG);
+       val &= ~PTP_IO_PIN_CFG_OBUF_TYPE_(perout_pin);
+       lan743x_csr_write(adapter, PTP_IO_PIN_CFG, val);
+       /* Dummy read to make sure write operation success */
+       val = lan743x_csr_read(adapter, PTP_IO_PIN_CFG);
+}
+
+static int lan743x_ptp_io_perout(struct lan743x_adapter *adapter, int on,
+                                struct ptp_perout_request *perout_request)
+{
+       struct lan743x_ptp *ptp = &adapter->ptp;
+       u32 period_sec, period_nsec;
+       u32 start_sec, start_nsec;
+       u32 pulse_sec, pulse_nsec;
+       int pulse_width;
+       int perout_pin;
+       int event_ch;
+       u32 gen_cfg;
+       u32 index;
+       int val;
+
+       index = perout_request->index;
+       event_ch = ptp->ptp_io_perout[index];
+
+       if (on) {
+               perout_pin = ptp_find_pin(ptp->ptp_clock, PTP_PF_PEROUT, index);
+               if (perout_pin < 0)
+                       return -EBUSY;
+       } else {
+               lan743x_ptp_io_perout_off(adapter, index);
+               return 0;
+       }
+
+       if (event_ch >= LAN743X_PTP_N_EVENT_CHAN) {
+               /* already on, turn off first */
+               lan743x_ptp_io_perout_off(adapter, index);
+       }
+
+       event_ch = lan743x_ptp_reserve_event_ch(adapter, index);
+       if (event_ch < 0) {
+               netif_warn(adapter, drv, adapter->netdev,
+                          "Failed to reserve event channel %d for PEROUT\n",
+                          index);
+               goto failed;
+       }
+       ptp->ptp_io_perout[index] = event_ch;
+
+       if (perout_request->flags & PTP_PEROUT_DUTY_CYCLE) {
+               pulse_sec = perout_request->on.sec;
+               pulse_sec += perout_request->on.nsec / 1000000000;
+               pulse_nsec = perout_request->on.nsec % 1000000000;
+       } else {
+               pulse_sec = perout_request->period.sec;
+               pulse_sec += perout_request->period.nsec / 1000000000;
+               pulse_nsec = perout_request->period.nsec % 1000000000;
+       }
+
+       if (pulse_sec == 0) {
+               if (pulse_nsec >= 400000000) {
+                       pulse_width = PTP_GENERAL_CONFIG_CLOCK_EVENT_200MS_;
+               } else if (pulse_nsec >= 200000000) {
+                       pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_100MS_;
+               } else if (pulse_nsec >= 100000000) {
+                       pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_50MS_;
+               } else if (pulse_nsec >= 20000000) {
+                       pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_10MS_;
+               } else if (pulse_nsec >= 10000000) {
+                       pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_5MS_;
+               } else if (pulse_nsec >= 2000000) {
+                       pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_1MS_;
+               } else if (pulse_nsec >= 1000000) {
+                       pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_500US_;
+               } else if (pulse_nsec >= 200000) {
+                       pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_100US_;
+               } else if (pulse_nsec >= 100000) {
+                       pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_50US_;
+               } else if (pulse_nsec >= 20000) {
+                       pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_10US_;
+               } else if (pulse_nsec >= 10000) {
+                       pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_5US_;
+               } else if (pulse_nsec >= 2000) {
+                       pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_1US_;
+               } else if (pulse_nsec >= 1000) {
+                       pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_500NS_;
+               } else if (pulse_nsec >= 200) {
+                       pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_100NS_;
+               } else {
+                       netif_warn(adapter, drv, adapter->netdev,
+                                  "perout period too small, min is 200nS\n");
+                       goto failed;
+               }
+       } else {
+               pulse_width = HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_200MS_;
+       }
+
+       /* turn off by setting target far in future */
+       lan743x_csr_write(adapter,
+                         PTP_CLOCK_TARGET_SEC_X(event_ch),
+                         0xFFFF0000);
+       lan743x_csr_write(adapter,
+                         PTP_CLOCK_TARGET_NS_X(event_ch), 0);
+
+       /* Configure to pulse every period */
+       gen_cfg = lan743x_csr_read(adapter, HS_PTP_GENERAL_CONFIG);
+       gen_cfg &= ~(HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_X_MASK_(event_ch));
+       gen_cfg |= HS_PTP_GENERAL_CONFIG_CLOCK_EVENT_X_SET_
+                         (event_ch, pulse_width);
+       gen_cfg |= HS_PTP_GENERAL_CONFIG_EVENT_POL_X_(event_ch);
+       gen_cfg &= ~(HS_PTP_GENERAL_CONFIG_RELOAD_ADD_X_(event_ch));
+       lan743x_csr_write(adapter, HS_PTP_GENERAL_CONFIG, gen_cfg);
+
+       /* set the reload to one toggle cycle */
+       period_sec = perout_request->period.sec;
+       period_sec += perout_request->period.nsec / 1000000000;
+       period_nsec = perout_request->period.nsec % 1000000000;
+       lan743x_csr_write(adapter,
+                         PTP_CLOCK_TARGET_RELOAD_SEC_X(event_ch),
+                         period_sec);
+       lan743x_csr_write(adapter,
+                         PTP_CLOCK_TARGET_RELOAD_NS_X(event_ch),
+                         period_nsec);
+
+       start_sec = perout_request->start.sec;
+       start_sec += perout_request->start.nsec / 1000000000;
+       start_nsec = perout_request->start.nsec % 1000000000;
+
+       /* set the start time */
+       lan743x_csr_write(adapter,
+                         PTP_CLOCK_TARGET_SEC_X(event_ch),
+                         start_sec);
+       lan743x_csr_write(adapter,
+                         PTP_CLOCK_TARGET_NS_X(event_ch),
+                         start_nsec);
+
+       /* Enable LTC Target Read */
+       val = lan743x_csr_read(adapter, PTP_CMD_CTL);
+       val |= PTP_CMD_CTL_PTP_LTC_TARGET_READ_;
+       lan743x_csr_write(adapter, PTP_CMD_CTL, val);
+
+       /* Configure as an push/pull driver */
+       val = lan743x_csr_read(adapter, PTP_IO_PIN_CFG);
+       val |= PTP_IO_PIN_CFG_OBUF_TYPE_(perout_pin);
+       lan743x_csr_write(adapter, PTP_IO_PIN_CFG, val);
+
+       /* Select Event output */
+       val = lan743x_csr_read(adapter, PTP_IO_EVENT_OUTPUT_CFG);
+       if (event_ch)
+               /* Channel B as the output */
+               val |= PTP_IO_EVENT_OUTPUT_CFG_SEL_(perout_pin);
+       else
+               /* Channel A as the output */
+               val &= ~PTP_IO_EVENT_OUTPUT_CFG_SEL_(perout_pin);
+
+       /* Enables the output of Local Time Target compare events */
+       val |= PTP_IO_EVENT_OUTPUT_CFG_EN_(perout_pin);
+       lan743x_csr_write(adapter, PTP_IO_EVENT_OUTPUT_CFG, val);
+
+       return 0;
+
+failed:
+       lan743x_ptp_io_perout_off(adapter, index);
+       return -ENODEV;
+}
+
 static void lan743x_ptp_io_extts_off(struct lan743x_adapter *adapter,
                                     u32 index)
 {
@@ -812,9 +1021,14 @@ static int lan743x_ptpci_enable(struct ptp_clock_info *ptpci,
                                                         &request->extts);
                        return -EINVAL;
                case PTP_CLK_REQ_PEROUT:
-                       if (request->perout.index < ptpci->n_per_out)
-                               return lan743x_ptp_perout(adapter, on,
+                       if (request->perout.index < ptpci->n_per_out) {
+                               if (adapter->is_pci11x1x)
+                                       return lan743x_ptp_io_perout(adapter, on,
+                                                            &request->perout);
+                               else
+                                       return lan743x_ptp_perout(adapter, on,
                                                          &request->perout);
+                       }
                        return -EINVAL;
                case PTP_CLK_REQ_PPS:
                        return -EINVAL;
index 96d3a13..e26d4ef 100644 (file)
@@ -80,6 +80,7 @@ struct lan743x_ptp {
 
        unsigned long used_event_ch;
        struct lan743x_ptp_perout perout[LAN743X_PTP_N_PEROUT];
+       int ptp_io_perout[LAN743X_PTP_N_PEROUT]; /* PTP event channel (0=channel A, 1=channel B) */
        struct lan743x_extts extts[LAN743X_PTP_N_EXTTS];
 
        bool leds_multiplexed;