162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (C) 2021 Gerhard Engleder <gerhard@engleder-embedded.com> */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include "tsnep.h" 562306a36Sopenharmony_ci 662306a36Sopenharmony_civoid tsnep_get_system_time(struct tsnep_adapter *adapter, u64 *time) 762306a36Sopenharmony_ci{ 862306a36Sopenharmony_ci u32 high_before; 962306a36Sopenharmony_ci u32 low; 1062306a36Sopenharmony_ci u32 high; 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci /* read high dword twice to detect overrun */ 1362306a36Sopenharmony_ci high = ioread32(adapter->addr + ECM_SYSTEM_TIME_HIGH); 1462306a36Sopenharmony_ci do { 1562306a36Sopenharmony_ci low = ioread32(adapter->addr + ECM_SYSTEM_TIME_LOW); 1662306a36Sopenharmony_ci high_before = high; 1762306a36Sopenharmony_ci high = ioread32(adapter->addr + ECM_SYSTEM_TIME_HIGH); 1862306a36Sopenharmony_ci } while (high != high_before); 1962306a36Sopenharmony_ci *time = (((u64)high) << 32) | ((u64)low); 2062306a36Sopenharmony_ci} 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ciint tsnep_ptp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci struct tsnep_adapter *adapter = netdev_priv(netdev); 2562306a36Sopenharmony_ci struct hwtstamp_config config; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci if (!ifr) 2862306a36Sopenharmony_ci return -EINVAL; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci if (cmd == SIOCSHWTSTAMP) { 3162306a36Sopenharmony_ci if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) 3262306a36Sopenharmony_ci return -EFAULT; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci switch (config.tx_type) { 3562306a36Sopenharmony_ci case HWTSTAMP_TX_OFF: 3662306a36Sopenharmony_ci case HWTSTAMP_TX_ON: 3762306a36Sopenharmony_ci break; 3862306a36Sopenharmony_ci default: 3962306a36Sopenharmony_ci return -ERANGE; 4062306a36Sopenharmony_ci } 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci switch (config.rx_filter) { 4362306a36Sopenharmony_ci case HWTSTAMP_FILTER_NONE: 4462306a36Sopenharmony_ci break; 4562306a36Sopenharmony_ci case HWTSTAMP_FILTER_ALL: 4662306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: 4762306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: 4862306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: 4962306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: 5062306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: 5162306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: 5262306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 5362306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: 5462306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: 5562306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_EVENT: 5662306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_SYNC: 5762306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: 5862306a36Sopenharmony_ci case HWTSTAMP_FILTER_NTP_ALL: 5962306a36Sopenharmony_ci config.rx_filter = HWTSTAMP_FILTER_ALL; 6062306a36Sopenharmony_ci break; 6162306a36Sopenharmony_ci default: 6262306a36Sopenharmony_ci return -ERANGE; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci memcpy(&adapter->hwtstamp_config, &config, 6662306a36Sopenharmony_ci sizeof(adapter->hwtstamp_config)); 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (copy_to_user(ifr->ifr_data, &adapter->hwtstamp_config, 7062306a36Sopenharmony_ci sizeof(adapter->hwtstamp_config))) 7162306a36Sopenharmony_ci return -EFAULT; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return 0; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int tsnep_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct tsnep_adapter *adapter = container_of(ptp, struct tsnep_adapter, 7962306a36Sopenharmony_ci ptp_clock_info); 8062306a36Sopenharmony_ci bool negative = false; 8162306a36Sopenharmony_ci u64 rate_offset; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (scaled_ppm < 0) { 8462306a36Sopenharmony_ci scaled_ppm = -scaled_ppm; 8562306a36Sopenharmony_ci negative = true; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* convert from 16 bit to 32 bit binary fractional, divide by 1000000 to 8962306a36Sopenharmony_ci * eliminate ppm, multiply with 8 to compensate 8ns clock cycle time, 9062306a36Sopenharmony_ci * simplify calculation because 15625 * 8 = 1000000 / 8 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ci rate_offset = scaled_ppm; 9362306a36Sopenharmony_ci rate_offset <<= 16 - 3; 9462306a36Sopenharmony_ci rate_offset = div_u64(rate_offset, 15625); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci rate_offset &= ECM_CLOCK_RATE_OFFSET_MASK; 9762306a36Sopenharmony_ci if (negative) 9862306a36Sopenharmony_ci rate_offset |= ECM_CLOCK_RATE_OFFSET_SIGN; 9962306a36Sopenharmony_ci iowrite32(rate_offset & 0xFFFFFFFF, adapter->addr + ECM_CLOCK_RATE); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int tsnep_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct tsnep_adapter *adapter = container_of(ptp, struct tsnep_adapter, 10762306a36Sopenharmony_ci ptp_clock_info); 10862306a36Sopenharmony_ci u64 system_time; 10962306a36Sopenharmony_ci unsigned long flags; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci spin_lock_irqsave(&adapter->ptp_lock, flags); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci tsnep_get_system_time(adapter, &system_time); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci system_time += delta; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* high dword is buffered in hardware and synchronously written to 11862306a36Sopenharmony_ci * system time when low dword is written 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_ci iowrite32(system_time >> 32, adapter->addr + ECM_SYSTEM_TIME_HIGH); 12162306a36Sopenharmony_ci iowrite32(system_time & 0xFFFFFFFF, 12262306a36Sopenharmony_ci adapter->addr + ECM_SYSTEM_TIME_LOW); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->ptp_lock, flags); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int tsnep_ptp_gettimex64(struct ptp_clock_info *ptp, 13062306a36Sopenharmony_ci struct timespec64 *ts, 13162306a36Sopenharmony_ci struct ptp_system_timestamp *sts) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct tsnep_adapter *adapter = container_of(ptp, struct tsnep_adapter, 13462306a36Sopenharmony_ci ptp_clock_info); 13562306a36Sopenharmony_ci u32 high_before; 13662306a36Sopenharmony_ci u32 low; 13762306a36Sopenharmony_ci u32 high; 13862306a36Sopenharmony_ci u64 system_time; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* read high dword twice to detect overrun */ 14162306a36Sopenharmony_ci high = ioread32(adapter->addr + ECM_SYSTEM_TIME_HIGH); 14262306a36Sopenharmony_ci do { 14362306a36Sopenharmony_ci ptp_read_system_prets(sts); 14462306a36Sopenharmony_ci low = ioread32(adapter->addr + ECM_SYSTEM_TIME_LOW); 14562306a36Sopenharmony_ci ptp_read_system_postts(sts); 14662306a36Sopenharmony_ci high_before = high; 14762306a36Sopenharmony_ci high = ioread32(adapter->addr + ECM_SYSTEM_TIME_HIGH); 14862306a36Sopenharmony_ci } while (high != high_before); 14962306a36Sopenharmony_ci system_time = (((u64)high) << 32) | ((u64)low); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci *ts = ns_to_timespec64(system_time); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int tsnep_ptp_settime64(struct ptp_clock_info *ptp, 15762306a36Sopenharmony_ci const struct timespec64 *ts) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct tsnep_adapter *adapter = container_of(ptp, struct tsnep_adapter, 16062306a36Sopenharmony_ci ptp_clock_info); 16162306a36Sopenharmony_ci u64 system_time = timespec64_to_ns(ts); 16262306a36Sopenharmony_ci unsigned long flags; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci spin_lock_irqsave(&adapter->ptp_lock, flags); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* high dword is buffered in hardware and synchronously written to 16762306a36Sopenharmony_ci * system time when low dword is written 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ci iowrite32(system_time >> 32, adapter->addr + ECM_SYSTEM_TIME_HIGH); 17062306a36Sopenharmony_ci iowrite32(system_time & 0xFFFFFFFF, 17162306a36Sopenharmony_ci adapter->addr + ECM_SYSTEM_TIME_LOW); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci spin_unlock_irqrestore(&adapter->ptp_lock, flags); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic int tsnep_ptp_getcyclesx64(struct ptp_clock_info *ptp, 17962306a36Sopenharmony_ci struct timespec64 *ts, 18062306a36Sopenharmony_ci struct ptp_system_timestamp *sts) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct tsnep_adapter *adapter = container_of(ptp, struct tsnep_adapter, 18362306a36Sopenharmony_ci ptp_clock_info); 18462306a36Sopenharmony_ci u32 high_before; 18562306a36Sopenharmony_ci u32 low; 18662306a36Sopenharmony_ci u32 high; 18762306a36Sopenharmony_ci u64 counter; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* read high dword twice to detect overrun */ 19062306a36Sopenharmony_ci high = ioread32(adapter->addr + ECM_COUNTER_HIGH); 19162306a36Sopenharmony_ci do { 19262306a36Sopenharmony_ci ptp_read_system_prets(sts); 19362306a36Sopenharmony_ci low = ioread32(adapter->addr + ECM_COUNTER_LOW); 19462306a36Sopenharmony_ci ptp_read_system_postts(sts); 19562306a36Sopenharmony_ci high_before = high; 19662306a36Sopenharmony_ci high = ioread32(adapter->addr + ECM_COUNTER_HIGH); 19762306a36Sopenharmony_ci } while (high != high_before); 19862306a36Sopenharmony_ci counter = (((u64)high) << 32) | ((u64)low); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci *ts = ns_to_timespec64(counter); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return 0; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ciint tsnep_ptp_init(struct tsnep_adapter *adapter) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci int retval = 0; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci adapter->hwtstamp_config.rx_filter = HWTSTAMP_FILTER_NONE; 21062306a36Sopenharmony_ci adapter->hwtstamp_config.tx_type = HWTSTAMP_TX_OFF; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci snprintf(adapter->ptp_clock_info.name, 16, "%s", TSNEP); 21362306a36Sopenharmony_ci adapter->ptp_clock_info.owner = THIS_MODULE; 21462306a36Sopenharmony_ci /* at most 2^-1ns adjustment every clock cycle for 8ns clock cycle time, 21562306a36Sopenharmony_ci * stay slightly below because only bits below 2^-1ns are supported 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ci adapter->ptp_clock_info.max_adj = (500000000 / 8 - 1); 21862306a36Sopenharmony_ci adapter->ptp_clock_info.adjfine = tsnep_ptp_adjfine; 21962306a36Sopenharmony_ci adapter->ptp_clock_info.adjtime = tsnep_ptp_adjtime; 22062306a36Sopenharmony_ci adapter->ptp_clock_info.gettimex64 = tsnep_ptp_gettimex64; 22162306a36Sopenharmony_ci adapter->ptp_clock_info.settime64 = tsnep_ptp_settime64; 22262306a36Sopenharmony_ci adapter->ptp_clock_info.getcyclesx64 = tsnep_ptp_getcyclesx64; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci spin_lock_init(&adapter->ptp_lock); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci adapter->ptp_clock = ptp_clock_register(&adapter->ptp_clock_info, 22762306a36Sopenharmony_ci &adapter->pdev->dev); 22862306a36Sopenharmony_ci if (IS_ERR(adapter->ptp_clock)) { 22962306a36Sopenharmony_ci netdev_err(adapter->netdev, "ptp_clock_register failed\n"); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci retval = PTR_ERR(adapter->ptp_clock); 23262306a36Sopenharmony_ci adapter->ptp_clock = NULL; 23362306a36Sopenharmony_ci } else if (adapter->ptp_clock) { 23462306a36Sopenharmony_ci netdev_info(adapter->netdev, "PHC added\n"); 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci return retval; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_civoid tsnep_ptp_cleanup(struct tsnep_adapter *adapter) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci if (adapter->ptp_clock) { 24362306a36Sopenharmony_ci ptp_clock_unregister(adapter->ptp_clock); 24462306a36Sopenharmony_ci netdev_info(adapter->netdev, "PHC removed\n"); 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci} 247