162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright (C) 2018 MOSER-BAER AG 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#define pr_fmt(fmt) "InES_PTP: " fmt 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/ethtool.h> 962306a36Sopenharmony_ci#include <linux/export.h> 1062306a36Sopenharmony_ci#include <linux/if_vlan.h> 1162306a36Sopenharmony_ci#include <linux/mii_timestamper.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/net_tstamp.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/of_address.h> 1662306a36Sopenharmony_ci#include <linux/of_irq.h> 1762306a36Sopenharmony_ci#include <linux/phy.h> 1862306a36Sopenharmony_ci#include <linux/platform_device.h> 1962306a36Sopenharmony_ci#include <linux/ptp_classify.h> 2062306a36Sopenharmony_ci#include <linux/ptp_clock_kernel.h> 2162306a36Sopenharmony_ci#include <linux/stddef.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for the ZHAW InES PTP time stamping IP core"); 2462306a36Sopenharmony_ciMODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>"); 2562306a36Sopenharmony_ciMODULE_VERSION("1.0"); 2662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* GLOBAL register */ 2962306a36Sopenharmony_ci#define MCAST_MAC_SELECT_SHIFT 2 3062306a36Sopenharmony_ci#define MCAST_MAC_SELECT_MASK 0x3 3162306a36Sopenharmony_ci#define IO_RESET BIT(1) 3262306a36Sopenharmony_ci#define PTP_RESET BIT(0) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* VERSION register */ 3562306a36Sopenharmony_ci#define IF_MAJOR_VER_SHIFT 12 3662306a36Sopenharmony_ci#define IF_MAJOR_VER_MASK 0xf 3762306a36Sopenharmony_ci#define IF_MINOR_VER_SHIFT 8 3862306a36Sopenharmony_ci#define IF_MINOR_VER_MASK 0xf 3962306a36Sopenharmony_ci#define FPGA_MAJOR_VER_SHIFT 4 4062306a36Sopenharmony_ci#define FPGA_MAJOR_VER_MASK 0xf 4162306a36Sopenharmony_ci#define FPGA_MINOR_VER_SHIFT 0 4262306a36Sopenharmony_ci#define FPGA_MINOR_VER_MASK 0xf 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* INT_STAT register */ 4562306a36Sopenharmony_ci#define RX_INTR_STATUS_3 BIT(5) 4662306a36Sopenharmony_ci#define RX_INTR_STATUS_2 BIT(4) 4762306a36Sopenharmony_ci#define RX_INTR_STATUS_1 BIT(3) 4862306a36Sopenharmony_ci#define TX_INTR_STATUS_3 BIT(2) 4962306a36Sopenharmony_ci#define TX_INTR_STATUS_2 BIT(1) 5062306a36Sopenharmony_ci#define TX_INTR_STATUS_1 BIT(0) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* INT_MSK register */ 5362306a36Sopenharmony_ci#define RX_INTR_MASK_3 BIT(5) 5462306a36Sopenharmony_ci#define RX_INTR_MASK_2 BIT(4) 5562306a36Sopenharmony_ci#define RX_INTR_MASK_1 BIT(3) 5662306a36Sopenharmony_ci#define TX_INTR_MASK_3 BIT(2) 5762306a36Sopenharmony_ci#define TX_INTR_MASK_2 BIT(1) 5862306a36Sopenharmony_ci#define TX_INTR_MASK_1 BIT(0) 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* BUF_STAT register */ 6162306a36Sopenharmony_ci#define RX_FIFO_NE_3 BIT(5) 6262306a36Sopenharmony_ci#define RX_FIFO_NE_2 BIT(4) 6362306a36Sopenharmony_ci#define RX_FIFO_NE_1 BIT(3) 6462306a36Sopenharmony_ci#define TX_FIFO_NE_3 BIT(2) 6562306a36Sopenharmony_ci#define TX_FIFO_NE_2 BIT(1) 6662306a36Sopenharmony_ci#define TX_FIFO_NE_1 BIT(0) 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* PORT_CONF register */ 6962306a36Sopenharmony_ci#define CM_ONE_STEP BIT(6) 7062306a36Sopenharmony_ci#define PHY_SPEED_SHIFT 4 7162306a36Sopenharmony_ci#define PHY_SPEED_MASK 0x3 7262306a36Sopenharmony_ci#define P2P_DELAY_WR_POS_SHIFT 2 7362306a36Sopenharmony_ci#define P2P_DELAY_WR_POS_MASK 0x3 7462306a36Sopenharmony_ci#define PTP_MODE_SHIFT 0 7562306a36Sopenharmony_ci#define PTP_MODE_MASK 0x3 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* TS_STAT_TX register */ 7862306a36Sopenharmony_ci#define TS_ENABLE BIT(15) 7962306a36Sopenharmony_ci#define DATA_READ_POS_SHIFT 8 8062306a36Sopenharmony_ci#define DATA_READ_POS_MASK 0x1f 8162306a36Sopenharmony_ci#define DISCARDED_EVENTS_SHIFT 4 8262306a36Sopenharmony_ci#define DISCARDED_EVENTS_MASK 0xf 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define INES_N_PORTS 3 8562306a36Sopenharmony_ci#define INES_REGISTER_SIZE 0x80 8662306a36Sopenharmony_ci#define INES_PORT_OFFSET 0x20 8762306a36Sopenharmony_ci#define INES_PORT_SIZE 0x20 8862306a36Sopenharmony_ci#define INES_FIFO_DEPTH 90 8962306a36Sopenharmony_ci#define INES_MAX_EVENTS 100 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define BC_PTP_V1 0 9262306a36Sopenharmony_ci#define BC_PTP_V2 1 9362306a36Sopenharmony_ci#define TC_E2E_PTP_V2 2 9462306a36Sopenharmony_ci#define TC_P2P_PTP_V2 3 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define PHY_SPEED_10 0 9762306a36Sopenharmony_ci#define PHY_SPEED_100 1 9862306a36Sopenharmony_ci#define PHY_SPEED_1000 2 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define PORT_CONF \ 10162306a36Sopenharmony_ci ((PHY_SPEED_1000 << PHY_SPEED_SHIFT) | (BC_PTP_V2 << PTP_MODE_SHIFT)) 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define ines_read32(s, r) __raw_readl((void __iomem *)&s->regs->r) 10462306a36Sopenharmony_ci#define ines_write32(s, v, r) __raw_writel(v, (void __iomem *)&s->regs->r) 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci#define MESSAGE_TYPE_SYNC 1 10762306a36Sopenharmony_ci#define MESSAGE_TYPE_P_DELAY_REQ 2 10862306a36Sopenharmony_ci#define MESSAGE_TYPE_P_DELAY_RESP 3 10962306a36Sopenharmony_ci#define MESSAGE_TYPE_DELAY_REQ 4 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic LIST_HEAD(ines_clocks); 11262306a36Sopenharmony_cistatic DEFINE_MUTEX(ines_clocks_lock); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistruct ines_global_regs { 11562306a36Sopenharmony_ci u32 id; 11662306a36Sopenharmony_ci u32 test; 11762306a36Sopenharmony_ci u32 global; 11862306a36Sopenharmony_ci u32 version; 11962306a36Sopenharmony_ci u32 test2; 12062306a36Sopenharmony_ci u32 int_stat; 12162306a36Sopenharmony_ci u32 int_msk; 12262306a36Sopenharmony_ci u32 buf_stat; 12362306a36Sopenharmony_ci}; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistruct ines_port_registers { 12662306a36Sopenharmony_ci u32 port_conf; 12762306a36Sopenharmony_ci u32 p_delay; 12862306a36Sopenharmony_ci u32 ts_stat_tx; 12962306a36Sopenharmony_ci u32 ts_stat_rx; 13062306a36Sopenharmony_ci u32 ts_tx; 13162306a36Sopenharmony_ci u32 ts_rx; 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistruct ines_timestamp { 13562306a36Sopenharmony_ci struct list_head list; 13662306a36Sopenharmony_ci unsigned long tmo; 13762306a36Sopenharmony_ci u16 tag; 13862306a36Sopenharmony_ci u64 sec; 13962306a36Sopenharmony_ci u64 nsec; 14062306a36Sopenharmony_ci u64 clkid; 14162306a36Sopenharmony_ci u16 portnum; 14262306a36Sopenharmony_ci u16 seqid; 14362306a36Sopenharmony_ci}; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistruct ines_port { 14662306a36Sopenharmony_ci struct ines_port_registers *regs; 14762306a36Sopenharmony_ci struct mii_timestamper mii_ts; 14862306a36Sopenharmony_ci struct ines_clock *clock; 14962306a36Sopenharmony_ci bool rxts_enabled; 15062306a36Sopenharmony_ci bool txts_enabled; 15162306a36Sopenharmony_ci unsigned int index; 15262306a36Sopenharmony_ci struct delayed_work ts_work; 15362306a36Sopenharmony_ci /* lock protects event list and tx_skb */ 15462306a36Sopenharmony_ci spinlock_t lock; 15562306a36Sopenharmony_ci struct sk_buff *tx_skb; 15662306a36Sopenharmony_ci struct list_head events; 15762306a36Sopenharmony_ci struct list_head pool; 15862306a36Sopenharmony_ci struct ines_timestamp pool_data[INES_MAX_EVENTS]; 15962306a36Sopenharmony_ci}; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistruct ines_clock { 16262306a36Sopenharmony_ci struct ines_port port[INES_N_PORTS]; 16362306a36Sopenharmony_ci struct ines_global_regs __iomem *regs; 16462306a36Sopenharmony_ci void __iomem *base; 16562306a36Sopenharmony_ci struct device_node *node; 16662306a36Sopenharmony_ci struct device *dev; 16762306a36Sopenharmony_ci struct list_head list; 16862306a36Sopenharmony_ci}; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic bool ines_match(struct sk_buff *skb, unsigned int ptp_class, 17162306a36Sopenharmony_ci struct ines_timestamp *ts, struct device *dev); 17262306a36Sopenharmony_cistatic int ines_rxfifo_read(struct ines_port *port); 17362306a36Sopenharmony_cistatic u64 ines_rxts64(struct ines_port *port, unsigned int words); 17462306a36Sopenharmony_cistatic bool ines_timestamp_expired(struct ines_timestamp *ts); 17562306a36Sopenharmony_cistatic u64 ines_txts64(struct ines_port *port, unsigned int words); 17662306a36Sopenharmony_cistatic void ines_txtstamp_work(struct work_struct *work); 17762306a36Sopenharmony_cistatic bool is_sync_pdelay_resp(struct sk_buff *skb, int type); 17862306a36Sopenharmony_cistatic u8 tag_to_msgtype(u8 tag); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void ines_clock_cleanup(struct ines_clock *clock) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct ines_port *port; 18362306a36Sopenharmony_ci int i; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci for (i = 0; i < INES_N_PORTS; i++) { 18662306a36Sopenharmony_ci port = &clock->port[i]; 18762306a36Sopenharmony_ci cancel_delayed_work_sync(&port->ts_work); 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int ines_clock_init(struct ines_clock *clock, struct device *device, 19262306a36Sopenharmony_ci void __iomem *addr) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct device_node *node = device->of_node; 19562306a36Sopenharmony_ci unsigned long port_addr; 19662306a36Sopenharmony_ci struct ines_port *port; 19762306a36Sopenharmony_ci int i, j; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci INIT_LIST_HEAD(&clock->list); 20062306a36Sopenharmony_ci clock->node = node; 20162306a36Sopenharmony_ci clock->dev = device; 20262306a36Sopenharmony_ci clock->base = addr; 20362306a36Sopenharmony_ci clock->regs = clock->base; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci for (i = 0; i < INES_N_PORTS; i++) { 20662306a36Sopenharmony_ci port = &clock->port[i]; 20762306a36Sopenharmony_ci port_addr = (unsigned long) clock->base + 20862306a36Sopenharmony_ci INES_PORT_OFFSET + i * INES_PORT_SIZE; 20962306a36Sopenharmony_ci port->regs = (struct ines_port_registers *) port_addr; 21062306a36Sopenharmony_ci port->clock = clock; 21162306a36Sopenharmony_ci port->index = i; 21262306a36Sopenharmony_ci INIT_DELAYED_WORK(&port->ts_work, ines_txtstamp_work); 21362306a36Sopenharmony_ci spin_lock_init(&port->lock); 21462306a36Sopenharmony_ci INIT_LIST_HEAD(&port->events); 21562306a36Sopenharmony_ci INIT_LIST_HEAD(&port->pool); 21662306a36Sopenharmony_ci for (j = 0; j < INES_MAX_EVENTS; j++) 21762306a36Sopenharmony_ci list_add(&port->pool_data[j].list, &port->pool); 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci ines_write32(clock, 0xBEEF, test); 22162306a36Sopenharmony_ci ines_write32(clock, 0xBEEF, test2); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci dev_dbg(device, "ID 0x%x\n", ines_read32(clock, id)); 22462306a36Sopenharmony_ci dev_dbg(device, "TEST 0x%x\n", ines_read32(clock, test)); 22562306a36Sopenharmony_ci dev_dbg(device, "VERSION 0x%x\n", ines_read32(clock, version)); 22662306a36Sopenharmony_ci dev_dbg(device, "TEST2 0x%x\n", ines_read32(clock, test2)); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci for (i = 0; i < INES_N_PORTS; i++) { 22962306a36Sopenharmony_ci port = &clock->port[i]; 23062306a36Sopenharmony_ci ines_write32(port, PORT_CONF, port_conf); 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return 0; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic struct ines_port *ines_find_port(struct device_node *node, u32 index) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct ines_port *port = NULL; 23962306a36Sopenharmony_ci struct ines_clock *clock; 24062306a36Sopenharmony_ci struct list_head *this; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci mutex_lock(&ines_clocks_lock); 24362306a36Sopenharmony_ci list_for_each(this, &ines_clocks) { 24462306a36Sopenharmony_ci clock = list_entry(this, struct ines_clock, list); 24562306a36Sopenharmony_ci if (clock->node == node) { 24662306a36Sopenharmony_ci port = &clock->port[index]; 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci mutex_unlock(&ines_clocks_lock); 25162306a36Sopenharmony_ci return port; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic u64 ines_find_rxts(struct ines_port *port, struct sk_buff *skb, int type) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct list_head *this, *next; 25762306a36Sopenharmony_ci struct ines_timestamp *ts; 25862306a36Sopenharmony_ci unsigned long flags; 25962306a36Sopenharmony_ci u64 ns = 0; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (type == PTP_CLASS_NONE) 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 26562306a36Sopenharmony_ci ines_rxfifo_read(port); 26662306a36Sopenharmony_ci list_for_each_safe(this, next, &port->events) { 26762306a36Sopenharmony_ci ts = list_entry(this, struct ines_timestamp, list); 26862306a36Sopenharmony_ci if (ines_timestamp_expired(ts)) { 26962306a36Sopenharmony_ci list_del_init(&ts->list); 27062306a36Sopenharmony_ci list_add(&ts->list, &port->pool); 27162306a36Sopenharmony_ci continue; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci if (ines_match(skb, type, ts, port->clock->dev)) { 27462306a36Sopenharmony_ci ns = ts->sec * 1000000000ULL + ts->nsec; 27562306a36Sopenharmony_ci list_del_init(&ts->list); 27662306a36Sopenharmony_ci list_add(&ts->list, &port->pool); 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return ns; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic u64 ines_find_txts(struct ines_port *port, struct sk_buff *skb) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci unsigned int class = ptp_classify_raw(skb), i; 28862306a36Sopenharmony_ci u32 data_rd_pos, buf_stat, mask, ts_stat_tx; 28962306a36Sopenharmony_ci struct ines_timestamp ts; 29062306a36Sopenharmony_ci unsigned long flags; 29162306a36Sopenharmony_ci u64 ns = 0; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci mask = TX_FIFO_NE_1 << port->index; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci for (i = 0; i < INES_FIFO_DEPTH; i++) { 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci buf_stat = ines_read32(port->clock, buf_stat); 30062306a36Sopenharmony_ci if (!(buf_stat & mask)) { 30162306a36Sopenharmony_ci dev_dbg(port->clock->dev, 30262306a36Sopenharmony_ci "Tx timestamp FIFO unexpectedly empty\n"); 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci ts_stat_tx = ines_read32(port, ts_stat_tx); 30662306a36Sopenharmony_ci data_rd_pos = (ts_stat_tx >> DATA_READ_POS_SHIFT) & 30762306a36Sopenharmony_ci DATA_READ_POS_MASK; 30862306a36Sopenharmony_ci if (data_rd_pos) { 30962306a36Sopenharmony_ci dev_err(port->clock->dev, 31062306a36Sopenharmony_ci "unexpected Tx read pos %u\n", data_rd_pos); 31162306a36Sopenharmony_ci break; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci ts.tag = ines_read32(port, ts_tx); 31562306a36Sopenharmony_ci ts.sec = ines_txts64(port, 3); 31662306a36Sopenharmony_ci ts.nsec = ines_txts64(port, 2); 31762306a36Sopenharmony_ci ts.clkid = ines_txts64(port, 4); 31862306a36Sopenharmony_ci ts.portnum = ines_read32(port, ts_tx); 31962306a36Sopenharmony_ci ts.seqid = ines_read32(port, ts_tx); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (ines_match(skb, class, &ts, port->clock->dev)) { 32262306a36Sopenharmony_ci ns = ts.sec * 1000000000ULL + ts.nsec; 32362306a36Sopenharmony_ci break; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 32862306a36Sopenharmony_ci return ns; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic int ines_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct ines_port *port = container_of(mii_ts, struct ines_port, mii_ts); 33462306a36Sopenharmony_ci u32 cm_one_step = 0, port_conf, ts_stat_rx, ts_stat_tx; 33562306a36Sopenharmony_ci struct hwtstamp_config cfg; 33662306a36Sopenharmony_ci unsigned long flags; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) 33962306a36Sopenharmony_ci return -EFAULT; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci switch (cfg.tx_type) { 34262306a36Sopenharmony_ci case HWTSTAMP_TX_OFF: 34362306a36Sopenharmony_ci ts_stat_tx = 0; 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci case HWTSTAMP_TX_ON: 34662306a36Sopenharmony_ci ts_stat_tx = TS_ENABLE; 34762306a36Sopenharmony_ci break; 34862306a36Sopenharmony_ci case HWTSTAMP_TX_ONESTEP_P2P: 34962306a36Sopenharmony_ci ts_stat_tx = TS_ENABLE; 35062306a36Sopenharmony_ci cm_one_step = CM_ONE_STEP; 35162306a36Sopenharmony_ci break; 35262306a36Sopenharmony_ci default: 35362306a36Sopenharmony_ci return -ERANGE; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci switch (cfg.rx_filter) { 35762306a36Sopenharmony_ci case HWTSTAMP_FILTER_NONE: 35862306a36Sopenharmony_ci ts_stat_rx = 0; 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci case HWTSTAMP_FILTER_ALL: 36162306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: 36262306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: 36362306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: 36462306a36Sopenharmony_ci return -ERANGE; 36562306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: 36662306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: 36762306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: 36862306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 36962306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: 37062306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: 37162306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_EVENT: 37262306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_SYNC: 37362306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: 37462306a36Sopenharmony_ci ts_stat_rx = TS_ENABLE; 37562306a36Sopenharmony_ci cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; 37662306a36Sopenharmony_ci break; 37762306a36Sopenharmony_ci default: 37862306a36Sopenharmony_ci return -ERANGE; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci port_conf = ines_read32(port, port_conf); 38462306a36Sopenharmony_ci port_conf &= ~CM_ONE_STEP; 38562306a36Sopenharmony_ci port_conf |= cm_one_step; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci ines_write32(port, port_conf, port_conf); 38862306a36Sopenharmony_ci ines_write32(port, ts_stat_rx, ts_stat_rx); 38962306a36Sopenharmony_ci ines_write32(port, ts_stat_tx, ts_stat_tx); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci port->rxts_enabled = ts_stat_rx == TS_ENABLE; 39262306a36Sopenharmony_ci port->txts_enabled = ts_stat_tx == TS_ENABLE; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic void ines_link_state(struct mii_timestamper *mii_ts, 40062306a36Sopenharmony_ci struct phy_device *phydev) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci struct ines_port *port = container_of(mii_ts, struct ines_port, mii_ts); 40362306a36Sopenharmony_ci u32 port_conf, speed_conf; 40462306a36Sopenharmony_ci unsigned long flags; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci switch (phydev->speed) { 40762306a36Sopenharmony_ci case SPEED_10: 40862306a36Sopenharmony_ci speed_conf = PHY_SPEED_10 << PHY_SPEED_SHIFT; 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci case SPEED_100: 41162306a36Sopenharmony_ci speed_conf = PHY_SPEED_100 << PHY_SPEED_SHIFT; 41262306a36Sopenharmony_ci break; 41362306a36Sopenharmony_ci case SPEED_1000: 41462306a36Sopenharmony_ci speed_conf = PHY_SPEED_1000 << PHY_SPEED_SHIFT; 41562306a36Sopenharmony_ci break; 41662306a36Sopenharmony_ci default: 41762306a36Sopenharmony_ci dev_err(port->clock->dev, "bad speed: %d\n", phydev->speed); 41862306a36Sopenharmony_ci return; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci port_conf = ines_read32(port, port_conf); 42362306a36Sopenharmony_ci port_conf &= ~(0x3 << PHY_SPEED_SHIFT); 42462306a36Sopenharmony_ci port_conf |= speed_conf; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci ines_write32(port, port_conf, port_conf); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic bool ines_match(struct sk_buff *skb, unsigned int ptp_class, 43262306a36Sopenharmony_ci struct ines_timestamp *ts, struct device *dev) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct ptp_header *hdr; 43562306a36Sopenharmony_ci u16 portn, seqid; 43662306a36Sopenharmony_ci u8 msgtype; 43762306a36Sopenharmony_ci u64 clkid; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (unlikely(ptp_class & PTP_CLASS_V1)) 44062306a36Sopenharmony_ci return false; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci hdr = ptp_parse_header(skb, ptp_class); 44362306a36Sopenharmony_ci if (!hdr) 44462306a36Sopenharmony_ci return false; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci msgtype = ptp_get_msgtype(hdr, ptp_class); 44762306a36Sopenharmony_ci clkid = be64_to_cpup((__be64 *)&hdr->source_port_identity.clock_identity.id[0]); 44862306a36Sopenharmony_ci portn = be16_to_cpu(hdr->source_port_identity.port_number); 44962306a36Sopenharmony_ci seqid = be16_to_cpu(hdr->sequence_id); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (tag_to_msgtype(ts->tag & 0x7) != msgtype) { 45262306a36Sopenharmony_ci dev_dbg(dev, "msgtype mismatch ts %hhu != skb %hhu\n", 45362306a36Sopenharmony_ci tag_to_msgtype(ts->tag & 0x7), msgtype); 45462306a36Sopenharmony_ci return false; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci if (ts->clkid != clkid) { 45762306a36Sopenharmony_ci dev_dbg(dev, "clkid mismatch ts %llx != skb %llx\n", 45862306a36Sopenharmony_ci ts->clkid, clkid); 45962306a36Sopenharmony_ci return false; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci if (ts->portnum != portn) { 46262306a36Sopenharmony_ci dev_dbg(dev, "portn mismatch ts %hu != skb %hu\n", 46362306a36Sopenharmony_ci ts->portnum, portn); 46462306a36Sopenharmony_ci return false; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci if (ts->seqid != seqid) { 46762306a36Sopenharmony_ci dev_dbg(dev, "seqid mismatch ts %hu != skb %hu\n", 46862306a36Sopenharmony_ci ts->seqid, seqid); 46962306a36Sopenharmony_ci return false; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci return true; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic bool ines_rxtstamp(struct mii_timestamper *mii_ts, 47662306a36Sopenharmony_ci struct sk_buff *skb, int type) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci struct ines_port *port = container_of(mii_ts, struct ines_port, mii_ts); 47962306a36Sopenharmony_ci struct skb_shared_hwtstamps *ssh; 48062306a36Sopenharmony_ci u64 ns; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (!port->rxts_enabled) 48362306a36Sopenharmony_ci return false; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci ns = ines_find_rxts(port, skb, type); 48662306a36Sopenharmony_ci if (!ns) 48762306a36Sopenharmony_ci return false; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci ssh = skb_hwtstamps(skb); 49062306a36Sopenharmony_ci ssh->hwtstamp = ns_to_ktime(ns); 49162306a36Sopenharmony_ci netif_rx(skb); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci return true; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic int ines_rxfifo_read(struct ines_port *port) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci u32 data_rd_pos, buf_stat, mask, ts_stat_rx; 49962306a36Sopenharmony_ci struct ines_timestamp *ts; 50062306a36Sopenharmony_ci unsigned int i; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci mask = RX_FIFO_NE_1 << port->index; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci for (i = 0; i < INES_FIFO_DEPTH; i++) { 50562306a36Sopenharmony_ci if (list_empty(&port->pool)) { 50662306a36Sopenharmony_ci dev_err(port->clock->dev, "event pool is empty\n"); 50762306a36Sopenharmony_ci return -1; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci buf_stat = ines_read32(port->clock, buf_stat); 51062306a36Sopenharmony_ci if (!(buf_stat & mask)) 51162306a36Sopenharmony_ci break; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci ts_stat_rx = ines_read32(port, ts_stat_rx); 51462306a36Sopenharmony_ci data_rd_pos = (ts_stat_rx >> DATA_READ_POS_SHIFT) & 51562306a36Sopenharmony_ci DATA_READ_POS_MASK; 51662306a36Sopenharmony_ci if (data_rd_pos) { 51762306a36Sopenharmony_ci dev_err(port->clock->dev, "unexpected Rx read pos %u\n", 51862306a36Sopenharmony_ci data_rd_pos); 51962306a36Sopenharmony_ci break; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci ts = list_first_entry(&port->pool, struct ines_timestamp, list); 52362306a36Sopenharmony_ci ts->tmo = jiffies + HZ; 52462306a36Sopenharmony_ci ts->tag = ines_read32(port, ts_rx); 52562306a36Sopenharmony_ci ts->sec = ines_rxts64(port, 3); 52662306a36Sopenharmony_ci ts->nsec = ines_rxts64(port, 2); 52762306a36Sopenharmony_ci ts->clkid = ines_rxts64(port, 4); 52862306a36Sopenharmony_ci ts->portnum = ines_read32(port, ts_rx); 52962306a36Sopenharmony_ci ts->seqid = ines_read32(port, ts_rx); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci list_del_init(&ts->list); 53262306a36Sopenharmony_ci list_add_tail(&ts->list, &port->events); 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci return 0; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic u64 ines_rxts64(struct ines_port *port, unsigned int words) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci unsigned int i; 54162306a36Sopenharmony_ci u64 result; 54262306a36Sopenharmony_ci u16 word; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci word = ines_read32(port, ts_rx); 54562306a36Sopenharmony_ci result = word; 54662306a36Sopenharmony_ci words--; 54762306a36Sopenharmony_ci for (i = 0; i < words; i++) { 54862306a36Sopenharmony_ci word = ines_read32(port, ts_rx); 54962306a36Sopenharmony_ci result <<= 16; 55062306a36Sopenharmony_ci result |= word; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci return result; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic bool ines_timestamp_expired(struct ines_timestamp *ts) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci return time_after(jiffies, ts->tmo); 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic int ines_ts_info(struct mii_timestamper *mii_ts, 56162306a36Sopenharmony_ci struct ethtool_ts_info *info) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci info->so_timestamping = 56462306a36Sopenharmony_ci SOF_TIMESTAMPING_TX_HARDWARE | 56562306a36Sopenharmony_ci SOF_TIMESTAMPING_TX_SOFTWARE | 56662306a36Sopenharmony_ci SOF_TIMESTAMPING_RX_HARDWARE | 56762306a36Sopenharmony_ci SOF_TIMESTAMPING_RX_SOFTWARE | 56862306a36Sopenharmony_ci SOF_TIMESTAMPING_SOFTWARE | 56962306a36Sopenharmony_ci SOF_TIMESTAMPING_RAW_HARDWARE; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci info->phc_index = -1; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci info->tx_types = 57462306a36Sopenharmony_ci (1 << HWTSTAMP_TX_OFF) | 57562306a36Sopenharmony_ci (1 << HWTSTAMP_TX_ON) | 57662306a36Sopenharmony_ci (1 << HWTSTAMP_TX_ONESTEP_P2P); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci info->rx_filters = 57962306a36Sopenharmony_ci (1 << HWTSTAMP_FILTER_NONE) | 58062306a36Sopenharmony_ci (1 << HWTSTAMP_FILTER_PTP_V2_EVENT); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci return 0; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic u64 ines_txts64(struct ines_port *port, unsigned int words) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci unsigned int i; 58862306a36Sopenharmony_ci u64 result; 58962306a36Sopenharmony_ci u16 word; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci word = ines_read32(port, ts_tx); 59262306a36Sopenharmony_ci result = word; 59362306a36Sopenharmony_ci words--; 59462306a36Sopenharmony_ci for (i = 0; i < words; i++) { 59562306a36Sopenharmony_ci word = ines_read32(port, ts_tx); 59662306a36Sopenharmony_ci result <<= 16; 59762306a36Sopenharmony_ci result |= word; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci return result; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic bool ines_txts_onestep(struct ines_port *port, struct sk_buff *skb, int type) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci unsigned long flags; 60562306a36Sopenharmony_ci u32 port_conf; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 60862306a36Sopenharmony_ci port_conf = ines_read32(port, port_conf); 60962306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (port_conf & CM_ONE_STEP) 61262306a36Sopenharmony_ci return is_sync_pdelay_resp(skb, type); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci return false; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic void ines_txtstamp(struct mii_timestamper *mii_ts, 61862306a36Sopenharmony_ci struct sk_buff *skb, int type) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci struct ines_port *port = container_of(mii_ts, struct ines_port, mii_ts); 62162306a36Sopenharmony_ci struct sk_buff *old_skb = NULL; 62262306a36Sopenharmony_ci unsigned long flags; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (!port->txts_enabled || ines_txts_onestep(port, skb, type)) { 62562306a36Sopenharmony_ci kfree_skb(skb); 62662306a36Sopenharmony_ci return; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci if (port->tx_skb) 63262306a36Sopenharmony_ci old_skb = port->tx_skb; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci port->tx_skb = skb; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci kfree_skb(old_skb); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci schedule_delayed_work(&port->ts_work, 1); 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic void ines_txtstamp_work(struct work_struct *work) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci struct ines_port *port = 64662306a36Sopenharmony_ci container_of(work, struct ines_port, ts_work.work); 64762306a36Sopenharmony_ci struct skb_shared_hwtstamps ssh; 64862306a36Sopenharmony_ci struct sk_buff *skb; 64962306a36Sopenharmony_ci unsigned long flags; 65062306a36Sopenharmony_ci u64 ns; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 65362306a36Sopenharmony_ci skb = port->tx_skb; 65462306a36Sopenharmony_ci port->tx_skb = NULL; 65562306a36Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci ns = ines_find_txts(port, skb); 65862306a36Sopenharmony_ci if (!ns) { 65962306a36Sopenharmony_ci kfree_skb(skb); 66062306a36Sopenharmony_ci return; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci ssh.hwtstamp = ns_to_ktime(ns); 66362306a36Sopenharmony_ci skb_complete_tx_timestamp(skb, &ssh); 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic bool is_sync_pdelay_resp(struct sk_buff *skb, int type) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci struct ptp_header *hdr; 66962306a36Sopenharmony_ci u8 msgtype; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci hdr = ptp_parse_header(skb, type); 67262306a36Sopenharmony_ci if (!hdr) 67362306a36Sopenharmony_ci return false; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci msgtype = ptp_get_msgtype(hdr, type); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci switch (msgtype) { 67862306a36Sopenharmony_ci case PTP_MSGTYPE_SYNC: 67962306a36Sopenharmony_ci case PTP_MSGTYPE_PDELAY_RESP: 68062306a36Sopenharmony_ci return true; 68162306a36Sopenharmony_ci default: 68262306a36Sopenharmony_ci return false; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cistatic u8 tag_to_msgtype(u8 tag) 68762306a36Sopenharmony_ci{ 68862306a36Sopenharmony_ci switch (tag) { 68962306a36Sopenharmony_ci case MESSAGE_TYPE_SYNC: 69062306a36Sopenharmony_ci return PTP_MSGTYPE_SYNC; 69162306a36Sopenharmony_ci case MESSAGE_TYPE_P_DELAY_REQ: 69262306a36Sopenharmony_ci return PTP_MSGTYPE_PDELAY_REQ; 69362306a36Sopenharmony_ci case MESSAGE_TYPE_P_DELAY_RESP: 69462306a36Sopenharmony_ci return PTP_MSGTYPE_PDELAY_RESP; 69562306a36Sopenharmony_ci case MESSAGE_TYPE_DELAY_REQ: 69662306a36Sopenharmony_ci return PTP_MSGTYPE_DELAY_REQ; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci return 0xf; 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic struct mii_timestamper *ines_ptp_probe_channel(struct device *device, 70262306a36Sopenharmony_ci unsigned int index) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci struct device_node *node = device->of_node; 70562306a36Sopenharmony_ci struct ines_port *port; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if (index > INES_N_PORTS - 1) { 70862306a36Sopenharmony_ci dev_err(device, "bad port index %u\n", index); 70962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci port = ines_find_port(node, index); 71262306a36Sopenharmony_ci if (!port) { 71362306a36Sopenharmony_ci dev_err(device, "missing port index %u\n", index); 71462306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci port->mii_ts.rxtstamp = ines_rxtstamp; 71762306a36Sopenharmony_ci port->mii_ts.txtstamp = ines_txtstamp; 71862306a36Sopenharmony_ci port->mii_ts.hwtstamp = ines_hwtstamp; 71962306a36Sopenharmony_ci port->mii_ts.link_state = ines_link_state; 72062306a36Sopenharmony_ci port->mii_ts.ts_info = ines_ts_info; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci return &port->mii_ts; 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_cistatic void ines_ptp_release_channel(struct device *device, 72662306a36Sopenharmony_ci struct mii_timestamper *mii_ts) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_cistatic struct mii_timestamping_ctrl ines_ctrl = { 73162306a36Sopenharmony_ci .probe_channel = ines_ptp_probe_channel, 73262306a36Sopenharmony_ci .release_channel = ines_ptp_release_channel, 73362306a36Sopenharmony_ci}; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistatic int ines_ptp_ctrl_probe(struct platform_device *pld) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci struct ines_clock *clock; 73862306a36Sopenharmony_ci void __iomem *addr; 73962306a36Sopenharmony_ci int err = 0; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci addr = devm_platform_ioremap_resource(pld, 0); 74262306a36Sopenharmony_ci if (IS_ERR(addr)) { 74362306a36Sopenharmony_ci err = PTR_ERR(addr); 74462306a36Sopenharmony_ci goto out; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci clock = kzalloc(sizeof(*clock), GFP_KERNEL); 74762306a36Sopenharmony_ci if (!clock) { 74862306a36Sopenharmony_ci err = -ENOMEM; 74962306a36Sopenharmony_ci goto out; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci if (ines_clock_init(clock, &pld->dev, addr)) { 75262306a36Sopenharmony_ci kfree(clock); 75362306a36Sopenharmony_ci err = -ENOMEM; 75462306a36Sopenharmony_ci goto out; 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci err = register_mii_tstamp_controller(&pld->dev, &ines_ctrl); 75762306a36Sopenharmony_ci if (err) { 75862306a36Sopenharmony_ci kfree(clock); 75962306a36Sopenharmony_ci goto out; 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci mutex_lock(&ines_clocks_lock); 76262306a36Sopenharmony_ci list_add_tail(&ines_clocks, &clock->list); 76362306a36Sopenharmony_ci mutex_unlock(&ines_clocks_lock); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci dev_set_drvdata(&pld->dev, clock); 76662306a36Sopenharmony_ciout: 76762306a36Sopenharmony_ci return err; 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cistatic int ines_ptp_ctrl_remove(struct platform_device *pld) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci struct ines_clock *clock = dev_get_drvdata(&pld->dev); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci unregister_mii_tstamp_controller(&pld->dev); 77562306a36Sopenharmony_ci mutex_lock(&ines_clocks_lock); 77662306a36Sopenharmony_ci list_del(&clock->list); 77762306a36Sopenharmony_ci mutex_unlock(&ines_clocks_lock); 77862306a36Sopenharmony_ci ines_clock_cleanup(clock); 77962306a36Sopenharmony_ci kfree(clock); 78062306a36Sopenharmony_ci return 0; 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic const struct of_device_id ines_ptp_ctrl_of_match[] = { 78462306a36Sopenharmony_ci { .compatible = "ines,ptp-ctrl" }, 78562306a36Sopenharmony_ci { } 78662306a36Sopenharmony_ci}; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ines_ptp_ctrl_of_match); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_cistatic struct platform_driver ines_ptp_ctrl_driver = { 79162306a36Sopenharmony_ci .probe = ines_ptp_ctrl_probe, 79262306a36Sopenharmony_ci .remove = ines_ptp_ctrl_remove, 79362306a36Sopenharmony_ci .driver = { 79462306a36Sopenharmony_ci .name = "ines_ptp_ctrl", 79562306a36Sopenharmony_ci .of_match_table = ines_ptp_ctrl_of_match, 79662306a36Sopenharmony_ci }, 79762306a36Sopenharmony_ci}; 79862306a36Sopenharmony_cimodule_platform_driver(ines_ptp_ctrl_driver); 799