18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for the National Semiconductor DP83640 PHYTER 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 OMICRON electronics GmbH 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/crc32.h> 118c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/list.h> 148c2ecf20Sopenharmony_ci#include <linux/mii.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/net_tstamp.h> 178c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 188c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 198c2ecf20Sopenharmony_ci#include <linux/phy.h> 208c2ecf20Sopenharmony_ci#include <linux/ptp_classify.h> 218c2ecf20Sopenharmony_ci#include <linux/ptp_clock_kernel.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "dp83640_reg.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define DP83640_PHY_ID 0x20005ce1 268c2ecf20Sopenharmony_ci#define PAGESEL 0x13 278c2ecf20Sopenharmony_ci#define MAX_RXTS 64 288c2ecf20Sopenharmony_ci#define N_EXT_TS 6 298c2ecf20Sopenharmony_ci#define N_PER_OUT 7 308c2ecf20Sopenharmony_ci#define PSF_PTPVER 2 318c2ecf20Sopenharmony_ci#define PSF_EVNT 0x4000 328c2ecf20Sopenharmony_ci#define PSF_RX 0x2000 338c2ecf20Sopenharmony_ci#define PSF_TX 0x1000 348c2ecf20Sopenharmony_ci#define EXT_EVENT 1 358c2ecf20Sopenharmony_ci#define CAL_EVENT 7 368c2ecf20Sopenharmony_ci#define CAL_TRIGGER 1 378c2ecf20Sopenharmony_ci#define DP83640_N_PINS 12 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define MII_DP83640_MICR 0x11 408c2ecf20Sopenharmony_ci#define MII_DP83640_MISR 0x12 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define MII_DP83640_MICR_OE 0x1 438c2ecf20Sopenharmony_ci#define MII_DP83640_MICR_IE 0x2 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define MII_DP83640_MISR_RHF_INT_EN 0x01 468c2ecf20Sopenharmony_ci#define MII_DP83640_MISR_FHF_INT_EN 0x02 478c2ecf20Sopenharmony_ci#define MII_DP83640_MISR_ANC_INT_EN 0x04 488c2ecf20Sopenharmony_ci#define MII_DP83640_MISR_DUP_INT_EN 0x08 498c2ecf20Sopenharmony_ci#define MII_DP83640_MISR_SPD_INT_EN 0x10 508c2ecf20Sopenharmony_ci#define MII_DP83640_MISR_LINK_INT_EN 0x20 518c2ecf20Sopenharmony_ci#define MII_DP83640_MISR_ED_INT_EN 0x40 528c2ecf20Sopenharmony_ci#define MII_DP83640_MISR_LQ_INT_EN 0x80 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* phyter seems to miss the mark by 16 ns */ 558c2ecf20Sopenharmony_ci#define ADJTIME_FIX 16 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define SKB_TIMESTAMP_TIMEOUT 2 /* jiffies */ 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#if defined(__BIG_ENDIAN) 608c2ecf20Sopenharmony_ci#define ENDIAN_FLAG 0 618c2ecf20Sopenharmony_ci#elif defined(__LITTLE_ENDIAN) 628c2ecf20Sopenharmony_ci#define ENDIAN_FLAG PSF_ENDIAN 638c2ecf20Sopenharmony_ci#endif 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistruct dp83640_skb_info { 668c2ecf20Sopenharmony_ci int ptp_type; 678c2ecf20Sopenharmony_ci unsigned long tmo; 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistruct phy_rxts { 718c2ecf20Sopenharmony_ci u16 ns_lo; /* ns[15:0] */ 728c2ecf20Sopenharmony_ci u16 ns_hi; /* overflow[1:0], ns[29:16] */ 738c2ecf20Sopenharmony_ci u16 sec_lo; /* sec[15:0] */ 748c2ecf20Sopenharmony_ci u16 sec_hi; /* sec[31:16] */ 758c2ecf20Sopenharmony_ci u16 seqid; /* sequenceId[15:0] */ 768c2ecf20Sopenharmony_ci u16 msgtype; /* messageType[3:0], hash[11:0] */ 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistruct phy_txts { 808c2ecf20Sopenharmony_ci u16 ns_lo; /* ns[15:0] */ 818c2ecf20Sopenharmony_ci u16 ns_hi; /* overflow[1:0], ns[29:16] */ 828c2ecf20Sopenharmony_ci u16 sec_lo; /* sec[15:0] */ 838c2ecf20Sopenharmony_ci u16 sec_hi; /* sec[31:16] */ 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistruct rxts { 878c2ecf20Sopenharmony_ci struct list_head list; 888c2ecf20Sopenharmony_ci unsigned long tmo; 898c2ecf20Sopenharmony_ci u64 ns; 908c2ecf20Sopenharmony_ci u16 seqid; 918c2ecf20Sopenharmony_ci u8 msgtype; 928c2ecf20Sopenharmony_ci u16 hash; 938c2ecf20Sopenharmony_ci}; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistruct dp83640_clock; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistruct dp83640_private { 988c2ecf20Sopenharmony_ci struct list_head list; 998c2ecf20Sopenharmony_ci struct dp83640_clock *clock; 1008c2ecf20Sopenharmony_ci struct phy_device *phydev; 1018c2ecf20Sopenharmony_ci struct mii_timestamper mii_ts; 1028c2ecf20Sopenharmony_ci struct delayed_work ts_work; 1038c2ecf20Sopenharmony_ci int hwts_tx_en; 1048c2ecf20Sopenharmony_ci int hwts_rx_en; 1058c2ecf20Sopenharmony_ci int layer; 1068c2ecf20Sopenharmony_ci int version; 1078c2ecf20Sopenharmony_ci /* remember state of cfg0 during calibration */ 1088c2ecf20Sopenharmony_ci int cfg0; 1098c2ecf20Sopenharmony_ci /* remember the last event time stamp */ 1108c2ecf20Sopenharmony_ci struct phy_txts edata; 1118c2ecf20Sopenharmony_ci /* list of rx timestamps */ 1128c2ecf20Sopenharmony_ci struct list_head rxts; 1138c2ecf20Sopenharmony_ci struct list_head rxpool; 1148c2ecf20Sopenharmony_ci struct rxts rx_pool_data[MAX_RXTS]; 1158c2ecf20Sopenharmony_ci /* protects above three fields from concurrent access */ 1168c2ecf20Sopenharmony_ci spinlock_t rx_lock; 1178c2ecf20Sopenharmony_ci /* queues of incoming and outgoing packets */ 1188c2ecf20Sopenharmony_ci struct sk_buff_head rx_queue; 1198c2ecf20Sopenharmony_ci struct sk_buff_head tx_queue; 1208c2ecf20Sopenharmony_ci}; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistruct dp83640_clock { 1238c2ecf20Sopenharmony_ci /* keeps the instance in the 'phyter_clocks' list */ 1248c2ecf20Sopenharmony_ci struct list_head list; 1258c2ecf20Sopenharmony_ci /* we create one clock instance per MII bus */ 1268c2ecf20Sopenharmony_ci struct mii_bus *bus; 1278c2ecf20Sopenharmony_ci /* protects extended registers from concurrent access */ 1288c2ecf20Sopenharmony_ci struct mutex extreg_lock; 1298c2ecf20Sopenharmony_ci /* remembers which page was last selected */ 1308c2ecf20Sopenharmony_ci int page; 1318c2ecf20Sopenharmony_ci /* our advertised capabilities */ 1328c2ecf20Sopenharmony_ci struct ptp_clock_info caps; 1338c2ecf20Sopenharmony_ci /* protects the three fields below from concurrent access */ 1348c2ecf20Sopenharmony_ci struct mutex clock_lock; 1358c2ecf20Sopenharmony_ci /* the one phyter from which we shall read */ 1368c2ecf20Sopenharmony_ci struct dp83640_private *chosen; 1378c2ecf20Sopenharmony_ci /* list of the other attached phyters, not chosen */ 1388c2ecf20Sopenharmony_ci struct list_head phylist; 1398c2ecf20Sopenharmony_ci /* reference to our PTP hardware clock */ 1408c2ecf20Sopenharmony_ci struct ptp_clock *ptp_clock; 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* globals */ 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cienum { 1468c2ecf20Sopenharmony_ci CALIBRATE_GPIO, 1478c2ecf20Sopenharmony_ci PEROUT_GPIO, 1488c2ecf20Sopenharmony_ci EXTTS0_GPIO, 1498c2ecf20Sopenharmony_ci EXTTS1_GPIO, 1508c2ecf20Sopenharmony_ci EXTTS2_GPIO, 1518c2ecf20Sopenharmony_ci EXTTS3_GPIO, 1528c2ecf20Sopenharmony_ci EXTTS4_GPIO, 1538c2ecf20Sopenharmony_ci EXTTS5_GPIO, 1548c2ecf20Sopenharmony_ci GPIO_TABLE_SIZE 1558c2ecf20Sopenharmony_ci}; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic int chosen_phy = -1; 1588c2ecf20Sopenharmony_cistatic ushort gpio_tab[GPIO_TABLE_SIZE] = { 1598c2ecf20Sopenharmony_ci 1, 2, 3, 4, 8, 9, 10, 11 1608c2ecf20Sopenharmony_ci}; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cimodule_param(chosen_phy, int, 0444); 1638c2ecf20Sopenharmony_cimodule_param_array(gpio_tab, ushort, NULL, 0444); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ciMODULE_PARM_DESC(chosen_phy, \ 1668c2ecf20Sopenharmony_ci "The address of the PHY to use for the ancillary clock features"); 1678c2ecf20Sopenharmony_ciMODULE_PARM_DESC(gpio_tab, \ 1688c2ecf20Sopenharmony_ci "Which GPIO line to use for which purpose: cal,perout,extts1,...,extts6"); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic void dp83640_gpio_defaults(struct ptp_pin_desc *pd) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci int i, index; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci for (i = 0; i < DP83640_N_PINS; i++) { 1758c2ecf20Sopenharmony_ci snprintf(pd[i].name, sizeof(pd[i].name), "GPIO%d", 1 + i); 1768c2ecf20Sopenharmony_ci pd[i].index = i; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci for (i = 0; i < GPIO_TABLE_SIZE; i++) { 1808c2ecf20Sopenharmony_ci if (gpio_tab[i] < 1 || gpio_tab[i] > DP83640_N_PINS) { 1818c2ecf20Sopenharmony_ci pr_err("gpio_tab[%d]=%hu out of range", i, gpio_tab[i]); 1828c2ecf20Sopenharmony_ci return; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci index = gpio_tab[CALIBRATE_GPIO] - 1; 1878c2ecf20Sopenharmony_ci pd[index].func = PTP_PF_PHYSYNC; 1888c2ecf20Sopenharmony_ci pd[index].chan = 0; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci index = gpio_tab[PEROUT_GPIO] - 1; 1918c2ecf20Sopenharmony_ci pd[index].func = PTP_PF_PEROUT; 1928c2ecf20Sopenharmony_ci pd[index].chan = 0; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci for (i = EXTTS0_GPIO; i < GPIO_TABLE_SIZE; i++) { 1958c2ecf20Sopenharmony_ci index = gpio_tab[i] - 1; 1968c2ecf20Sopenharmony_ci pd[index].func = PTP_PF_EXTTS; 1978c2ecf20Sopenharmony_ci pd[index].chan = i - EXTTS0_GPIO; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci/* a list of clocks and a mutex to protect it */ 2028c2ecf20Sopenharmony_cistatic LIST_HEAD(phyter_clocks); 2038c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(phyter_clocks_lock); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic void rx_timestamp_work(struct work_struct *work); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/* extended register access functions */ 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci#define BROADCAST_ADDR 31 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic inline int broadcast_write(struct phy_device *phydev, u32 regnum, 2128c2ecf20Sopenharmony_ci u16 val) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci return mdiobus_write(phydev->mdio.bus, BROADCAST_ADDR, regnum, val); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci/* Caller must hold extreg_lock. */ 2188c2ecf20Sopenharmony_cistatic int ext_read(struct phy_device *phydev, int page, u32 regnum) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct dp83640_private *dp83640 = phydev->priv; 2218c2ecf20Sopenharmony_ci int val; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (dp83640->clock->page != page) { 2248c2ecf20Sopenharmony_ci broadcast_write(phydev, PAGESEL, page); 2258c2ecf20Sopenharmony_ci dp83640->clock->page = page; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci val = phy_read(phydev, regnum); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return val; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/* Caller must hold extreg_lock. */ 2338c2ecf20Sopenharmony_cistatic void ext_write(int broadcast, struct phy_device *phydev, 2348c2ecf20Sopenharmony_ci int page, u32 regnum, u16 val) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct dp83640_private *dp83640 = phydev->priv; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (dp83640->clock->page != page) { 2398c2ecf20Sopenharmony_ci broadcast_write(phydev, PAGESEL, page); 2408c2ecf20Sopenharmony_ci dp83640->clock->page = page; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci if (broadcast) 2438c2ecf20Sopenharmony_ci broadcast_write(phydev, regnum, val); 2448c2ecf20Sopenharmony_ci else 2458c2ecf20Sopenharmony_ci phy_write(phydev, regnum, val); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci/* Caller must hold extreg_lock. */ 2498c2ecf20Sopenharmony_cistatic int tdr_write(int bc, struct phy_device *dev, 2508c2ecf20Sopenharmony_ci const struct timespec64 *ts, u16 cmd) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci ext_write(bc, dev, PAGE4, PTP_TDR, ts->tv_nsec & 0xffff);/* ns[15:0] */ 2538c2ecf20Sopenharmony_ci ext_write(bc, dev, PAGE4, PTP_TDR, ts->tv_nsec >> 16); /* ns[31:16] */ 2548c2ecf20Sopenharmony_ci ext_write(bc, dev, PAGE4, PTP_TDR, ts->tv_sec & 0xffff); /* sec[15:0] */ 2558c2ecf20Sopenharmony_ci ext_write(bc, dev, PAGE4, PTP_TDR, ts->tv_sec >> 16); /* sec[31:16]*/ 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci ext_write(bc, dev, PAGE4, PTP_CTL, cmd); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci/* convert phy timestamps into driver timestamps */ 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic void phy2rxts(struct phy_rxts *p, struct rxts *rxts) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci u32 sec; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci sec = p->sec_lo; 2698c2ecf20Sopenharmony_ci sec |= p->sec_hi << 16; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci rxts->ns = p->ns_lo; 2728c2ecf20Sopenharmony_ci rxts->ns |= (p->ns_hi & 0x3fff) << 16; 2738c2ecf20Sopenharmony_ci rxts->ns += ((u64)sec) * 1000000000ULL; 2748c2ecf20Sopenharmony_ci rxts->seqid = p->seqid; 2758c2ecf20Sopenharmony_ci rxts->msgtype = (p->msgtype >> 12) & 0xf; 2768c2ecf20Sopenharmony_ci rxts->hash = p->msgtype & 0x0fff; 2778c2ecf20Sopenharmony_ci rxts->tmo = jiffies + SKB_TIMESTAMP_TIMEOUT; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic u64 phy2txts(struct phy_txts *p) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci u64 ns; 2838c2ecf20Sopenharmony_ci u32 sec; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci sec = p->sec_lo; 2868c2ecf20Sopenharmony_ci sec |= p->sec_hi << 16; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ns = p->ns_lo; 2898c2ecf20Sopenharmony_ci ns |= (p->ns_hi & 0x3fff) << 16; 2908c2ecf20Sopenharmony_ci ns += ((u64)sec) * 1000000000ULL; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci return ns; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic int periodic_output(struct dp83640_clock *clock, 2968c2ecf20Sopenharmony_ci struct ptp_clock_request *clkreq, bool on, 2978c2ecf20Sopenharmony_ci int trigger) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct dp83640_private *dp83640 = clock->chosen; 3008c2ecf20Sopenharmony_ci struct phy_device *phydev = dp83640->phydev; 3018c2ecf20Sopenharmony_ci u32 sec, nsec, pwidth; 3028c2ecf20Sopenharmony_ci u16 gpio, ptp_trig, val; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (on) { 3058c2ecf20Sopenharmony_ci gpio = 1 + ptp_find_pin(clock->ptp_clock, PTP_PF_PEROUT, 3068c2ecf20Sopenharmony_ci trigger); 3078c2ecf20Sopenharmony_ci if (gpio < 1) 3088c2ecf20Sopenharmony_ci return -EINVAL; 3098c2ecf20Sopenharmony_ci } else { 3108c2ecf20Sopenharmony_ci gpio = 0; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci ptp_trig = TRIG_WR | 3148c2ecf20Sopenharmony_ci (trigger & TRIG_CSEL_MASK) << TRIG_CSEL_SHIFT | 3158c2ecf20Sopenharmony_ci (gpio & TRIG_GPIO_MASK) << TRIG_GPIO_SHIFT | 3168c2ecf20Sopenharmony_ci TRIG_PER | 3178c2ecf20Sopenharmony_ci TRIG_PULSE; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci val = (trigger & TRIG_SEL_MASK) << TRIG_SEL_SHIFT; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (!on) { 3228c2ecf20Sopenharmony_ci val |= TRIG_DIS; 3238c2ecf20Sopenharmony_ci mutex_lock(&clock->extreg_lock); 3248c2ecf20Sopenharmony_ci ext_write(0, phydev, PAGE5, PTP_TRIG, ptp_trig); 3258c2ecf20Sopenharmony_ci ext_write(0, phydev, PAGE4, PTP_CTL, val); 3268c2ecf20Sopenharmony_ci mutex_unlock(&clock->extreg_lock); 3278c2ecf20Sopenharmony_ci return 0; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci sec = clkreq->perout.start.sec; 3318c2ecf20Sopenharmony_ci nsec = clkreq->perout.start.nsec; 3328c2ecf20Sopenharmony_ci pwidth = clkreq->perout.period.sec * 1000000000UL; 3338c2ecf20Sopenharmony_ci pwidth += clkreq->perout.period.nsec; 3348c2ecf20Sopenharmony_ci pwidth /= 2; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci mutex_lock(&clock->extreg_lock); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci ext_write(0, phydev, PAGE5, PTP_TRIG, ptp_trig); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /*load trigger*/ 3418c2ecf20Sopenharmony_ci val |= TRIG_LOAD; 3428c2ecf20Sopenharmony_ci ext_write(0, phydev, PAGE4, PTP_CTL, val); 3438c2ecf20Sopenharmony_ci ext_write(0, phydev, PAGE4, PTP_TDR, nsec & 0xffff); /* ns[15:0] */ 3448c2ecf20Sopenharmony_ci ext_write(0, phydev, PAGE4, PTP_TDR, nsec >> 16); /* ns[31:16] */ 3458c2ecf20Sopenharmony_ci ext_write(0, phydev, PAGE4, PTP_TDR, sec & 0xffff); /* sec[15:0] */ 3468c2ecf20Sopenharmony_ci ext_write(0, phydev, PAGE4, PTP_TDR, sec >> 16); /* sec[31:16] */ 3478c2ecf20Sopenharmony_ci ext_write(0, phydev, PAGE4, PTP_TDR, pwidth & 0xffff); /* ns[15:0] */ 3488c2ecf20Sopenharmony_ci ext_write(0, phydev, PAGE4, PTP_TDR, pwidth >> 16); /* ns[31:16] */ 3498c2ecf20Sopenharmony_ci /* Triggers 0 and 1 has programmable pulsewidth2 */ 3508c2ecf20Sopenharmony_ci if (trigger < 2) { 3518c2ecf20Sopenharmony_ci ext_write(0, phydev, PAGE4, PTP_TDR, pwidth & 0xffff); 3528c2ecf20Sopenharmony_ci ext_write(0, phydev, PAGE4, PTP_TDR, pwidth >> 16); 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /*enable trigger*/ 3568c2ecf20Sopenharmony_ci val &= ~TRIG_LOAD; 3578c2ecf20Sopenharmony_ci val |= TRIG_EN; 3588c2ecf20Sopenharmony_ci ext_write(0, phydev, PAGE4, PTP_CTL, val); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci mutex_unlock(&clock->extreg_lock); 3618c2ecf20Sopenharmony_ci return 0; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci/* ptp clock methods */ 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic int ptp_dp83640_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct dp83640_clock *clock = 3698c2ecf20Sopenharmony_ci container_of(ptp, struct dp83640_clock, caps); 3708c2ecf20Sopenharmony_ci struct phy_device *phydev = clock->chosen->phydev; 3718c2ecf20Sopenharmony_ci u64 rate; 3728c2ecf20Sopenharmony_ci int neg_adj = 0; 3738c2ecf20Sopenharmony_ci u16 hi, lo; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (scaled_ppm < 0) { 3768c2ecf20Sopenharmony_ci neg_adj = 1; 3778c2ecf20Sopenharmony_ci scaled_ppm = -scaled_ppm; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci rate = scaled_ppm; 3808c2ecf20Sopenharmony_ci rate <<= 13; 3818c2ecf20Sopenharmony_ci rate = div_u64(rate, 15625); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci hi = (rate >> 16) & PTP_RATE_HI_MASK; 3848c2ecf20Sopenharmony_ci if (neg_adj) 3858c2ecf20Sopenharmony_ci hi |= PTP_RATE_DIR; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci lo = rate & 0xffff; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci mutex_lock(&clock->extreg_lock); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci ext_write(1, phydev, PAGE4, PTP_RATEH, hi); 3928c2ecf20Sopenharmony_ci ext_write(1, phydev, PAGE4, PTP_RATEL, lo); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci mutex_unlock(&clock->extreg_lock); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return 0; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic int ptp_dp83640_adjtime(struct ptp_clock_info *ptp, s64 delta) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct dp83640_clock *clock = 4028c2ecf20Sopenharmony_ci container_of(ptp, struct dp83640_clock, caps); 4038c2ecf20Sopenharmony_ci struct phy_device *phydev = clock->chosen->phydev; 4048c2ecf20Sopenharmony_ci struct timespec64 ts; 4058c2ecf20Sopenharmony_ci int err; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci delta += ADJTIME_FIX; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci ts = ns_to_timespec64(delta); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci mutex_lock(&clock->extreg_lock); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci err = tdr_write(1, phydev, &ts, PTP_STEP_CLK); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci mutex_unlock(&clock->extreg_lock); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return err; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic int ptp_dp83640_gettime(struct ptp_clock_info *ptp, 4218c2ecf20Sopenharmony_ci struct timespec64 *ts) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct dp83640_clock *clock = 4248c2ecf20Sopenharmony_ci container_of(ptp, struct dp83640_clock, caps); 4258c2ecf20Sopenharmony_ci struct phy_device *phydev = clock->chosen->phydev; 4268c2ecf20Sopenharmony_ci unsigned int val[4]; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci mutex_lock(&clock->extreg_lock); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci ext_write(0, phydev, PAGE4, PTP_CTL, PTP_RD_CLK); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci val[0] = ext_read(phydev, PAGE4, PTP_TDR); /* ns[15:0] */ 4338c2ecf20Sopenharmony_ci val[1] = ext_read(phydev, PAGE4, PTP_TDR); /* ns[31:16] */ 4348c2ecf20Sopenharmony_ci val[2] = ext_read(phydev, PAGE4, PTP_TDR); /* sec[15:0] */ 4358c2ecf20Sopenharmony_ci val[3] = ext_read(phydev, PAGE4, PTP_TDR); /* sec[31:16] */ 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci mutex_unlock(&clock->extreg_lock); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci ts->tv_nsec = val[0] | (val[1] << 16); 4408c2ecf20Sopenharmony_ci ts->tv_sec = val[2] | (val[3] << 16); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci return 0; 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic int ptp_dp83640_settime(struct ptp_clock_info *ptp, 4468c2ecf20Sopenharmony_ci const struct timespec64 *ts) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci struct dp83640_clock *clock = 4498c2ecf20Sopenharmony_ci container_of(ptp, struct dp83640_clock, caps); 4508c2ecf20Sopenharmony_ci struct phy_device *phydev = clock->chosen->phydev; 4518c2ecf20Sopenharmony_ci int err; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci mutex_lock(&clock->extreg_lock); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci err = tdr_write(1, phydev, ts, PTP_LOAD_CLK); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci mutex_unlock(&clock->extreg_lock); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci return err; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic int ptp_dp83640_enable(struct ptp_clock_info *ptp, 4638c2ecf20Sopenharmony_ci struct ptp_clock_request *rq, int on) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci struct dp83640_clock *clock = 4668c2ecf20Sopenharmony_ci container_of(ptp, struct dp83640_clock, caps); 4678c2ecf20Sopenharmony_ci struct phy_device *phydev = clock->chosen->phydev; 4688c2ecf20Sopenharmony_ci unsigned int index; 4698c2ecf20Sopenharmony_ci u16 evnt, event_num, gpio_num; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci switch (rq->type) { 4728c2ecf20Sopenharmony_ci case PTP_CLK_REQ_EXTTS: 4738c2ecf20Sopenharmony_ci /* Reject requests with unsupported flags */ 4748c2ecf20Sopenharmony_ci if (rq->extts.flags & ~(PTP_ENABLE_FEATURE | 4758c2ecf20Sopenharmony_ci PTP_RISING_EDGE | 4768c2ecf20Sopenharmony_ci PTP_FALLING_EDGE | 4778c2ecf20Sopenharmony_ci PTP_STRICT_FLAGS)) 4788c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci /* Reject requests to enable time stamping on both edges. */ 4818c2ecf20Sopenharmony_ci if ((rq->extts.flags & PTP_STRICT_FLAGS) && 4828c2ecf20Sopenharmony_ci (rq->extts.flags & PTP_ENABLE_FEATURE) && 4838c2ecf20Sopenharmony_ci (rq->extts.flags & PTP_EXTTS_EDGES) == PTP_EXTTS_EDGES) 4848c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci index = rq->extts.index; 4878c2ecf20Sopenharmony_ci if (index >= N_EXT_TS) 4888c2ecf20Sopenharmony_ci return -EINVAL; 4898c2ecf20Sopenharmony_ci event_num = EXT_EVENT + index; 4908c2ecf20Sopenharmony_ci evnt = EVNT_WR | (event_num & EVNT_SEL_MASK) << EVNT_SEL_SHIFT; 4918c2ecf20Sopenharmony_ci if (on) { 4928c2ecf20Sopenharmony_ci gpio_num = 1 + ptp_find_pin(clock->ptp_clock, 4938c2ecf20Sopenharmony_ci PTP_PF_EXTTS, index); 4948c2ecf20Sopenharmony_ci if (gpio_num < 1) 4958c2ecf20Sopenharmony_ci return -EINVAL; 4968c2ecf20Sopenharmony_ci evnt |= (gpio_num & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT; 4978c2ecf20Sopenharmony_ci if (rq->extts.flags & PTP_FALLING_EDGE) 4988c2ecf20Sopenharmony_ci evnt |= EVNT_FALL; 4998c2ecf20Sopenharmony_ci else 5008c2ecf20Sopenharmony_ci evnt |= EVNT_RISE; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci mutex_lock(&clock->extreg_lock); 5038c2ecf20Sopenharmony_ci ext_write(0, phydev, PAGE5, PTP_EVNT, evnt); 5048c2ecf20Sopenharmony_ci mutex_unlock(&clock->extreg_lock); 5058c2ecf20Sopenharmony_ci return 0; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci case PTP_CLK_REQ_PEROUT: 5088c2ecf20Sopenharmony_ci /* Reject requests with unsupported flags */ 5098c2ecf20Sopenharmony_ci if (rq->perout.flags) 5108c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5118c2ecf20Sopenharmony_ci if (rq->perout.index >= N_PER_OUT) 5128c2ecf20Sopenharmony_ci return -EINVAL; 5138c2ecf20Sopenharmony_ci return periodic_output(clock, rq, on, rq->perout.index); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci default: 5168c2ecf20Sopenharmony_ci break; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic int ptp_dp83640_verify(struct ptp_clock_info *ptp, unsigned int pin, 5238c2ecf20Sopenharmony_ci enum ptp_pin_function func, unsigned int chan) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci struct dp83640_clock *clock = 5268c2ecf20Sopenharmony_ci container_of(ptp, struct dp83640_clock, caps); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (clock->caps.pin_config[pin].func == PTP_PF_PHYSYNC && 5298c2ecf20Sopenharmony_ci !list_empty(&clock->phylist)) 5308c2ecf20Sopenharmony_ci return 1; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (func == PTP_PF_PHYSYNC) 5338c2ecf20Sopenharmony_ci return 1; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci return 0; 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic u8 status_frame_dst[6] = { 0x01, 0x1B, 0x19, 0x00, 0x00, 0x00 }; 5398c2ecf20Sopenharmony_cistatic u8 status_frame_src[6] = { 0x08, 0x00, 0x17, 0x0B, 0x6B, 0x0F }; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic void enable_status_frames(struct phy_device *phydev, bool on) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci struct dp83640_private *dp83640 = phydev->priv; 5448c2ecf20Sopenharmony_ci struct dp83640_clock *clock = dp83640->clock; 5458c2ecf20Sopenharmony_ci u16 cfg0 = 0, ver; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci if (on) 5488c2ecf20Sopenharmony_ci cfg0 = PSF_EVNT_EN | PSF_RXTS_EN | PSF_TXTS_EN | ENDIAN_FLAG; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci ver = (PSF_PTPVER & VERSIONPTP_MASK) << VERSIONPTP_SHIFT; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci mutex_lock(&clock->extreg_lock); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci ext_write(0, phydev, PAGE5, PSF_CFG0, cfg0); 5558c2ecf20Sopenharmony_ci ext_write(0, phydev, PAGE6, PSF_CFG1, ver); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci mutex_unlock(&clock->extreg_lock); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (!phydev->attached_dev) { 5608c2ecf20Sopenharmony_ci phydev_warn(phydev, 5618c2ecf20Sopenharmony_ci "expected to find an attached netdevice\n"); 5628c2ecf20Sopenharmony_ci return; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (on) { 5668c2ecf20Sopenharmony_ci if (dev_mc_add(phydev->attached_dev, status_frame_dst)) 5678c2ecf20Sopenharmony_ci phydev_warn(phydev, "failed to add mc address\n"); 5688c2ecf20Sopenharmony_ci } else { 5698c2ecf20Sopenharmony_ci if (dev_mc_del(phydev->attached_dev, status_frame_dst)) 5708c2ecf20Sopenharmony_ci phydev_warn(phydev, "failed to delete mc address\n"); 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cistatic bool is_status_frame(struct sk_buff *skb, int type) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci struct ethhdr *h = eth_hdr(skb); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (PTP_CLASS_V2_L2 == type && 5798c2ecf20Sopenharmony_ci !memcmp(h->h_source, status_frame_src, sizeof(status_frame_src))) 5808c2ecf20Sopenharmony_ci return true; 5818c2ecf20Sopenharmony_ci else 5828c2ecf20Sopenharmony_ci return false; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic int expired(struct rxts *rxts) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci return time_after(jiffies, rxts->tmo); 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci/* Caller must hold rx_lock. */ 5918c2ecf20Sopenharmony_cistatic void prune_rx_ts(struct dp83640_private *dp83640) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci struct list_head *this, *next; 5948c2ecf20Sopenharmony_ci struct rxts *rxts; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci list_for_each_safe(this, next, &dp83640->rxts) { 5978c2ecf20Sopenharmony_ci rxts = list_entry(this, struct rxts, list); 5988c2ecf20Sopenharmony_ci if (expired(rxts)) { 5998c2ecf20Sopenharmony_ci list_del_init(&rxts->list); 6008c2ecf20Sopenharmony_ci list_add(&rxts->list, &dp83640->rxpool); 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci/* synchronize the phyters so they act as one clock */ 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cistatic void enable_broadcast(struct phy_device *phydev, int init_page, int on) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci int val; 6108c2ecf20Sopenharmony_ci phy_write(phydev, PAGESEL, 0); 6118c2ecf20Sopenharmony_ci val = phy_read(phydev, PHYCR2); 6128c2ecf20Sopenharmony_ci if (on) 6138c2ecf20Sopenharmony_ci val |= BC_WRITE; 6148c2ecf20Sopenharmony_ci else 6158c2ecf20Sopenharmony_ci val &= ~BC_WRITE; 6168c2ecf20Sopenharmony_ci phy_write(phydev, PHYCR2, val); 6178c2ecf20Sopenharmony_ci phy_write(phydev, PAGESEL, init_page); 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cistatic void recalibrate(struct dp83640_clock *clock) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci s64 now, diff; 6238c2ecf20Sopenharmony_ci struct phy_txts event_ts; 6248c2ecf20Sopenharmony_ci struct timespec64 ts; 6258c2ecf20Sopenharmony_ci struct list_head *this; 6268c2ecf20Sopenharmony_ci struct dp83640_private *tmp; 6278c2ecf20Sopenharmony_ci struct phy_device *master = clock->chosen->phydev; 6288c2ecf20Sopenharmony_ci u16 cal_gpio, cfg0, evnt, ptp_trig, trigger, val; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci trigger = CAL_TRIGGER; 6318c2ecf20Sopenharmony_ci cal_gpio = 1 + ptp_find_pin_unlocked(clock->ptp_clock, PTP_PF_PHYSYNC, 0); 6328c2ecf20Sopenharmony_ci if (cal_gpio < 1) { 6338c2ecf20Sopenharmony_ci pr_err("PHY calibration pin not available - PHY is not calibrated."); 6348c2ecf20Sopenharmony_ci return; 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci mutex_lock(&clock->extreg_lock); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci /* 6408c2ecf20Sopenharmony_ci * enable broadcast, disable status frames, enable ptp clock 6418c2ecf20Sopenharmony_ci */ 6428c2ecf20Sopenharmony_ci list_for_each(this, &clock->phylist) { 6438c2ecf20Sopenharmony_ci tmp = list_entry(this, struct dp83640_private, list); 6448c2ecf20Sopenharmony_ci enable_broadcast(tmp->phydev, clock->page, 1); 6458c2ecf20Sopenharmony_ci tmp->cfg0 = ext_read(tmp->phydev, PAGE5, PSF_CFG0); 6468c2ecf20Sopenharmony_ci ext_write(0, tmp->phydev, PAGE5, PSF_CFG0, 0); 6478c2ecf20Sopenharmony_ci ext_write(0, tmp->phydev, PAGE4, PTP_CTL, PTP_ENABLE); 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci enable_broadcast(master, clock->page, 1); 6508c2ecf20Sopenharmony_ci cfg0 = ext_read(master, PAGE5, PSF_CFG0); 6518c2ecf20Sopenharmony_ci ext_write(0, master, PAGE5, PSF_CFG0, 0); 6528c2ecf20Sopenharmony_ci ext_write(0, master, PAGE4, PTP_CTL, PTP_ENABLE); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* 6558c2ecf20Sopenharmony_ci * enable an event timestamp 6568c2ecf20Sopenharmony_ci */ 6578c2ecf20Sopenharmony_ci evnt = EVNT_WR | EVNT_RISE | EVNT_SINGLE; 6588c2ecf20Sopenharmony_ci evnt |= (CAL_EVENT & EVNT_SEL_MASK) << EVNT_SEL_SHIFT; 6598c2ecf20Sopenharmony_ci evnt |= (cal_gpio & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci list_for_each(this, &clock->phylist) { 6628c2ecf20Sopenharmony_ci tmp = list_entry(this, struct dp83640_private, list); 6638c2ecf20Sopenharmony_ci ext_write(0, tmp->phydev, PAGE5, PTP_EVNT, evnt); 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci ext_write(0, master, PAGE5, PTP_EVNT, evnt); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci /* 6688c2ecf20Sopenharmony_ci * configure a trigger 6698c2ecf20Sopenharmony_ci */ 6708c2ecf20Sopenharmony_ci ptp_trig = TRIG_WR | TRIG_IF_LATE | TRIG_PULSE; 6718c2ecf20Sopenharmony_ci ptp_trig |= (trigger & TRIG_CSEL_MASK) << TRIG_CSEL_SHIFT; 6728c2ecf20Sopenharmony_ci ptp_trig |= (cal_gpio & TRIG_GPIO_MASK) << TRIG_GPIO_SHIFT; 6738c2ecf20Sopenharmony_ci ext_write(0, master, PAGE5, PTP_TRIG, ptp_trig); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci /* load trigger */ 6768c2ecf20Sopenharmony_ci val = (trigger & TRIG_SEL_MASK) << TRIG_SEL_SHIFT; 6778c2ecf20Sopenharmony_ci val |= TRIG_LOAD; 6788c2ecf20Sopenharmony_ci ext_write(0, master, PAGE4, PTP_CTL, val); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci /* enable trigger */ 6818c2ecf20Sopenharmony_ci val &= ~TRIG_LOAD; 6828c2ecf20Sopenharmony_ci val |= TRIG_EN; 6838c2ecf20Sopenharmony_ci ext_write(0, master, PAGE4, PTP_CTL, val); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci /* disable trigger */ 6868c2ecf20Sopenharmony_ci val = (trigger & TRIG_SEL_MASK) << TRIG_SEL_SHIFT; 6878c2ecf20Sopenharmony_ci val |= TRIG_DIS; 6888c2ecf20Sopenharmony_ci ext_write(0, master, PAGE4, PTP_CTL, val); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci /* 6918c2ecf20Sopenharmony_ci * read out and correct offsets 6928c2ecf20Sopenharmony_ci */ 6938c2ecf20Sopenharmony_ci val = ext_read(master, PAGE4, PTP_STS); 6948c2ecf20Sopenharmony_ci phydev_info(master, "master PTP_STS 0x%04hx\n", val); 6958c2ecf20Sopenharmony_ci val = ext_read(master, PAGE4, PTP_ESTS); 6968c2ecf20Sopenharmony_ci phydev_info(master, "master PTP_ESTS 0x%04hx\n", val); 6978c2ecf20Sopenharmony_ci event_ts.ns_lo = ext_read(master, PAGE4, PTP_EDATA); 6988c2ecf20Sopenharmony_ci event_ts.ns_hi = ext_read(master, PAGE4, PTP_EDATA); 6998c2ecf20Sopenharmony_ci event_ts.sec_lo = ext_read(master, PAGE4, PTP_EDATA); 7008c2ecf20Sopenharmony_ci event_ts.sec_hi = ext_read(master, PAGE4, PTP_EDATA); 7018c2ecf20Sopenharmony_ci now = phy2txts(&event_ts); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci list_for_each(this, &clock->phylist) { 7048c2ecf20Sopenharmony_ci tmp = list_entry(this, struct dp83640_private, list); 7058c2ecf20Sopenharmony_ci val = ext_read(tmp->phydev, PAGE4, PTP_STS); 7068c2ecf20Sopenharmony_ci phydev_info(tmp->phydev, "slave PTP_STS 0x%04hx\n", val); 7078c2ecf20Sopenharmony_ci val = ext_read(tmp->phydev, PAGE4, PTP_ESTS); 7088c2ecf20Sopenharmony_ci phydev_info(tmp->phydev, "slave PTP_ESTS 0x%04hx\n", val); 7098c2ecf20Sopenharmony_ci event_ts.ns_lo = ext_read(tmp->phydev, PAGE4, PTP_EDATA); 7108c2ecf20Sopenharmony_ci event_ts.ns_hi = ext_read(tmp->phydev, PAGE4, PTP_EDATA); 7118c2ecf20Sopenharmony_ci event_ts.sec_lo = ext_read(tmp->phydev, PAGE4, PTP_EDATA); 7128c2ecf20Sopenharmony_ci event_ts.sec_hi = ext_read(tmp->phydev, PAGE4, PTP_EDATA); 7138c2ecf20Sopenharmony_ci diff = now - (s64) phy2txts(&event_ts); 7148c2ecf20Sopenharmony_ci phydev_info(tmp->phydev, "slave offset %lld nanoseconds\n", 7158c2ecf20Sopenharmony_ci diff); 7168c2ecf20Sopenharmony_ci diff += ADJTIME_FIX; 7178c2ecf20Sopenharmony_ci ts = ns_to_timespec64(diff); 7188c2ecf20Sopenharmony_ci tdr_write(0, tmp->phydev, &ts, PTP_STEP_CLK); 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci /* 7228c2ecf20Sopenharmony_ci * restore status frames 7238c2ecf20Sopenharmony_ci */ 7248c2ecf20Sopenharmony_ci list_for_each(this, &clock->phylist) { 7258c2ecf20Sopenharmony_ci tmp = list_entry(this, struct dp83640_private, list); 7268c2ecf20Sopenharmony_ci ext_write(0, tmp->phydev, PAGE5, PSF_CFG0, tmp->cfg0); 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci ext_write(0, master, PAGE5, PSF_CFG0, cfg0); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci mutex_unlock(&clock->extreg_lock); 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci/* time stamping methods */ 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_cistatic inline u16 exts_chan_to_edata(int ch) 7368c2ecf20Sopenharmony_ci{ 7378c2ecf20Sopenharmony_ci return 1 << ((ch + EXT_EVENT) * 2); 7388c2ecf20Sopenharmony_ci} 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_cistatic int decode_evnt(struct dp83640_private *dp83640, 7418c2ecf20Sopenharmony_ci void *data, int len, u16 ests) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci struct phy_txts *phy_txts; 7448c2ecf20Sopenharmony_ci struct ptp_clock_event event; 7458c2ecf20Sopenharmony_ci int i, parsed; 7468c2ecf20Sopenharmony_ci int words = (ests >> EVNT_TS_LEN_SHIFT) & EVNT_TS_LEN_MASK; 7478c2ecf20Sopenharmony_ci u16 ext_status = 0; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci /* calculate length of the event timestamp status message */ 7508c2ecf20Sopenharmony_ci if (ests & MULT_EVNT) 7518c2ecf20Sopenharmony_ci parsed = (words + 2) * sizeof(u16); 7528c2ecf20Sopenharmony_ci else 7538c2ecf20Sopenharmony_ci parsed = (words + 1) * sizeof(u16); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci /* check if enough data is available */ 7568c2ecf20Sopenharmony_ci if (len < parsed) 7578c2ecf20Sopenharmony_ci return len; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci if (ests & MULT_EVNT) { 7608c2ecf20Sopenharmony_ci ext_status = *(u16 *) data; 7618c2ecf20Sopenharmony_ci data += sizeof(ext_status); 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci phy_txts = data; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci switch (words) { 7678c2ecf20Sopenharmony_ci case 3: 7688c2ecf20Sopenharmony_ci dp83640->edata.sec_hi = phy_txts->sec_hi; 7698c2ecf20Sopenharmony_ci fallthrough; 7708c2ecf20Sopenharmony_ci case 2: 7718c2ecf20Sopenharmony_ci dp83640->edata.sec_lo = phy_txts->sec_lo; 7728c2ecf20Sopenharmony_ci fallthrough; 7738c2ecf20Sopenharmony_ci case 1: 7748c2ecf20Sopenharmony_ci dp83640->edata.ns_hi = phy_txts->ns_hi; 7758c2ecf20Sopenharmony_ci fallthrough; 7768c2ecf20Sopenharmony_ci case 0: 7778c2ecf20Sopenharmony_ci dp83640->edata.ns_lo = phy_txts->ns_lo; 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci if (!ext_status) { 7818c2ecf20Sopenharmony_ci i = ((ests >> EVNT_NUM_SHIFT) & EVNT_NUM_MASK) - EXT_EVENT; 7828c2ecf20Sopenharmony_ci ext_status = exts_chan_to_edata(i); 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci event.type = PTP_CLOCK_EXTTS; 7868c2ecf20Sopenharmony_ci event.timestamp = phy2txts(&dp83640->edata); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci /* Compensate for input path and synchronization delays */ 7898c2ecf20Sopenharmony_ci event.timestamp -= 35; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci for (i = 0; i < N_EXT_TS; i++) { 7928c2ecf20Sopenharmony_ci if (ext_status & exts_chan_to_edata(i)) { 7938c2ecf20Sopenharmony_ci event.index = i; 7948c2ecf20Sopenharmony_ci ptp_clock_event(dp83640->clock->ptp_clock, &event); 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci return parsed; 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci#define DP83640_PACKET_HASH_LEN 10 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_cistatic int match(struct sk_buff *skb, unsigned int type, struct rxts *rxts) 8048c2ecf20Sopenharmony_ci{ 8058c2ecf20Sopenharmony_ci struct ptp_header *hdr; 8068c2ecf20Sopenharmony_ci u8 msgtype; 8078c2ecf20Sopenharmony_ci u16 seqid; 8088c2ecf20Sopenharmony_ci u16 hash; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci /* check sequenceID, messageType, 12 bit hash of offset 20-29 */ 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci hdr = ptp_parse_header(skb, type); 8138c2ecf20Sopenharmony_ci if (!hdr) 8148c2ecf20Sopenharmony_ci return 0; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci msgtype = ptp_get_msgtype(hdr, type); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci if (rxts->msgtype != (msgtype & 0xf)) 8198c2ecf20Sopenharmony_ci return 0; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci seqid = be16_to_cpu(hdr->sequence_id); 8228c2ecf20Sopenharmony_ci if (rxts->seqid != seqid) 8238c2ecf20Sopenharmony_ci return 0; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci hash = ether_crc(DP83640_PACKET_HASH_LEN, 8268c2ecf20Sopenharmony_ci (unsigned char *)&hdr->source_port_identity) >> 20; 8278c2ecf20Sopenharmony_ci if (rxts->hash != hash) 8288c2ecf20Sopenharmony_ci return 0; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci return 1; 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cistatic void decode_rxts(struct dp83640_private *dp83640, 8348c2ecf20Sopenharmony_ci struct phy_rxts *phy_rxts) 8358c2ecf20Sopenharmony_ci{ 8368c2ecf20Sopenharmony_ci struct rxts *rxts; 8378c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps *shhwtstamps = NULL; 8388c2ecf20Sopenharmony_ci struct sk_buff *skb; 8398c2ecf20Sopenharmony_ci unsigned long flags; 8408c2ecf20Sopenharmony_ci u8 overflow; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci overflow = (phy_rxts->ns_hi >> 14) & 0x3; 8438c2ecf20Sopenharmony_ci if (overflow) 8448c2ecf20Sopenharmony_ci pr_debug("rx timestamp queue overflow, count %d\n", overflow); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci spin_lock_irqsave(&dp83640->rx_lock, flags); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci prune_rx_ts(dp83640); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci if (list_empty(&dp83640->rxpool)) { 8518c2ecf20Sopenharmony_ci pr_debug("rx timestamp pool is empty\n"); 8528c2ecf20Sopenharmony_ci goto out; 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci rxts = list_first_entry(&dp83640->rxpool, struct rxts, list); 8558c2ecf20Sopenharmony_ci list_del_init(&rxts->list); 8568c2ecf20Sopenharmony_ci phy2rxts(phy_rxts, rxts); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci spin_lock(&dp83640->rx_queue.lock); 8598c2ecf20Sopenharmony_ci skb_queue_walk(&dp83640->rx_queue, skb) { 8608c2ecf20Sopenharmony_ci struct dp83640_skb_info *skb_info; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci skb_info = (struct dp83640_skb_info *)skb->cb; 8638c2ecf20Sopenharmony_ci if (match(skb, skb_info->ptp_type, rxts)) { 8648c2ecf20Sopenharmony_ci __skb_unlink(skb, &dp83640->rx_queue); 8658c2ecf20Sopenharmony_ci shhwtstamps = skb_hwtstamps(skb); 8668c2ecf20Sopenharmony_ci memset(shhwtstamps, 0, sizeof(*shhwtstamps)); 8678c2ecf20Sopenharmony_ci shhwtstamps->hwtstamp = ns_to_ktime(rxts->ns); 8688c2ecf20Sopenharmony_ci list_add(&rxts->list, &dp83640->rxpool); 8698c2ecf20Sopenharmony_ci break; 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci spin_unlock(&dp83640->rx_queue.lock); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci if (!shhwtstamps) 8758c2ecf20Sopenharmony_ci list_add_tail(&rxts->list, &dp83640->rxts); 8768c2ecf20Sopenharmony_ciout: 8778c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dp83640->rx_lock, flags); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci if (shhwtstamps) 8808c2ecf20Sopenharmony_ci netif_rx_ni(skb); 8818c2ecf20Sopenharmony_ci} 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_cistatic void decode_txts(struct dp83640_private *dp83640, 8848c2ecf20Sopenharmony_ci struct phy_txts *phy_txts) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps shhwtstamps; 8878c2ecf20Sopenharmony_ci struct dp83640_skb_info *skb_info; 8888c2ecf20Sopenharmony_ci struct sk_buff *skb; 8898c2ecf20Sopenharmony_ci u8 overflow; 8908c2ecf20Sopenharmony_ci u64 ns; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci /* We must already have the skb that triggered this. */ 8938c2ecf20Sopenharmony_ciagain: 8948c2ecf20Sopenharmony_ci skb = skb_dequeue(&dp83640->tx_queue); 8958c2ecf20Sopenharmony_ci if (!skb) { 8968c2ecf20Sopenharmony_ci pr_debug("have timestamp but tx_queue empty\n"); 8978c2ecf20Sopenharmony_ci return; 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci overflow = (phy_txts->ns_hi >> 14) & 0x3; 9018c2ecf20Sopenharmony_ci if (overflow) { 9028c2ecf20Sopenharmony_ci pr_debug("tx timestamp queue overflow, count %d\n", overflow); 9038c2ecf20Sopenharmony_ci while (skb) { 9048c2ecf20Sopenharmony_ci kfree_skb(skb); 9058c2ecf20Sopenharmony_ci skb = skb_dequeue(&dp83640->tx_queue); 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci return; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci skb_info = (struct dp83640_skb_info *)skb->cb; 9108c2ecf20Sopenharmony_ci if (time_after(jiffies, skb_info->tmo)) { 9118c2ecf20Sopenharmony_ci kfree_skb(skb); 9128c2ecf20Sopenharmony_ci goto again; 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci ns = phy2txts(phy_txts); 9168c2ecf20Sopenharmony_ci memset(&shhwtstamps, 0, sizeof(shhwtstamps)); 9178c2ecf20Sopenharmony_ci shhwtstamps.hwtstamp = ns_to_ktime(ns); 9188c2ecf20Sopenharmony_ci skb_complete_tx_timestamp(skb, &shhwtstamps); 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_cistatic void decode_status_frame(struct dp83640_private *dp83640, 9228c2ecf20Sopenharmony_ci struct sk_buff *skb) 9238c2ecf20Sopenharmony_ci{ 9248c2ecf20Sopenharmony_ci struct phy_rxts *phy_rxts; 9258c2ecf20Sopenharmony_ci struct phy_txts *phy_txts; 9268c2ecf20Sopenharmony_ci u8 *ptr; 9278c2ecf20Sopenharmony_ci int len, size; 9288c2ecf20Sopenharmony_ci u16 ests, type; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci ptr = skb->data + 2; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci for (len = skb_headlen(skb) - 2; len > sizeof(type); len -= size) { 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci type = *(u16 *)ptr; 9358c2ecf20Sopenharmony_ci ests = type & 0x0fff; 9368c2ecf20Sopenharmony_ci type = type & 0xf000; 9378c2ecf20Sopenharmony_ci len -= sizeof(type); 9388c2ecf20Sopenharmony_ci ptr += sizeof(type); 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci if (PSF_RX == type && len >= sizeof(*phy_rxts)) { 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci phy_rxts = (struct phy_rxts *) ptr; 9438c2ecf20Sopenharmony_ci decode_rxts(dp83640, phy_rxts); 9448c2ecf20Sopenharmony_ci size = sizeof(*phy_rxts); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci } else if (PSF_TX == type && len >= sizeof(*phy_txts)) { 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci phy_txts = (struct phy_txts *) ptr; 9498c2ecf20Sopenharmony_ci decode_txts(dp83640, phy_txts); 9508c2ecf20Sopenharmony_ci size = sizeof(*phy_txts); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci } else if (PSF_EVNT == type) { 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci size = decode_evnt(dp83640, ptr, len, ests); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci } else { 9578c2ecf20Sopenharmony_ci size = 0; 9588c2ecf20Sopenharmony_ci break; 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci ptr += size; 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci} 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_cistatic int is_sync(struct sk_buff *skb, int type) 9658c2ecf20Sopenharmony_ci{ 9668c2ecf20Sopenharmony_ci struct ptp_header *hdr; 9678c2ecf20Sopenharmony_ci u8 msgtype; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci hdr = ptp_parse_header(skb, type); 9708c2ecf20Sopenharmony_ci if (!hdr) 9718c2ecf20Sopenharmony_ci return 0; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci msgtype = ptp_get_msgtype(hdr, type); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci return (msgtype & 0xf) == 0; 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_cistatic void dp83640_free_clocks(void) 9798c2ecf20Sopenharmony_ci{ 9808c2ecf20Sopenharmony_ci struct dp83640_clock *clock; 9818c2ecf20Sopenharmony_ci struct list_head *this, *next; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci mutex_lock(&phyter_clocks_lock); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci list_for_each_safe(this, next, &phyter_clocks) { 9868c2ecf20Sopenharmony_ci clock = list_entry(this, struct dp83640_clock, list); 9878c2ecf20Sopenharmony_ci if (!list_empty(&clock->phylist)) { 9888c2ecf20Sopenharmony_ci pr_warn("phy list non-empty while unloading\n"); 9898c2ecf20Sopenharmony_ci BUG(); 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci list_del(&clock->list); 9928c2ecf20Sopenharmony_ci mutex_destroy(&clock->extreg_lock); 9938c2ecf20Sopenharmony_ci mutex_destroy(&clock->clock_lock); 9948c2ecf20Sopenharmony_ci put_device(&clock->bus->dev); 9958c2ecf20Sopenharmony_ci kfree(clock->caps.pin_config); 9968c2ecf20Sopenharmony_ci kfree(clock); 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci mutex_unlock(&phyter_clocks_lock); 10008c2ecf20Sopenharmony_ci} 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_cistatic void dp83640_clock_init(struct dp83640_clock *clock, struct mii_bus *bus) 10038c2ecf20Sopenharmony_ci{ 10048c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&clock->list); 10058c2ecf20Sopenharmony_ci clock->bus = bus; 10068c2ecf20Sopenharmony_ci mutex_init(&clock->extreg_lock); 10078c2ecf20Sopenharmony_ci mutex_init(&clock->clock_lock); 10088c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&clock->phylist); 10098c2ecf20Sopenharmony_ci clock->caps.owner = THIS_MODULE; 10108c2ecf20Sopenharmony_ci sprintf(clock->caps.name, "dp83640 timer"); 10118c2ecf20Sopenharmony_ci clock->caps.max_adj = 1953124; 10128c2ecf20Sopenharmony_ci clock->caps.n_alarm = 0; 10138c2ecf20Sopenharmony_ci clock->caps.n_ext_ts = N_EXT_TS; 10148c2ecf20Sopenharmony_ci clock->caps.n_per_out = N_PER_OUT; 10158c2ecf20Sopenharmony_ci clock->caps.n_pins = DP83640_N_PINS; 10168c2ecf20Sopenharmony_ci clock->caps.pps = 0; 10178c2ecf20Sopenharmony_ci clock->caps.adjfine = ptp_dp83640_adjfine; 10188c2ecf20Sopenharmony_ci clock->caps.adjtime = ptp_dp83640_adjtime; 10198c2ecf20Sopenharmony_ci clock->caps.gettime64 = ptp_dp83640_gettime; 10208c2ecf20Sopenharmony_ci clock->caps.settime64 = ptp_dp83640_settime; 10218c2ecf20Sopenharmony_ci clock->caps.enable = ptp_dp83640_enable; 10228c2ecf20Sopenharmony_ci clock->caps.verify = ptp_dp83640_verify; 10238c2ecf20Sopenharmony_ci /* 10248c2ecf20Sopenharmony_ci * Convert the module param defaults into a dynamic pin configuration. 10258c2ecf20Sopenharmony_ci */ 10268c2ecf20Sopenharmony_ci dp83640_gpio_defaults(clock->caps.pin_config); 10278c2ecf20Sopenharmony_ci /* 10288c2ecf20Sopenharmony_ci * Get a reference to this bus instance. 10298c2ecf20Sopenharmony_ci */ 10308c2ecf20Sopenharmony_ci get_device(&bus->dev); 10318c2ecf20Sopenharmony_ci} 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_cistatic int choose_this_phy(struct dp83640_clock *clock, 10348c2ecf20Sopenharmony_ci struct phy_device *phydev) 10358c2ecf20Sopenharmony_ci{ 10368c2ecf20Sopenharmony_ci if (chosen_phy == -1 && !clock->chosen) 10378c2ecf20Sopenharmony_ci return 1; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci if (chosen_phy == phydev->mdio.addr) 10408c2ecf20Sopenharmony_ci return 1; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci return 0; 10438c2ecf20Sopenharmony_ci} 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_cistatic struct dp83640_clock *dp83640_clock_get(struct dp83640_clock *clock) 10468c2ecf20Sopenharmony_ci{ 10478c2ecf20Sopenharmony_ci if (clock) 10488c2ecf20Sopenharmony_ci mutex_lock(&clock->clock_lock); 10498c2ecf20Sopenharmony_ci return clock; 10508c2ecf20Sopenharmony_ci} 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci/* 10538c2ecf20Sopenharmony_ci * Look up and lock a clock by bus instance. 10548c2ecf20Sopenharmony_ci * If there is no clock for this bus, then create it first. 10558c2ecf20Sopenharmony_ci */ 10568c2ecf20Sopenharmony_cistatic struct dp83640_clock *dp83640_clock_get_bus(struct mii_bus *bus) 10578c2ecf20Sopenharmony_ci{ 10588c2ecf20Sopenharmony_ci struct dp83640_clock *clock = NULL, *tmp; 10598c2ecf20Sopenharmony_ci struct list_head *this; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci mutex_lock(&phyter_clocks_lock); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci list_for_each(this, &phyter_clocks) { 10648c2ecf20Sopenharmony_ci tmp = list_entry(this, struct dp83640_clock, list); 10658c2ecf20Sopenharmony_ci if (tmp->bus == bus) { 10668c2ecf20Sopenharmony_ci clock = tmp; 10678c2ecf20Sopenharmony_ci break; 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ci } 10708c2ecf20Sopenharmony_ci if (clock) 10718c2ecf20Sopenharmony_ci goto out; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci clock = kzalloc(sizeof(struct dp83640_clock), GFP_KERNEL); 10748c2ecf20Sopenharmony_ci if (!clock) 10758c2ecf20Sopenharmony_ci goto out; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci clock->caps.pin_config = kcalloc(DP83640_N_PINS, 10788c2ecf20Sopenharmony_ci sizeof(struct ptp_pin_desc), 10798c2ecf20Sopenharmony_ci GFP_KERNEL); 10808c2ecf20Sopenharmony_ci if (!clock->caps.pin_config) { 10818c2ecf20Sopenharmony_ci kfree(clock); 10828c2ecf20Sopenharmony_ci clock = NULL; 10838c2ecf20Sopenharmony_ci goto out; 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci dp83640_clock_init(clock, bus); 10868c2ecf20Sopenharmony_ci list_add_tail(&clock->list, &phyter_clocks); 10878c2ecf20Sopenharmony_ciout: 10888c2ecf20Sopenharmony_ci mutex_unlock(&phyter_clocks_lock); 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci return dp83640_clock_get(clock); 10918c2ecf20Sopenharmony_ci} 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_cistatic void dp83640_clock_put(struct dp83640_clock *clock) 10948c2ecf20Sopenharmony_ci{ 10958c2ecf20Sopenharmony_ci mutex_unlock(&clock->clock_lock); 10968c2ecf20Sopenharmony_ci} 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_cistatic int dp83640_soft_reset(struct phy_device *phydev) 10998c2ecf20Sopenharmony_ci{ 11008c2ecf20Sopenharmony_ci int ret; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci ret = genphy_soft_reset(phydev); 11038c2ecf20Sopenharmony_ci if (ret < 0) 11048c2ecf20Sopenharmony_ci return ret; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci /* From DP83640 datasheet: "Software driver code must wait 3 us 11078c2ecf20Sopenharmony_ci * following a software reset before allowing further serial MII 11088c2ecf20Sopenharmony_ci * operations with the DP83640." 11098c2ecf20Sopenharmony_ci */ 11108c2ecf20Sopenharmony_ci udelay(10); /* Taking udelay inaccuracy into account */ 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci return 0; 11138c2ecf20Sopenharmony_ci} 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_cistatic int dp83640_config_init(struct phy_device *phydev) 11168c2ecf20Sopenharmony_ci{ 11178c2ecf20Sopenharmony_ci struct dp83640_private *dp83640 = phydev->priv; 11188c2ecf20Sopenharmony_ci struct dp83640_clock *clock = dp83640->clock; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci if (clock->chosen && !list_empty(&clock->phylist)) 11218c2ecf20Sopenharmony_ci recalibrate(clock); 11228c2ecf20Sopenharmony_ci else { 11238c2ecf20Sopenharmony_ci mutex_lock(&clock->extreg_lock); 11248c2ecf20Sopenharmony_ci enable_broadcast(phydev, clock->page, 1); 11258c2ecf20Sopenharmony_ci mutex_unlock(&clock->extreg_lock); 11268c2ecf20Sopenharmony_ci } 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci enable_status_frames(phydev, true); 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci mutex_lock(&clock->extreg_lock); 11318c2ecf20Sopenharmony_ci ext_write(0, phydev, PAGE4, PTP_CTL, PTP_ENABLE); 11328c2ecf20Sopenharmony_ci mutex_unlock(&clock->extreg_lock); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci return 0; 11358c2ecf20Sopenharmony_ci} 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_cistatic int dp83640_ack_interrupt(struct phy_device *phydev) 11388c2ecf20Sopenharmony_ci{ 11398c2ecf20Sopenharmony_ci int err = phy_read(phydev, MII_DP83640_MISR); 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci if (err < 0) 11428c2ecf20Sopenharmony_ci return err; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci return 0; 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_cistatic int dp83640_config_intr(struct phy_device *phydev) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci int micr; 11508c2ecf20Sopenharmony_ci int misr; 11518c2ecf20Sopenharmony_ci int err; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 11548c2ecf20Sopenharmony_ci misr = phy_read(phydev, MII_DP83640_MISR); 11558c2ecf20Sopenharmony_ci if (misr < 0) 11568c2ecf20Sopenharmony_ci return misr; 11578c2ecf20Sopenharmony_ci misr |= 11588c2ecf20Sopenharmony_ci (MII_DP83640_MISR_ANC_INT_EN | 11598c2ecf20Sopenharmony_ci MII_DP83640_MISR_DUP_INT_EN | 11608c2ecf20Sopenharmony_ci MII_DP83640_MISR_SPD_INT_EN | 11618c2ecf20Sopenharmony_ci MII_DP83640_MISR_LINK_INT_EN); 11628c2ecf20Sopenharmony_ci err = phy_write(phydev, MII_DP83640_MISR, misr); 11638c2ecf20Sopenharmony_ci if (err < 0) 11648c2ecf20Sopenharmony_ci return err; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci micr = phy_read(phydev, MII_DP83640_MICR); 11678c2ecf20Sopenharmony_ci if (micr < 0) 11688c2ecf20Sopenharmony_ci return micr; 11698c2ecf20Sopenharmony_ci micr |= 11708c2ecf20Sopenharmony_ci (MII_DP83640_MICR_OE | 11718c2ecf20Sopenharmony_ci MII_DP83640_MICR_IE); 11728c2ecf20Sopenharmony_ci return phy_write(phydev, MII_DP83640_MICR, micr); 11738c2ecf20Sopenharmony_ci } else { 11748c2ecf20Sopenharmony_ci micr = phy_read(phydev, MII_DP83640_MICR); 11758c2ecf20Sopenharmony_ci if (micr < 0) 11768c2ecf20Sopenharmony_ci return micr; 11778c2ecf20Sopenharmony_ci micr &= 11788c2ecf20Sopenharmony_ci ~(MII_DP83640_MICR_OE | 11798c2ecf20Sopenharmony_ci MII_DP83640_MICR_IE); 11808c2ecf20Sopenharmony_ci err = phy_write(phydev, MII_DP83640_MICR, micr); 11818c2ecf20Sopenharmony_ci if (err < 0) 11828c2ecf20Sopenharmony_ci return err; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci misr = phy_read(phydev, MII_DP83640_MISR); 11858c2ecf20Sopenharmony_ci if (misr < 0) 11868c2ecf20Sopenharmony_ci return misr; 11878c2ecf20Sopenharmony_ci misr &= 11888c2ecf20Sopenharmony_ci ~(MII_DP83640_MISR_ANC_INT_EN | 11898c2ecf20Sopenharmony_ci MII_DP83640_MISR_DUP_INT_EN | 11908c2ecf20Sopenharmony_ci MII_DP83640_MISR_SPD_INT_EN | 11918c2ecf20Sopenharmony_ci MII_DP83640_MISR_LINK_INT_EN); 11928c2ecf20Sopenharmony_ci return phy_write(phydev, MII_DP83640_MISR, misr); 11938c2ecf20Sopenharmony_ci } 11948c2ecf20Sopenharmony_ci} 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_cistatic int dp83640_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) 11978c2ecf20Sopenharmony_ci{ 11988c2ecf20Sopenharmony_ci struct dp83640_private *dp83640 = 11998c2ecf20Sopenharmony_ci container_of(mii_ts, struct dp83640_private, mii_ts); 12008c2ecf20Sopenharmony_ci struct hwtstamp_config cfg; 12018c2ecf20Sopenharmony_ci u16 txcfg0, rxcfg0; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) 12048c2ecf20Sopenharmony_ci return -EFAULT; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci if (cfg.flags) /* reserved for future extensions */ 12078c2ecf20Sopenharmony_ci return -EINVAL; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci if (cfg.tx_type < 0 || cfg.tx_type > HWTSTAMP_TX_ONESTEP_SYNC) 12108c2ecf20Sopenharmony_ci return -ERANGE; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci dp83640->hwts_tx_en = cfg.tx_type; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci switch (cfg.rx_filter) { 12158c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_NONE: 12168c2ecf20Sopenharmony_ci dp83640->hwts_rx_en = 0; 12178c2ecf20Sopenharmony_ci dp83640->layer = 0; 12188c2ecf20Sopenharmony_ci dp83640->version = 0; 12198c2ecf20Sopenharmony_ci break; 12208c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: 12218c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: 12228c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: 12238c2ecf20Sopenharmony_ci dp83640->hwts_rx_en = 1; 12248c2ecf20Sopenharmony_ci dp83640->layer = PTP_CLASS_L4; 12258c2ecf20Sopenharmony_ci dp83640->version = PTP_CLASS_V1; 12268c2ecf20Sopenharmony_ci cfg.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; 12278c2ecf20Sopenharmony_ci break; 12288c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: 12298c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: 12308c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: 12318c2ecf20Sopenharmony_ci dp83640->hwts_rx_en = 1; 12328c2ecf20Sopenharmony_ci dp83640->layer = PTP_CLASS_L4; 12338c2ecf20Sopenharmony_ci dp83640->version = PTP_CLASS_V2; 12348c2ecf20Sopenharmony_ci cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; 12358c2ecf20Sopenharmony_ci break; 12368c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 12378c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: 12388c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: 12398c2ecf20Sopenharmony_ci dp83640->hwts_rx_en = 1; 12408c2ecf20Sopenharmony_ci dp83640->layer = PTP_CLASS_L2; 12418c2ecf20Sopenharmony_ci dp83640->version = PTP_CLASS_V2; 12428c2ecf20Sopenharmony_ci cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; 12438c2ecf20Sopenharmony_ci break; 12448c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_EVENT: 12458c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_SYNC: 12468c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: 12478c2ecf20Sopenharmony_ci dp83640->hwts_rx_en = 1; 12488c2ecf20Sopenharmony_ci dp83640->layer = PTP_CLASS_L4 | PTP_CLASS_L2; 12498c2ecf20Sopenharmony_ci dp83640->version = PTP_CLASS_V2; 12508c2ecf20Sopenharmony_ci cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; 12518c2ecf20Sopenharmony_ci break; 12528c2ecf20Sopenharmony_ci default: 12538c2ecf20Sopenharmony_ci return -ERANGE; 12548c2ecf20Sopenharmony_ci } 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci txcfg0 = (dp83640->version & TX_PTP_VER_MASK) << TX_PTP_VER_SHIFT; 12578c2ecf20Sopenharmony_ci rxcfg0 = (dp83640->version & TX_PTP_VER_MASK) << TX_PTP_VER_SHIFT; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci if (dp83640->layer & PTP_CLASS_L2) { 12608c2ecf20Sopenharmony_ci txcfg0 |= TX_L2_EN; 12618c2ecf20Sopenharmony_ci rxcfg0 |= RX_L2_EN; 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci if (dp83640->layer & PTP_CLASS_L4) { 12648c2ecf20Sopenharmony_ci txcfg0 |= TX_IPV6_EN | TX_IPV4_EN; 12658c2ecf20Sopenharmony_ci rxcfg0 |= RX_IPV6_EN | RX_IPV4_EN; 12668c2ecf20Sopenharmony_ci } 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci if (dp83640->hwts_tx_en) 12698c2ecf20Sopenharmony_ci txcfg0 |= TX_TS_EN; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci if (dp83640->hwts_tx_en == HWTSTAMP_TX_ONESTEP_SYNC) 12728c2ecf20Sopenharmony_ci txcfg0 |= SYNC_1STEP | CHK_1STEP; 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci if (dp83640->hwts_rx_en) 12758c2ecf20Sopenharmony_ci rxcfg0 |= RX_TS_EN; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci mutex_lock(&dp83640->clock->extreg_lock); 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci ext_write(0, dp83640->phydev, PAGE5, PTP_TXCFG0, txcfg0); 12808c2ecf20Sopenharmony_ci ext_write(0, dp83640->phydev, PAGE5, PTP_RXCFG0, rxcfg0); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci mutex_unlock(&dp83640->clock->extreg_lock); 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; 12858c2ecf20Sopenharmony_ci} 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_cistatic void rx_timestamp_work(struct work_struct *work) 12888c2ecf20Sopenharmony_ci{ 12898c2ecf20Sopenharmony_ci struct dp83640_private *dp83640 = 12908c2ecf20Sopenharmony_ci container_of(work, struct dp83640_private, ts_work.work); 12918c2ecf20Sopenharmony_ci struct sk_buff *skb; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci /* Deliver expired packets. */ 12948c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&dp83640->rx_queue))) { 12958c2ecf20Sopenharmony_ci struct dp83640_skb_info *skb_info; 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci skb_info = (struct dp83640_skb_info *)skb->cb; 12988c2ecf20Sopenharmony_ci if (!time_after(jiffies, skb_info->tmo)) { 12998c2ecf20Sopenharmony_ci skb_queue_head(&dp83640->rx_queue, skb); 13008c2ecf20Sopenharmony_ci break; 13018c2ecf20Sopenharmony_ci } 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci netif_rx_ni(skb); 13048c2ecf20Sopenharmony_ci } 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci if (!skb_queue_empty(&dp83640->rx_queue)) 13078c2ecf20Sopenharmony_ci schedule_delayed_work(&dp83640->ts_work, SKB_TIMESTAMP_TIMEOUT); 13088c2ecf20Sopenharmony_ci} 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_cistatic bool dp83640_rxtstamp(struct mii_timestamper *mii_ts, 13118c2ecf20Sopenharmony_ci struct sk_buff *skb, int type) 13128c2ecf20Sopenharmony_ci{ 13138c2ecf20Sopenharmony_ci struct dp83640_private *dp83640 = 13148c2ecf20Sopenharmony_ci container_of(mii_ts, struct dp83640_private, mii_ts); 13158c2ecf20Sopenharmony_ci struct dp83640_skb_info *skb_info = (struct dp83640_skb_info *)skb->cb; 13168c2ecf20Sopenharmony_ci struct list_head *this, *next; 13178c2ecf20Sopenharmony_ci struct rxts *rxts; 13188c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps *shhwtstamps = NULL; 13198c2ecf20Sopenharmony_ci unsigned long flags; 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci if (is_status_frame(skb, type)) { 13228c2ecf20Sopenharmony_ci decode_status_frame(dp83640, skb); 13238c2ecf20Sopenharmony_ci kfree_skb(skb); 13248c2ecf20Sopenharmony_ci return true; 13258c2ecf20Sopenharmony_ci } 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci if (!dp83640->hwts_rx_en) 13288c2ecf20Sopenharmony_ci return false; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci if ((type & dp83640->version) == 0 || (type & dp83640->layer) == 0) 13318c2ecf20Sopenharmony_ci return false; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci spin_lock_irqsave(&dp83640->rx_lock, flags); 13348c2ecf20Sopenharmony_ci prune_rx_ts(dp83640); 13358c2ecf20Sopenharmony_ci list_for_each_safe(this, next, &dp83640->rxts) { 13368c2ecf20Sopenharmony_ci rxts = list_entry(this, struct rxts, list); 13378c2ecf20Sopenharmony_ci if (match(skb, type, rxts)) { 13388c2ecf20Sopenharmony_ci shhwtstamps = skb_hwtstamps(skb); 13398c2ecf20Sopenharmony_ci memset(shhwtstamps, 0, sizeof(*shhwtstamps)); 13408c2ecf20Sopenharmony_ci shhwtstamps->hwtstamp = ns_to_ktime(rxts->ns); 13418c2ecf20Sopenharmony_ci list_del_init(&rxts->list); 13428c2ecf20Sopenharmony_ci list_add(&rxts->list, &dp83640->rxpool); 13438c2ecf20Sopenharmony_ci break; 13448c2ecf20Sopenharmony_ci } 13458c2ecf20Sopenharmony_ci } 13468c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dp83640->rx_lock, flags); 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci if (!shhwtstamps) { 13498c2ecf20Sopenharmony_ci skb_info->ptp_type = type; 13508c2ecf20Sopenharmony_ci skb_info->tmo = jiffies + SKB_TIMESTAMP_TIMEOUT; 13518c2ecf20Sopenharmony_ci skb_queue_tail(&dp83640->rx_queue, skb); 13528c2ecf20Sopenharmony_ci schedule_delayed_work(&dp83640->ts_work, SKB_TIMESTAMP_TIMEOUT); 13538c2ecf20Sopenharmony_ci } else { 13548c2ecf20Sopenharmony_ci netif_rx_ni(skb); 13558c2ecf20Sopenharmony_ci } 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci return true; 13588c2ecf20Sopenharmony_ci} 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_cistatic void dp83640_txtstamp(struct mii_timestamper *mii_ts, 13618c2ecf20Sopenharmony_ci struct sk_buff *skb, int type) 13628c2ecf20Sopenharmony_ci{ 13638c2ecf20Sopenharmony_ci struct dp83640_skb_info *skb_info = (struct dp83640_skb_info *)skb->cb; 13648c2ecf20Sopenharmony_ci struct dp83640_private *dp83640 = 13658c2ecf20Sopenharmony_ci container_of(mii_ts, struct dp83640_private, mii_ts); 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci switch (dp83640->hwts_tx_en) { 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci case HWTSTAMP_TX_ONESTEP_SYNC: 13708c2ecf20Sopenharmony_ci if (is_sync(skb, type)) { 13718c2ecf20Sopenharmony_ci kfree_skb(skb); 13728c2ecf20Sopenharmony_ci return; 13738c2ecf20Sopenharmony_ci } 13748c2ecf20Sopenharmony_ci fallthrough; 13758c2ecf20Sopenharmony_ci case HWTSTAMP_TX_ON: 13768c2ecf20Sopenharmony_ci skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 13778c2ecf20Sopenharmony_ci skb_info->tmo = jiffies + SKB_TIMESTAMP_TIMEOUT; 13788c2ecf20Sopenharmony_ci skb_queue_tail(&dp83640->tx_queue, skb); 13798c2ecf20Sopenharmony_ci break; 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci case HWTSTAMP_TX_OFF: 13828c2ecf20Sopenharmony_ci default: 13838c2ecf20Sopenharmony_ci kfree_skb(skb); 13848c2ecf20Sopenharmony_ci break; 13858c2ecf20Sopenharmony_ci } 13868c2ecf20Sopenharmony_ci} 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_cistatic int dp83640_ts_info(struct mii_timestamper *mii_ts, 13898c2ecf20Sopenharmony_ci struct ethtool_ts_info *info) 13908c2ecf20Sopenharmony_ci{ 13918c2ecf20Sopenharmony_ci struct dp83640_private *dp83640 = 13928c2ecf20Sopenharmony_ci container_of(mii_ts, struct dp83640_private, mii_ts); 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci info->so_timestamping = 13958c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_TX_HARDWARE | 13968c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_RX_HARDWARE | 13978c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_RAW_HARDWARE; 13988c2ecf20Sopenharmony_ci info->phc_index = ptp_clock_index(dp83640->clock->ptp_clock); 13998c2ecf20Sopenharmony_ci info->tx_types = 14008c2ecf20Sopenharmony_ci (1 << HWTSTAMP_TX_OFF) | 14018c2ecf20Sopenharmony_ci (1 << HWTSTAMP_TX_ON) | 14028c2ecf20Sopenharmony_ci (1 << HWTSTAMP_TX_ONESTEP_SYNC); 14038c2ecf20Sopenharmony_ci info->rx_filters = 14048c2ecf20Sopenharmony_ci (1 << HWTSTAMP_FILTER_NONE) | 14058c2ecf20Sopenharmony_ci (1 << HWTSTAMP_FILTER_PTP_V1_L4_EVENT) | 14068c2ecf20Sopenharmony_ci (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT) | 14078c2ecf20Sopenharmony_ci (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | 14088c2ecf20Sopenharmony_ci (1 << HWTSTAMP_FILTER_PTP_V2_EVENT); 14098c2ecf20Sopenharmony_ci return 0; 14108c2ecf20Sopenharmony_ci} 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_cistatic int dp83640_probe(struct phy_device *phydev) 14138c2ecf20Sopenharmony_ci{ 14148c2ecf20Sopenharmony_ci struct dp83640_clock *clock; 14158c2ecf20Sopenharmony_ci struct dp83640_private *dp83640; 14168c2ecf20Sopenharmony_ci int err = -ENOMEM, i; 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci if (phydev->mdio.addr == BROADCAST_ADDR) 14198c2ecf20Sopenharmony_ci return 0; 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci clock = dp83640_clock_get_bus(phydev->mdio.bus); 14228c2ecf20Sopenharmony_ci if (!clock) 14238c2ecf20Sopenharmony_ci goto no_clock; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci dp83640 = kzalloc(sizeof(struct dp83640_private), GFP_KERNEL); 14268c2ecf20Sopenharmony_ci if (!dp83640) 14278c2ecf20Sopenharmony_ci goto no_memory; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci dp83640->phydev = phydev; 14308c2ecf20Sopenharmony_ci dp83640->mii_ts.rxtstamp = dp83640_rxtstamp; 14318c2ecf20Sopenharmony_ci dp83640->mii_ts.txtstamp = dp83640_txtstamp; 14328c2ecf20Sopenharmony_ci dp83640->mii_ts.hwtstamp = dp83640_hwtstamp; 14338c2ecf20Sopenharmony_ci dp83640->mii_ts.ts_info = dp83640_ts_info; 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&dp83640->ts_work, rx_timestamp_work); 14368c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dp83640->rxts); 14378c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dp83640->rxpool); 14388c2ecf20Sopenharmony_ci for (i = 0; i < MAX_RXTS; i++) 14398c2ecf20Sopenharmony_ci list_add(&dp83640->rx_pool_data[i].list, &dp83640->rxpool); 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci phydev->mii_ts = &dp83640->mii_ts; 14428c2ecf20Sopenharmony_ci phydev->priv = dp83640; 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci spin_lock_init(&dp83640->rx_lock); 14458c2ecf20Sopenharmony_ci skb_queue_head_init(&dp83640->rx_queue); 14468c2ecf20Sopenharmony_ci skb_queue_head_init(&dp83640->tx_queue); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci dp83640->clock = clock; 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci if (choose_this_phy(clock, phydev)) { 14518c2ecf20Sopenharmony_ci clock->chosen = dp83640; 14528c2ecf20Sopenharmony_ci clock->ptp_clock = ptp_clock_register(&clock->caps, 14538c2ecf20Sopenharmony_ci &phydev->mdio.dev); 14548c2ecf20Sopenharmony_ci if (IS_ERR(clock->ptp_clock)) { 14558c2ecf20Sopenharmony_ci err = PTR_ERR(clock->ptp_clock); 14568c2ecf20Sopenharmony_ci goto no_register; 14578c2ecf20Sopenharmony_ci } 14588c2ecf20Sopenharmony_ci } else 14598c2ecf20Sopenharmony_ci list_add_tail(&dp83640->list, &clock->phylist); 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci dp83640_clock_put(clock); 14628c2ecf20Sopenharmony_ci return 0; 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_cino_register: 14658c2ecf20Sopenharmony_ci clock->chosen = NULL; 14668c2ecf20Sopenharmony_ci kfree(dp83640); 14678c2ecf20Sopenharmony_cino_memory: 14688c2ecf20Sopenharmony_ci dp83640_clock_put(clock); 14698c2ecf20Sopenharmony_cino_clock: 14708c2ecf20Sopenharmony_ci return err; 14718c2ecf20Sopenharmony_ci} 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_cistatic void dp83640_remove(struct phy_device *phydev) 14748c2ecf20Sopenharmony_ci{ 14758c2ecf20Sopenharmony_ci struct dp83640_clock *clock; 14768c2ecf20Sopenharmony_ci struct list_head *this, *next; 14778c2ecf20Sopenharmony_ci struct dp83640_private *tmp, *dp83640 = phydev->priv; 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci if (phydev->mdio.addr == BROADCAST_ADDR) 14808c2ecf20Sopenharmony_ci return; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci phydev->mii_ts = NULL; 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci enable_status_frames(phydev, false); 14858c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&dp83640->ts_work); 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci skb_queue_purge(&dp83640->rx_queue); 14888c2ecf20Sopenharmony_ci skb_queue_purge(&dp83640->tx_queue); 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci clock = dp83640_clock_get(dp83640->clock); 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci if (dp83640 == clock->chosen) { 14938c2ecf20Sopenharmony_ci ptp_clock_unregister(clock->ptp_clock); 14948c2ecf20Sopenharmony_ci clock->chosen = NULL; 14958c2ecf20Sopenharmony_ci } else { 14968c2ecf20Sopenharmony_ci list_for_each_safe(this, next, &clock->phylist) { 14978c2ecf20Sopenharmony_ci tmp = list_entry(this, struct dp83640_private, list); 14988c2ecf20Sopenharmony_ci if (tmp == dp83640) { 14998c2ecf20Sopenharmony_ci list_del_init(&tmp->list); 15008c2ecf20Sopenharmony_ci break; 15018c2ecf20Sopenharmony_ci } 15028c2ecf20Sopenharmony_ci } 15038c2ecf20Sopenharmony_ci } 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci dp83640_clock_put(clock); 15068c2ecf20Sopenharmony_ci kfree(dp83640); 15078c2ecf20Sopenharmony_ci} 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_cistatic struct phy_driver dp83640_driver = { 15108c2ecf20Sopenharmony_ci .phy_id = DP83640_PHY_ID, 15118c2ecf20Sopenharmony_ci .phy_id_mask = 0xfffffff0, 15128c2ecf20Sopenharmony_ci .name = "NatSemi DP83640", 15138c2ecf20Sopenharmony_ci /* PHY_BASIC_FEATURES */ 15148c2ecf20Sopenharmony_ci .probe = dp83640_probe, 15158c2ecf20Sopenharmony_ci .remove = dp83640_remove, 15168c2ecf20Sopenharmony_ci .soft_reset = dp83640_soft_reset, 15178c2ecf20Sopenharmony_ci .config_init = dp83640_config_init, 15188c2ecf20Sopenharmony_ci .ack_interrupt = dp83640_ack_interrupt, 15198c2ecf20Sopenharmony_ci .config_intr = dp83640_config_intr, 15208c2ecf20Sopenharmony_ci}; 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_cistatic int __init dp83640_init(void) 15238c2ecf20Sopenharmony_ci{ 15248c2ecf20Sopenharmony_ci return phy_driver_register(&dp83640_driver, THIS_MODULE); 15258c2ecf20Sopenharmony_ci} 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_cistatic void __exit dp83640_exit(void) 15288c2ecf20Sopenharmony_ci{ 15298c2ecf20Sopenharmony_ci dp83640_free_clocks(); 15308c2ecf20Sopenharmony_ci phy_driver_unregister(&dp83640_driver); 15318c2ecf20Sopenharmony_ci} 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("National Semiconductor DP83640 PHY driver"); 15348c2ecf20Sopenharmony_ciMODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>"); 15358c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_cimodule_init(dp83640_init); 15388c2ecf20Sopenharmony_cimodule_exit(dp83640_exit); 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_cistatic struct mdio_device_id __maybe_unused dp83640_tbl[] = { 15418c2ecf20Sopenharmony_ci { DP83640_PHY_ID, 0xfffffff0 }, 15428c2ecf20Sopenharmony_ci { } 15438c2ecf20Sopenharmony_ci}; 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, dp83640_tbl); 1546