linux/drivers/net/ethernet/intel/fm10k/fm10k_ptp.c

463 lines
13 KiB
C
Raw Normal View History

/* Intel Ethernet Switch Host Interface Driver
* Copyright(c) 2013 - 2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*
* Contact Information:
* e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*/
#include <linux/ptp_classify.h>
#include <linux/ptp_clock_kernel.h>
#include "fm10k.h"
#define FM10K_TS_TX_TIMEOUT (HZ * 15)
void fm10k_systime_to_hwtstamp(struct fm10k_intfc *interface,
struct skb_shared_hwtstamps *hwtstamp,
u64 systime)
{
unsigned long flags;
read_lock_irqsave(&interface->systime_lock, flags);
systime += interface->ptp_adjust;
read_unlock_irqrestore(&interface->systime_lock, flags);
hwtstamp->hwtstamp = ns_to_ktime(systime);
}
static struct sk_buff *fm10k_ts_tx_skb(struct fm10k_intfc *interface,
__le16 dglort)
{
struct sk_buff_head *list = &interface->ts_tx_skb_queue;
struct sk_buff *skb;
skb_queue_walk(list, skb) {
if (FM10K_CB(skb)->fi.w.dglort == dglort)
return skb;
}
return NULL;
}
void fm10k_ts_tx_enqueue(struct fm10k_intfc *interface, struct sk_buff *skb)
{
struct sk_buff_head *list = &interface->ts_tx_skb_queue;
struct sk_buff *clone;
unsigned long flags;
/* create clone for us to return on the Tx path */
clone = skb_clone_sk(skb);
if (!clone)
return;
FM10K_CB(clone)->ts_tx_timeout = jiffies + FM10K_TS_TX_TIMEOUT;
spin_lock_irqsave(&list->lock, flags);
/* attempt to locate any buffers with the same dglort,
* if none are present then insert skb in tail of list
*/
skb = fm10k_ts_tx_skb(interface, FM10K_CB(clone)->fi.w.dglort);
if (!skb) {
skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS;
__skb_queue_tail(list, clone);
}
spin_unlock_irqrestore(&list->lock, flags);
/* if list is already has one then we just free the clone */
if (skb)
dev_kfree_skb(clone);
}
void fm10k_ts_tx_hwtstamp(struct fm10k_intfc *interface, __le16 dglort,
u64 systime)
{
struct skb_shared_hwtstamps shhwtstamps;
struct sk_buff_head *list = &interface->ts_tx_skb_queue;
struct sk_buff *skb;
unsigned long flags;
spin_lock_irqsave(&list->lock, flags);
/* attempt to locate and pull the sk_buff out of the list */
skb = fm10k_ts_tx_skb(interface, dglort);
if (skb)
__skb_unlink(skb, list);
spin_unlock_irqrestore(&list->lock, flags);
/* if not found do nothing */
if (!skb)
return;
fm10k: use correct ethernet driver Tx timestamp function skb_complete_tx_timestamp is intended for use by PHY drivers which implement a different method of returning timestamps. This method is intended to be used after a PHY driver accepts a cloned packet via its phy_driver.txtstamp function. It is not correct to use in the standard ethernet driver such as fm10k. This patch fixes the following possible kernel panic. [ 2744.552896] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G W OE 3.19.3-200.fc21.x86_64 #1 [ 2744.552899] Hardware name: Intel Corporation S2600CO/S2600CO, BIOS SE5C600.86B.02.03.8x23.060520140825 06/05/2014 [ 2744.552901] 0000000000000000 2f4c8b10ea3f9848 ffff88081ee03a38 ffffffff8176e215 [ 2744.552906] 0000000000000000 0000000000000000 ffff88081ee03a78 ffffffff8109bc1a [ 2744.552910] ffff88081ee03c50 ffff88080e55fc00 ffff88080e55fc00 ffffffff81647c50 [ 2744.552914] Call Trace: [ 2744.552917] <IRQ> [<ffffffff8176e215>] dump_stack+0x45/0x57 [ 2744.552931] [<ffffffff8109bc1a>] warn_slowpath_common+0x8a/0xc0 [ 2744.552936] [<ffffffff81647c50>] ? skb_queue_purge+0x20/0x40 [ 2744.552941] [<ffffffff8109bd4a>] warn_slowpath_null+0x1a/0x20 [ 2744.552946] [<ffffffff81646911>] skb_release_head_state+0xe1/0xf0 [ 2744.552950] [<ffffffff81647b26>] skb_release_all+0x16/0x30 [ 2744.552954] [<ffffffff81647ba6>] kfree_skb+0x36/0x90 [ 2744.552958] [<ffffffff81647c50>] skb_queue_purge+0x20/0x40 [ 2744.552964] [<ffffffff81751f8d>] packet_sock_destruct+0x1d/0x90 [ 2744.552968] [<ffffffff81642053>] __sk_free+0x23/0x140 [ 2744.552973] [<ffffffff81642189>] sk_free+0x19/0x20 [ 2744.552977] [<ffffffff81647d60>] skb_complete_tx_timestamp+0x50/0x60 [ 2744.552988] [<ffffffffa02eee40>] fm10k_ts_tx_hwtstamp+0xd0/0x100 [fm10k] [ 2744.552994] [<ffffffffa02e054e>] fm10k_1588_msg_pf+0x12e/0x140 [fm10k] [ 2744.553002] [<ffffffffa02edf1d>] fm10k_tlv_msg_parse+0x8d/0xc0 [fm10k] [ 2744.553010] [<ffffffffa02eb2d0>] fm10k_mbx_dequeue_rx+0x60/0xb0 [fm10k] [ 2744.553016] [<ffffffffa02ebf98>] fm10k_sm_mbx_process+0x178/0x3c0 [fm10k] [ 2744.553022] [<ffffffffa02e09ca>] fm10k_msix_mbx_pf+0xfa/0x360 [fm10k] [ 2744.553030] [<ffffffff811030a7>] ? get_next_timer_interrupt+0x1f7/0x270 [ 2744.553036] [<ffffffff810f2a47>] handle_irq_event_percpu+0x77/0x1a0 [ 2744.553041] [<ffffffff810f2bab>] handle_irq_event+0x3b/0x60 [ 2744.553045] [<ffffffff810f5d6e>] handle_edge_irq+0x6e/0x120 [ 2744.553054] [<ffffffff81017414>] handle_irq+0x74/0x140 [ 2744.553061] [<ffffffff810bb54a>] ? atomic_notifier_call_chain+0x1a/0x20 [ 2744.553066] [<ffffffff8177777f>] do_IRQ+0x4f/0xf0 [ 2744.553072] [<ffffffff8177556d>] common_interrupt+0x6d/0x6d [ 2744.553074] <EOI> [<ffffffff81609b16>] ? cpuidle_enter_state+0x66/0x160 [ 2744.553084] [<ffffffff81609b01>] ? cpuidle_enter_state+0x51/0x160 [ 2744.553087] [<ffffffff81609cf7>] cpuidle_enter+0x17/0x20 [ 2744.553092] [<ffffffff810de101>] cpu_startup_entry+0x321/0x3c0 [ 2744.553098] [<ffffffff81764497>] rest_init+0x77/0x80 [ 2744.553103] [<ffffffff81d4f02c>] start_kernel+0x4a4/0x4c5 [ 2744.553107] [<ffffffff81d4e120>] ? early_idt_handlers+0x120/0x120 [ 2744.553110] [<ffffffff81d4e4d7>] x86_64_start_reservations+0x2a/0x2c [ 2744.553114] [<ffffffff81d4e62b>] x86_64_start_kernel+0x152/0x175 Signed-off-by: Jacob Keller <jacob.e.keller@intel.com> Tested-by: Krishneil Singh <Krishneil.k.singh@intel.com> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
2015-06-04 07:30:59 +08:00
/* timestamp the sk_buff and free out copy */
fm10k_systime_to_hwtstamp(interface, &shhwtstamps, systime);
fm10k: use correct ethernet driver Tx timestamp function skb_complete_tx_timestamp is intended for use by PHY drivers which implement a different method of returning timestamps. This method is intended to be used after a PHY driver accepts a cloned packet via its phy_driver.txtstamp function. It is not correct to use in the standard ethernet driver such as fm10k. This patch fixes the following possible kernel panic. [ 2744.552896] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G W OE 3.19.3-200.fc21.x86_64 #1 [ 2744.552899] Hardware name: Intel Corporation S2600CO/S2600CO, BIOS SE5C600.86B.02.03.8x23.060520140825 06/05/2014 [ 2744.552901] 0000000000000000 2f4c8b10ea3f9848 ffff88081ee03a38 ffffffff8176e215 [ 2744.552906] 0000000000000000 0000000000000000 ffff88081ee03a78 ffffffff8109bc1a [ 2744.552910] ffff88081ee03c50 ffff88080e55fc00 ffff88080e55fc00 ffffffff81647c50 [ 2744.552914] Call Trace: [ 2744.552917] <IRQ> [<ffffffff8176e215>] dump_stack+0x45/0x57 [ 2744.552931] [<ffffffff8109bc1a>] warn_slowpath_common+0x8a/0xc0 [ 2744.552936] [<ffffffff81647c50>] ? skb_queue_purge+0x20/0x40 [ 2744.552941] [<ffffffff8109bd4a>] warn_slowpath_null+0x1a/0x20 [ 2744.552946] [<ffffffff81646911>] skb_release_head_state+0xe1/0xf0 [ 2744.552950] [<ffffffff81647b26>] skb_release_all+0x16/0x30 [ 2744.552954] [<ffffffff81647ba6>] kfree_skb+0x36/0x90 [ 2744.552958] [<ffffffff81647c50>] skb_queue_purge+0x20/0x40 [ 2744.552964] [<ffffffff81751f8d>] packet_sock_destruct+0x1d/0x90 [ 2744.552968] [<ffffffff81642053>] __sk_free+0x23/0x140 [ 2744.552973] [<ffffffff81642189>] sk_free+0x19/0x20 [ 2744.552977] [<ffffffff81647d60>] skb_complete_tx_timestamp+0x50/0x60 [ 2744.552988] [<ffffffffa02eee40>] fm10k_ts_tx_hwtstamp+0xd0/0x100 [fm10k] [ 2744.552994] [<ffffffffa02e054e>] fm10k_1588_msg_pf+0x12e/0x140 [fm10k] [ 2744.553002] [<ffffffffa02edf1d>] fm10k_tlv_msg_parse+0x8d/0xc0 [fm10k] [ 2744.553010] [<ffffffffa02eb2d0>] fm10k_mbx_dequeue_rx+0x60/0xb0 [fm10k] [ 2744.553016] [<ffffffffa02ebf98>] fm10k_sm_mbx_process+0x178/0x3c0 [fm10k] [ 2744.553022] [<ffffffffa02e09ca>] fm10k_msix_mbx_pf+0xfa/0x360 [fm10k] [ 2744.553030] [<ffffffff811030a7>] ? get_next_timer_interrupt+0x1f7/0x270 [ 2744.553036] [<ffffffff810f2a47>] handle_irq_event_percpu+0x77/0x1a0 [ 2744.553041] [<ffffffff810f2bab>] handle_irq_event+0x3b/0x60 [ 2744.553045] [<ffffffff810f5d6e>] handle_edge_irq+0x6e/0x120 [ 2744.553054] [<ffffffff81017414>] handle_irq+0x74/0x140 [ 2744.553061] [<ffffffff810bb54a>] ? atomic_notifier_call_chain+0x1a/0x20 [ 2744.553066] [<ffffffff8177777f>] do_IRQ+0x4f/0xf0 [ 2744.553072] [<ffffffff8177556d>] common_interrupt+0x6d/0x6d [ 2744.553074] <EOI> [<ffffffff81609b16>] ? cpuidle_enter_state+0x66/0x160 [ 2744.553084] [<ffffffff81609b01>] ? cpuidle_enter_state+0x51/0x160 [ 2744.553087] [<ffffffff81609cf7>] cpuidle_enter+0x17/0x20 [ 2744.553092] [<ffffffff810de101>] cpu_startup_entry+0x321/0x3c0 [ 2744.553098] [<ffffffff81764497>] rest_init+0x77/0x80 [ 2744.553103] [<ffffffff81d4f02c>] start_kernel+0x4a4/0x4c5 [ 2744.553107] [<ffffffff81d4e120>] ? early_idt_handlers+0x120/0x120 [ 2744.553110] [<ffffffff81d4e4d7>] x86_64_start_reservations+0x2a/0x2c [ 2744.553114] [<ffffffff81d4e62b>] x86_64_start_kernel+0x152/0x175 Signed-off-by: Jacob Keller <jacob.e.keller@intel.com> Tested-by: Krishneil Singh <Krishneil.k.singh@intel.com> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
2015-06-04 07:30:59 +08:00
skb_tstamp_tx(skb, &shhwtstamps);
dev_kfree_skb_any(skb);
}
void fm10k_ts_tx_subtask(struct fm10k_intfc *interface)
{
struct sk_buff_head *list = &interface->ts_tx_skb_queue;
struct sk_buff *skb, *tmp;
unsigned long flags;
/* If we're down or resetting, just bail */
if (test_bit(__FM10K_DOWN, &interface->state) ||
test_bit(__FM10K_RESETTING, &interface->state))
return;
spin_lock_irqsave(&list->lock, flags);
/* walk though the list and flush any expired timestamp packets */
skb_queue_walk_safe(list, skb, tmp) {
if (!time_is_after_jiffies(FM10K_CB(skb)->ts_tx_timeout))
continue;
__skb_unlink(skb, list);
kfree_skb(skb);
interface->tx_hwtstamp_timeouts++;
}
spin_unlock_irqrestore(&list->lock, flags);
}
static u64 fm10k_systime_read(struct fm10k_intfc *interface)
{
struct fm10k_hw *hw = &interface->hw;
return hw->mac.ops.read_systime(hw);
}
void fm10k_ts_reset(struct fm10k_intfc *interface)
{
s64 ns = ktime_to_ns(ktime_get_real());
unsigned long flags;
/* reinitialize the clock */
write_lock_irqsave(&interface->systime_lock, flags);
interface->ptp_adjust = fm10k_systime_read(interface) - ns;
write_unlock_irqrestore(&interface->systime_lock, flags);
}
void fm10k_ts_init(struct fm10k_intfc *interface)
{
/* Initialize lock protecting systime access */
rwlock_init(&interface->systime_lock);
/* Initialize skb queue for pending timestamp requests */
skb_queue_head_init(&interface->ts_tx_skb_queue);
/* reset the clock to current kernel time */
fm10k_ts_reset(interface);
}
/**
* fm10k_get_ts_config - get current hardware timestamping configuration
* @netdev: network interface device structure
* @ifreq: ioctl data
*
* This function returns the current timestamping settings. Rather than
* attempt to deconstruct registers to fill in the values, simply keep a copy
* of the old settings around, and return a copy when requested.
*/
int fm10k_get_ts_config(struct net_device *netdev, struct ifreq *ifr)
{
struct fm10k_intfc *interface = netdev_priv(netdev);
struct hwtstamp_config *config = &interface->ts_config;
return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ?
-EFAULT : 0;
}
/**
* fm10k_set_ts_config - control hardware time stamping
* @netdev: network interface device structure
* @ifreq: ioctl data
*
* Outgoing time stamping can be enabled and disabled. Play nice and
* disable it when requested, although it shouldn't cause any overhead
* when no packet needs it. At most one packet in the queue may be
* marked for time stamping, otherwise it would be impossible to tell
* for sure to which packet the hardware time stamp belongs.
*
* Incoming time stamping has to be configured via the hardware
* filters. Not all combinations are supported, in particular event
* type has to be specified. Matching the kind of event packet is
* not supported, with the exception of "all V2 events regardless of
* level 2 or 4".
*
* Since hardware always timestamps Path delay packets when timestamping V2
* packets, regardless of the type specified in the register, only use V2
* Event mode. This more accurately tells the user what the hardware is going
* to do anyways.
*/
int fm10k_set_ts_config(struct net_device *netdev, struct ifreq *ifr)
{
struct fm10k_intfc *interface = netdev_priv(netdev);
struct hwtstamp_config ts_config;
if (copy_from_user(&ts_config, ifr->ifr_data, sizeof(ts_config)))
return -EFAULT;
/* reserved for future extensions */
if (ts_config.flags)
return -EINVAL;
switch (ts_config.tx_type) {
case HWTSTAMP_TX_OFF:
break;
case HWTSTAMP_TX_ON:
/* we likely need some check here to see if this is supported */
break;
default:
return -ERANGE;
}
switch (ts_config.rx_filter) {
case HWTSTAMP_FILTER_NONE:
interface->flags &= ~FM10K_FLAG_RX_TS_ENABLED;
break;
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
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:
case HWTSTAMP_FILTER_ALL:
interface->flags |= FM10K_FLAG_RX_TS_ENABLED;
ts_config.rx_filter = HWTSTAMP_FILTER_ALL;
break;
default:
return -ERANGE;
}
/* save these settings for future reference */
interface->ts_config = ts_config;
return copy_to_user(ifr->ifr_data, &ts_config, sizeof(ts_config)) ?
-EFAULT : 0;
}
static int fm10k_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
{
struct fm10k_intfc *interface;
struct fm10k_hw *hw;
int err;
interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
hw = &interface->hw;
err = hw->mac.ops.adjust_systime(hw, ppb);
/* the only error we should see is if the value is out of range */
return (err == FM10K_ERR_PARAM) ? -ERANGE : err;
}
static int fm10k_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
struct fm10k_intfc *interface;
unsigned long flags;
interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
write_lock_irqsave(&interface->systime_lock, flags);
interface->ptp_adjust += delta;
write_unlock_irqrestore(&interface->systime_lock, flags);
return 0;
}
static int fm10k_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
{
struct fm10k_intfc *interface;
unsigned long flags;
u64 now;
interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
read_lock_irqsave(&interface->systime_lock, flags);
now = fm10k_systime_read(interface) + interface->ptp_adjust;
read_unlock_irqrestore(&interface->systime_lock, flags);
*ts = ns_to_timespec64(now);
return 0;
}
static int fm10k_ptp_settime(struct ptp_clock_info *ptp,
const struct timespec64 *ts)
{
struct fm10k_intfc *interface;
unsigned long flags;
u64 ns = timespec64_to_ns(ts);
interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
write_lock_irqsave(&interface->systime_lock, flags);
interface->ptp_adjust = fm10k_systime_read(interface) - ns;
write_unlock_irqrestore(&interface->systime_lock, flags);
return 0;
}
static int fm10k_ptp_enable(struct ptp_clock_info *ptp,
struct ptp_clock_request *rq,
int __always_unused on)
{
struct ptp_clock_time *t = &rq->perout.period;
struct fm10k_intfc *interface;
struct fm10k_hw *hw;
u64 period;
u32 step;
/* we can only support periodic output */
if (rq->type != PTP_CLK_REQ_PEROUT)
return -EINVAL;
/* verify the requested channel is there */
if (rq->perout.index >= ptp->n_per_out)
return -EINVAL;
/* we cannot enforce start time as there is no
* mechanism for that in the hardware, we can only control
* the period.
*/
/* we cannot support periods greater than 4 seconds due to reg limit */
if (t->sec > 4 || t->sec < 0)
return -ERANGE;
interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
hw = &interface->hw;
/* we simply cannot support the operation if we don't have BAR4 */
if (!hw->sw_addr)
return -ENOTSUPP;
/* convert to unsigned 64b ns, verify we can put it in a 32b register */
period = t->sec * 1000000000LL + t->nsec;
/* determine the minimum size for period */
step = 2 * (fm10k_read_reg(hw, FM10K_SYSTIME_CFG) &
FM10K_SYSTIME_CFG_STEP_MASK);
/* verify the value is in range supported by hardware */
if ((period && (period < step)) || (period > U32_MAX))
return -ERANGE;
/* notify hardware of request to being sending pulses */
fm10k_write_sw_reg(hw, FM10K_SW_SYSTIME_PULSE(rq->perout.index),
(u32)period);
return 0;
}
static struct ptp_pin_desc fm10k_ptp_pd[2] = {
{
.name = "IEEE1588_PULSE0",
.index = 0,
.func = PTP_PF_PEROUT,
.chan = 0
},
{
.name = "IEEE1588_PULSE1",
.index = 1,
.func = PTP_PF_PEROUT,
.chan = 1
}
};
static int fm10k_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin,
enum ptp_pin_function func, unsigned int chan)
{
/* verify the requested pin is there */
if (pin >= ptp->n_pins || !ptp->pin_config)
return -EINVAL;
/* enforce locked channels, no changing them */
if (chan != ptp->pin_config[pin].chan)
return -EINVAL;
/* we want to keep the functions locked as well */
if (func != ptp->pin_config[pin].func)
return -EINVAL;
return 0;
}
void fm10k_ptp_register(struct fm10k_intfc *interface)
{
struct ptp_clock_info *ptp_caps = &interface->ptp_caps;
struct device *dev = &interface->pdev->dev;
struct ptp_clock *ptp_clock;
snprintf(ptp_caps->name, sizeof(ptp_caps->name),
"%s", interface->netdev->name);
ptp_caps->owner = THIS_MODULE;
/* This math is simply the inverse of the math in
* fm10k_adjust_systime_pf applied to an adjustment value
* of 2^30 - 1 which is the maximum value of the register:
* max_ppb == ((2^30 - 1) * 5^9) / 2^31
*/
ptp_caps->max_adj = 976562;
ptp_caps->adjfreq = fm10k_ptp_adjfreq;
ptp_caps->adjtime = fm10k_ptp_adjtime;
ptp_caps->gettime64 = fm10k_ptp_gettime;
ptp_caps->settime64 = fm10k_ptp_settime;
/* provide pins if BAR4 is accessible */
if (interface->sw_addr) {
/* enable periodic outputs */
ptp_caps->n_per_out = 2;
ptp_caps->enable = fm10k_ptp_enable;
/* enable clock pins */
ptp_caps->verify = fm10k_ptp_verify;
ptp_caps->n_pins = 2;
ptp_caps->pin_config = fm10k_ptp_pd;
}
ptp_clock = ptp_clock_register(ptp_caps, dev);
if (IS_ERR(ptp_clock)) {
ptp_clock = NULL;
dev_err(dev, "ptp_clock_register failed\n");
} else {
dev_info(dev, "registered PHC device %s\n", ptp_caps->name);
}
interface->ptp_clock = ptp_clock;
}
void fm10k_ptp_unregister(struct fm10k_intfc *interface)
{
struct ptp_clock *ptp_clock = interface->ptp_clock;
struct device *dev = &interface->pdev->dev;
if (!ptp_clock)
return;
interface->ptp_clock = NULL;
ptp_clock_unregister(ptp_clock);
dev_info(dev, "removed PHC %s\n", interface->ptp_caps.name);
}