18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Copyright (C) 2018 MOSER-BAER AG 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "InES_PTP: " fmt 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 98c2ecf20Sopenharmony_ci#include <linux/export.h> 108c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 118c2ecf20Sopenharmony_ci#include <linux/mii_timestamper.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/net_tstamp.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/of_address.h> 168c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 178c2ecf20Sopenharmony_ci#include <linux/phy.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci#include <linux/ptp_classify.h> 208c2ecf20Sopenharmony_ci#include <linux/ptp_clock_kernel.h> 218c2ecf20Sopenharmony_ci#include <linux/stddef.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for the ZHAW InES PTP time stamping IP core"); 248c2ecf20Sopenharmony_ciMODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>"); 258c2ecf20Sopenharmony_ciMODULE_VERSION("1.0"); 268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* GLOBAL register */ 298c2ecf20Sopenharmony_ci#define MCAST_MAC_SELECT_SHIFT 2 308c2ecf20Sopenharmony_ci#define MCAST_MAC_SELECT_MASK 0x3 318c2ecf20Sopenharmony_ci#define IO_RESET BIT(1) 328c2ecf20Sopenharmony_ci#define PTP_RESET BIT(0) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* VERSION register */ 358c2ecf20Sopenharmony_ci#define IF_MAJOR_VER_SHIFT 12 368c2ecf20Sopenharmony_ci#define IF_MAJOR_VER_MASK 0xf 378c2ecf20Sopenharmony_ci#define IF_MINOR_VER_SHIFT 8 388c2ecf20Sopenharmony_ci#define IF_MINOR_VER_MASK 0xf 398c2ecf20Sopenharmony_ci#define FPGA_MAJOR_VER_SHIFT 4 408c2ecf20Sopenharmony_ci#define FPGA_MAJOR_VER_MASK 0xf 418c2ecf20Sopenharmony_ci#define FPGA_MINOR_VER_SHIFT 0 428c2ecf20Sopenharmony_ci#define FPGA_MINOR_VER_MASK 0xf 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* INT_STAT register */ 458c2ecf20Sopenharmony_ci#define RX_INTR_STATUS_3 BIT(5) 468c2ecf20Sopenharmony_ci#define RX_INTR_STATUS_2 BIT(4) 478c2ecf20Sopenharmony_ci#define RX_INTR_STATUS_1 BIT(3) 488c2ecf20Sopenharmony_ci#define TX_INTR_STATUS_3 BIT(2) 498c2ecf20Sopenharmony_ci#define TX_INTR_STATUS_2 BIT(1) 508c2ecf20Sopenharmony_ci#define TX_INTR_STATUS_1 BIT(0) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* INT_MSK register */ 538c2ecf20Sopenharmony_ci#define RX_INTR_MASK_3 BIT(5) 548c2ecf20Sopenharmony_ci#define RX_INTR_MASK_2 BIT(4) 558c2ecf20Sopenharmony_ci#define RX_INTR_MASK_1 BIT(3) 568c2ecf20Sopenharmony_ci#define TX_INTR_MASK_3 BIT(2) 578c2ecf20Sopenharmony_ci#define TX_INTR_MASK_2 BIT(1) 588c2ecf20Sopenharmony_ci#define TX_INTR_MASK_1 BIT(0) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* BUF_STAT register */ 618c2ecf20Sopenharmony_ci#define RX_FIFO_NE_3 BIT(5) 628c2ecf20Sopenharmony_ci#define RX_FIFO_NE_2 BIT(4) 638c2ecf20Sopenharmony_ci#define RX_FIFO_NE_1 BIT(3) 648c2ecf20Sopenharmony_ci#define TX_FIFO_NE_3 BIT(2) 658c2ecf20Sopenharmony_ci#define TX_FIFO_NE_2 BIT(1) 668c2ecf20Sopenharmony_ci#define TX_FIFO_NE_1 BIT(0) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* PORT_CONF register */ 698c2ecf20Sopenharmony_ci#define CM_ONE_STEP BIT(6) 708c2ecf20Sopenharmony_ci#define PHY_SPEED_SHIFT 4 718c2ecf20Sopenharmony_ci#define PHY_SPEED_MASK 0x3 728c2ecf20Sopenharmony_ci#define P2P_DELAY_WR_POS_SHIFT 2 738c2ecf20Sopenharmony_ci#define P2P_DELAY_WR_POS_MASK 0x3 748c2ecf20Sopenharmony_ci#define PTP_MODE_SHIFT 0 758c2ecf20Sopenharmony_ci#define PTP_MODE_MASK 0x3 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* TS_STAT_TX register */ 788c2ecf20Sopenharmony_ci#define TS_ENABLE BIT(15) 798c2ecf20Sopenharmony_ci#define DATA_READ_POS_SHIFT 8 808c2ecf20Sopenharmony_ci#define DATA_READ_POS_MASK 0x1f 818c2ecf20Sopenharmony_ci#define DISCARDED_EVENTS_SHIFT 4 828c2ecf20Sopenharmony_ci#define DISCARDED_EVENTS_MASK 0xf 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#define INES_N_PORTS 3 858c2ecf20Sopenharmony_ci#define INES_REGISTER_SIZE 0x80 868c2ecf20Sopenharmony_ci#define INES_PORT_OFFSET 0x20 878c2ecf20Sopenharmony_ci#define INES_PORT_SIZE 0x20 888c2ecf20Sopenharmony_ci#define INES_FIFO_DEPTH 90 898c2ecf20Sopenharmony_ci#define INES_MAX_EVENTS 100 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#define BC_PTP_V1 0 928c2ecf20Sopenharmony_ci#define BC_PTP_V2 1 938c2ecf20Sopenharmony_ci#define TC_E2E_PTP_V2 2 948c2ecf20Sopenharmony_ci#define TC_P2P_PTP_V2 3 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#define PHY_SPEED_10 0 978c2ecf20Sopenharmony_ci#define PHY_SPEED_100 1 988c2ecf20Sopenharmony_ci#define PHY_SPEED_1000 2 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define PORT_CONF \ 1018c2ecf20Sopenharmony_ci ((PHY_SPEED_1000 << PHY_SPEED_SHIFT) | (BC_PTP_V2 << PTP_MODE_SHIFT)) 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci#define ines_read32(s, r) __raw_readl((void __iomem *)&s->regs->r) 1048c2ecf20Sopenharmony_ci#define ines_write32(s, v, r) __raw_writel(v, (void __iomem *)&s->regs->r) 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci#define MESSAGE_TYPE_SYNC 1 1078c2ecf20Sopenharmony_ci#define MESSAGE_TYPE_P_DELAY_REQ 2 1088c2ecf20Sopenharmony_ci#define MESSAGE_TYPE_P_DELAY_RESP 3 1098c2ecf20Sopenharmony_ci#define MESSAGE_TYPE_DELAY_REQ 4 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci#define SYNC 0x0 1128c2ecf20Sopenharmony_ci#define DELAY_REQ 0x1 1138c2ecf20Sopenharmony_ci#define PDELAY_REQ 0x2 1148c2ecf20Sopenharmony_ci#define PDELAY_RESP 0x3 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic LIST_HEAD(ines_clocks); 1178c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ines_clocks_lock); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistruct ines_global_regs { 1208c2ecf20Sopenharmony_ci u32 id; 1218c2ecf20Sopenharmony_ci u32 test; 1228c2ecf20Sopenharmony_ci u32 global; 1238c2ecf20Sopenharmony_ci u32 version; 1248c2ecf20Sopenharmony_ci u32 test2; 1258c2ecf20Sopenharmony_ci u32 int_stat; 1268c2ecf20Sopenharmony_ci u32 int_msk; 1278c2ecf20Sopenharmony_ci u32 buf_stat; 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistruct ines_port_registers { 1318c2ecf20Sopenharmony_ci u32 port_conf; 1328c2ecf20Sopenharmony_ci u32 p_delay; 1338c2ecf20Sopenharmony_ci u32 ts_stat_tx; 1348c2ecf20Sopenharmony_ci u32 ts_stat_rx; 1358c2ecf20Sopenharmony_ci u32 ts_tx; 1368c2ecf20Sopenharmony_ci u32 ts_rx; 1378c2ecf20Sopenharmony_ci}; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistruct ines_timestamp { 1408c2ecf20Sopenharmony_ci struct list_head list; 1418c2ecf20Sopenharmony_ci unsigned long tmo; 1428c2ecf20Sopenharmony_ci u16 tag; 1438c2ecf20Sopenharmony_ci u64 sec; 1448c2ecf20Sopenharmony_ci u64 nsec; 1458c2ecf20Sopenharmony_ci u64 clkid; 1468c2ecf20Sopenharmony_ci u16 portnum; 1478c2ecf20Sopenharmony_ci u16 seqid; 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistruct ines_port { 1518c2ecf20Sopenharmony_ci struct ines_port_registers *regs; 1528c2ecf20Sopenharmony_ci struct mii_timestamper mii_ts; 1538c2ecf20Sopenharmony_ci struct ines_clock *clock; 1548c2ecf20Sopenharmony_ci bool rxts_enabled; 1558c2ecf20Sopenharmony_ci bool txts_enabled; 1568c2ecf20Sopenharmony_ci unsigned int index; 1578c2ecf20Sopenharmony_ci struct delayed_work ts_work; 1588c2ecf20Sopenharmony_ci /* lock protects event list and tx_skb */ 1598c2ecf20Sopenharmony_ci spinlock_t lock; 1608c2ecf20Sopenharmony_ci struct sk_buff *tx_skb; 1618c2ecf20Sopenharmony_ci struct list_head events; 1628c2ecf20Sopenharmony_ci struct list_head pool; 1638c2ecf20Sopenharmony_ci struct ines_timestamp pool_data[INES_MAX_EVENTS]; 1648c2ecf20Sopenharmony_ci}; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistruct ines_clock { 1678c2ecf20Sopenharmony_ci struct ines_port port[INES_N_PORTS]; 1688c2ecf20Sopenharmony_ci struct ines_global_regs __iomem *regs; 1698c2ecf20Sopenharmony_ci void __iomem *base; 1708c2ecf20Sopenharmony_ci struct device_node *node; 1718c2ecf20Sopenharmony_ci struct device *dev; 1728c2ecf20Sopenharmony_ci struct list_head list; 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic bool ines_match(struct sk_buff *skb, unsigned int ptp_class, 1768c2ecf20Sopenharmony_ci struct ines_timestamp *ts, struct device *dev); 1778c2ecf20Sopenharmony_cistatic int ines_rxfifo_read(struct ines_port *port); 1788c2ecf20Sopenharmony_cistatic u64 ines_rxts64(struct ines_port *port, unsigned int words); 1798c2ecf20Sopenharmony_cistatic bool ines_timestamp_expired(struct ines_timestamp *ts); 1808c2ecf20Sopenharmony_cistatic u64 ines_txts64(struct ines_port *port, unsigned int words); 1818c2ecf20Sopenharmony_cistatic void ines_txtstamp_work(struct work_struct *work); 1828c2ecf20Sopenharmony_cistatic bool is_sync_pdelay_resp(struct sk_buff *skb, int type); 1838c2ecf20Sopenharmony_cistatic u8 tag_to_msgtype(u8 tag); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic void ines_clock_cleanup(struct ines_clock *clock) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct ines_port *port; 1888c2ecf20Sopenharmony_ci int i; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci for (i = 0; i < INES_N_PORTS; i++) { 1918c2ecf20Sopenharmony_ci port = &clock->port[i]; 1928c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&port->ts_work); 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int ines_clock_init(struct ines_clock *clock, struct device *device, 1978c2ecf20Sopenharmony_ci void __iomem *addr) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct device_node *node = device->of_node; 2008c2ecf20Sopenharmony_ci unsigned long port_addr; 2018c2ecf20Sopenharmony_ci struct ines_port *port; 2028c2ecf20Sopenharmony_ci int i, j; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&clock->list); 2058c2ecf20Sopenharmony_ci clock->node = node; 2068c2ecf20Sopenharmony_ci clock->dev = device; 2078c2ecf20Sopenharmony_ci clock->base = addr; 2088c2ecf20Sopenharmony_ci clock->regs = clock->base; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci for (i = 0; i < INES_N_PORTS; i++) { 2118c2ecf20Sopenharmony_ci port = &clock->port[i]; 2128c2ecf20Sopenharmony_ci port_addr = (unsigned long) clock->base + 2138c2ecf20Sopenharmony_ci INES_PORT_OFFSET + i * INES_PORT_SIZE; 2148c2ecf20Sopenharmony_ci port->regs = (struct ines_port_registers *) port_addr; 2158c2ecf20Sopenharmony_ci port->clock = clock; 2168c2ecf20Sopenharmony_ci port->index = i; 2178c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&port->ts_work, ines_txtstamp_work); 2188c2ecf20Sopenharmony_ci spin_lock_init(&port->lock); 2198c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&port->events); 2208c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&port->pool); 2218c2ecf20Sopenharmony_ci for (j = 0; j < INES_MAX_EVENTS; j++) 2228c2ecf20Sopenharmony_ci list_add(&port->pool_data[j].list, &port->pool); 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ines_write32(clock, 0xBEEF, test); 2268c2ecf20Sopenharmony_ci ines_write32(clock, 0xBEEF, test2); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci dev_dbg(device, "ID 0x%x\n", ines_read32(clock, id)); 2298c2ecf20Sopenharmony_ci dev_dbg(device, "TEST 0x%x\n", ines_read32(clock, test)); 2308c2ecf20Sopenharmony_ci dev_dbg(device, "VERSION 0x%x\n", ines_read32(clock, version)); 2318c2ecf20Sopenharmony_ci dev_dbg(device, "TEST2 0x%x\n", ines_read32(clock, test2)); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci for (i = 0; i < INES_N_PORTS; i++) { 2348c2ecf20Sopenharmony_ci port = &clock->port[i]; 2358c2ecf20Sopenharmony_ci ines_write32(port, PORT_CONF, port_conf); 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return 0; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic struct ines_port *ines_find_port(struct device_node *node, u32 index) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct ines_port *port = NULL; 2448c2ecf20Sopenharmony_ci struct ines_clock *clock; 2458c2ecf20Sopenharmony_ci struct list_head *this; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci mutex_lock(&ines_clocks_lock); 2488c2ecf20Sopenharmony_ci list_for_each(this, &ines_clocks) { 2498c2ecf20Sopenharmony_ci clock = list_entry(this, struct ines_clock, list); 2508c2ecf20Sopenharmony_ci if (clock->node == node) { 2518c2ecf20Sopenharmony_ci port = &clock->port[index]; 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci mutex_unlock(&ines_clocks_lock); 2568c2ecf20Sopenharmony_ci return port; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic u64 ines_find_rxts(struct ines_port *port, struct sk_buff *skb, int type) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct list_head *this, *next; 2628c2ecf20Sopenharmony_ci struct ines_timestamp *ts; 2638c2ecf20Sopenharmony_ci unsigned long flags; 2648c2ecf20Sopenharmony_ci u64 ns = 0; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (type == PTP_CLASS_NONE) 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 2708c2ecf20Sopenharmony_ci ines_rxfifo_read(port); 2718c2ecf20Sopenharmony_ci list_for_each_safe(this, next, &port->events) { 2728c2ecf20Sopenharmony_ci ts = list_entry(this, struct ines_timestamp, list); 2738c2ecf20Sopenharmony_ci if (ines_timestamp_expired(ts)) { 2748c2ecf20Sopenharmony_ci list_del_init(&ts->list); 2758c2ecf20Sopenharmony_ci list_add(&ts->list, &port->pool); 2768c2ecf20Sopenharmony_ci continue; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci if (ines_match(skb, type, ts, port->clock->dev)) { 2798c2ecf20Sopenharmony_ci ns = ts->sec * 1000000000ULL + ts->nsec; 2808c2ecf20Sopenharmony_ci list_del_init(&ts->list); 2818c2ecf20Sopenharmony_ci list_add(&ts->list, &port->pool); 2828c2ecf20Sopenharmony_ci break; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return ns; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic u64 ines_find_txts(struct ines_port *port, struct sk_buff *skb) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci unsigned int class = ptp_classify_raw(skb), i; 2938c2ecf20Sopenharmony_ci u32 data_rd_pos, buf_stat, mask, ts_stat_tx; 2948c2ecf20Sopenharmony_ci struct ines_timestamp ts; 2958c2ecf20Sopenharmony_ci unsigned long flags; 2968c2ecf20Sopenharmony_ci u64 ns = 0; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci mask = TX_FIFO_NE_1 << port->index; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci for (i = 0; i < INES_FIFO_DEPTH; i++) { 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci buf_stat = ines_read32(port->clock, buf_stat); 3058c2ecf20Sopenharmony_ci if (!(buf_stat & mask)) { 3068c2ecf20Sopenharmony_ci dev_dbg(port->clock->dev, 3078c2ecf20Sopenharmony_ci "Tx timestamp FIFO unexpectedly empty\n"); 3088c2ecf20Sopenharmony_ci break; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci ts_stat_tx = ines_read32(port, ts_stat_tx); 3118c2ecf20Sopenharmony_ci data_rd_pos = (ts_stat_tx >> DATA_READ_POS_SHIFT) & 3128c2ecf20Sopenharmony_ci DATA_READ_POS_MASK; 3138c2ecf20Sopenharmony_ci if (data_rd_pos) { 3148c2ecf20Sopenharmony_ci dev_err(port->clock->dev, 3158c2ecf20Sopenharmony_ci "unexpected Tx read pos %u\n", data_rd_pos); 3168c2ecf20Sopenharmony_ci break; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci ts.tag = ines_read32(port, ts_tx); 3208c2ecf20Sopenharmony_ci ts.sec = ines_txts64(port, 3); 3218c2ecf20Sopenharmony_ci ts.nsec = ines_txts64(port, 2); 3228c2ecf20Sopenharmony_ci ts.clkid = ines_txts64(port, 4); 3238c2ecf20Sopenharmony_ci ts.portnum = ines_read32(port, ts_tx); 3248c2ecf20Sopenharmony_ci ts.seqid = ines_read32(port, ts_tx); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (ines_match(skb, class, &ts, port->clock->dev)) { 3278c2ecf20Sopenharmony_ci ns = ts.sec * 1000000000ULL + ts.nsec; 3288c2ecf20Sopenharmony_ci break; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 3338c2ecf20Sopenharmony_ci return ns; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic int ines_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci struct ines_port *port = container_of(mii_ts, struct ines_port, mii_ts); 3398c2ecf20Sopenharmony_ci u32 cm_one_step = 0, port_conf, ts_stat_rx, ts_stat_tx; 3408c2ecf20Sopenharmony_ci struct hwtstamp_config cfg; 3418c2ecf20Sopenharmony_ci unsigned long flags; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) 3448c2ecf20Sopenharmony_ci return -EFAULT; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* reserved for future extensions */ 3478c2ecf20Sopenharmony_ci if (cfg.flags) 3488c2ecf20Sopenharmony_ci return -EINVAL; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci switch (cfg.tx_type) { 3518c2ecf20Sopenharmony_ci case HWTSTAMP_TX_OFF: 3528c2ecf20Sopenharmony_ci ts_stat_tx = 0; 3538c2ecf20Sopenharmony_ci break; 3548c2ecf20Sopenharmony_ci case HWTSTAMP_TX_ON: 3558c2ecf20Sopenharmony_ci ts_stat_tx = TS_ENABLE; 3568c2ecf20Sopenharmony_ci break; 3578c2ecf20Sopenharmony_ci case HWTSTAMP_TX_ONESTEP_P2P: 3588c2ecf20Sopenharmony_ci ts_stat_tx = TS_ENABLE; 3598c2ecf20Sopenharmony_ci cm_one_step = CM_ONE_STEP; 3608c2ecf20Sopenharmony_ci break; 3618c2ecf20Sopenharmony_ci default: 3628c2ecf20Sopenharmony_ci return -ERANGE; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci switch (cfg.rx_filter) { 3668c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_NONE: 3678c2ecf20Sopenharmony_ci ts_stat_rx = 0; 3688c2ecf20Sopenharmony_ci break; 3698c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_ALL: 3708c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: 3718c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: 3728c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: 3738c2ecf20Sopenharmony_ci return -ERANGE; 3748c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: 3758c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: 3768c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: 3778c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 3788c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: 3798c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: 3808c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_EVENT: 3818c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_SYNC: 3828c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: 3838c2ecf20Sopenharmony_ci ts_stat_rx = TS_ENABLE; 3848c2ecf20Sopenharmony_ci cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; 3858c2ecf20Sopenharmony_ci break; 3868c2ecf20Sopenharmony_ci default: 3878c2ecf20Sopenharmony_ci return -ERANGE; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci port_conf = ines_read32(port, port_conf); 3938c2ecf20Sopenharmony_ci port_conf &= ~CM_ONE_STEP; 3948c2ecf20Sopenharmony_ci port_conf |= cm_one_step; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci ines_write32(port, port_conf, port_conf); 3978c2ecf20Sopenharmony_ci ines_write32(port, ts_stat_rx, ts_stat_rx); 3988c2ecf20Sopenharmony_ci ines_write32(port, ts_stat_tx, ts_stat_tx); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci port->rxts_enabled = ts_stat_rx == TS_ENABLE; 4018c2ecf20Sopenharmony_ci port->txts_enabled = ts_stat_tx == TS_ENABLE; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic void ines_link_state(struct mii_timestamper *mii_ts, 4098c2ecf20Sopenharmony_ci struct phy_device *phydev) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci struct ines_port *port = container_of(mii_ts, struct ines_port, mii_ts); 4128c2ecf20Sopenharmony_ci u32 port_conf, speed_conf; 4138c2ecf20Sopenharmony_ci unsigned long flags; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci switch (phydev->speed) { 4168c2ecf20Sopenharmony_ci case SPEED_10: 4178c2ecf20Sopenharmony_ci speed_conf = PHY_SPEED_10 << PHY_SPEED_SHIFT; 4188c2ecf20Sopenharmony_ci break; 4198c2ecf20Sopenharmony_ci case SPEED_100: 4208c2ecf20Sopenharmony_ci speed_conf = PHY_SPEED_100 << PHY_SPEED_SHIFT; 4218c2ecf20Sopenharmony_ci break; 4228c2ecf20Sopenharmony_ci case SPEED_1000: 4238c2ecf20Sopenharmony_ci speed_conf = PHY_SPEED_1000 << PHY_SPEED_SHIFT; 4248c2ecf20Sopenharmony_ci break; 4258c2ecf20Sopenharmony_ci default: 4268c2ecf20Sopenharmony_ci dev_err(port->clock->dev, "bad speed: %d\n", phydev->speed); 4278c2ecf20Sopenharmony_ci return; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci port_conf = ines_read32(port, port_conf); 4328c2ecf20Sopenharmony_ci port_conf &= ~(0x3 << PHY_SPEED_SHIFT); 4338c2ecf20Sopenharmony_ci port_conf |= speed_conf; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci ines_write32(port, port_conf, port_conf); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic bool ines_match(struct sk_buff *skb, unsigned int ptp_class, 4418c2ecf20Sopenharmony_ci struct ines_timestamp *ts, struct device *dev) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci struct ptp_header *hdr; 4448c2ecf20Sopenharmony_ci u16 portn, seqid; 4458c2ecf20Sopenharmony_ci u8 msgtype; 4468c2ecf20Sopenharmony_ci u64 clkid; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (unlikely(ptp_class & PTP_CLASS_V1)) 4498c2ecf20Sopenharmony_ci return false; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci hdr = ptp_parse_header(skb, ptp_class); 4528c2ecf20Sopenharmony_ci if (!hdr) 4538c2ecf20Sopenharmony_ci return false; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci msgtype = ptp_get_msgtype(hdr, ptp_class); 4568c2ecf20Sopenharmony_ci clkid = be64_to_cpup((__be64 *)&hdr->source_port_identity.clock_identity.id[0]); 4578c2ecf20Sopenharmony_ci portn = be16_to_cpu(hdr->source_port_identity.port_number); 4588c2ecf20Sopenharmony_ci seqid = be16_to_cpu(hdr->sequence_id); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (tag_to_msgtype(ts->tag & 0x7) != msgtype) { 4618c2ecf20Sopenharmony_ci dev_dbg(dev, "msgtype mismatch ts %hhu != skb %hhu\n", 4628c2ecf20Sopenharmony_ci tag_to_msgtype(ts->tag & 0x7), msgtype); 4638c2ecf20Sopenharmony_ci return false; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci if (ts->clkid != clkid) { 4668c2ecf20Sopenharmony_ci dev_dbg(dev, "clkid mismatch ts %llx != skb %llx\n", 4678c2ecf20Sopenharmony_ci ts->clkid, clkid); 4688c2ecf20Sopenharmony_ci return false; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci if (ts->portnum != portn) { 4718c2ecf20Sopenharmony_ci dev_dbg(dev, "portn mismatch ts %hu != skb %hu\n", 4728c2ecf20Sopenharmony_ci ts->portnum, portn); 4738c2ecf20Sopenharmony_ci return false; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci if (ts->seqid != seqid) { 4768c2ecf20Sopenharmony_ci dev_dbg(dev, "seqid mismatch ts %hu != skb %hu\n", 4778c2ecf20Sopenharmony_ci ts->seqid, seqid); 4788c2ecf20Sopenharmony_ci return false; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci return true; 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic bool ines_rxtstamp(struct mii_timestamper *mii_ts, 4858c2ecf20Sopenharmony_ci struct sk_buff *skb, int type) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci struct ines_port *port = container_of(mii_ts, struct ines_port, mii_ts); 4888c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps *ssh; 4898c2ecf20Sopenharmony_ci u64 ns; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (!port->rxts_enabled) 4928c2ecf20Sopenharmony_ci return false; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci ns = ines_find_rxts(port, skb, type); 4958c2ecf20Sopenharmony_ci if (!ns) 4968c2ecf20Sopenharmony_ci return false; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci ssh = skb_hwtstamps(skb); 4998c2ecf20Sopenharmony_ci ssh->hwtstamp = ns_to_ktime(ns); 5008c2ecf20Sopenharmony_ci netif_rx(skb); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci return true; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic int ines_rxfifo_read(struct ines_port *port) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci u32 data_rd_pos, buf_stat, mask, ts_stat_rx; 5088c2ecf20Sopenharmony_ci struct ines_timestamp *ts; 5098c2ecf20Sopenharmony_ci unsigned int i; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci mask = RX_FIFO_NE_1 << port->index; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci for (i = 0; i < INES_FIFO_DEPTH; i++) { 5148c2ecf20Sopenharmony_ci if (list_empty(&port->pool)) { 5158c2ecf20Sopenharmony_ci dev_err(port->clock->dev, "event pool is empty\n"); 5168c2ecf20Sopenharmony_ci return -1; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci buf_stat = ines_read32(port->clock, buf_stat); 5198c2ecf20Sopenharmony_ci if (!(buf_stat & mask)) 5208c2ecf20Sopenharmony_ci break; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci ts_stat_rx = ines_read32(port, ts_stat_rx); 5238c2ecf20Sopenharmony_ci data_rd_pos = (ts_stat_rx >> DATA_READ_POS_SHIFT) & 5248c2ecf20Sopenharmony_ci DATA_READ_POS_MASK; 5258c2ecf20Sopenharmony_ci if (data_rd_pos) { 5268c2ecf20Sopenharmony_ci dev_err(port->clock->dev, "unexpected Rx read pos %u\n", 5278c2ecf20Sopenharmony_ci data_rd_pos); 5288c2ecf20Sopenharmony_ci break; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci ts = list_first_entry(&port->pool, struct ines_timestamp, list); 5328c2ecf20Sopenharmony_ci ts->tmo = jiffies + HZ; 5338c2ecf20Sopenharmony_ci ts->tag = ines_read32(port, ts_rx); 5348c2ecf20Sopenharmony_ci ts->sec = ines_rxts64(port, 3); 5358c2ecf20Sopenharmony_ci ts->nsec = ines_rxts64(port, 2); 5368c2ecf20Sopenharmony_ci ts->clkid = ines_rxts64(port, 4); 5378c2ecf20Sopenharmony_ci ts->portnum = ines_read32(port, ts_rx); 5388c2ecf20Sopenharmony_ci ts->seqid = ines_read32(port, ts_rx); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci list_del_init(&ts->list); 5418c2ecf20Sopenharmony_ci list_add_tail(&ts->list, &port->events); 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci return 0; 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic u64 ines_rxts64(struct ines_port *port, unsigned int words) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci unsigned int i; 5508c2ecf20Sopenharmony_ci u64 result; 5518c2ecf20Sopenharmony_ci u16 word; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci word = ines_read32(port, ts_rx); 5548c2ecf20Sopenharmony_ci result = word; 5558c2ecf20Sopenharmony_ci words--; 5568c2ecf20Sopenharmony_ci for (i = 0; i < words; i++) { 5578c2ecf20Sopenharmony_ci word = ines_read32(port, ts_rx); 5588c2ecf20Sopenharmony_ci result <<= 16; 5598c2ecf20Sopenharmony_ci result |= word; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci return result; 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_cistatic bool ines_timestamp_expired(struct ines_timestamp *ts) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci return time_after(jiffies, ts->tmo); 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic int ines_ts_info(struct mii_timestamper *mii_ts, 5708c2ecf20Sopenharmony_ci struct ethtool_ts_info *info) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci info->so_timestamping = 5738c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_TX_HARDWARE | 5748c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_TX_SOFTWARE | 5758c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_RX_HARDWARE | 5768c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_RX_SOFTWARE | 5778c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_SOFTWARE | 5788c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_RAW_HARDWARE; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci info->phc_index = -1; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci info->tx_types = 5838c2ecf20Sopenharmony_ci (1 << HWTSTAMP_TX_OFF) | 5848c2ecf20Sopenharmony_ci (1 << HWTSTAMP_TX_ON) | 5858c2ecf20Sopenharmony_ci (1 << HWTSTAMP_TX_ONESTEP_P2P); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci info->rx_filters = 5888c2ecf20Sopenharmony_ci (1 << HWTSTAMP_FILTER_NONE) | 5898c2ecf20Sopenharmony_ci (1 << HWTSTAMP_FILTER_PTP_V2_EVENT); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci return 0; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic u64 ines_txts64(struct ines_port *port, unsigned int words) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci unsigned int i; 5978c2ecf20Sopenharmony_ci u64 result; 5988c2ecf20Sopenharmony_ci u16 word; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci word = ines_read32(port, ts_tx); 6018c2ecf20Sopenharmony_ci result = word; 6028c2ecf20Sopenharmony_ci words--; 6038c2ecf20Sopenharmony_ci for (i = 0; i < words; i++) { 6048c2ecf20Sopenharmony_ci word = ines_read32(port, ts_tx); 6058c2ecf20Sopenharmony_ci result <<= 16; 6068c2ecf20Sopenharmony_ci result |= word; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci return result; 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_cistatic bool ines_txts_onestep(struct ines_port *port, struct sk_buff *skb, int type) 6128c2ecf20Sopenharmony_ci{ 6138c2ecf20Sopenharmony_ci unsigned long flags; 6148c2ecf20Sopenharmony_ci u32 port_conf; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 6178c2ecf20Sopenharmony_ci port_conf = ines_read32(port, port_conf); 6188c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci if (port_conf & CM_ONE_STEP) 6218c2ecf20Sopenharmony_ci return is_sync_pdelay_resp(skb, type); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci return false; 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic void ines_txtstamp(struct mii_timestamper *mii_ts, 6278c2ecf20Sopenharmony_ci struct sk_buff *skb, int type) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci struct ines_port *port = container_of(mii_ts, struct ines_port, mii_ts); 6308c2ecf20Sopenharmony_ci struct sk_buff *old_skb = NULL; 6318c2ecf20Sopenharmony_ci unsigned long flags; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci if (!port->txts_enabled || ines_txts_onestep(port, skb, type)) { 6348c2ecf20Sopenharmony_ci kfree_skb(skb); 6358c2ecf20Sopenharmony_ci return; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci if (port->tx_skb) 6418c2ecf20Sopenharmony_ci old_skb = port->tx_skb; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci port->tx_skb = skb; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci kfree_skb(old_skb); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci schedule_delayed_work(&port->ts_work, 1); 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_cistatic void ines_txtstamp_work(struct work_struct *work) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci struct ines_port *port = 6558c2ecf20Sopenharmony_ci container_of(work, struct ines_port, ts_work.work); 6568c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps ssh; 6578c2ecf20Sopenharmony_ci struct sk_buff *skb; 6588c2ecf20Sopenharmony_ci unsigned long flags; 6598c2ecf20Sopenharmony_ci u64 ns; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 6628c2ecf20Sopenharmony_ci skb = port->tx_skb; 6638c2ecf20Sopenharmony_ci port->tx_skb = NULL; 6648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci ns = ines_find_txts(port, skb); 6678c2ecf20Sopenharmony_ci if (!ns) { 6688c2ecf20Sopenharmony_ci kfree_skb(skb); 6698c2ecf20Sopenharmony_ci return; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci ssh.hwtstamp = ns_to_ktime(ns); 6728c2ecf20Sopenharmony_ci skb_complete_tx_timestamp(skb, &ssh); 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_cistatic bool is_sync_pdelay_resp(struct sk_buff *skb, int type) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci struct ptp_header *hdr; 6788c2ecf20Sopenharmony_ci u8 msgtype; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci hdr = ptp_parse_header(skb, type); 6818c2ecf20Sopenharmony_ci if (!hdr) 6828c2ecf20Sopenharmony_ci return false; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci msgtype = ptp_get_msgtype(hdr, type); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci switch ((msgtype & 0xf)) { 6878c2ecf20Sopenharmony_ci case SYNC: 6888c2ecf20Sopenharmony_ci case PDELAY_RESP: 6898c2ecf20Sopenharmony_ci return true; 6908c2ecf20Sopenharmony_ci default: 6918c2ecf20Sopenharmony_ci return false; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_cistatic u8 tag_to_msgtype(u8 tag) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci switch (tag) { 6988c2ecf20Sopenharmony_ci case MESSAGE_TYPE_SYNC: 6998c2ecf20Sopenharmony_ci return SYNC; 7008c2ecf20Sopenharmony_ci case MESSAGE_TYPE_P_DELAY_REQ: 7018c2ecf20Sopenharmony_ci return PDELAY_REQ; 7028c2ecf20Sopenharmony_ci case MESSAGE_TYPE_P_DELAY_RESP: 7038c2ecf20Sopenharmony_ci return PDELAY_RESP; 7048c2ecf20Sopenharmony_ci case MESSAGE_TYPE_DELAY_REQ: 7058c2ecf20Sopenharmony_ci return DELAY_REQ; 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci return 0xf; 7088c2ecf20Sopenharmony_ci} 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_cistatic struct mii_timestamper *ines_ptp_probe_channel(struct device *device, 7118c2ecf20Sopenharmony_ci unsigned int index) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci struct device_node *node = device->of_node; 7148c2ecf20Sopenharmony_ci struct ines_port *port; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci if (index > INES_N_PORTS - 1) { 7178c2ecf20Sopenharmony_ci dev_err(device, "bad port index %u\n", index); 7188c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci port = ines_find_port(node, index); 7218c2ecf20Sopenharmony_ci if (!port) { 7228c2ecf20Sopenharmony_ci dev_err(device, "missing port index %u\n", index); 7238c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci port->mii_ts.rxtstamp = ines_rxtstamp; 7268c2ecf20Sopenharmony_ci port->mii_ts.txtstamp = ines_txtstamp; 7278c2ecf20Sopenharmony_ci port->mii_ts.hwtstamp = ines_hwtstamp; 7288c2ecf20Sopenharmony_ci port->mii_ts.link_state = ines_link_state; 7298c2ecf20Sopenharmony_ci port->mii_ts.ts_info = ines_ts_info; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci return &port->mii_ts; 7328c2ecf20Sopenharmony_ci} 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_cistatic void ines_ptp_release_channel(struct device *device, 7358c2ecf20Sopenharmony_ci struct mii_timestamper *mii_ts) 7368c2ecf20Sopenharmony_ci{ 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_cistatic struct mii_timestamping_ctrl ines_ctrl = { 7408c2ecf20Sopenharmony_ci .probe_channel = ines_ptp_probe_channel, 7418c2ecf20Sopenharmony_ci .release_channel = ines_ptp_release_channel, 7428c2ecf20Sopenharmony_ci}; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_cistatic int ines_ptp_ctrl_probe(struct platform_device *pld) 7458c2ecf20Sopenharmony_ci{ 7468c2ecf20Sopenharmony_ci struct ines_clock *clock; 7478c2ecf20Sopenharmony_ci void __iomem *addr; 7488c2ecf20Sopenharmony_ci int err = 0; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci addr = devm_platform_ioremap_resource(pld, 0); 7518c2ecf20Sopenharmony_ci if (IS_ERR(addr)) { 7528c2ecf20Sopenharmony_ci err = PTR_ERR(addr); 7538c2ecf20Sopenharmony_ci goto out; 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci clock = kzalloc(sizeof(*clock), GFP_KERNEL); 7568c2ecf20Sopenharmony_ci if (!clock) { 7578c2ecf20Sopenharmony_ci err = -ENOMEM; 7588c2ecf20Sopenharmony_ci goto out; 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci if (ines_clock_init(clock, &pld->dev, addr)) { 7618c2ecf20Sopenharmony_ci kfree(clock); 7628c2ecf20Sopenharmony_ci err = -ENOMEM; 7638c2ecf20Sopenharmony_ci goto out; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci err = register_mii_tstamp_controller(&pld->dev, &ines_ctrl); 7668c2ecf20Sopenharmony_ci if (err) { 7678c2ecf20Sopenharmony_ci kfree(clock); 7688c2ecf20Sopenharmony_ci goto out; 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci mutex_lock(&ines_clocks_lock); 7718c2ecf20Sopenharmony_ci list_add_tail(&ines_clocks, &clock->list); 7728c2ecf20Sopenharmony_ci mutex_unlock(&ines_clocks_lock); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci dev_set_drvdata(&pld->dev, clock); 7758c2ecf20Sopenharmony_ciout: 7768c2ecf20Sopenharmony_ci return err; 7778c2ecf20Sopenharmony_ci} 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_cistatic int ines_ptp_ctrl_remove(struct platform_device *pld) 7808c2ecf20Sopenharmony_ci{ 7818c2ecf20Sopenharmony_ci struct ines_clock *clock = dev_get_drvdata(&pld->dev); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci unregister_mii_tstamp_controller(&pld->dev); 7848c2ecf20Sopenharmony_ci mutex_lock(&ines_clocks_lock); 7858c2ecf20Sopenharmony_ci list_del(&clock->list); 7868c2ecf20Sopenharmony_ci mutex_unlock(&ines_clocks_lock); 7878c2ecf20Sopenharmony_ci ines_clock_cleanup(clock); 7888c2ecf20Sopenharmony_ci kfree(clock); 7898c2ecf20Sopenharmony_ci return 0; 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_cistatic const struct of_device_id ines_ptp_ctrl_of_match[] = { 7938c2ecf20Sopenharmony_ci { .compatible = "ines,ptp-ctrl" }, 7948c2ecf20Sopenharmony_ci { } 7958c2ecf20Sopenharmony_ci}; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ines_ptp_ctrl_of_match); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_cistatic struct platform_driver ines_ptp_ctrl_driver = { 8008c2ecf20Sopenharmony_ci .probe = ines_ptp_ctrl_probe, 8018c2ecf20Sopenharmony_ci .remove = ines_ptp_ctrl_remove, 8028c2ecf20Sopenharmony_ci .driver = { 8038c2ecf20Sopenharmony_ci .name = "ines_ptp_ctrl", 8048c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(ines_ptp_ctrl_of_match), 8058c2ecf20Sopenharmony_ci }, 8068c2ecf20Sopenharmony_ci}; 8078c2ecf20Sopenharmony_cimodule_platform_driver(ines_ptp_ctrl_driver); 808