123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798 |
- // SPDX-License-Identifier: GPL-2.0
- //
- // Copyright (C) 2018 MOSER-BAER AG
- //
- #define pr_fmt(fmt) "InES_PTP: " fmt
- #include <linux/ethtool.h>
- #include <linux/export.h>
- #include <linux/if_vlan.h>
- #include <linux/mii_timestamper.h>
- #include <linux/module.h>
- #include <linux/net_tstamp.h>
- #include <linux/of.h>
- #include <linux/of_address.h>
- #include <linux/of_irq.h>
- #include <linux/phy.h>
- #include <linux/platform_device.h>
- #include <linux/ptp_classify.h>
- #include <linux/ptp_clock_kernel.h>
- #include <linux/stddef.h>
- MODULE_DESCRIPTION("Driver for the ZHAW InES PTP time stamping IP core");
- MODULE_AUTHOR("Richard Cochran <[email protected]>");
- MODULE_VERSION("1.0");
- MODULE_LICENSE("GPL");
- /* GLOBAL register */
- #define MCAST_MAC_SELECT_SHIFT 2
- #define MCAST_MAC_SELECT_MASK 0x3
- #define IO_RESET BIT(1)
- #define PTP_RESET BIT(0)
- /* VERSION register */
- #define IF_MAJOR_VER_SHIFT 12
- #define IF_MAJOR_VER_MASK 0xf
- #define IF_MINOR_VER_SHIFT 8
- #define IF_MINOR_VER_MASK 0xf
- #define FPGA_MAJOR_VER_SHIFT 4
- #define FPGA_MAJOR_VER_MASK 0xf
- #define FPGA_MINOR_VER_SHIFT 0
- #define FPGA_MINOR_VER_MASK 0xf
- /* INT_STAT register */
- #define RX_INTR_STATUS_3 BIT(5)
- #define RX_INTR_STATUS_2 BIT(4)
- #define RX_INTR_STATUS_1 BIT(3)
- #define TX_INTR_STATUS_3 BIT(2)
- #define TX_INTR_STATUS_2 BIT(1)
- #define TX_INTR_STATUS_1 BIT(0)
- /* INT_MSK register */
- #define RX_INTR_MASK_3 BIT(5)
- #define RX_INTR_MASK_2 BIT(4)
- #define RX_INTR_MASK_1 BIT(3)
- #define TX_INTR_MASK_3 BIT(2)
- #define TX_INTR_MASK_2 BIT(1)
- #define TX_INTR_MASK_1 BIT(0)
- /* BUF_STAT register */
- #define RX_FIFO_NE_3 BIT(5)
- #define RX_FIFO_NE_2 BIT(4)
- #define RX_FIFO_NE_1 BIT(3)
- #define TX_FIFO_NE_3 BIT(2)
- #define TX_FIFO_NE_2 BIT(1)
- #define TX_FIFO_NE_1 BIT(0)
- /* PORT_CONF register */
- #define CM_ONE_STEP BIT(6)
- #define PHY_SPEED_SHIFT 4
- #define PHY_SPEED_MASK 0x3
- #define P2P_DELAY_WR_POS_SHIFT 2
- #define P2P_DELAY_WR_POS_MASK 0x3
- #define PTP_MODE_SHIFT 0
- #define PTP_MODE_MASK 0x3
- /* TS_STAT_TX register */
- #define TS_ENABLE BIT(15)
- #define DATA_READ_POS_SHIFT 8
- #define DATA_READ_POS_MASK 0x1f
- #define DISCARDED_EVENTS_SHIFT 4
- #define DISCARDED_EVENTS_MASK 0xf
- #define INES_N_PORTS 3
- #define INES_REGISTER_SIZE 0x80
- #define INES_PORT_OFFSET 0x20
- #define INES_PORT_SIZE 0x20
- #define INES_FIFO_DEPTH 90
- #define INES_MAX_EVENTS 100
- #define BC_PTP_V1 0
- #define BC_PTP_V2 1
- #define TC_E2E_PTP_V2 2
- #define TC_P2P_PTP_V2 3
- #define PHY_SPEED_10 0
- #define PHY_SPEED_100 1
- #define PHY_SPEED_1000 2
- #define PORT_CONF \
- ((PHY_SPEED_1000 << PHY_SPEED_SHIFT) | (BC_PTP_V2 << PTP_MODE_SHIFT))
- #define ines_read32(s, r) __raw_readl((void __iomem *)&s->regs->r)
- #define ines_write32(s, v, r) __raw_writel(v, (void __iomem *)&s->regs->r)
- #define MESSAGE_TYPE_SYNC 1
- #define MESSAGE_TYPE_P_DELAY_REQ 2
- #define MESSAGE_TYPE_P_DELAY_RESP 3
- #define MESSAGE_TYPE_DELAY_REQ 4
- static LIST_HEAD(ines_clocks);
- static DEFINE_MUTEX(ines_clocks_lock);
- struct ines_global_regs {
- u32 id;
- u32 test;
- u32 global;
- u32 version;
- u32 test2;
- u32 int_stat;
- u32 int_msk;
- u32 buf_stat;
- };
- struct ines_port_registers {
- u32 port_conf;
- u32 p_delay;
- u32 ts_stat_tx;
- u32 ts_stat_rx;
- u32 ts_tx;
- u32 ts_rx;
- };
- struct ines_timestamp {
- struct list_head list;
- unsigned long tmo;
- u16 tag;
- u64 sec;
- u64 nsec;
- u64 clkid;
- u16 portnum;
- u16 seqid;
- };
- struct ines_port {
- struct ines_port_registers *regs;
- struct mii_timestamper mii_ts;
- struct ines_clock *clock;
- bool rxts_enabled;
- bool txts_enabled;
- unsigned int index;
- struct delayed_work ts_work;
- /* lock protects event list and tx_skb */
- spinlock_t lock;
- struct sk_buff *tx_skb;
- struct list_head events;
- struct list_head pool;
- struct ines_timestamp pool_data[INES_MAX_EVENTS];
- };
- struct ines_clock {
- struct ines_port port[INES_N_PORTS];
- struct ines_global_regs __iomem *regs;
- void __iomem *base;
- struct device_node *node;
- struct device *dev;
- struct list_head list;
- };
- static bool ines_match(struct sk_buff *skb, unsigned int ptp_class,
- struct ines_timestamp *ts, struct device *dev);
- static int ines_rxfifo_read(struct ines_port *port);
- static u64 ines_rxts64(struct ines_port *port, unsigned int words);
- static bool ines_timestamp_expired(struct ines_timestamp *ts);
- static u64 ines_txts64(struct ines_port *port, unsigned int words);
- static void ines_txtstamp_work(struct work_struct *work);
- static bool is_sync_pdelay_resp(struct sk_buff *skb, int type);
- static u8 tag_to_msgtype(u8 tag);
- static void ines_clock_cleanup(struct ines_clock *clock)
- {
- struct ines_port *port;
- int i;
- for (i = 0; i < INES_N_PORTS; i++) {
- port = &clock->port[i];
- cancel_delayed_work_sync(&port->ts_work);
- }
- }
- static int ines_clock_init(struct ines_clock *clock, struct device *device,
- void __iomem *addr)
- {
- struct device_node *node = device->of_node;
- unsigned long port_addr;
- struct ines_port *port;
- int i, j;
- INIT_LIST_HEAD(&clock->list);
- clock->node = node;
- clock->dev = device;
- clock->base = addr;
- clock->regs = clock->base;
- for (i = 0; i < INES_N_PORTS; i++) {
- port = &clock->port[i];
- port_addr = (unsigned long) clock->base +
- INES_PORT_OFFSET + i * INES_PORT_SIZE;
- port->regs = (struct ines_port_registers *) port_addr;
- port->clock = clock;
- port->index = i;
- INIT_DELAYED_WORK(&port->ts_work, ines_txtstamp_work);
- spin_lock_init(&port->lock);
- INIT_LIST_HEAD(&port->events);
- INIT_LIST_HEAD(&port->pool);
- for (j = 0; j < INES_MAX_EVENTS; j++)
- list_add(&port->pool_data[j].list, &port->pool);
- }
- ines_write32(clock, 0xBEEF, test);
- ines_write32(clock, 0xBEEF, test2);
- dev_dbg(device, "ID 0x%x\n", ines_read32(clock, id));
- dev_dbg(device, "TEST 0x%x\n", ines_read32(clock, test));
- dev_dbg(device, "VERSION 0x%x\n", ines_read32(clock, version));
- dev_dbg(device, "TEST2 0x%x\n", ines_read32(clock, test2));
- for (i = 0; i < INES_N_PORTS; i++) {
- port = &clock->port[i];
- ines_write32(port, PORT_CONF, port_conf);
- }
- return 0;
- }
- static struct ines_port *ines_find_port(struct device_node *node, u32 index)
- {
- struct ines_port *port = NULL;
- struct ines_clock *clock;
- struct list_head *this;
- mutex_lock(&ines_clocks_lock);
- list_for_each(this, &ines_clocks) {
- clock = list_entry(this, struct ines_clock, list);
- if (clock->node == node) {
- port = &clock->port[index];
- break;
- }
- }
- mutex_unlock(&ines_clocks_lock);
- return port;
- }
- static u64 ines_find_rxts(struct ines_port *port, struct sk_buff *skb, int type)
- {
- struct list_head *this, *next;
- struct ines_timestamp *ts;
- unsigned long flags;
- u64 ns = 0;
- if (type == PTP_CLASS_NONE)
- return 0;
- spin_lock_irqsave(&port->lock, flags);
- ines_rxfifo_read(port);
- list_for_each_safe(this, next, &port->events) {
- ts = list_entry(this, struct ines_timestamp, list);
- if (ines_timestamp_expired(ts)) {
- list_del_init(&ts->list);
- list_add(&ts->list, &port->pool);
- continue;
- }
- if (ines_match(skb, type, ts, port->clock->dev)) {
- ns = ts->sec * 1000000000ULL + ts->nsec;
- list_del_init(&ts->list);
- list_add(&ts->list, &port->pool);
- break;
- }
- }
- spin_unlock_irqrestore(&port->lock, flags);
- return ns;
- }
- static u64 ines_find_txts(struct ines_port *port, struct sk_buff *skb)
- {
- unsigned int class = ptp_classify_raw(skb), i;
- u32 data_rd_pos, buf_stat, mask, ts_stat_tx;
- struct ines_timestamp ts;
- unsigned long flags;
- u64 ns = 0;
- mask = TX_FIFO_NE_1 << port->index;
- spin_lock_irqsave(&port->lock, flags);
- for (i = 0; i < INES_FIFO_DEPTH; i++) {
- buf_stat = ines_read32(port->clock, buf_stat);
- if (!(buf_stat & mask)) {
- dev_dbg(port->clock->dev,
- "Tx timestamp FIFO unexpectedly empty\n");
- break;
- }
- ts_stat_tx = ines_read32(port, ts_stat_tx);
- data_rd_pos = (ts_stat_tx >> DATA_READ_POS_SHIFT) &
- DATA_READ_POS_MASK;
- if (data_rd_pos) {
- dev_err(port->clock->dev,
- "unexpected Tx read pos %u\n", data_rd_pos);
- break;
- }
- ts.tag = ines_read32(port, ts_tx);
- ts.sec = ines_txts64(port, 3);
- ts.nsec = ines_txts64(port, 2);
- ts.clkid = ines_txts64(port, 4);
- ts.portnum = ines_read32(port, ts_tx);
- ts.seqid = ines_read32(port, ts_tx);
- if (ines_match(skb, class, &ts, port->clock->dev)) {
- ns = ts.sec * 1000000000ULL + ts.nsec;
- break;
- }
- }
- spin_unlock_irqrestore(&port->lock, flags);
- return ns;
- }
- static int ines_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr)
- {
- struct ines_port *port = container_of(mii_ts, struct ines_port, mii_ts);
- u32 cm_one_step = 0, port_conf, ts_stat_rx, ts_stat_tx;
- struct hwtstamp_config cfg;
- unsigned long flags;
- if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
- return -EFAULT;
- switch (cfg.tx_type) {
- case HWTSTAMP_TX_OFF:
- ts_stat_tx = 0;
- break;
- case HWTSTAMP_TX_ON:
- ts_stat_tx = TS_ENABLE;
- break;
- case HWTSTAMP_TX_ONESTEP_P2P:
- ts_stat_tx = TS_ENABLE;
- cm_one_step = CM_ONE_STEP;
- break;
- default:
- return -ERANGE;
- }
- switch (cfg.rx_filter) {
- case HWTSTAMP_FILTER_NONE:
- ts_stat_rx = 0;
- break;
- case HWTSTAMP_FILTER_ALL:
- case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
- case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
- case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
- return -ERANGE;
- case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
- case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
- case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
- case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
- case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
- case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
- case HWTSTAMP_FILTER_PTP_V2_EVENT:
- case HWTSTAMP_FILTER_PTP_V2_SYNC:
- case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
- ts_stat_rx = TS_ENABLE;
- cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
- break;
- default:
- return -ERANGE;
- }
- spin_lock_irqsave(&port->lock, flags);
- port_conf = ines_read32(port, port_conf);
- port_conf &= ~CM_ONE_STEP;
- port_conf |= cm_one_step;
- ines_write32(port, port_conf, port_conf);
- ines_write32(port, ts_stat_rx, ts_stat_rx);
- ines_write32(port, ts_stat_tx, ts_stat_tx);
- port->rxts_enabled = ts_stat_rx == TS_ENABLE;
- port->txts_enabled = ts_stat_tx == TS_ENABLE;
- spin_unlock_irqrestore(&port->lock, flags);
- return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
- }
- static void ines_link_state(struct mii_timestamper *mii_ts,
- struct phy_device *phydev)
- {
- struct ines_port *port = container_of(mii_ts, struct ines_port, mii_ts);
- u32 port_conf, speed_conf;
- unsigned long flags;
- switch (phydev->speed) {
- case SPEED_10:
- speed_conf = PHY_SPEED_10 << PHY_SPEED_SHIFT;
- break;
- case SPEED_100:
- speed_conf = PHY_SPEED_100 << PHY_SPEED_SHIFT;
- break;
- case SPEED_1000:
- speed_conf = PHY_SPEED_1000 << PHY_SPEED_SHIFT;
- break;
- default:
- dev_err(port->clock->dev, "bad speed: %d\n", phydev->speed);
- return;
- }
- spin_lock_irqsave(&port->lock, flags);
- port_conf = ines_read32(port, port_conf);
- port_conf &= ~(0x3 << PHY_SPEED_SHIFT);
- port_conf |= speed_conf;
- ines_write32(port, port_conf, port_conf);
- spin_unlock_irqrestore(&port->lock, flags);
- }
- static bool ines_match(struct sk_buff *skb, unsigned int ptp_class,
- struct ines_timestamp *ts, struct device *dev)
- {
- struct ptp_header *hdr;
- u16 portn, seqid;
- u8 msgtype;
- u64 clkid;
- if (unlikely(ptp_class & PTP_CLASS_V1))
- return false;
- hdr = ptp_parse_header(skb, ptp_class);
- if (!hdr)
- return false;
- msgtype = ptp_get_msgtype(hdr, ptp_class);
- clkid = be64_to_cpup((__be64 *)&hdr->source_port_identity.clock_identity.id[0]);
- portn = be16_to_cpu(hdr->source_port_identity.port_number);
- seqid = be16_to_cpu(hdr->sequence_id);
- if (tag_to_msgtype(ts->tag & 0x7) != msgtype) {
- dev_dbg(dev, "msgtype mismatch ts %hhu != skb %hhu\n",
- tag_to_msgtype(ts->tag & 0x7), msgtype);
- return false;
- }
- if (ts->clkid != clkid) {
- dev_dbg(dev, "clkid mismatch ts %llx != skb %llx\n",
- ts->clkid, clkid);
- return false;
- }
- if (ts->portnum != portn) {
- dev_dbg(dev, "portn mismatch ts %hu != skb %hu\n",
- ts->portnum, portn);
- return false;
- }
- if (ts->seqid != seqid) {
- dev_dbg(dev, "seqid mismatch ts %hu != skb %hu\n",
- ts->seqid, seqid);
- return false;
- }
- return true;
- }
- static bool ines_rxtstamp(struct mii_timestamper *mii_ts,
- struct sk_buff *skb, int type)
- {
- struct ines_port *port = container_of(mii_ts, struct ines_port, mii_ts);
- struct skb_shared_hwtstamps *ssh;
- u64 ns;
- if (!port->rxts_enabled)
- return false;
- ns = ines_find_rxts(port, skb, type);
- if (!ns)
- return false;
- ssh = skb_hwtstamps(skb);
- ssh->hwtstamp = ns_to_ktime(ns);
- netif_rx(skb);
- return true;
- }
- static int ines_rxfifo_read(struct ines_port *port)
- {
- u32 data_rd_pos, buf_stat, mask, ts_stat_rx;
- struct ines_timestamp *ts;
- unsigned int i;
- mask = RX_FIFO_NE_1 << port->index;
- for (i = 0; i < INES_FIFO_DEPTH; i++) {
- if (list_empty(&port->pool)) {
- dev_err(port->clock->dev, "event pool is empty\n");
- return -1;
- }
- buf_stat = ines_read32(port->clock, buf_stat);
- if (!(buf_stat & mask))
- break;
- ts_stat_rx = ines_read32(port, ts_stat_rx);
- data_rd_pos = (ts_stat_rx >> DATA_READ_POS_SHIFT) &
- DATA_READ_POS_MASK;
- if (data_rd_pos) {
- dev_err(port->clock->dev, "unexpected Rx read pos %u\n",
- data_rd_pos);
- break;
- }
- ts = list_first_entry(&port->pool, struct ines_timestamp, list);
- ts->tmo = jiffies + HZ;
- ts->tag = ines_read32(port, ts_rx);
- ts->sec = ines_rxts64(port, 3);
- ts->nsec = ines_rxts64(port, 2);
- ts->clkid = ines_rxts64(port, 4);
- ts->portnum = ines_read32(port, ts_rx);
- ts->seqid = ines_read32(port, ts_rx);
- list_del_init(&ts->list);
- list_add_tail(&ts->list, &port->events);
- }
- return 0;
- }
- static u64 ines_rxts64(struct ines_port *port, unsigned int words)
- {
- unsigned int i;
- u64 result;
- u16 word;
- word = ines_read32(port, ts_rx);
- result = word;
- words--;
- for (i = 0; i < words; i++) {
- word = ines_read32(port, ts_rx);
- result <<= 16;
- result |= word;
- }
- return result;
- }
- static bool ines_timestamp_expired(struct ines_timestamp *ts)
- {
- return time_after(jiffies, ts->tmo);
- }
- static int ines_ts_info(struct mii_timestamper *mii_ts,
- struct ethtool_ts_info *info)
- {
- info->so_timestamping =
- SOF_TIMESTAMPING_TX_HARDWARE |
- SOF_TIMESTAMPING_TX_SOFTWARE |
- SOF_TIMESTAMPING_RX_HARDWARE |
- SOF_TIMESTAMPING_RX_SOFTWARE |
- SOF_TIMESTAMPING_SOFTWARE |
- SOF_TIMESTAMPING_RAW_HARDWARE;
- info->phc_index = -1;
- info->tx_types =
- (1 << HWTSTAMP_TX_OFF) |
- (1 << HWTSTAMP_TX_ON) |
- (1 << HWTSTAMP_TX_ONESTEP_P2P);
- info->rx_filters =
- (1 << HWTSTAMP_FILTER_NONE) |
- (1 << HWTSTAMP_FILTER_PTP_V2_EVENT);
- return 0;
- }
- static u64 ines_txts64(struct ines_port *port, unsigned int words)
- {
- unsigned int i;
- u64 result;
- u16 word;
- word = ines_read32(port, ts_tx);
- result = word;
- words--;
- for (i = 0; i < words; i++) {
- word = ines_read32(port, ts_tx);
- result <<= 16;
- result |= word;
- }
- return result;
- }
- static bool ines_txts_onestep(struct ines_port *port, struct sk_buff *skb, int type)
- {
- unsigned long flags;
- u32 port_conf;
- spin_lock_irqsave(&port->lock, flags);
- port_conf = ines_read32(port, port_conf);
- spin_unlock_irqrestore(&port->lock, flags);
- if (port_conf & CM_ONE_STEP)
- return is_sync_pdelay_resp(skb, type);
- return false;
- }
- static void ines_txtstamp(struct mii_timestamper *mii_ts,
- struct sk_buff *skb, int type)
- {
- struct ines_port *port = container_of(mii_ts, struct ines_port, mii_ts);
- struct sk_buff *old_skb = NULL;
- unsigned long flags;
- if (!port->txts_enabled || ines_txts_onestep(port, skb, type)) {
- kfree_skb(skb);
- return;
- }
- spin_lock_irqsave(&port->lock, flags);
- if (port->tx_skb)
- old_skb = port->tx_skb;
- port->tx_skb = skb;
- spin_unlock_irqrestore(&port->lock, flags);
- kfree_skb(old_skb);
- schedule_delayed_work(&port->ts_work, 1);
- }
- static void ines_txtstamp_work(struct work_struct *work)
- {
- struct ines_port *port =
- container_of(work, struct ines_port, ts_work.work);
- struct skb_shared_hwtstamps ssh;
- struct sk_buff *skb;
- unsigned long flags;
- u64 ns;
- spin_lock_irqsave(&port->lock, flags);
- skb = port->tx_skb;
- port->tx_skb = NULL;
- spin_unlock_irqrestore(&port->lock, flags);
- ns = ines_find_txts(port, skb);
- if (!ns) {
- kfree_skb(skb);
- return;
- }
- ssh.hwtstamp = ns_to_ktime(ns);
- skb_complete_tx_timestamp(skb, &ssh);
- }
- static bool is_sync_pdelay_resp(struct sk_buff *skb, int type)
- {
- struct ptp_header *hdr;
- u8 msgtype;
- hdr = ptp_parse_header(skb, type);
- if (!hdr)
- return false;
- msgtype = ptp_get_msgtype(hdr, type);
- switch (msgtype) {
- case PTP_MSGTYPE_SYNC:
- case PTP_MSGTYPE_PDELAY_RESP:
- return true;
- default:
- return false;
- }
- }
- static u8 tag_to_msgtype(u8 tag)
- {
- switch (tag) {
- case MESSAGE_TYPE_SYNC:
- return PTP_MSGTYPE_SYNC;
- case MESSAGE_TYPE_P_DELAY_REQ:
- return PTP_MSGTYPE_PDELAY_REQ;
- case MESSAGE_TYPE_P_DELAY_RESP:
- return PTP_MSGTYPE_PDELAY_RESP;
- case MESSAGE_TYPE_DELAY_REQ:
- return PTP_MSGTYPE_DELAY_REQ;
- }
- return 0xf;
- }
- static struct mii_timestamper *ines_ptp_probe_channel(struct device *device,
- unsigned int index)
- {
- struct device_node *node = device->of_node;
- struct ines_port *port;
- if (index > INES_N_PORTS - 1) {
- dev_err(device, "bad port index %u\n", index);
- return ERR_PTR(-EINVAL);
- }
- port = ines_find_port(node, index);
- if (!port) {
- dev_err(device, "missing port index %u\n", index);
- return ERR_PTR(-ENODEV);
- }
- port->mii_ts.rxtstamp = ines_rxtstamp;
- port->mii_ts.txtstamp = ines_txtstamp;
- port->mii_ts.hwtstamp = ines_hwtstamp;
- port->mii_ts.link_state = ines_link_state;
- port->mii_ts.ts_info = ines_ts_info;
- return &port->mii_ts;
- }
- static void ines_ptp_release_channel(struct device *device,
- struct mii_timestamper *mii_ts)
- {
- }
- static struct mii_timestamping_ctrl ines_ctrl = {
- .probe_channel = ines_ptp_probe_channel,
- .release_channel = ines_ptp_release_channel,
- };
- static int ines_ptp_ctrl_probe(struct platform_device *pld)
- {
- struct ines_clock *clock;
- void __iomem *addr;
- int err = 0;
- addr = devm_platform_ioremap_resource(pld, 0);
- if (IS_ERR(addr)) {
- err = PTR_ERR(addr);
- goto out;
- }
- clock = kzalloc(sizeof(*clock), GFP_KERNEL);
- if (!clock) {
- err = -ENOMEM;
- goto out;
- }
- if (ines_clock_init(clock, &pld->dev, addr)) {
- kfree(clock);
- err = -ENOMEM;
- goto out;
- }
- err = register_mii_tstamp_controller(&pld->dev, &ines_ctrl);
- if (err) {
- kfree(clock);
- goto out;
- }
- mutex_lock(&ines_clocks_lock);
- list_add_tail(&ines_clocks, &clock->list);
- mutex_unlock(&ines_clocks_lock);
- dev_set_drvdata(&pld->dev, clock);
- out:
- return err;
- }
- static int ines_ptp_ctrl_remove(struct platform_device *pld)
- {
- struct ines_clock *clock = dev_get_drvdata(&pld->dev);
- unregister_mii_tstamp_controller(&pld->dev);
- mutex_lock(&ines_clocks_lock);
- list_del(&clock->list);
- mutex_unlock(&ines_clocks_lock);
- ines_clock_cleanup(clock);
- kfree(clock);
- return 0;
- }
- static const struct of_device_id ines_ptp_ctrl_of_match[] = {
- { .compatible = "ines,ptp-ctrl" },
- { }
- };
- MODULE_DEVICE_TABLE(of, ines_ptp_ctrl_of_match);
- static struct platform_driver ines_ptp_ctrl_driver = {
- .probe = ines_ptp_ctrl_probe,
- .remove = ines_ptp_ctrl_remove,
- .driver = {
- .name = "ines_ptp_ctrl",
- .of_match_table = of_match_ptr(ines_ptp_ctrl_of_match),
- },
- };
- module_platform_driver(ines_ptp_ctrl_driver);
|