162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Microchip KSZ PTP Implementation 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2020 ARRI Lighting 562306a36Sopenharmony_ci * Copyright (C) 2022 Microchip Technology Inc. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/dsa/ksz_common.h> 962306a36Sopenharmony_ci#include <linux/irq.h> 1062306a36Sopenharmony_ci#include <linux/irqdomain.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/ptp_classify.h> 1362306a36Sopenharmony_ci#include <linux/ptp_clock_kernel.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "ksz_common.h" 1662306a36Sopenharmony_ci#include "ksz_ptp.h" 1762306a36Sopenharmony_ci#include "ksz_ptp_reg.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define ptp_caps_to_data(d) container_of((d), struct ksz_ptp_data, caps) 2062306a36Sopenharmony_ci#define ptp_data_to_ksz_dev(d) container_of((d), struct ksz_device, ptp_data) 2162306a36Sopenharmony_ci#define work_to_xmit_work(w) \ 2262306a36Sopenharmony_ci container_of((w), struct ksz_deferred_xmit_work, work) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* Sub-nanoseconds-adj,max * sub-nanoseconds / 40ns * 1ns 2562306a36Sopenharmony_ci * = (2^30-1) * (2 ^ 32) / 40 ns * 1 ns = 6249999 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci#define KSZ_MAX_DRIFT_CORR 6249999 2862306a36Sopenharmony_ci#define KSZ_MAX_PULSE_WIDTH 125000000LL 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define KSZ_PTP_INC_NS 40ULL /* HW clock is incremented every 40 ns (by 40) */ 3162306a36Sopenharmony_ci#define KSZ_PTP_SUBNS_BITS 32 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define KSZ_PTP_INT_START 13 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int ksz_ptp_tou_gpio(struct ksz_device *dev) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci int ret; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci if (!is_lan937x(dev)) 4062306a36Sopenharmony_ci return 0; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci ret = ksz_rmw32(dev, REG_PTP_CTRL_STAT__4, GPIO_OUT, 4362306a36Sopenharmony_ci GPIO_OUT); 4462306a36Sopenharmony_ci if (ret) 4562306a36Sopenharmony_ci return ret; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci ret = ksz_rmw32(dev, REG_SW_GLOBAL_LED_OVR__4, LED_OVR_1 | LED_OVR_2, 4862306a36Sopenharmony_ci LED_OVR_1 | LED_OVR_2); 4962306a36Sopenharmony_ci if (ret) 5062306a36Sopenharmony_ci return ret; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci return ksz_rmw32(dev, REG_SW_GLOBAL_LED_SRC__4, 5362306a36Sopenharmony_ci LED_SRC_PTP_GPIO_1 | LED_SRC_PTP_GPIO_2, 5462306a36Sopenharmony_ci LED_SRC_PTP_GPIO_1 | LED_SRC_PTP_GPIO_2); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int ksz_ptp_tou_reset(struct ksz_device *dev, u8 unit) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci u32 data; 6062306a36Sopenharmony_ci int ret; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* Reset trigger unit (clears TRIGGER_EN, but not GPIOSTATx) */ 6362306a36Sopenharmony_ci ret = ksz_rmw32(dev, REG_PTP_CTRL_STAT__4, TRIG_RESET, TRIG_RESET); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci data = FIELD_PREP(TRIG_DONE_M, BIT(unit)); 6662306a36Sopenharmony_ci ret = ksz_write32(dev, REG_PTP_TRIG_STATUS__4, data); 6762306a36Sopenharmony_ci if (ret) 6862306a36Sopenharmony_ci return ret; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci data = FIELD_PREP(TRIG_INT_M, BIT(unit)); 7162306a36Sopenharmony_ci ret = ksz_write32(dev, REG_PTP_INT_STATUS__4, data); 7262306a36Sopenharmony_ci if (ret) 7362306a36Sopenharmony_ci return ret; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* Clear reset and set GPIO direction */ 7662306a36Sopenharmony_ci return ksz_rmw32(dev, REG_PTP_CTRL_STAT__4, (TRIG_RESET | TRIG_ENABLE), 7762306a36Sopenharmony_ci 0); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic int ksz_ptp_tou_pulse_verify(u64 pulse_ns) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci u32 data; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (pulse_ns & 0x3) 8562306a36Sopenharmony_ci return -EINVAL; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci data = (pulse_ns / 8); 8862306a36Sopenharmony_ci if (!FIELD_FIT(TRIG_PULSE_WIDTH_M, data)) 8962306a36Sopenharmony_ci return -ERANGE; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return 0; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic int ksz_ptp_tou_target_time_set(struct ksz_device *dev, 9562306a36Sopenharmony_ci struct timespec64 const *ts) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci int ret; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* Hardware has only 32 bit */ 10062306a36Sopenharmony_ci if ((ts->tv_sec & 0xffffffff) != ts->tv_sec) 10162306a36Sopenharmony_ci return -EINVAL; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci ret = ksz_write32(dev, REG_TRIG_TARGET_NANOSEC, ts->tv_nsec); 10462306a36Sopenharmony_ci if (ret) 10562306a36Sopenharmony_ci return ret; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci ret = ksz_write32(dev, REG_TRIG_TARGET_SEC, ts->tv_sec); 10862306a36Sopenharmony_ci if (ret) 10962306a36Sopenharmony_ci return ret; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return 0; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic int ksz_ptp_tou_start(struct ksz_device *dev, u8 unit) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci u32 data; 11762306a36Sopenharmony_ci int ret; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci ret = ksz_rmw32(dev, REG_PTP_CTRL_STAT__4, TRIG_ENABLE, TRIG_ENABLE); 12062306a36Sopenharmony_ci if (ret) 12162306a36Sopenharmony_ci return ret; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* Check error flag: 12462306a36Sopenharmony_ci * - the ACTIVE flag is NOT cleared an error! 12562306a36Sopenharmony_ci */ 12662306a36Sopenharmony_ci ret = ksz_read32(dev, REG_PTP_TRIG_STATUS__4, &data); 12762306a36Sopenharmony_ci if (ret) 12862306a36Sopenharmony_ci return ret; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (FIELD_GET(TRIG_ERROR_M, data) & (1 << unit)) { 13162306a36Sopenharmony_ci dev_err(dev->dev, "%s: Trigger unit%d error!\n", __func__, 13262306a36Sopenharmony_ci unit); 13362306a36Sopenharmony_ci ret = -EIO; 13462306a36Sopenharmony_ci /* Unit will be reset on next access */ 13562306a36Sopenharmony_ci return ret; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic int ksz_ptp_configure_perout(struct ksz_device *dev, 14262306a36Sopenharmony_ci u32 cycle_width_ns, u32 pulse_width_ns, 14362306a36Sopenharmony_ci struct timespec64 const *target_time, 14462306a36Sopenharmony_ci u8 index) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci u32 data; 14762306a36Sopenharmony_ci int ret; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci data = FIELD_PREP(TRIG_NOTIFY, 1) | 15062306a36Sopenharmony_ci FIELD_PREP(TRIG_GPO_M, index) | 15162306a36Sopenharmony_ci FIELD_PREP(TRIG_PATTERN_M, TRIG_POS_PERIOD); 15262306a36Sopenharmony_ci ret = ksz_write32(dev, REG_TRIG_CTRL__4, data); 15362306a36Sopenharmony_ci if (ret) 15462306a36Sopenharmony_ci return ret; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci ret = ksz_write32(dev, REG_TRIG_CYCLE_WIDTH, cycle_width_ns); 15762306a36Sopenharmony_ci if (ret) 15862306a36Sopenharmony_ci return ret; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Set cycle count 0 - Infinite */ 16162306a36Sopenharmony_ci ret = ksz_rmw32(dev, REG_TRIG_CYCLE_CNT, TRIG_CYCLE_CNT_M, 0); 16262306a36Sopenharmony_ci if (ret) 16362306a36Sopenharmony_ci return ret; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci data = (pulse_width_ns / 8); 16662306a36Sopenharmony_ci ret = ksz_write32(dev, REG_TRIG_PULSE_WIDTH__4, data); 16762306a36Sopenharmony_ci if (ret) 16862306a36Sopenharmony_ci return ret; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci ret = ksz_ptp_tou_target_time_set(dev, target_time); 17162306a36Sopenharmony_ci if (ret) 17262306a36Sopenharmony_ci return ret; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return 0; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic int ksz_ptp_enable_perout(struct ksz_device *dev, 17862306a36Sopenharmony_ci struct ptp_perout_request const *request, 17962306a36Sopenharmony_ci int on) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct ksz_ptp_data *ptp_data = &dev->ptp_data; 18262306a36Sopenharmony_ci u64 req_pulse_width_ns; 18362306a36Sopenharmony_ci u64 cycle_width_ns; 18462306a36Sopenharmony_ci u64 pulse_width_ns; 18562306a36Sopenharmony_ci int pin = 0; 18662306a36Sopenharmony_ci u32 data32; 18762306a36Sopenharmony_ci int ret; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (request->flags & ~PTP_PEROUT_DUTY_CYCLE) 19062306a36Sopenharmony_ci return -EOPNOTSUPP; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (ptp_data->tou_mode != KSZ_PTP_TOU_PEROUT && 19362306a36Sopenharmony_ci ptp_data->tou_mode != KSZ_PTP_TOU_IDLE) 19462306a36Sopenharmony_ci return -EBUSY; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci pin = ptp_find_pin(ptp_data->clock, PTP_PF_PEROUT, request->index); 19762306a36Sopenharmony_ci if (pin < 0) 19862306a36Sopenharmony_ci return -EINVAL; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci data32 = FIELD_PREP(PTP_GPIO_INDEX, pin) | 20162306a36Sopenharmony_ci FIELD_PREP(PTP_TOU_INDEX, request->index); 20262306a36Sopenharmony_ci ret = ksz_rmw32(dev, REG_PTP_UNIT_INDEX__4, 20362306a36Sopenharmony_ci PTP_GPIO_INDEX | PTP_TOU_INDEX, data32); 20462306a36Sopenharmony_ci if (ret) 20562306a36Sopenharmony_ci return ret; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci ret = ksz_ptp_tou_reset(dev, request->index); 20862306a36Sopenharmony_ci if (ret) 20962306a36Sopenharmony_ci return ret; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (!on) { 21262306a36Sopenharmony_ci ptp_data->tou_mode = KSZ_PTP_TOU_IDLE; 21362306a36Sopenharmony_ci return 0; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci ptp_data->perout_target_time_first.tv_sec = request->start.sec; 21762306a36Sopenharmony_ci ptp_data->perout_target_time_first.tv_nsec = request->start.nsec; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci ptp_data->perout_period.tv_sec = request->period.sec; 22062306a36Sopenharmony_ci ptp_data->perout_period.tv_nsec = request->period.nsec; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci cycle_width_ns = timespec64_to_ns(&ptp_data->perout_period); 22362306a36Sopenharmony_ci if ((cycle_width_ns & TRIG_CYCLE_WIDTH_M) != cycle_width_ns) 22462306a36Sopenharmony_ci return -EINVAL; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (request->flags & PTP_PEROUT_DUTY_CYCLE) { 22762306a36Sopenharmony_ci pulse_width_ns = request->on.sec * NSEC_PER_SEC + 22862306a36Sopenharmony_ci request->on.nsec; 22962306a36Sopenharmony_ci } else { 23062306a36Sopenharmony_ci /* Use a duty cycle of 50%. Maximum pulse width supported by the 23162306a36Sopenharmony_ci * hardware is a little bit more than 125 ms. 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_ci req_pulse_width_ns = (request->period.sec * NSEC_PER_SEC + 23462306a36Sopenharmony_ci request->period.nsec) / 2; 23562306a36Sopenharmony_ci pulse_width_ns = min_t(u64, req_pulse_width_ns, 23662306a36Sopenharmony_ci KSZ_MAX_PULSE_WIDTH); 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci ret = ksz_ptp_tou_pulse_verify(pulse_width_ns); 24062306a36Sopenharmony_ci if (ret) 24162306a36Sopenharmony_ci return ret; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci ret = ksz_ptp_configure_perout(dev, cycle_width_ns, pulse_width_ns, 24462306a36Sopenharmony_ci &ptp_data->perout_target_time_first, 24562306a36Sopenharmony_ci pin); 24662306a36Sopenharmony_ci if (ret) 24762306a36Sopenharmony_ci return ret; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci ret = ksz_ptp_tou_gpio(dev); 25062306a36Sopenharmony_ci if (ret) 25162306a36Sopenharmony_ci return ret; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci ret = ksz_ptp_tou_start(dev, request->index); 25462306a36Sopenharmony_ci if (ret) 25562306a36Sopenharmony_ci return ret; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci ptp_data->tou_mode = KSZ_PTP_TOU_PEROUT; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci return 0; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic int ksz_ptp_enable_mode(struct ksz_device *dev) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci struct ksz_tagger_data *tagger_data = ksz_tagger_data(dev->ds); 26562306a36Sopenharmony_ci struct ksz_ptp_data *ptp_data = &dev->ptp_data; 26662306a36Sopenharmony_ci struct ksz_port *prt; 26762306a36Sopenharmony_ci struct dsa_port *dp; 26862306a36Sopenharmony_ci bool tag_en = false; 26962306a36Sopenharmony_ci int ret; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci dsa_switch_for_each_user_port(dp, dev->ds) { 27262306a36Sopenharmony_ci prt = &dev->ports[dp->index]; 27362306a36Sopenharmony_ci if (prt->hwts_tx_en || prt->hwts_rx_en) { 27462306a36Sopenharmony_ci tag_en = true; 27562306a36Sopenharmony_ci break; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (tag_en) { 28062306a36Sopenharmony_ci ret = ptp_schedule_worker(ptp_data->clock, 0); 28162306a36Sopenharmony_ci if (ret) 28262306a36Sopenharmony_ci return ret; 28362306a36Sopenharmony_ci } else { 28462306a36Sopenharmony_ci ptp_cancel_worker_sync(ptp_data->clock); 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci tagger_data->hwtstamp_set_state(dev->ds, tag_en); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci return ksz_rmw16(dev, REG_PTP_MSG_CONF1, PTP_ENABLE, 29062306a36Sopenharmony_ci tag_en ? PTP_ENABLE : 0); 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci/* The function is return back the capability of timestamping feature when 29462306a36Sopenharmony_ci * requested through ethtool -T <interface> utility 29562306a36Sopenharmony_ci */ 29662306a36Sopenharmony_ciint ksz_get_ts_info(struct dsa_switch *ds, int port, struct ethtool_ts_info *ts) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct ksz_device *dev = ds->priv; 29962306a36Sopenharmony_ci struct ksz_ptp_data *ptp_data; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci ptp_data = &dev->ptp_data; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (!ptp_data->clock) 30462306a36Sopenharmony_ci return -ENODEV; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci ts->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | 30762306a36Sopenharmony_ci SOF_TIMESTAMPING_RX_HARDWARE | 30862306a36Sopenharmony_ci SOF_TIMESTAMPING_RAW_HARDWARE; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci ts->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ONESTEP_P2P); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (is_lan937x(dev)) 31362306a36Sopenharmony_ci ts->tx_types |= BIT(HWTSTAMP_TX_ON); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci ts->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | 31662306a36Sopenharmony_ci BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT) | 31762306a36Sopenharmony_ci BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | 31862306a36Sopenharmony_ci BIT(HWTSTAMP_FILTER_PTP_V2_EVENT); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci ts->phc_index = ptp_clock_index(ptp_data->clock); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ciint ksz_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci struct ksz_device *dev = ds->priv; 32862306a36Sopenharmony_ci struct hwtstamp_config *config; 32962306a36Sopenharmony_ci struct ksz_port *prt; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci prt = &dev->ports[port]; 33262306a36Sopenharmony_ci config = &prt->tstamp_config; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ? 33562306a36Sopenharmony_ci -EFAULT : 0; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic int ksz_set_hwtstamp_config(struct ksz_device *dev, 33962306a36Sopenharmony_ci struct ksz_port *prt, 34062306a36Sopenharmony_ci struct hwtstamp_config *config) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci int ret; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (config->flags) 34562306a36Sopenharmony_ci return -EINVAL; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci switch (config->tx_type) { 34862306a36Sopenharmony_ci case HWTSTAMP_TX_OFF: 34962306a36Sopenharmony_ci prt->ptpmsg_irq[KSZ_SYNC_MSG].ts_en = false; 35062306a36Sopenharmony_ci prt->ptpmsg_irq[KSZ_XDREQ_MSG].ts_en = false; 35162306a36Sopenharmony_ci prt->ptpmsg_irq[KSZ_PDRES_MSG].ts_en = false; 35262306a36Sopenharmony_ci prt->hwts_tx_en = false; 35362306a36Sopenharmony_ci break; 35462306a36Sopenharmony_ci case HWTSTAMP_TX_ONESTEP_P2P: 35562306a36Sopenharmony_ci prt->ptpmsg_irq[KSZ_SYNC_MSG].ts_en = false; 35662306a36Sopenharmony_ci prt->ptpmsg_irq[KSZ_XDREQ_MSG].ts_en = true; 35762306a36Sopenharmony_ci prt->ptpmsg_irq[KSZ_PDRES_MSG].ts_en = false; 35862306a36Sopenharmony_ci prt->hwts_tx_en = true; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci ret = ksz_rmw16(dev, REG_PTP_MSG_CONF1, PTP_1STEP, PTP_1STEP); 36162306a36Sopenharmony_ci if (ret) 36262306a36Sopenharmony_ci return ret; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci break; 36562306a36Sopenharmony_ci case HWTSTAMP_TX_ON: 36662306a36Sopenharmony_ci if (!is_lan937x(dev)) 36762306a36Sopenharmony_ci return -ERANGE; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci prt->ptpmsg_irq[KSZ_SYNC_MSG].ts_en = true; 37062306a36Sopenharmony_ci prt->ptpmsg_irq[KSZ_XDREQ_MSG].ts_en = true; 37162306a36Sopenharmony_ci prt->ptpmsg_irq[KSZ_PDRES_MSG].ts_en = true; 37262306a36Sopenharmony_ci prt->hwts_tx_en = true; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci ret = ksz_rmw16(dev, REG_PTP_MSG_CONF1, PTP_1STEP, 0); 37562306a36Sopenharmony_ci if (ret) 37662306a36Sopenharmony_ci return ret; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci break; 37962306a36Sopenharmony_ci default: 38062306a36Sopenharmony_ci return -ERANGE; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci switch (config->rx_filter) { 38462306a36Sopenharmony_ci case HWTSTAMP_FILTER_NONE: 38562306a36Sopenharmony_ci prt->hwts_rx_en = false; 38662306a36Sopenharmony_ci break; 38762306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: 38862306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: 38962306a36Sopenharmony_ci config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; 39062306a36Sopenharmony_ci prt->hwts_rx_en = true; 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 39362306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: 39462306a36Sopenharmony_ci config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; 39562306a36Sopenharmony_ci prt->hwts_rx_en = true; 39662306a36Sopenharmony_ci break; 39762306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_EVENT: 39862306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_SYNC: 39962306a36Sopenharmony_ci config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; 40062306a36Sopenharmony_ci prt->hwts_rx_en = true; 40162306a36Sopenharmony_ci break; 40262306a36Sopenharmony_ci default: 40362306a36Sopenharmony_ci config->rx_filter = HWTSTAMP_FILTER_NONE; 40462306a36Sopenharmony_ci return -ERANGE; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci return ksz_ptp_enable_mode(dev); 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ciint ksz_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct ksz_device *dev = ds->priv; 41362306a36Sopenharmony_ci struct hwtstamp_config config; 41462306a36Sopenharmony_ci struct ksz_port *prt; 41562306a36Sopenharmony_ci int ret; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci prt = &dev->ports[port]; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) 42062306a36Sopenharmony_ci return -EFAULT; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci ret = ksz_set_hwtstamp_config(dev, prt, &config); 42362306a36Sopenharmony_ci if (ret) 42462306a36Sopenharmony_ci return ret; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci memcpy(&prt->tstamp_config, &config, sizeof(config)); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (copy_to_user(ifr->ifr_data, &config, sizeof(config))) 42962306a36Sopenharmony_ci return -EFAULT; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return 0; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic ktime_t ksz_tstamp_reconstruct(struct ksz_device *dev, ktime_t tstamp) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct timespec64 ptp_clock_time; 43762306a36Sopenharmony_ci struct ksz_ptp_data *ptp_data; 43862306a36Sopenharmony_ci struct timespec64 diff; 43962306a36Sopenharmony_ci struct timespec64 ts; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci ptp_data = &dev->ptp_data; 44262306a36Sopenharmony_ci ts = ktime_to_timespec64(tstamp); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci spin_lock_bh(&ptp_data->clock_lock); 44562306a36Sopenharmony_ci ptp_clock_time = ptp_data->clock_time; 44662306a36Sopenharmony_ci spin_unlock_bh(&ptp_data->clock_lock); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* calculate full time from partial time stamp */ 44962306a36Sopenharmony_ci ts.tv_sec = (ptp_clock_time.tv_sec & ~3) | ts.tv_sec; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* find nearest possible point in time */ 45262306a36Sopenharmony_ci diff = timespec64_sub(ts, ptp_clock_time); 45362306a36Sopenharmony_ci if (diff.tv_sec > 2) 45462306a36Sopenharmony_ci ts.tv_sec -= 4; 45562306a36Sopenharmony_ci else if (diff.tv_sec < -2) 45662306a36Sopenharmony_ci ts.tv_sec += 4; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci return timespec64_to_ktime(ts); 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cibool ksz_port_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb, 46262306a36Sopenharmony_ci unsigned int type) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb); 46562306a36Sopenharmony_ci struct ksz_device *dev = ds->priv; 46662306a36Sopenharmony_ci struct ptp_header *ptp_hdr; 46762306a36Sopenharmony_ci struct ksz_port *prt; 46862306a36Sopenharmony_ci u8 ptp_msg_type; 46962306a36Sopenharmony_ci ktime_t tstamp; 47062306a36Sopenharmony_ci s64 correction; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci prt = &dev->ports[port]; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci tstamp = KSZ_SKB_CB(skb)->tstamp; 47562306a36Sopenharmony_ci memset(hwtstamps, 0, sizeof(*hwtstamps)); 47662306a36Sopenharmony_ci hwtstamps->hwtstamp = ksz_tstamp_reconstruct(dev, tstamp); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (prt->tstamp_config.tx_type != HWTSTAMP_TX_ONESTEP_P2P) 47962306a36Sopenharmony_ci goto out; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci ptp_hdr = ptp_parse_header(skb, type); 48262306a36Sopenharmony_ci if (!ptp_hdr) 48362306a36Sopenharmony_ci goto out; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci ptp_msg_type = ptp_get_msgtype(ptp_hdr, type); 48662306a36Sopenharmony_ci if (ptp_msg_type != PTP_MSGTYPE_PDELAY_REQ) 48762306a36Sopenharmony_ci goto out; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* Only subtract the partial time stamp from the correction field. When 49062306a36Sopenharmony_ci * the hardware adds the egress time stamp to the correction field of 49162306a36Sopenharmony_ci * the PDelay_Resp message on tx, also only the partial time stamp will 49262306a36Sopenharmony_ci * be added. 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_ci correction = (s64)get_unaligned_be64(&ptp_hdr->correction); 49562306a36Sopenharmony_ci correction -= ktime_to_ns(tstamp) << 16; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci ptp_header_update_correction(skb, type, ptp_hdr, correction); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ciout: 50062306a36Sopenharmony_ci return false; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_civoid ksz_port_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci struct ksz_device *dev = ds->priv; 50662306a36Sopenharmony_ci struct ptp_header *hdr; 50762306a36Sopenharmony_ci struct sk_buff *clone; 50862306a36Sopenharmony_ci struct ksz_port *prt; 50962306a36Sopenharmony_ci unsigned int type; 51062306a36Sopenharmony_ci u8 ptp_msg_type; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci prt = &dev->ports[port]; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (!prt->hwts_tx_en) 51562306a36Sopenharmony_ci return; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci type = ptp_classify_raw(skb); 51862306a36Sopenharmony_ci if (type == PTP_CLASS_NONE) 51962306a36Sopenharmony_ci return; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci hdr = ptp_parse_header(skb, type); 52262306a36Sopenharmony_ci if (!hdr) 52362306a36Sopenharmony_ci return; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci ptp_msg_type = ptp_get_msgtype(hdr, type); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci switch (ptp_msg_type) { 52862306a36Sopenharmony_ci case PTP_MSGTYPE_SYNC: 52962306a36Sopenharmony_ci if (prt->tstamp_config.tx_type == HWTSTAMP_TX_ONESTEP_P2P) 53062306a36Sopenharmony_ci return; 53162306a36Sopenharmony_ci break; 53262306a36Sopenharmony_ci case PTP_MSGTYPE_PDELAY_REQ: 53362306a36Sopenharmony_ci break; 53462306a36Sopenharmony_ci case PTP_MSGTYPE_PDELAY_RESP: 53562306a36Sopenharmony_ci if (prt->tstamp_config.tx_type == HWTSTAMP_TX_ONESTEP_P2P) { 53662306a36Sopenharmony_ci KSZ_SKB_CB(skb)->ptp_type = type; 53762306a36Sopenharmony_ci KSZ_SKB_CB(skb)->update_correction = true; 53862306a36Sopenharmony_ci return; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci break; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci default: 54362306a36Sopenharmony_ci return; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci clone = skb_clone_sk(skb); 54762306a36Sopenharmony_ci if (!clone) 54862306a36Sopenharmony_ci return; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* caching the value to be used in tag_ksz.c */ 55162306a36Sopenharmony_ci KSZ_SKB_CB(skb)->clone = clone; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic void ksz_ptp_txtstamp_skb(struct ksz_device *dev, 55562306a36Sopenharmony_ci struct ksz_port *prt, struct sk_buff *skb) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci struct skb_shared_hwtstamps hwtstamps = {}; 55862306a36Sopenharmony_ci int ret; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* timeout must include DSA master to transmit data, tstamp latency, 56162306a36Sopenharmony_ci * IRQ latency and time for reading the time stamp. 56262306a36Sopenharmony_ci */ 56362306a36Sopenharmony_ci ret = wait_for_completion_timeout(&prt->tstamp_msg_comp, 56462306a36Sopenharmony_ci msecs_to_jiffies(100)); 56562306a36Sopenharmony_ci if (!ret) 56662306a36Sopenharmony_ci return; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci hwtstamps.hwtstamp = prt->tstamp_msg; 56962306a36Sopenharmony_ci skb_complete_tx_timestamp(skb, &hwtstamps); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_civoid ksz_port_deferred_xmit(struct kthread_work *work) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci struct ksz_deferred_xmit_work *xmit_work = work_to_xmit_work(work); 57562306a36Sopenharmony_ci struct sk_buff *clone, *skb = xmit_work->skb; 57662306a36Sopenharmony_ci struct dsa_switch *ds = xmit_work->dp->ds; 57762306a36Sopenharmony_ci struct ksz_device *dev = ds->priv; 57862306a36Sopenharmony_ci struct ksz_port *prt; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci prt = &dev->ports[xmit_work->dp->index]; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci clone = KSZ_SKB_CB(skb)->clone; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci reinit_completion(&prt->tstamp_msg_comp); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci dsa_enqueue_skb(skb, skb->dev); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci ksz_ptp_txtstamp_skb(dev, prt, clone); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci kfree(xmit_work); 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic int _ksz_ptp_gettime(struct ksz_device *dev, struct timespec64 *ts) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci u32 nanoseconds; 59862306a36Sopenharmony_ci u32 seconds; 59962306a36Sopenharmony_ci u8 phase; 60062306a36Sopenharmony_ci int ret; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci /* Copy current PTP clock into shadow registers and read */ 60362306a36Sopenharmony_ci ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_READ_TIME, PTP_READ_TIME); 60462306a36Sopenharmony_ci if (ret) 60562306a36Sopenharmony_ci return ret; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci ret = ksz_read8(dev, REG_PTP_RTC_SUB_NANOSEC__2, &phase); 60862306a36Sopenharmony_ci if (ret) 60962306a36Sopenharmony_ci return ret; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci ret = ksz_read32(dev, REG_PTP_RTC_NANOSEC, &nanoseconds); 61262306a36Sopenharmony_ci if (ret) 61362306a36Sopenharmony_ci return ret; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci ret = ksz_read32(dev, REG_PTP_RTC_SEC, &seconds); 61662306a36Sopenharmony_ci if (ret) 61762306a36Sopenharmony_ci return ret; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci ts->tv_sec = seconds; 62062306a36Sopenharmony_ci ts->tv_nsec = nanoseconds + phase * 8; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci return 0; 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic int ksz_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp); 62862306a36Sopenharmony_ci struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data); 62962306a36Sopenharmony_ci int ret; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci mutex_lock(&ptp_data->lock); 63262306a36Sopenharmony_ci ret = _ksz_ptp_gettime(dev, ts); 63362306a36Sopenharmony_ci mutex_unlock(&ptp_data->lock); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci return ret; 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic int ksz_ptp_restart_perout(struct ksz_device *dev) 63962306a36Sopenharmony_ci{ 64062306a36Sopenharmony_ci struct ksz_ptp_data *ptp_data = &dev->ptp_data; 64162306a36Sopenharmony_ci s64 now_ns, first_ns, period_ns, next_ns; 64262306a36Sopenharmony_ci struct ptp_perout_request request; 64362306a36Sopenharmony_ci struct timespec64 next; 64462306a36Sopenharmony_ci struct timespec64 now; 64562306a36Sopenharmony_ci unsigned int count; 64662306a36Sopenharmony_ci int ret; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci dev_info(dev->dev, "Restarting periodic output signal\n"); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci ret = _ksz_ptp_gettime(dev, &now); 65162306a36Sopenharmony_ci if (ret) 65262306a36Sopenharmony_ci return ret; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci now_ns = timespec64_to_ns(&now); 65562306a36Sopenharmony_ci first_ns = timespec64_to_ns(&ptp_data->perout_target_time_first); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci /* Calculate next perout event based on start time and period */ 65862306a36Sopenharmony_ci period_ns = timespec64_to_ns(&ptp_data->perout_period); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (first_ns < now_ns) { 66162306a36Sopenharmony_ci count = div_u64(now_ns - first_ns, period_ns); 66262306a36Sopenharmony_ci next_ns = first_ns + count * period_ns; 66362306a36Sopenharmony_ci } else { 66462306a36Sopenharmony_ci next_ns = first_ns; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci /* Ensure 100 ms guard time prior next event */ 66862306a36Sopenharmony_ci while (next_ns < now_ns + 100000000) 66962306a36Sopenharmony_ci next_ns += period_ns; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* Restart periodic output signal */ 67262306a36Sopenharmony_ci next = ns_to_timespec64(next_ns); 67362306a36Sopenharmony_ci request.start.sec = next.tv_sec; 67462306a36Sopenharmony_ci request.start.nsec = next.tv_nsec; 67562306a36Sopenharmony_ci request.period.sec = ptp_data->perout_period.tv_sec; 67662306a36Sopenharmony_ci request.period.nsec = ptp_data->perout_period.tv_nsec; 67762306a36Sopenharmony_ci request.index = 0; 67862306a36Sopenharmony_ci request.flags = 0; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci return ksz_ptp_enable_perout(dev, &request, 1); 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic int ksz_ptp_settime(struct ptp_clock_info *ptp, 68462306a36Sopenharmony_ci const struct timespec64 *ts) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp); 68762306a36Sopenharmony_ci struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data); 68862306a36Sopenharmony_ci int ret; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci mutex_lock(&ptp_data->lock); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci /* Write to shadow registers and Load PTP clock */ 69362306a36Sopenharmony_ci ret = ksz_write16(dev, REG_PTP_RTC_SUB_NANOSEC__2, PTP_RTC_0NS); 69462306a36Sopenharmony_ci if (ret) 69562306a36Sopenharmony_ci goto unlock; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci ret = ksz_write32(dev, REG_PTP_RTC_NANOSEC, ts->tv_nsec); 69862306a36Sopenharmony_ci if (ret) 69962306a36Sopenharmony_ci goto unlock; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci ret = ksz_write32(dev, REG_PTP_RTC_SEC, ts->tv_sec); 70262306a36Sopenharmony_ci if (ret) 70362306a36Sopenharmony_ci goto unlock; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_LOAD_TIME, PTP_LOAD_TIME); 70662306a36Sopenharmony_ci if (ret) 70762306a36Sopenharmony_ci goto unlock; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci switch (ptp_data->tou_mode) { 71062306a36Sopenharmony_ci case KSZ_PTP_TOU_IDLE: 71162306a36Sopenharmony_ci break; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci case KSZ_PTP_TOU_PEROUT: 71462306a36Sopenharmony_ci ret = ksz_ptp_restart_perout(dev); 71562306a36Sopenharmony_ci if (ret) 71662306a36Sopenharmony_ci goto unlock; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci break; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci spin_lock_bh(&ptp_data->clock_lock); 72262306a36Sopenharmony_ci ptp_data->clock_time = *ts; 72362306a36Sopenharmony_ci spin_unlock_bh(&ptp_data->clock_lock); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ciunlock: 72662306a36Sopenharmony_ci mutex_unlock(&ptp_data->lock); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci return ret; 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic int ksz_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp); 73462306a36Sopenharmony_ci struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data); 73562306a36Sopenharmony_ci u64 base, adj; 73662306a36Sopenharmony_ci bool negative; 73762306a36Sopenharmony_ci u32 data32; 73862306a36Sopenharmony_ci int ret; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci mutex_lock(&ptp_data->lock); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci if (scaled_ppm) { 74362306a36Sopenharmony_ci base = KSZ_PTP_INC_NS << KSZ_PTP_SUBNS_BITS; 74462306a36Sopenharmony_ci negative = diff_by_scaled_ppm(base, scaled_ppm, &adj); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci data32 = (u32)adj; 74762306a36Sopenharmony_ci data32 &= PTP_SUBNANOSEC_M; 74862306a36Sopenharmony_ci if (!negative) 74962306a36Sopenharmony_ci data32 |= PTP_RATE_DIR; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci ret = ksz_write32(dev, REG_PTP_SUBNANOSEC_RATE, data32); 75262306a36Sopenharmony_ci if (ret) 75362306a36Sopenharmony_ci goto unlock; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ADJ_ENABLE, 75662306a36Sopenharmony_ci PTP_CLK_ADJ_ENABLE); 75762306a36Sopenharmony_ci if (ret) 75862306a36Sopenharmony_ci goto unlock; 75962306a36Sopenharmony_ci } else { 76062306a36Sopenharmony_ci ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ADJ_ENABLE, 0); 76162306a36Sopenharmony_ci if (ret) 76262306a36Sopenharmony_ci goto unlock; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ciunlock: 76662306a36Sopenharmony_ci mutex_unlock(&ptp_data->lock); 76762306a36Sopenharmony_ci return ret; 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cistatic int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp); 77362306a36Sopenharmony_ci struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data); 77462306a36Sopenharmony_ci struct timespec64 delta64 = ns_to_timespec64(delta); 77562306a36Sopenharmony_ci s32 sec, nsec; 77662306a36Sopenharmony_ci u16 data16; 77762306a36Sopenharmony_ci int ret; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci mutex_lock(&ptp_data->lock); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci /* do not use ns_to_timespec64(), 78262306a36Sopenharmony_ci * both sec and nsec are subtracted by hw 78362306a36Sopenharmony_ci */ 78462306a36Sopenharmony_ci sec = div_s64_rem(delta, NSEC_PER_SEC, &nsec); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci ret = ksz_write32(dev, REG_PTP_RTC_NANOSEC, abs(nsec)); 78762306a36Sopenharmony_ci if (ret) 78862306a36Sopenharmony_ci goto unlock; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci ret = ksz_write32(dev, REG_PTP_RTC_SEC, abs(sec)); 79162306a36Sopenharmony_ci if (ret) 79262306a36Sopenharmony_ci goto unlock; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci ret = ksz_read16(dev, REG_PTP_CLK_CTRL, &data16); 79562306a36Sopenharmony_ci if (ret) 79662306a36Sopenharmony_ci goto unlock; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci data16 |= PTP_STEP_ADJ; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci /* PTP_STEP_DIR -- 0: subtract, 1: add */ 80162306a36Sopenharmony_ci if (delta < 0) 80262306a36Sopenharmony_ci data16 &= ~PTP_STEP_DIR; 80362306a36Sopenharmony_ci else 80462306a36Sopenharmony_ci data16 |= PTP_STEP_DIR; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci ret = ksz_write16(dev, REG_PTP_CLK_CTRL, data16); 80762306a36Sopenharmony_ci if (ret) 80862306a36Sopenharmony_ci goto unlock; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci switch (ptp_data->tou_mode) { 81162306a36Sopenharmony_ci case KSZ_PTP_TOU_IDLE: 81262306a36Sopenharmony_ci break; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci case KSZ_PTP_TOU_PEROUT: 81562306a36Sopenharmony_ci ret = ksz_ptp_restart_perout(dev); 81662306a36Sopenharmony_ci if (ret) 81762306a36Sopenharmony_ci goto unlock; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci break; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci spin_lock_bh(&ptp_data->clock_lock); 82362306a36Sopenharmony_ci ptp_data->clock_time = timespec64_add(ptp_data->clock_time, delta64); 82462306a36Sopenharmony_ci spin_unlock_bh(&ptp_data->clock_lock); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ciunlock: 82762306a36Sopenharmony_ci mutex_unlock(&ptp_data->lock); 82862306a36Sopenharmony_ci return ret; 82962306a36Sopenharmony_ci} 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_cistatic int ksz_ptp_enable(struct ptp_clock_info *ptp, 83262306a36Sopenharmony_ci struct ptp_clock_request *req, int on) 83362306a36Sopenharmony_ci{ 83462306a36Sopenharmony_ci struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp); 83562306a36Sopenharmony_ci struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data); 83662306a36Sopenharmony_ci int ret; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci switch (req->type) { 83962306a36Sopenharmony_ci case PTP_CLK_REQ_PEROUT: 84062306a36Sopenharmony_ci mutex_lock(&ptp_data->lock); 84162306a36Sopenharmony_ci ret = ksz_ptp_enable_perout(dev, &req->perout, on); 84262306a36Sopenharmony_ci mutex_unlock(&ptp_data->lock); 84362306a36Sopenharmony_ci break; 84462306a36Sopenharmony_ci default: 84562306a36Sopenharmony_ci return -EOPNOTSUPP; 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci return ret; 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistatic int ksz_ptp_verify_pin(struct ptp_clock_info *ptp, unsigned int pin, 85262306a36Sopenharmony_ci enum ptp_pin_function func, unsigned int chan) 85362306a36Sopenharmony_ci{ 85462306a36Sopenharmony_ci int ret = 0; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci switch (func) { 85762306a36Sopenharmony_ci case PTP_PF_NONE: 85862306a36Sopenharmony_ci case PTP_PF_PEROUT: 85962306a36Sopenharmony_ci break; 86062306a36Sopenharmony_ci default: 86162306a36Sopenharmony_ci ret = -1; 86262306a36Sopenharmony_ci break; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci return ret; 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci/* Function is pointer to the do_aux_work in the ptp_clock capability */ 86962306a36Sopenharmony_cistatic long ksz_ptp_do_aux_work(struct ptp_clock_info *ptp) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp); 87262306a36Sopenharmony_ci struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data); 87362306a36Sopenharmony_ci struct timespec64 ts; 87462306a36Sopenharmony_ci int ret; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci mutex_lock(&ptp_data->lock); 87762306a36Sopenharmony_ci ret = _ksz_ptp_gettime(dev, &ts); 87862306a36Sopenharmony_ci if (ret) 87962306a36Sopenharmony_ci goto out; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci spin_lock_bh(&ptp_data->clock_lock); 88262306a36Sopenharmony_ci ptp_data->clock_time = ts; 88362306a36Sopenharmony_ci spin_unlock_bh(&ptp_data->clock_lock); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ciout: 88662306a36Sopenharmony_ci mutex_unlock(&ptp_data->lock); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci return HZ; /* reschedule in 1 second */ 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_cistatic int ksz_ptp_start_clock(struct ksz_device *dev) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci struct ksz_ptp_data *ptp_data = &dev->ptp_data; 89462306a36Sopenharmony_ci int ret; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ENABLE, PTP_CLK_ENABLE); 89762306a36Sopenharmony_ci if (ret) 89862306a36Sopenharmony_ci return ret; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci ptp_data->clock_time.tv_sec = 0; 90162306a36Sopenharmony_ci ptp_data->clock_time.tv_nsec = 0; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci return 0; 90462306a36Sopenharmony_ci} 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ciint ksz_ptp_clock_register(struct dsa_switch *ds) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci struct ksz_device *dev = ds->priv; 90962306a36Sopenharmony_ci struct ksz_ptp_data *ptp_data; 91062306a36Sopenharmony_ci int ret; 91162306a36Sopenharmony_ci u8 i; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci ptp_data = &dev->ptp_data; 91462306a36Sopenharmony_ci mutex_init(&ptp_data->lock); 91562306a36Sopenharmony_ci spin_lock_init(&ptp_data->clock_lock); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci ptp_data->caps.owner = THIS_MODULE; 91862306a36Sopenharmony_ci snprintf(ptp_data->caps.name, 16, "Microchip Clock"); 91962306a36Sopenharmony_ci ptp_data->caps.max_adj = KSZ_MAX_DRIFT_CORR; 92062306a36Sopenharmony_ci ptp_data->caps.gettime64 = ksz_ptp_gettime; 92162306a36Sopenharmony_ci ptp_data->caps.settime64 = ksz_ptp_settime; 92262306a36Sopenharmony_ci ptp_data->caps.adjfine = ksz_ptp_adjfine; 92362306a36Sopenharmony_ci ptp_data->caps.adjtime = ksz_ptp_adjtime; 92462306a36Sopenharmony_ci ptp_data->caps.do_aux_work = ksz_ptp_do_aux_work; 92562306a36Sopenharmony_ci ptp_data->caps.enable = ksz_ptp_enable; 92662306a36Sopenharmony_ci ptp_data->caps.verify = ksz_ptp_verify_pin; 92762306a36Sopenharmony_ci ptp_data->caps.n_pins = KSZ_PTP_N_GPIO; 92862306a36Sopenharmony_ci ptp_data->caps.n_per_out = 3; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci ret = ksz_ptp_start_clock(dev); 93162306a36Sopenharmony_ci if (ret) 93262306a36Sopenharmony_ci return ret; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci for (i = 0; i < KSZ_PTP_N_GPIO; i++) { 93562306a36Sopenharmony_ci struct ptp_pin_desc *ptp_pin = &ptp_data->pin_config[i]; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci snprintf(ptp_pin->name, 93862306a36Sopenharmony_ci sizeof(ptp_pin->name), "ksz_ptp_pin_%02d", i); 93962306a36Sopenharmony_ci ptp_pin->index = i; 94062306a36Sopenharmony_ci ptp_pin->func = PTP_PF_NONE; 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci ptp_data->caps.pin_config = ptp_data->pin_config; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci /* Currently only P2P mode is supported. When 802_1AS bit is set, it 94662306a36Sopenharmony_ci * forwards all PTP packets to host port and none to other ports. 94762306a36Sopenharmony_ci */ 94862306a36Sopenharmony_ci ret = ksz_rmw16(dev, REG_PTP_MSG_CONF1, PTP_TC_P2P | PTP_802_1AS, 94962306a36Sopenharmony_ci PTP_TC_P2P | PTP_802_1AS); 95062306a36Sopenharmony_ci if (ret) 95162306a36Sopenharmony_ci return ret; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci ptp_data->clock = ptp_clock_register(&ptp_data->caps, dev->dev); 95462306a36Sopenharmony_ci if (IS_ERR_OR_NULL(ptp_data->clock)) 95562306a36Sopenharmony_ci return PTR_ERR(ptp_data->clock); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci return 0; 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_civoid ksz_ptp_clock_unregister(struct dsa_switch *ds) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci struct ksz_device *dev = ds->priv; 96362306a36Sopenharmony_ci struct ksz_ptp_data *ptp_data; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci ptp_data = &dev->ptp_data; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci if (ptp_data->clock) 96862306a36Sopenharmony_ci ptp_clock_unregister(ptp_data->clock); 96962306a36Sopenharmony_ci} 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_cistatic irqreturn_t ksz_ptp_msg_thread_fn(int irq, void *dev_id) 97262306a36Sopenharmony_ci{ 97362306a36Sopenharmony_ci struct ksz_ptp_irq *ptpmsg_irq = dev_id; 97462306a36Sopenharmony_ci struct ksz_device *dev; 97562306a36Sopenharmony_ci struct ksz_port *port; 97662306a36Sopenharmony_ci u32 tstamp_raw; 97762306a36Sopenharmony_ci ktime_t tstamp; 97862306a36Sopenharmony_ci int ret; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci port = ptpmsg_irq->port; 98162306a36Sopenharmony_ci dev = port->ksz_dev; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci if (ptpmsg_irq->ts_en) { 98462306a36Sopenharmony_ci ret = ksz_read32(dev, ptpmsg_irq->ts_reg, &tstamp_raw); 98562306a36Sopenharmony_ci if (ret) 98662306a36Sopenharmony_ci return IRQ_NONE; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci tstamp = ksz_decode_tstamp(tstamp_raw); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci port->tstamp_msg = ksz_tstamp_reconstruct(dev, tstamp); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci complete(&port->tstamp_msg_comp); 99362306a36Sopenharmony_ci } 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci return IRQ_HANDLED; 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cistatic irqreturn_t ksz_ptp_irq_thread_fn(int irq, void *dev_id) 99962306a36Sopenharmony_ci{ 100062306a36Sopenharmony_ci struct ksz_irq *ptpirq = dev_id; 100162306a36Sopenharmony_ci unsigned int nhandled = 0; 100262306a36Sopenharmony_ci struct ksz_device *dev; 100362306a36Sopenharmony_ci unsigned int sub_irq; 100462306a36Sopenharmony_ci u16 data; 100562306a36Sopenharmony_ci int ret; 100662306a36Sopenharmony_ci u8 n; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci dev = ptpirq->dev; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci ret = ksz_read16(dev, ptpirq->reg_status, &data); 101162306a36Sopenharmony_ci if (ret) 101262306a36Sopenharmony_ci goto out; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci /* Clear the interrupts W1C */ 101562306a36Sopenharmony_ci ret = ksz_write16(dev, ptpirq->reg_status, data); 101662306a36Sopenharmony_ci if (ret) 101762306a36Sopenharmony_ci return IRQ_NONE; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci for (n = 0; n < ptpirq->nirqs; ++n) { 102062306a36Sopenharmony_ci if (data & BIT(n + KSZ_PTP_INT_START)) { 102162306a36Sopenharmony_ci sub_irq = irq_find_mapping(ptpirq->domain, n); 102262306a36Sopenharmony_ci handle_nested_irq(sub_irq); 102362306a36Sopenharmony_ci ++nhandled; 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ciout: 102862306a36Sopenharmony_ci return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); 102962306a36Sopenharmony_ci} 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_cistatic void ksz_ptp_irq_mask(struct irq_data *d) 103262306a36Sopenharmony_ci{ 103362306a36Sopenharmony_ci struct ksz_irq *kirq = irq_data_get_irq_chip_data(d); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci kirq->masked &= ~BIT(d->hwirq + KSZ_PTP_INT_START); 103662306a36Sopenharmony_ci} 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_cistatic void ksz_ptp_irq_unmask(struct irq_data *d) 103962306a36Sopenharmony_ci{ 104062306a36Sopenharmony_ci struct ksz_irq *kirq = irq_data_get_irq_chip_data(d); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci kirq->masked |= BIT(d->hwirq + KSZ_PTP_INT_START); 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_cistatic void ksz_ptp_irq_bus_lock(struct irq_data *d) 104662306a36Sopenharmony_ci{ 104762306a36Sopenharmony_ci struct ksz_irq *kirq = irq_data_get_irq_chip_data(d); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci mutex_lock(&kirq->dev->lock_irq); 105062306a36Sopenharmony_ci} 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_cistatic void ksz_ptp_irq_bus_sync_unlock(struct irq_data *d) 105362306a36Sopenharmony_ci{ 105462306a36Sopenharmony_ci struct ksz_irq *kirq = irq_data_get_irq_chip_data(d); 105562306a36Sopenharmony_ci struct ksz_device *dev = kirq->dev; 105662306a36Sopenharmony_ci int ret; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci ret = ksz_write16(dev, kirq->reg_mask, kirq->masked); 105962306a36Sopenharmony_ci if (ret) 106062306a36Sopenharmony_ci dev_err(dev->dev, "failed to change IRQ mask\n"); 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci mutex_unlock(&dev->lock_irq); 106362306a36Sopenharmony_ci} 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_cistatic const struct irq_chip ksz_ptp_irq_chip = { 106662306a36Sopenharmony_ci .name = "ksz-irq", 106762306a36Sopenharmony_ci .irq_mask = ksz_ptp_irq_mask, 106862306a36Sopenharmony_ci .irq_unmask = ksz_ptp_irq_unmask, 106962306a36Sopenharmony_ci .irq_bus_lock = ksz_ptp_irq_bus_lock, 107062306a36Sopenharmony_ci .irq_bus_sync_unlock = ksz_ptp_irq_bus_sync_unlock, 107162306a36Sopenharmony_ci}; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_cistatic int ksz_ptp_irq_domain_map(struct irq_domain *d, 107462306a36Sopenharmony_ci unsigned int irq, irq_hw_number_t hwirq) 107562306a36Sopenharmony_ci{ 107662306a36Sopenharmony_ci irq_set_chip_data(irq, d->host_data); 107762306a36Sopenharmony_ci irq_set_chip_and_handler(irq, &ksz_ptp_irq_chip, handle_level_irq); 107862306a36Sopenharmony_ci irq_set_noprobe(irq); 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci return 0; 108162306a36Sopenharmony_ci} 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_cistatic const struct irq_domain_ops ksz_ptp_irq_domain_ops = { 108462306a36Sopenharmony_ci .map = ksz_ptp_irq_domain_map, 108562306a36Sopenharmony_ci .xlate = irq_domain_xlate_twocell, 108662306a36Sopenharmony_ci}; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_cistatic void ksz_ptp_msg_irq_free(struct ksz_port *port, u8 n) 108962306a36Sopenharmony_ci{ 109062306a36Sopenharmony_ci struct ksz_ptp_irq *ptpmsg_irq; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci ptpmsg_irq = &port->ptpmsg_irq[n]; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci free_irq(ptpmsg_irq->num, ptpmsg_irq); 109562306a36Sopenharmony_ci irq_dispose_mapping(ptpmsg_irq->num); 109662306a36Sopenharmony_ci} 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_cistatic int ksz_ptp_msg_irq_setup(struct ksz_port *port, u8 n) 109962306a36Sopenharmony_ci{ 110062306a36Sopenharmony_ci u16 ts_reg[] = {REG_PTP_PORT_PDRESP_TS, REG_PTP_PORT_XDELAY_TS, 110162306a36Sopenharmony_ci REG_PTP_PORT_SYNC_TS}; 110262306a36Sopenharmony_ci static const char * const name[] = {"pdresp-msg", "xdreq-msg", 110362306a36Sopenharmony_ci "sync-msg"}; 110462306a36Sopenharmony_ci const struct ksz_dev_ops *ops = port->ksz_dev->dev_ops; 110562306a36Sopenharmony_ci struct ksz_ptp_irq *ptpmsg_irq; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci ptpmsg_irq = &port->ptpmsg_irq[n]; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci ptpmsg_irq->port = port; 111062306a36Sopenharmony_ci ptpmsg_irq->ts_reg = ops->get_port_addr(port->num, ts_reg[n]); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci snprintf(ptpmsg_irq->name, sizeof(ptpmsg_irq->name), name[n]); 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci ptpmsg_irq->num = irq_find_mapping(port->ptpirq.domain, n); 111562306a36Sopenharmony_ci if (ptpmsg_irq->num < 0) 111662306a36Sopenharmony_ci return ptpmsg_irq->num; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci return request_threaded_irq(ptpmsg_irq->num, NULL, 111962306a36Sopenharmony_ci ksz_ptp_msg_thread_fn, IRQF_ONESHOT, 112062306a36Sopenharmony_ci ptpmsg_irq->name, ptpmsg_irq); 112162306a36Sopenharmony_ci} 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ciint ksz_ptp_irq_setup(struct dsa_switch *ds, u8 p) 112462306a36Sopenharmony_ci{ 112562306a36Sopenharmony_ci struct ksz_device *dev = ds->priv; 112662306a36Sopenharmony_ci const struct ksz_dev_ops *ops = dev->dev_ops; 112762306a36Sopenharmony_ci struct ksz_port *port = &dev->ports[p]; 112862306a36Sopenharmony_ci struct ksz_irq *ptpirq = &port->ptpirq; 112962306a36Sopenharmony_ci int irq; 113062306a36Sopenharmony_ci int ret; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci ptpirq->dev = dev; 113362306a36Sopenharmony_ci ptpirq->masked = 0; 113462306a36Sopenharmony_ci ptpirq->nirqs = 3; 113562306a36Sopenharmony_ci ptpirq->reg_mask = ops->get_port_addr(p, REG_PTP_PORT_TX_INT_ENABLE__2); 113662306a36Sopenharmony_ci ptpirq->reg_status = ops->get_port_addr(p, 113762306a36Sopenharmony_ci REG_PTP_PORT_TX_INT_STATUS__2); 113862306a36Sopenharmony_ci snprintf(ptpirq->name, sizeof(ptpirq->name), "ptp-irq-%d", p); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci init_completion(&port->tstamp_msg_comp); 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci ptpirq->domain = irq_domain_add_linear(dev->dev->of_node, ptpirq->nirqs, 114362306a36Sopenharmony_ci &ksz_ptp_irq_domain_ops, ptpirq); 114462306a36Sopenharmony_ci if (!ptpirq->domain) 114562306a36Sopenharmony_ci return -ENOMEM; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci for (irq = 0; irq < ptpirq->nirqs; irq++) 114862306a36Sopenharmony_ci irq_create_mapping(ptpirq->domain, irq); 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci ptpirq->irq_num = irq_find_mapping(port->pirq.domain, PORT_SRC_PTP_INT); 115162306a36Sopenharmony_ci if (ptpirq->irq_num < 0) { 115262306a36Sopenharmony_ci ret = ptpirq->irq_num; 115362306a36Sopenharmony_ci goto out; 115462306a36Sopenharmony_ci } 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci ret = request_threaded_irq(ptpirq->irq_num, NULL, ksz_ptp_irq_thread_fn, 115762306a36Sopenharmony_ci IRQF_ONESHOT, ptpirq->name, ptpirq); 115862306a36Sopenharmony_ci if (ret) 115962306a36Sopenharmony_ci goto out; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci for (irq = 0; irq < ptpirq->nirqs; irq++) { 116262306a36Sopenharmony_ci ret = ksz_ptp_msg_irq_setup(port, irq); 116362306a36Sopenharmony_ci if (ret) 116462306a36Sopenharmony_ci goto out_ptp_msg; 116562306a36Sopenharmony_ci } 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci return 0; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ciout_ptp_msg: 117062306a36Sopenharmony_ci free_irq(ptpirq->irq_num, ptpirq); 117162306a36Sopenharmony_ci while (irq--) 117262306a36Sopenharmony_ci free_irq(port->ptpmsg_irq[irq].num, &port->ptpmsg_irq[irq]); 117362306a36Sopenharmony_ciout: 117462306a36Sopenharmony_ci for (irq = 0; irq < ptpirq->nirqs; irq++) 117562306a36Sopenharmony_ci irq_dispose_mapping(port->ptpmsg_irq[irq].num); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci irq_domain_remove(ptpirq->domain); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci return ret; 118062306a36Sopenharmony_ci} 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_civoid ksz_ptp_irq_free(struct dsa_switch *ds, u8 p) 118362306a36Sopenharmony_ci{ 118462306a36Sopenharmony_ci struct ksz_device *dev = ds->priv; 118562306a36Sopenharmony_ci struct ksz_port *port = &dev->ports[p]; 118662306a36Sopenharmony_ci struct ksz_irq *ptpirq = &port->ptpirq; 118762306a36Sopenharmony_ci u8 n; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci for (n = 0; n < ptpirq->nirqs; n++) 119062306a36Sopenharmony_ci ksz_ptp_msg_irq_free(port, n); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci free_irq(ptpirq->irq_num, ptpirq); 119362306a36Sopenharmony_ci irq_dispose_mapping(ptpirq->irq_num); 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci irq_domain_remove(ptpirq->domain); 119662306a36Sopenharmony_ci} 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ciMODULE_AUTHOR("Christian Eggers <ceggers@arri.de>"); 119962306a36Sopenharmony_ciMODULE_AUTHOR("Arun Ramadoss <arun.ramadoss@microchip.com>"); 120062306a36Sopenharmony_ciMODULE_DESCRIPTION("PTP support for KSZ switch"); 120162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1202