diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_hw.h b/drivers/net/ethernet/aquantia/atlantic/aq_hw.h index c36c0d7d038e..5246cf44ce51 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_hw.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_hw.h @@ -259,6 +259,16 @@ struct aq_hw_ops { int (*hw_set_sys_clock)(struct aq_hw_s *self, u64 time, u64 ts); + int (*hw_ts_to_sys_clock)(struct aq_hw_s *self, u64 ts, u64 *time); + + int (*hw_gpio_pulse)(struct aq_hw_s *self, u32 index, u64 start, + u32 period); + + int (*hw_extts_gpio_enable)(struct aq_hw_s *self, u32 index, + u32 enable); + + int (*hw_get_sync_ts)(struct aq_hw_s *self, u64 *ts); + u16 (*rx_extract_ts)(struct aq_hw_s *self, u8 *p, unsigned int len, u64 *timestamp); diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c index 1e12cedee11e..433adc099e44 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c @@ -152,6 +152,7 @@ static int aq_nic_update_link_status(struct aq_nic_s *self) aq_ptp_clock_init(self); aq_ptp_tm_offset_set(self, self->aq_hw->aq_link_status.mbps); + aq_ptp_link_change(self); } /* Driver has to update flow control settings on RX block diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c index 56613792abc8..3ec08415e53e 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c @@ -15,10 +15,13 @@ #include "aq_nic.h" #include "aq_ptp.h" #include "aq_ring.h" +#include "aq_phy.h" #include "aq_filters.h" #define AQ_PTP_TX_TIMEOUT (HZ * 10) +#define POLL_SYNC_TIMER_MS 15 + enum ptp_speed_offsets { ptp_offset_idx_10 = 0, ptp_offset_idx_100, @@ -68,6 +71,12 @@ struct aq_ptp_s { struct aq_rx_filter_l3l4 udp_filter; struct aq_rx_filter_l2 eth_type_filter; + + struct delayed_work poll_sync; + u32 poll_timeout_ms; + + bool extts_pin_enabled; + u64 last_sync1588_ts; }; struct ptp_tm_offset { @@ -348,6 +357,168 @@ static void aq_ptp_convert_to_hwtstamp(struct aq_ptp_s *aq_ptp, hwtstamp->hwtstamp = ns_to_ktime(timestamp); } +static int aq_ptp_hw_pin_conf(struct aq_nic_s *aq_nic, u32 pin_index, u64 start, + u64 period) +{ + if (period) + netdev_dbg(aq_nic->ndev, + "Enable GPIO %d pulsing, start time %llu, period %u\n", + pin_index, start, (u32)period); + else + netdev_dbg(aq_nic->ndev, + "Disable GPIO %d pulsing, start time %llu, period %u\n", + pin_index, start, (u32)period); + + /* Notify hardware of request to being sending pulses. + * If period is ZERO then pulsen is disabled. + */ + mutex_lock(&aq_nic->fwreq_mutex); + aq_nic->aq_hw_ops->hw_gpio_pulse(aq_nic->aq_hw, pin_index, + start, (u32)period); + mutex_unlock(&aq_nic->fwreq_mutex); + + return 0; +} + +static int aq_ptp_perout_pin_configure(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + struct aq_ptp_s *aq_ptp = container_of(ptp, struct aq_ptp_s, ptp_info); + struct ptp_clock_time *t = &rq->perout.period; + struct ptp_clock_time *s = &rq->perout.start; + struct aq_nic_s *aq_nic = aq_ptp->aq_nic; + u64 start, period; + u32 pin_index = rq->perout.index; + + /* verify the request channel is there */ + if (pin_index >= ptp->n_per_out) + return -EINVAL; + + /* we cannot support periods greater + * than 4 seconds due to reg limit + */ + if (t->sec > 4 || t->sec < 0) + return -ERANGE; + + /* convert to unsigned 64b ns, + * verify we can put it in a 32b register + */ + period = on ? t->sec * NSEC_PER_SEC + t->nsec : 0; + + /* verify the value is in range supported by hardware */ + if (period > U32_MAX) + return -ERANGE; + /* convert to unsigned 64b ns */ + /* TODO convert to AQ time */ + start = on ? s->sec * NSEC_PER_SEC + s->nsec : 0; + + aq_ptp_hw_pin_conf(aq_nic, pin_index, start, period); + + return 0; +} + +static int aq_ptp_pps_pin_configure(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + struct aq_ptp_s *aq_ptp = container_of(ptp, struct aq_ptp_s, ptp_info); + struct aq_nic_s *aq_nic = aq_ptp->aq_nic; + u64 start, period; + u32 pin_index = 0; + u32 rest = 0; + + /* verify the request channel is there */ + if (pin_index >= ptp->n_per_out) + return -EINVAL; + + aq_nic->aq_hw_ops->hw_get_ptp_ts(aq_nic->aq_hw, &start); + div_u64_rem(start, NSEC_PER_SEC, &rest); + period = on ? NSEC_PER_SEC : 0; /* PPS - pulse per second */ + start = on ? start - rest + NSEC_PER_SEC * + (rest > 990000000LL ? 2 : 1) : 0; + + aq_ptp_hw_pin_conf(aq_nic, pin_index, start, period); + + return 0; +} + +static void aq_ptp_extts_pin_ctrl(struct aq_ptp_s *aq_ptp) +{ + struct aq_nic_s *aq_nic = aq_ptp->aq_nic; + u32 enable = aq_ptp->extts_pin_enabled; + + if (aq_nic->aq_hw_ops->hw_extts_gpio_enable) + aq_nic->aq_hw_ops->hw_extts_gpio_enable(aq_nic->aq_hw, 0, + enable); +} + +static int aq_ptp_extts_pin_configure(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + struct aq_ptp_s *aq_ptp = container_of(ptp, struct aq_ptp_s, ptp_info); + + u32 pin_index = rq->extts.index; + + if (pin_index >= ptp->n_ext_ts) + return -EINVAL; + + aq_ptp->extts_pin_enabled = !!on; + if (on) { + aq_ptp->poll_timeout_ms = POLL_SYNC_TIMER_MS; + cancel_delayed_work_sync(&aq_ptp->poll_sync); + schedule_delayed_work(&aq_ptp->poll_sync, + msecs_to_jiffies(aq_ptp->poll_timeout_ms)); + } + + aq_ptp_extts_pin_ctrl(aq_ptp); + return 0; +} + +/* aq_ptp_gpio_feature_enable + * @ptp: the ptp clock structure + * @rq: the requested feature to change + * @on: whether to enable or disable the feature + */ +static int aq_ptp_gpio_feature_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + switch (rq->type) { + case PTP_CLK_REQ_EXTTS: + return aq_ptp_extts_pin_configure(ptp, rq, on); + case PTP_CLK_REQ_PEROUT: + return aq_ptp_perout_pin_configure(ptp, rq, on); + case PTP_CLK_REQ_PPS: + return aq_ptp_pps_pin_configure(ptp, rq, on); + default: + return -EOPNOTSUPP; + } + + return 0; +} + +/* aq_ptp_verify + * @ptp: the ptp clock structure + * @pin: index of the pin in question + * @func: the desired function to use + * @chan: the function channel index to use + */ +static int aq_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin, + enum ptp_pin_function func, unsigned int chan) +{ + /* verify the requested pin is there */ + if (!ptp->pin_config || pin >= ptp->n_pins) + return -EINVAL; + + /* enforce locked channels, no changing them */ + if (chan != ptp->pin_config[pin].chan) + return -EINVAL; + + /* we want to keep the functions locked as well */ + if (func != ptp->pin_config[pin].func) + return -EINVAL; + + return 0; +} + /* aq_ptp_tx_hwtstamp - utility function which checks for TX time stamp * @adapter: the private adapter struct * @@ -859,6 +1030,8 @@ void aq_ptp_ring_free(struct aq_nic_s *aq_nic) aq_ptp_skb_ring_release(&aq_ptp->skb_ring); } +#define MAX_PTP_GPIO_COUNT 4 + static struct ptp_clock_info aq_ptp_clock = { .owner = THIS_MODULE, .name = "atlantic ptp", @@ -870,7 +1043,9 @@ static struct ptp_clock_info aq_ptp_clock = { .gettime64 = aq_ptp_gettime, .settime64 = aq_ptp_settime, .n_per_out = 0, + .enable = aq_ptp_gpio_feature_enable, .n_pins = 0, + .verify = aq_ptp_verify, .pin_config = NULL, }; @@ -928,6 +1103,57 @@ static void aq_ptp_offset_init(const struct hw_aq_ptp_offset *offsets) aq_ptp_offset_init_from_fw(offsets); } +static void aq_ptp_gpio_init(struct ptp_clock_info *info, + struct hw_aq_info *hw_info) +{ + struct ptp_pin_desc pin_desc[MAX_PTP_GPIO_COUNT]; + u32 extts_pin_cnt = 0; + u32 out_pin_cnt = 0; + u32 i; + + memset(pin_desc, 0, sizeof(pin_desc)); + + for (i = 0; i < MAX_PTP_GPIO_COUNT - 1; i++) { + if (hw_info->gpio_pin[i] == + (GPIO_PIN_FUNCTION_PTP0 + out_pin_cnt)) { + snprintf(pin_desc[out_pin_cnt].name, + sizeof(pin_desc[out_pin_cnt].name), + "AQ_GPIO%d", i); + pin_desc[out_pin_cnt].index = out_pin_cnt; + pin_desc[out_pin_cnt].chan = out_pin_cnt; + pin_desc[out_pin_cnt++].func = PTP_PF_PEROUT; + } + } + + info->n_per_out = out_pin_cnt; + + if (hw_info->caps_ex & BIT(CAPS_EX_PHY_CTRL_TS_PIN)) { + extts_pin_cnt += 1; + + snprintf(pin_desc[out_pin_cnt].name, + sizeof(pin_desc[out_pin_cnt].name), + "AQ_GPIO%d", out_pin_cnt); + pin_desc[out_pin_cnt].index = out_pin_cnt; + pin_desc[out_pin_cnt].chan = 0; + pin_desc[out_pin_cnt].func = PTP_PF_EXTTS; + } + + info->n_pins = out_pin_cnt + extts_pin_cnt; + info->n_ext_ts = extts_pin_cnt; + + if (!info->n_pins) + return; + + info->pin_config = kcalloc(info->n_pins, sizeof(struct ptp_pin_desc), + GFP_KERNEL); + + if (!info->pin_config) + return; + + memcpy(info->pin_config, &pin_desc, + sizeof(struct ptp_pin_desc) * info->n_pins); +} + void aq_ptp_clock_init(struct aq_nic_s *aq_nic) { struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; @@ -937,6 +1163,8 @@ void aq_ptp_clock_init(struct aq_nic_s *aq_nic) aq_ptp_settime(&aq_ptp->ptp_info, &ts); } +static void aq_ptp_poll_sync_work_cb(struct work_struct *w); + int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_vec) { struct hw_atl_utils_mbox mbox; @@ -975,6 +1203,7 @@ int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_vec) spin_lock_init(&aq_ptp->ptp_ring_lock); aq_ptp->ptp_info = aq_ptp_clock; + aq_ptp_gpio_init(&aq_ptp->ptp_info, &mbox.info); clock = ptp_clock_register(&aq_ptp->ptp_info, &aq_nic->ndev->dev); if (!clock || IS_ERR(clock)) { netdev_err(aq_nic->ndev, "ptp_clock_register failed\n"); @@ -1001,6 +1230,7 @@ int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_vec) aq_ptp_clock_init(aq_nic); mutex_unlock(&aq_nic->fwreq_mutex); + INIT_DELAYED_WORK(&aq_ptp->poll_sync, &aq_ptp_poll_sync_work_cb); aq_ptp->eth_type_filter.location = aq_nic_reserve_filter(aq_nic, aq_rx_filter_ethertype); aq_ptp->udp_filter.location = @@ -1009,6 +1239,8 @@ int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_vec) return 0; err_exit: + if (aq_ptp) + kfree(aq_ptp->ptp_info.pin_config); kfree(aq_ptp); aq_nic->aq_ptp = NULL; return err; @@ -1035,11 +1267,14 @@ void aq_ptp_free(struct aq_nic_s *aq_nic) aq_ptp->eth_type_filter.location); aq_nic_release_filter(aq_nic, aq_rx_filter_l3l4, aq_ptp->udp_filter.location); + cancel_delayed_work_sync(&aq_ptp->poll_sync); /* disable ptp */ mutex_lock(&aq_nic->fwreq_mutex); aq_nic->aq_fw_ops->enable_ptp(aq_nic->aq_hw, 0); mutex_unlock(&aq_nic->fwreq_mutex); + kfree(aq_ptp->ptp_info.pin_config); + netif_napi_del(&aq_ptp->napi); kfree(aq_ptp); aq_nic->aq_ptp = NULL; @@ -1049,3 +1284,107 @@ struct ptp_clock *aq_ptp_get_ptp_clock(struct aq_ptp_s *aq_ptp) { return aq_ptp->ptp_clock; } + +/* PTP external GPIO nanoseconds count */ +static uint64_t aq_ptp_get_sync1588_ts(struct aq_nic_s *aq_nic) +{ + u64 ts = 0; + + if (aq_nic->aq_hw_ops->hw_get_sync_ts) + aq_nic->aq_hw_ops->hw_get_sync_ts(aq_nic->aq_hw, &ts); + + return ts; +} + +static void aq_ptp_start_work(struct aq_ptp_s *aq_ptp) +{ + if (aq_ptp->extts_pin_enabled) { + aq_ptp->poll_timeout_ms = POLL_SYNC_TIMER_MS; + aq_ptp->last_sync1588_ts = + aq_ptp_get_sync1588_ts(aq_ptp->aq_nic); + schedule_delayed_work(&aq_ptp->poll_sync, + msecs_to_jiffies(aq_ptp->poll_timeout_ms)); + } +} + +int aq_ptp_link_change(struct aq_nic_s *aq_nic) +{ + struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + + if (!aq_ptp) + return 0; + + if (aq_nic->aq_hw->aq_link_status.mbps) + aq_ptp_start_work(aq_ptp); + else + cancel_delayed_work_sync(&aq_ptp->poll_sync); + + return 0; +} + +static bool aq_ptp_sync_ts_updated(struct aq_ptp_s *aq_ptp, u64 *new_ts) +{ + struct aq_nic_s *aq_nic = aq_ptp->aq_nic; + u64 sync_ts2; + u64 sync_ts; + + sync_ts = aq_ptp_get_sync1588_ts(aq_nic); + + if (sync_ts != aq_ptp->last_sync1588_ts) { + sync_ts2 = aq_ptp_get_sync1588_ts(aq_nic); + if (sync_ts != sync_ts2) { + sync_ts = sync_ts2; + sync_ts2 = aq_ptp_get_sync1588_ts(aq_nic); + if (sync_ts != sync_ts2) { + netdev_err(aq_nic->ndev, + "%s: Unable to get correct GPIO TS", + __func__); + sync_ts = 0; + } + } + + *new_ts = sync_ts; + return true; + } + return false; +} + +static int aq_ptp_check_sync1588(struct aq_ptp_s *aq_ptp) +{ + struct aq_nic_s *aq_nic = aq_ptp->aq_nic; + u64 sync_ts; + + /* Sync1588 pin was triggered */ + if (aq_ptp_sync_ts_updated(aq_ptp, &sync_ts)) { + if (aq_ptp->extts_pin_enabled) { + struct ptp_clock_event ptp_event; + u64 time = 0; + + aq_nic->aq_hw_ops->hw_ts_to_sys_clock(aq_nic->aq_hw, + sync_ts, &time); + ptp_event.index = aq_ptp->ptp_info.n_pins - 1; + ptp_event.timestamp = time; + + ptp_event.type = PTP_CLOCK_EXTTS; + ptp_clock_event(aq_ptp->ptp_clock, &ptp_event); + } + + aq_ptp->last_sync1588_ts = sync_ts; + } + + return 0; +} + +void aq_ptp_poll_sync_work_cb(struct work_struct *w) +{ + struct delayed_work *dw = to_delayed_work(w); + struct aq_ptp_s *aq_ptp = container_of(dw, struct aq_ptp_s, poll_sync); + + aq_ptp_check_sync1588(aq_ptp); + + if (aq_ptp->extts_pin_enabled) { + unsigned long timeout = msecs_to_jiffies(aq_ptp->poll_timeout_ms); + + schedule_delayed_work(&aq_ptp->poll_sync, timeout); + } +} diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.h b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.h index 7a7f36f43ce0..3de4682f7c06 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.h @@ -52,4 +52,6 @@ u16 aq_ptp_extract_ts(struct aq_nic_s *aq_nic, struct sk_buff *skb, u8 *p, struct ptp_clock *aq_ptp_get_ptp_clock(struct aq_ptp_s *aq_ptp); +int aq_ptp_link_change(struct aq_nic_s *aq_nic); + #endif /* AQ_PTP_H */ diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c index 56cec2ea0af0..51ecf87e0198 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c @@ -10,6 +10,7 @@ #include "../aq_hw_utils.h" #include "../aq_ring.h" #include "../aq_nic.h" +#include "../aq_phy.h" #include "hw_atl_b0.h" #include "hw_atl_utils.h" #include "hw_atl_llh.h" @@ -1151,6 +1152,12 @@ static int hw_atl_b0_set_sys_clock(struct aq_hw_s *self, u64 time, u64 ts) return hw_atl_b0_adj_sys_clock(self, delta); } +int hw_atl_b0_ts_to_sys_clock(struct aq_hw_s *self, u64 ts, u64 *time) +{ + *time = self->ptp_clk_offset + ts; + return 0; +} + static int hw_atl_b0_adj_clock_freq(struct aq_hw_s *self, s32 ppb) { struct hw_fw_request_iface fwreq; @@ -1173,6 +1180,57 @@ static int hw_atl_b0_adj_clock_freq(struct aq_hw_s *self, s32 ppb) return self->aq_fw_ops->send_fw_request(self, &fwreq, size); } +static int hw_atl_b0_gpio_pulse(struct aq_hw_s *self, u32 index, + u64 start, u32 period) +{ + struct hw_fw_request_iface fwreq; + size_t size; + + memset(&fwreq, 0, sizeof(fwreq)); + + fwreq.msg_id = HW_AQ_FW_REQUEST_PTP_GPIO_CTRL; + fwreq.ptp_gpio_ctrl.index = index; + fwreq.ptp_gpio_ctrl.period = period; + /* Apply time offset */ + fwreq.ptp_gpio_ctrl.start = start - self->ptp_clk_offset; + + size = sizeof(fwreq.msg_id) + sizeof(fwreq.ptp_gpio_ctrl); + return self->aq_fw_ops->send_fw_request(self, &fwreq, size); +} + +static int hw_atl_b0_extts_gpio_enable(struct aq_hw_s *self, u32 index, + u32 enable) +{ + /* Enable/disable Sync1588 GPIO Timestamping */ + aq_phy_write_reg(self, MDIO_MMD_PCS, 0xc611, enable ? 0x71 : 0); + + return 0; +} + +static int hw_atl_b0_get_sync_ts(struct aq_hw_s *self, u64 *ts) +{ + u64 sec_l; + u64 sec_h; + u64 nsec_l; + u64 nsec_h; + + if (!ts) + return -1; + + /* PTP external GPIO clock seconds count 15:0 */ + sec_l = aq_phy_read_reg(self, MDIO_MMD_PCS, 0xc914); + /* PTP external GPIO clock seconds count 31:16 */ + sec_h = aq_phy_read_reg(self, MDIO_MMD_PCS, 0xc915); + /* PTP external GPIO clock nanoseconds count 15:0 */ + nsec_l = aq_phy_read_reg(self, MDIO_MMD_PCS, 0xc916); + /* PTP external GPIO clock nanoseconds count 31:16 */ + nsec_h = aq_phy_read_reg(self, MDIO_MMD_PCS, 0xc917); + + *ts = (nsec_h << 16) + nsec_l + ((sec_h << 16) + sec_l) * NSEC_PER_SEC; + + return 0; +} + static u16 hw_atl_b0_rx_extract_ts(struct aq_hw_s *self, u8 *p, unsigned int len, u64 *timestamp) { @@ -1416,8 +1474,11 @@ const struct aq_hw_ops hw_atl_ops_b0 = { .hw_get_ptp_ts = hw_atl_b0_get_ptp_ts, .hw_adj_sys_clock = hw_atl_b0_adj_sys_clock, .hw_set_sys_clock = hw_atl_b0_set_sys_clock, + .hw_ts_to_sys_clock = hw_atl_b0_ts_to_sys_clock, .hw_adj_clock_freq = hw_atl_b0_adj_clock_freq, - + .hw_gpio_pulse = hw_atl_b0_gpio_pulse, + .hw_extts_gpio_enable = hw_atl_b0_extts_gpio_enable, + .hw_get_sync_ts = hw_atl_b0_get_sync_ts, .rx_extract_ts = hw_atl_b0_rx_extract_ts, .extract_hwts = hw_atl_b0_extract_hwts, .hw_set_offload = hw_atl_b0_hw_offload_set, diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h index 77132bda4696..37e6b696009d 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h @@ -288,6 +288,12 @@ struct __packed offload_info { }; /* Mailbox FW Request interface */ +struct __packed hw_fw_request_ptp_gpio_ctrl { + u32 index; + u32 period; + u64 start; +}; + struct __packed hw_fw_request_ptp_adj_freq { u32 ns_mac; u32 fns_mac; @@ -303,6 +309,7 @@ struct __packed hw_fw_request_ptp_adj_clock { int sign; }; +#define HW_AQ_FW_REQUEST_PTP_GPIO_CTRL 0x11 #define HW_AQ_FW_REQUEST_PTP_ADJ_FREQ 0x12 #define HW_AQ_FW_REQUEST_PTP_ADJ_CLOCK 0x13 @@ -310,6 +317,7 @@ struct __packed hw_fw_request_iface { u32 msg_id; union { /* PTP FW Request */ + struct hw_fw_request_ptp_gpio_ctrl ptp_gpio_ctrl; struct hw_fw_request_ptp_adj_freq ptp_adj_freq; struct hw_fw_request_ptp_adj_clock ptp_adj_clock; };