Merge branch 'for-davem' of git://git.kernel.org/pub/scm/linux/kernel/git/bwh/sfc-next

Ben Hutchings says:

====================
1. Change PTP clock name to 'sfc'.
2. Complete support for hardware timestamping and PTP clock on the
SFC9100 family.
3. Various cleanups for the PTP code.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2013-12-14 22:33:45 -05:00
commit 7ed2a0d0d4
10 changed files with 796 additions and 216 deletions

View File

@ -264,6 +264,8 @@ static int efx_ef10_probe(struct efx_nic *efx)
if (rc)
goto fail3;
efx_ptp_probe(efx, NULL);
return 0;
fail3:
@ -472,9 +474,10 @@ static void efx_ef10_remove(struct efx_nic *efx)
struct efx_ef10_nic_data *nic_data = efx->nic_data;
int rc;
efx_ptp_remove(efx);
efx_mcdi_mon_remove(efx);
/* This needs to be after efx_ptp_remove_channel() with no filters */
efx_ef10_rx_free_indir_table(efx);
if (nic_data->wc_membase)
@ -1469,8 +1472,9 @@ static void efx_ef10_rx_init(struct efx_rx_queue *rx_queue)
MCDI_SET_DWORD(inbuf, INIT_RXQ_IN_LABEL, efx_rx_queue_index(rx_queue));
MCDI_SET_DWORD(inbuf, INIT_RXQ_IN_INSTANCE,
efx_rx_queue_index(rx_queue));
MCDI_POPULATE_DWORD_1(inbuf, INIT_RXQ_IN_FLAGS,
INIT_RXQ_IN_FLAG_PREFIX, 1);
MCDI_POPULATE_DWORD_2(inbuf, INIT_RXQ_IN_FLAGS,
INIT_RXQ_IN_FLAG_PREFIX, 1,
INIT_RXQ_IN_FLAG_TIMESTAMP, 1);
MCDI_SET_DWORD(inbuf, INIT_RXQ_IN_OWNER_ID, 0);
MCDI_SET_DWORD(inbuf, INIT_RXQ_IN_PORT_ID, EVB_PORT_ID_ASSIGNED);
@ -3406,6 +3410,119 @@ static void efx_ef10_ptp_write_host_time(struct efx_nic *efx, u32 host_time)
_efx_writed(efx, cpu_to_le32(host_time), ER_DZ_MC_DB_LWRD);
}
static int efx_ef10_rx_enable_timestamping(struct efx_channel *channel,
bool temp)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_TIME_EVENT_SUBSCRIBE_LEN);
int rc;
if (channel->sync_events_state == SYNC_EVENTS_REQUESTED ||
channel->sync_events_state == SYNC_EVENTS_VALID ||
(temp && channel->sync_events_state == SYNC_EVENTS_DISABLED))
return 0;
channel->sync_events_state = SYNC_EVENTS_REQUESTED;
MCDI_SET_DWORD(inbuf, PTP_IN_OP, MC_CMD_PTP_OP_TIME_EVENT_SUBSCRIBE);
MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0);
MCDI_SET_DWORD(inbuf, PTP_IN_TIME_EVENT_SUBSCRIBE_QUEUE,
channel->channel);
rc = efx_mcdi_rpc(channel->efx, MC_CMD_PTP,
inbuf, sizeof(inbuf), NULL, 0, NULL);
if (rc != 0)
channel->sync_events_state = temp ? SYNC_EVENTS_QUIESCENT :
SYNC_EVENTS_DISABLED;
return rc;
}
static int efx_ef10_rx_disable_timestamping(struct efx_channel *channel,
bool temp)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_TIME_EVENT_UNSUBSCRIBE_LEN);
int rc;
if (channel->sync_events_state == SYNC_EVENTS_DISABLED ||
(temp && channel->sync_events_state == SYNC_EVENTS_QUIESCENT))
return 0;
if (channel->sync_events_state == SYNC_EVENTS_QUIESCENT) {
channel->sync_events_state = SYNC_EVENTS_DISABLED;
return 0;
}
channel->sync_events_state = temp ? SYNC_EVENTS_QUIESCENT :
SYNC_EVENTS_DISABLED;
MCDI_SET_DWORD(inbuf, PTP_IN_OP, MC_CMD_PTP_OP_TIME_EVENT_UNSUBSCRIBE);
MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0);
MCDI_SET_DWORD(inbuf, PTP_IN_TIME_EVENT_UNSUBSCRIBE_CONTROL,
MC_CMD_PTP_IN_TIME_EVENT_UNSUBSCRIBE_SINGLE);
MCDI_SET_DWORD(inbuf, PTP_IN_TIME_EVENT_UNSUBSCRIBE_QUEUE,
channel->channel);
rc = efx_mcdi_rpc(channel->efx, MC_CMD_PTP,
inbuf, sizeof(inbuf), NULL, 0, NULL);
return rc;
}
static int efx_ef10_ptp_set_ts_sync_events(struct efx_nic *efx, bool en,
bool temp)
{
int (*set)(struct efx_channel *channel, bool temp);
struct efx_channel *channel;
set = en ?
efx_ef10_rx_enable_timestamping :
efx_ef10_rx_disable_timestamping;
efx_for_each_channel(channel, efx) {
int rc = set(channel, temp);
if (en && rc != 0) {
efx_ef10_ptp_set_ts_sync_events(efx, false, temp);
return rc;
}
}
return 0;
}
static int efx_ef10_ptp_set_ts_config(struct efx_nic *efx,
struct hwtstamp_config *init)
{
int rc;
switch (init->rx_filter) {
case HWTSTAMP_FILTER_NONE:
efx_ef10_ptp_set_ts_sync_events(efx, false, false);
/* if TX timestamping is still requested then leave PTP on */
return efx_ptp_change_mode(efx,
init->tx_type != HWTSTAMP_TX_OFF, 0);
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:
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:
init->rx_filter = HWTSTAMP_FILTER_ALL;
rc = efx_ptp_change_mode(efx, true, 0);
if (!rc)
rc = efx_ef10_ptp_set_ts_sync_events(efx, true, false);
if (rc)
efx_ptp_change_mode(efx, false, 0);
return rc;
default:
return -ERANGE;
}
}
const struct efx_nic_type efx_hunt_a0_nic_type = {
.mem_map_size = efx_ef10_mem_map_size,
.probe = efx_ef10_probe,
@ -3484,11 +3601,14 @@ const struct efx_nic_type efx_hunt_a0_nic_type = {
.mtd_sync = efx_mcdi_mtd_sync,
#endif
.ptp_write_host_time = efx_ef10_ptp_write_host_time,
.ptp_set_ts_sync_events = efx_ef10_ptp_set_ts_sync_events,
.ptp_set_ts_config = efx_ef10_ptp_set_ts_config,
.revision = EFX_REV_HUNT_A0,
.max_dma_mask = DMA_BIT_MASK(ESF_DZ_TX_KER_BUF_ADDR_WIDTH),
.rx_prefix_size = ES_DZ_RX_PREFIX_SIZE,
.rx_hash_offset = ES_DZ_RX_PREFIX_HASH_OFST,
.rx_ts_offset = ES_DZ_RX_PREFIX_TSTAMP_OFST,
.can_rx_scatter = true,
.always_rx_scatter = true,
.max_interrupt_mode = EFX_INT_MODE_MSIX,
@ -3497,4 +3617,6 @@ const struct efx_nic_type efx_hunt_a0_nic_type = {
NETIF_F_RXHASH | NETIF_F_NTUPLE),
.mcdi_max_ver = 2,
.max_rx_ip_filters = HUNT_FILTER_TBL_ROWS,
.hwtstamp_filters = 1 << HWTSTAMP_FILTER_NONE |
1 << HWTSTAMP_FILTER_ALL,
};

View File

@ -1117,6 +1117,77 @@ static void efx_remove_port(struct efx_nic *efx)
*
**************************************************************************/
static LIST_HEAD(efx_primary_list);
static LIST_HEAD(efx_unassociated_list);
static bool efx_same_controller(struct efx_nic *left, struct efx_nic *right)
{
return left->type == right->type &&
left->vpd_sn && right->vpd_sn &&
!strcmp(left->vpd_sn, right->vpd_sn);
}
static void efx_associate(struct efx_nic *efx)
{
struct efx_nic *other, *next;
if (efx->primary == efx) {
/* Adding primary function; look for secondaries */
netif_dbg(efx, probe, efx->net_dev, "adding to primary list\n");
list_add_tail(&efx->node, &efx_primary_list);
list_for_each_entry_safe(other, next, &efx_unassociated_list,
node) {
if (efx_same_controller(efx, other)) {
list_del(&other->node);
netif_dbg(other, probe, other->net_dev,
"moving to secondary list of %s %s\n",
pci_name(efx->pci_dev),
efx->net_dev->name);
list_add_tail(&other->node,
&efx->secondary_list);
other->primary = efx;
}
}
} else {
/* Adding secondary function; look for primary */
list_for_each_entry(other, &efx_primary_list, node) {
if (efx_same_controller(efx, other)) {
netif_dbg(efx, probe, efx->net_dev,
"adding to secondary list of %s %s\n",
pci_name(other->pci_dev),
other->net_dev->name);
list_add_tail(&efx->node,
&other->secondary_list);
efx->primary = other;
return;
}
}
netif_dbg(efx, probe, efx->net_dev,
"adding to unassociated list\n");
list_add_tail(&efx->node, &efx_unassociated_list);
}
}
static void efx_dissociate(struct efx_nic *efx)
{
struct efx_nic *other, *next;
list_del(&efx->node);
efx->primary = NULL;
list_for_each_entry_safe(other, next, &efx->secondary_list, node) {
list_del(&other->node);
netif_dbg(other, probe, other->net_dev,
"moving to unassociated list\n");
list_add_tail(&other->node, &efx_unassociated_list);
other->primary = NULL;
}
}
/* This configures the PCI device to enable I/O and DMA. */
static int efx_init_io(struct efx_nic *efx)
{
@ -2214,6 +2285,8 @@ static int efx_register_netdev(struct efx_nic *efx)
efx_init_tx_queue_core_txq(tx_queue);
}
efx_associate(efx);
rtnl_unlock();
rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_type);
@ -2227,6 +2300,7 @@ static int efx_register_netdev(struct efx_nic *efx)
fail_registered:
rtnl_lock();
efx_dissociate(efx);
unregister_netdevice(net_dev);
fail_locked:
efx->state = STATE_UNINIT;
@ -2568,6 +2642,8 @@ static int efx_init_struct(struct efx_nic *efx,
int i;
/* Initialise common structures */
INIT_LIST_HEAD(&efx->node);
INIT_LIST_HEAD(&efx->secondary_list);
spin_lock_init(&efx->biu_lock);
#ifdef CONFIG_SFC_MTD
INIT_LIST_HEAD(&efx->mtd_list);
@ -2586,6 +2662,8 @@ static int efx_init_struct(struct efx_nic *efx,
NET_IP_ALIGN ? (efx->rx_prefix_size + NET_IP_ALIGN) % 4 : 0;
efx->rx_packet_hash_offset =
efx->type->rx_hash_offset - efx->type->rx_prefix_size;
efx->rx_packet_ts_offset =
efx->type->rx_ts_offset - efx->type->rx_prefix_size;
spin_lock_init(&efx->stats_lock);
mutex_init(&efx->mac_lock);
efx->phy_op = &efx_dummy_phy_operations;
@ -2626,6 +2704,8 @@ static void efx_fini_struct(struct efx_nic *efx)
for (i = 0; i < EFX_MAX_CHANNELS; i++)
kfree(efx->channel[i]);
kfree(efx->vpd_sn);
if (efx->workqueue) {
destroy_workqueue(efx->workqueue);
efx->workqueue = NULL;
@ -2670,6 +2750,7 @@ static void efx_pci_remove(struct pci_dev *pci_dev)
/* Mark the NIC as fini, then stop the interface */
rtnl_lock();
efx_dissociate(efx);
dev_close(efx->net_dev);
efx_disable_interrupts(efx);
rtnl_unlock();
@ -2696,12 +2777,12 @@ static void efx_pci_remove(struct pci_dev *pci_dev)
* always appear within the first 512 bytes.
*/
#define SFC_VPD_LEN 512
static void efx_print_product_vpd(struct efx_nic *efx)
static void efx_probe_vpd_strings(struct efx_nic *efx)
{
struct pci_dev *dev = efx->pci_dev;
char vpd_data[SFC_VPD_LEN];
ssize_t vpd_size;
int i, j;
int ro_start, ro_size, i, j;
/* Get the vpd data from the device */
vpd_size = pci_read_vpd(dev, 0, sizeof(vpd_data), vpd_data);
@ -2711,14 +2792,15 @@ static void efx_print_product_vpd(struct efx_nic *efx)
}
/* Get the Read only section */
i = pci_vpd_find_tag(vpd_data, 0, vpd_size, PCI_VPD_LRDT_RO_DATA);
if (i < 0) {
ro_start = pci_vpd_find_tag(vpd_data, 0, vpd_size, PCI_VPD_LRDT_RO_DATA);
if (ro_start < 0) {
netif_err(efx, drv, efx->net_dev, "VPD Read-only not found\n");
return;
}
j = pci_vpd_lrdt_size(&vpd_data[i]);
i += PCI_VPD_LRDT_TAG_SIZE;
ro_size = pci_vpd_lrdt_size(&vpd_data[ro_start]);
j = ro_size;
i = ro_start + PCI_VPD_LRDT_TAG_SIZE;
if (i + j > vpd_size)
j = vpd_size - i;
@ -2738,6 +2820,27 @@ static void efx_print_product_vpd(struct efx_nic *efx)
netif_info(efx, drv, efx->net_dev,
"Part Number : %.*s\n", j, &vpd_data[i]);
i = ro_start + PCI_VPD_LRDT_TAG_SIZE;
j = ro_size;
i = pci_vpd_find_info_keyword(vpd_data, i, j, "SN");
if (i < 0) {
netif_err(efx, drv, efx->net_dev, "Serial number not found\n");
return;
}
j = pci_vpd_info_field_size(&vpd_data[i]);
i += PCI_VPD_INFO_FLD_HDR_SIZE;
if (i + j > vpd_size) {
netif_err(efx, drv, efx->net_dev, "Incomplete serial number\n");
return;
}
efx->vpd_sn = kmalloc(j + 1, GFP_KERNEL);
if (!efx->vpd_sn)
return;
snprintf(efx->vpd_sn, j + 1, "%s", &vpd_data[i]);
}
@ -2834,7 +2937,7 @@ static int efx_pci_probe(struct pci_dev *pci_dev,
netif_info(efx, probe, efx->net_dev,
"Solarflare NIC detected\n");
efx_print_product_vpd(efx);
efx_probe_vpd_strings(efx);
/* Set up basic I/O (BAR mappings etc) */
rc = efx_init_io(efx);

View File

@ -2247,6 +2247,8 @@ static int falcon_probe_nic(struct efx_nic *efx)
struct falcon_board *board;
int rc;
efx->primary = efx; /* only one usable function per controller */
/* Allocate storage for hardware specific data */
nic_data = kzalloc(sizeof(*nic_data), GFP_KERNEL);
if (!nic_data)

View File

@ -102,6 +102,10 @@ int efx_mcdi_init(struct efx_nic *efx)
netif_err(efx, probe, efx->net_dev,
"Host already registered with MCPU\n");
if (efx->mcdi->fn_flags &
(1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_PRIMARY))
efx->primary = efx;
return 0;
}
@ -1018,6 +1022,9 @@ void efx_mcdi_process_event(struct efx_channel *channel,
case MCDI_EVENT_CODE_PTP_PPS:
efx_ptp_event(efx, event);
break;
case MCDI_EVENT_CODE_PTP_TIME:
efx_time_sync_event(channel, event);
break;
case MCDI_EVENT_CODE_TX_FLUSH:
case MCDI_EVENT_CODE_RX_FLUSH:
/* Two flush events will be sent: one to the same event
@ -1132,13 +1139,27 @@ static int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating,
goto fail;
}
if (driver_operating) {
if (outlen >= MC_CMD_DRV_ATTACH_EXT_OUT_LEN) {
efx->mcdi->fn_flags =
MCDI_DWORD(outbuf,
DRV_ATTACH_EXT_OUT_FUNC_FLAGS);
} else {
/* Synthesise flags for Siena */
efx->mcdi->fn_flags =
1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_LINKCTRL |
1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_TRUSTED |
(efx_port_num(efx) == 0) <<
MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_PRIMARY;
}
}
/* We currently assume we have control of the external link
* and are completely trusted by firmware. Abort probing
* if that's not true for this function.
*/
if (driver_operating &&
outlen >= MC_CMD_DRV_ATTACH_EXT_OUT_LEN &&
(MCDI_DWORD(outbuf, DRV_ATTACH_EXT_OUT_FUNC_FLAGS) &
(efx->mcdi->fn_flags &
(1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_LINKCTRL |
1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_TRUSTED)) !=
(1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_LINKCTRL |

View File

@ -94,12 +94,14 @@ struct efx_mcdi_mtd_partition {
* struct efx_mcdi_data - extra state for NICs that implement MCDI
* @iface: Interface/protocol state
* @hwmon: Hardware monitor state
* @fn_flags: Flags for this function, as returned by %MC_CMD_DRV_ATTACH.
*/
struct efx_mcdi_data {
struct efx_mcdi_iface iface;
#ifdef CONFIG_SFC_MCDI_MON
struct efx_mcdi_mon hwmon;
#endif
u32 fn_flags;
};
#ifdef CONFIG_SFC_MCDI_MON

View File

@ -91,6 +91,7 @@
/* Forward declare Precision Time Protocol (PTP) support structure. */
struct efx_ptp_data;
struct hwtstamp_config;
struct efx_self_tests;
@ -368,6 +369,13 @@ enum efx_rx_alloc_method {
RX_ALLOC_METHOD_PAGE = 2,
};
enum efx_sync_events_state {
SYNC_EVENTS_DISABLED = 0,
SYNC_EVENTS_QUIESCENT,
SYNC_EVENTS_REQUESTED,
SYNC_EVENTS_VALID,
};
/**
* struct efx_channel - An Efx channel
*
@ -407,6 +415,9 @@ enum efx_rx_alloc_method {
* by __efx_rx_packet(), if @rx_pkt_n_frags != 0
* @rx_queue: RX queue for this channel
* @tx_queue: TX queues for this channel
* @sync_events_state: Current state of sync events on this channel
* @sync_timestamp_major: Major part of the last ptp sync event
* @sync_timestamp_minor: Minor part of the last ptp sync event
*/
struct efx_channel {
struct efx_nic *efx;
@ -445,6 +456,10 @@ struct efx_channel {
struct efx_rx_queue rx_queue;
struct efx_tx_queue tx_queue[EFX_TXQ_TYPES];
enum efx_sync_events_state sync_events_state;
u32 sync_timestamp_major;
u32 sync_timestamp_minor;
};
/**
@ -520,15 +535,6 @@ enum nic_state {
STATE_RECOVERY = 3, /* device recovering from PCI error */
};
/*
* Alignment of the skb->head which wraps a page-allocated RX buffer
*
* The skb allocated to wrap an rx_buffer can have this alignment. Since
* the data is memcpy'd from the rx_buf, it does not need to be equal to
* NET_IP_ALIGN.
*/
#define EFX_PAGE_SKB_ALIGN 2
/* Forward declaration */
struct efx_nic;
@ -651,6 +657,13 @@ struct vfdi_status;
* struct efx_nic - an Efx NIC
* @name: Device name (net device name or bus id before net device registered)
* @pci_dev: The PCI device
* @node: List node for maintaning primary/secondary function lists
* @primary: &struct efx_nic instance for the primary function of this
* controller. May be the same structure, and may be %NULL if no
* primary function is bound. Serialised by rtnl_lock.
* @secondary_list: List of &struct efx_nic instances for the secondary PCI
* functions of the controller, if this is for the primary function.
* Serialised by rtnl_lock.
* @type: Controller type attributes
* @legacy_irq: IRQ number
* @workqueue: Workqueue for port reconfigures and the HW monitor.
@ -694,6 +707,8 @@ struct vfdi_status;
* (valid only if @rx_prefix_size != 0; always negative)
* @rx_packet_len_offset: Offset of RX packet length from start of packet data
* (valid only for NICs that set %EFX_RX_PKT_PREFIX_LEN; always negative)
* @rx_packet_ts_offset: Offset of timestamp from start of packet data
* (valid only if channel->sync_timestamps_enabled; always negative)
* @rx_hash_key: Toeplitz hash key for RSS
* @rx_indir_table: Indirection table for RSS
* @rx_scatter: Scatter mode enabled for receives
@ -763,6 +778,7 @@ struct vfdi_status;
* @local_lock: Mutex protecting %local_addr_list and %local_page_list.
* @peer_work: Work item to broadcast peer addresses to VMs.
* @ptp_data: PTP state data
* @vpd_sn: Serial number read from VPD
* @monitor_work: Hardware monitor workitem
* @biu_lock: BIU (bus interface unit) lock
* @last_irq_cpu: Last CPU to handle a possible test interrupt. This
@ -777,6 +793,9 @@ struct efx_nic {
/* The following fields should be written very rarely */
char name[IFNAMSIZ];
struct list_head node;
struct efx_nic *primary;
struct list_head secondary_list;
struct pci_dev *pci_dev;
unsigned int port_num;
const struct efx_nic_type *type;
@ -828,6 +847,7 @@ struct efx_nic {
unsigned int rx_prefix_size;
int rx_packet_hash_offset;
int rx_packet_len_offset;
int rx_packet_ts_offset;
u8 rx_hash_key[40];
u32 rx_indir_table[128];
bool rx_scatter;
@ -911,6 +931,8 @@ struct efx_nic {
struct efx_ptp_data *ptp_data;
char *vpd_sn;
/* The following fields may be written more often */
struct delayed_work monitor_work ____cacheline_aligned_in_smp;
@ -1042,6 +1064,12 @@ struct efx_mtd_partition {
* @mtd_sync: Wait for write-back to complete on MTD partition. This
* also notifies the driver that a writer has finished using this
* partition.
* @ptp_write_host_time: Send host time to MC as part of sync protocol
* @ptp_set_ts_sync_events: Enable or disable sync events for inline RX
* timestamping, possibly only temporarily for the purposes of a reset.
* @ptp_set_ts_config: Set hardware timestamp configuration. The flags
* and tx_type will already have been validated but this operation
* must validate and update rx_filter.
* @revision: Hardware architecture revision
* @txd_ptr_tbl_base: TX descriptor ring base address
* @rxd_ptr_tbl_base: RX descriptor ring base address
@ -1051,6 +1079,7 @@ struct efx_mtd_partition {
* @max_dma_mask: Maximum possible DMA mask
* @rx_prefix_size: Size of RX prefix before packet data
* @rx_hash_offset: Offset of RX flow hash within prefix
* @rx_ts_offset: Offset of timestamp within prefix
* @rx_buffer_padding: Size of padding at end of RX packet
* @can_rx_scatter: NIC is able to scatter packets to multiple buffers
* @always_rx_scatter: NIC will always scatter packets to multiple buffers
@ -1060,6 +1089,7 @@ struct efx_mtd_partition {
* @offload_features: net_device feature flags for protocol offload
* features implemented in hardware
* @mcdi_max_ver: Maximum MCDI version supported
* @hwtstamp_filters: Mask of hardware timestamp filter types supported
*/
struct efx_nic_type {
unsigned int (*mem_map_size)(struct efx_nic *efx);
@ -1161,6 +1191,9 @@ struct efx_nic_type {
int (*mtd_sync)(struct mtd_info *mtd);
#endif
void (*ptp_write_host_time)(struct efx_nic *efx, u32 host_time);
int (*ptp_set_ts_sync_events)(struct efx_nic *efx, bool en, bool temp);
int (*ptp_set_ts_config)(struct efx_nic *efx,
struct hwtstamp_config *init);
int revision;
unsigned int txd_ptr_tbl_base;
@ -1171,6 +1204,7 @@ struct efx_nic_type {
u64 max_dma_mask;
unsigned int rx_prefix_size;
unsigned int rx_hash_offset;
unsigned int rx_ts_offset;
unsigned int rx_buffer_padding;
bool can_rx_scatter;
bool always_rx_scatter;
@ -1179,6 +1213,7 @@ struct efx_nic_type {
netdev_features_t offload_features;
int mcdi_max_ver;
unsigned int max_rx_ip_filters;
u32 hwtstamp_filters;
};
/**************************************************************************

View File

@ -561,8 +561,20 @@ int efx_ptp_set_ts_config(struct efx_nic *efx, struct ifreq *ifr);
int efx_ptp_get_ts_config(struct efx_nic *efx, struct ifreq *ifr);
void efx_ptp_get_ts_info(struct efx_nic *efx, struct ethtool_ts_info *ts_info);
bool efx_ptp_is_ptp_tx(struct efx_nic *efx, struct sk_buff *skb);
int efx_ptp_get_mode(struct efx_nic *efx);
int efx_ptp_change_mode(struct efx_nic *efx, bool enable_wanted,
unsigned int new_mode);
int efx_ptp_tx(struct efx_nic *efx, struct sk_buff *skb);
void efx_ptp_event(struct efx_nic *efx, efx_qword_t *ev);
void efx_time_sync_event(struct efx_channel *channel, efx_qword_t *ev);
void __efx_rx_skb_attach_timestamp(struct efx_channel *channel,
struct sk_buff *skb);
static inline void efx_rx_skb_attach_timestamp(struct efx_channel *channel,
struct sk_buff *skb)
{
if (channel->sync_events_state == SYNC_EVENTS_VALID)
__efx_rx_skb_attach_timestamp(channel, skb);
}
void efx_ptp_start_datapath(struct efx_nic *efx);
void efx_ptp_stop_datapath(struct efx_nic *efx);

View File

@ -62,7 +62,7 @@
#define SYNCHRONISATION_GRANULARITY_NS 200
/* Minimum permitted length of a (corrected) synchronisation time */
#define MIN_SYNCHRONISATION_NS 120
#define DEFAULT_MIN_SYNCHRONISATION_NS 120
/* Maximum permitted length of a (corrected) synchronisation time */
#define MAX_SYNCHRONISATION_NS 1000
@ -195,20 +195,20 @@ struct efx_ptp_event_rx {
/**
* struct efx_ptp_timeset - Synchronisation between host and MC
* @host_start: Host time immediately before hardware timestamp taken
* @seconds: Hardware timestamp, seconds
* @nanoseconds: Hardware timestamp, nanoseconds
* @major: Hardware timestamp, major
* @minor: Hardware timestamp, minor
* @host_end: Host time immediately after hardware timestamp taken
* @waitns: Number of nanoseconds between hardware timestamp being read and
* @wait: Number of NIC clock ticks between hardware timestamp being read and
* host end time being seen
* @window: Difference of host_end and host_start
* @valid: Whether this timeset is valid
*/
struct efx_ptp_timeset {
u32 host_start;
u32 seconds;
u32 nanoseconds;
u32 major;
u32 minor;
u32 host_end;
u32 waitns;
u32 wait;
u32 window; /* Derived: end - start, allowing for wrap */
};
@ -216,6 +216,8 @@ struct efx_ptp_timeset {
* struct efx_ptp_data - Precision Time Protocol (PTP) state
* @efx: The NIC context
* @channel: The PTP channel (Siena only)
* @rx_ts_inline: Flag for whether RX timestamps are inline (else they are
* separate events)
* @rxq: Receive queue (awaiting timestamps)
* @txq: Transmit queue
* @evt_list: List of MC receive events awaiting packets
@ -232,42 +234,33 @@ struct efx_ptp_timeset {
* @config: Current timestamp configuration
* @enabled: PTP operation enabled
* @mode: Mode in which PTP operating (PTP version)
* @time_format: Time format supported by this NIC
* @ns_to_nic_time: Function to convert from scalar nanoseconds to NIC time
* @nic_to_kernel_time: Function to convert from NIC to kernel time
* @min_synchronisation_ns: Minimum acceptable corrected sync window
* @ts_corrections.tx: Required driver correction of transmit timestamps
* @ts_corrections.rx: Required driver correction of receive timestamps
* @ts_corrections.pps_out: PPS output error (information only)
* @ts_corrections.pps_in: Required driver correction of PPS input timestamps
* @evt_frags: Partly assembled PTP events
* @evt_frag_idx: Current fragment number
* @evt_code: Last event code
* @start: Address at which MC indicates ready for synchronisation
* @host_time_pps: Host time at last PPS
* @last_sync_ns: Last number of nanoseconds between readings when synchronising
* @base_sync_ns: Number of nanoseconds for last synchronisation.
* @base_sync_valid: Whether base_sync_time is valid.
* @current_adjfreq: Current ppb adjustment.
* @phc_clock: Pointer to registered phc device
* @phc_clock: Pointer to registered phc device (if primary function)
* @phc_clock_info: Registration structure for phc device
* @pps_work: pps work task for handling pps events
* @pps_workwq: pps work queue
* @nic_ts_enabled: Flag indicating if NIC generated TS events are handled
* @txbuf: Buffer for use when transmitting (PTP) packets to MC (avoids
* allocations in main data path).
* @debug_ptp_dir: PTP debugfs directory
* @missed_rx_sync: Number of packets received without syncrhonisation.
* @good_syncs: Number of successful synchronisations.
* @no_time_syncs: Number of synchronisations with no good times.
* @bad_sync_durations: Number of synchronisations with bad durations.
* @bad_syncs: Number of failed synchronisations.
* @last_sync_time: Number of nanoseconds for last synchronisation.
* @sync_timeouts: Number of synchronisation timeouts
* @fast_syncs: Number of synchronisations requiring short delay
* @min_sync_delta: Minimum time between event and synchronisation
* @max_sync_delta: Maximum time between event and synchronisation
* @average_sync_delta: Average time between event and synchronisation.
* Modified moving average.
* @last_sync_delta: Last time between event and synchronisation
* @mc_stats: Context value for MC statistics
* @timeset: Last set of synchronisation statistics.
*/
struct efx_ptp_data {
struct efx_nic *efx;
struct efx_channel *channel;
bool rx_ts_inline;
struct sk_buff_head rxq;
struct sk_buff_head txq;
struct list_head evt_list;
@ -284,14 +277,22 @@ struct efx_ptp_data {
struct hwtstamp_config config;
bool enabled;
unsigned int mode;
unsigned int time_format;
void (*ns_to_nic_time)(s64 ns, u32 *nic_major, u32 *nic_minor);
ktime_t (*nic_to_kernel_time)(u32 nic_major, u32 nic_minor,
s32 correction);
unsigned int min_synchronisation_ns;
struct {
s32 tx;
s32 rx;
s32 pps_out;
s32 pps_in;
} ts_corrections;
efx_qword_t evt_frags[MAX_EVENT_FRAGS];
int evt_frag_idx;
int evt_code;
struct efx_buffer start;
struct pps_event_time host_time_pps;
unsigned last_sync_ns;
unsigned base_sync_ns;
bool base_sync_valid;
s64 current_adjfreq;
struct ptp_clock *phc_clock;
struct ptp_clock_info phc_clock_info;
@ -311,6 +312,169 @@ static int efx_phc_settime(struct ptp_clock_info *ptp,
static int efx_phc_enable(struct ptp_clock_info *ptp,
struct ptp_clock_request *request, int on);
/* For Siena platforms NIC time is s and ns */
static void efx_ptp_ns_to_s_ns(s64 ns, u32 *nic_major, u32 *nic_minor)
{
struct timespec ts = ns_to_timespec(ns);
*nic_major = ts.tv_sec;
*nic_minor = ts.tv_nsec;
}
static ktime_t efx_ptp_s_ns_to_ktime_correction(u32 nic_major, u32 nic_minor,
s32 correction)
{
ktime_t kt = ktime_set(nic_major, nic_minor);
if (correction >= 0)
kt = ktime_add_ns(kt, (u64)correction);
else
kt = ktime_sub_ns(kt, (u64)-correction);
return kt;
}
/* To convert from s27 format to ns we multiply then divide by a power of 2.
* For the conversion from ns to s27, the operation is also converted to a
* multiply and shift.
*/
#define S27_TO_NS_SHIFT (27)
#define NS_TO_S27_MULT (((1ULL << 63) + NSEC_PER_SEC / 2) / NSEC_PER_SEC)
#define NS_TO_S27_SHIFT (63 - S27_TO_NS_SHIFT)
#define S27_MINOR_MAX (1 << S27_TO_NS_SHIFT)
/* For Huntington platforms NIC time is in seconds and fractions of a second
* where the minor register only uses 27 bits in units of 2^-27s.
*/
static void efx_ptp_ns_to_s27(s64 ns, u32 *nic_major, u32 *nic_minor)
{
struct timespec ts = ns_to_timespec(ns);
u32 maj = ts.tv_sec;
u32 min = (u32)(((u64)ts.tv_nsec * NS_TO_S27_MULT +
(1ULL << (NS_TO_S27_SHIFT - 1))) >> NS_TO_S27_SHIFT);
/* The conversion can result in the minor value exceeding the maximum.
* In this case, round up to the next second.
*/
if (min >= S27_MINOR_MAX) {
min -= S27_MINOR_MAX;
maj++;
}
*nic_major = maj;
*nic_minor = min;
}
static inline ktime_t efx_ptp_s27_to_ktime(u32 nic_major, u32 nic_minor)
{
u32 ns = (u32)(((u64)nic_minor * NSEC_PER_SEC +
(1ULL << (S27_TO_NS_SHIFT - 1))) >> S27_TO_NS_SHIFT);
return ktime_set(nic_major, ns);
}
static ktime_t efx_ptp_s27_to_ktime_correction(u32 nic_major, u32 nic_minor,
s32 correction)
{
/* Apply the correction and deal with carry */
nic_minor += correction;
if ((s32)nic_minor < 0) {
nic_minor += S27_MINOR_MAX;
nic_major--;
} else if (nic_minor >= S27_MINOR_MAX) {
nic_minor -= S27_MINOR_MAX;
nic_major++;
}
return efx_ptp_s27_to_ktime(nic_major, nic_minor);
}
/* Get PTP attributes and set up time conversions */
static int efx_ptp_get_attributes(struct efx_nic *efx)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_GET_ATTRIBUTES_LEN);
MCDI_DECLARE_BUF(outbuf, MC_CMD_PTP_OUT_GET_ATTRIBUTES_LEN);
struct efx_ptp_data *ptp = efx->ptp_data;
int rc;
u32 fmt;
size_t out_len;
/* Get the PTP attributes. If the NIC doesn't support the operation we
* use the default format for compatibility with older NICs i.e.
* seconds and nanoseconds.
*/
MCDI_SET_DWORD(inbuf, PTP_IN_OP, MC_CMD_PTP_OP_GET_ATTRIBUTES);
MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0);
rc = efx_mcdi_rpc(efx, MC_CMD_PTP, inbuf, sizeof(inbuf),
outbuf, sizeof(outbuf), &out_len);
if (rc == 0)
fmt = MCDI_DWORD(outbuf, PTP_OUT_GET_ATTRIBUTES_TIME_FORMAT);
else if (rc == -EINVAL)
fmt = MC_CMD_PTP_OUT_GET_ATTRIBUTES_SECONDS_NANOSECONDS;
else
return rc;
if (fmt == MC_CMD_PTP_OUT_GET_ATTRIBUTES_SECONDS_27FRACTION) {
ptp->ns_to_nic_time = efx_ptp_ns_to_s27;
ptp->nic_to_kernel_time = efx_ptp_s27_to_ktime_correction;
} else if (fmt == MC_CMD_PTP_OUT_GET_ATTRIBUTES_SECONDS_NANOSECONDS) {
ptp->ns_to_nic_time = efx_ptp_ns_to_s_ns;
ptp->nic_to_kernel_time = efx_ptp_s_ns_to_ktime_correction;
} else {
return -ERANGE;
}
ptp->time_format = fmt;
/* MC_CMD_PTP_OP_GET_ATTRIBUTES is an extended version of an older
* operation MC_CMD_PTP_OP_GET_TIME_FORMAT that also returns a value
* to use for the minimum acceptable corrected synchronization window.
* If we have the extra information store it. For older firmware that
* does not implement the extended command use the default value.
*/
if (rc == 0 && out_len >= MC_CMD_PTP_OUT_GET_ATTRIBUTES_LEN)
ptp->min_synchronisation_ns =
MCDI_DWORD(outbuf,
PTP_OUT_GET_ATTRIBUTES_SYNC_WINDOW_MIN);
else
ptp->min_synchronisation_ns = DEFAULT_MIN_SYNCHRONISATION_NS;
return 0;
}
/* Get PTP timestamp corrections */
static int efx_ptp_get_timestamp_corrections(struct efx_nic *efx)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_GET_TIMESTAMP_CORRECTIONS_LEN);
MCDI_DECLARE_BUF(outbuf, MC_CMD_PTP_OUT_GET_TIMESTAMP_CORRECTIONS_LEN);
int rc;
/* Get the timestamp corrections from the NIC. If this operation is
* not supported (older NICs) then no correction is required.
*/
MCDI_SET_DWORD(inbuf, PTP_IN_OP,
MC_CMD_PTP_OP_GET_TIMESTAMP_CORRECTIONS);
MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0);
rc = efx_mcdi_rpc(efx, MC_CMD_PTP, inbuf, sizeof(inbuf),
outbuf, sizeof(outbuf), NULL);
if (rc == 0) {
efx->ptp_data->ts_corrections.tx = MCDI_DWORD(outbuf,
PTP_OUT_GET_TIMESTAMP_CORRECTIONS_TRANSMIT);
efx->ptp_data->ts_corrections.rx = MCDI_DWORD(outbuf,
PTP_OUT_GET_TIMESTAMP_CORRECTIONS_RECEIVE);
efx->ptp_data->ts_corrections.pps_out = MCDI_DWORD(outbuf,
PTP_OUT_GET_TIMESTAMP_CORRECTIONS_PPS_OUT);
efx->ptp_data->ts_corrections.pps_in = MCDI_DWORD(outbuf,
PTP_OUT_GET_TIMESTAMP_CORRECTIONS_PPS_IN);
} else if (rc == -EINVAL) {
efx->ptp_data->ts_corrections.tx = 0;
efx->ptp_data->ts_corrections.rx = 0;
efx->ptp_data->ts_corrections.pps_out = 0;
efx->ptp_data->ts_corrections.pps_in = 0;
} else {
return rc;
}
return 0;
}
/* Enable MCDI PTP support. */
static int efx_ptp_enable(struct efx_nic *efx)
{
@ -423,11 +587,10 @@ static void efx_ptp_read_timeset(MCDI_DECLARE_STRUCT_PTR(data),
unsigned start_ns, end_ns;
timeset->host_start = MCDI_DWORD(data, PTP_OUT_SYNCHRONIZE_HOSTSTART);
timeset->seconds = MCDI_DWORD(data, PTP_OUT_SYNCHRONIZE_SECONDS);
timeset->nanoseconds = MCDI_DWORD(data,
PTP_OUT_SYNCHRONIZE_NANOSECONDS);
timeset->major = MCDI_DWORD(data, PTP_OUT_SYNCHRONIZE_MAJOR);
timeset->minor = MCDI_DWORD(data, PTP_OUT_SYNCHRONIZE_MINOR);
timeset->host_end = MCDI_DWORD(data, PTP_OUT_SYNCHRONIZE_HOSTEND),
timeset->waitns = MCDI_DWORD(data, PTP_OUT_SYNCHRONIZE_WAITNS);
timeset->wait = MCDI_DWORD(data, PTP_OUT_SYNCHRONIZE_WAITNS);
/* Ignore seconds */
start_ns = timeset->host_start & MC_NANOSECOND_MASK;
@ -456,62 +619,68 @@ efx_ptp_process_times(struct efx_nic *efx, MCDI_DECLARE_STRUCT_PTR(synch_buf),
MCDI_VAR_ARRAY_LEN(response_length,
PTP_OUT_SYNCHRONIZE_TIMESET);
unsigned i;
unsigned total;
unsigned ngood = 0;
unsigned last_good = 0;
struct efx_ptp_data *ptp = efx->ptp_data;
u32 last_sec;
u32 start_sec;
struct timespec delta;
ktime_t mc_time;
if (number_readings == 0)
return -EAGAIN;
/* Read the set of results and increment stats for any results that
* appera to be erroneous.
/* Read the set of results and find the last good host-MC
* synchronization result. The MC times when it finishes reading the
* host time so the corrected window time should be fairly constant
* for a given platform.
*/
for (i = 0; i < number_readings; i++) {
s32 window, corrected;
struct timespec wait;
efx_ptp_read_timeset(
MCDI_ARRAY_STRUCT_PTR(synch_buf,
PTP_OUT_SYNCHRONIZE_TIMESET, i),
&ptp->timeset[i]);
}
/* Find the last good host-MC synchronization result. The MC times
* when it finishes reading the host time so the corrected window time
* should be fairly constant for a given platform.
*/
total = 0;
for (i = 0; i < number_readings; i++)
if (ptp->timeset[i].window > ptp->timeset[i].waitns) {
unsigned win;
wait = ktime_to_timespec(
ptp->nic_to_kernel_time(0, ptp->timeset[i].wait, 0));
window = ptp->timeset[i].window;
corrected = window - wait.tv_nsec;
win = ptp->timeset[i].window - ptp->timeset[i].waitns;
if (win >= MIN_SYNCHRONISATION_NS &&
win < MAX_SYNCHRONISATION_NS) {
total += ptp->timeset[i].window;
ngood++;
last_good = i;
}
/* We expect the uncorrected synchronization window to be at
* least as large as the interval between host start and end
* times. If it is smaller than this then this is mostly likely
* to be a consequence of the host's time being adjusted.
* Check that the corrected sync window is in a reasonable
* range. If it is out of range it is likely to be because an
* interrupt or other delay occurred between reading the system
* time and writing it to MC memory.
*/
if (window >= SYNCHRONISATION_GRANULARITY_NS &&
corrected < MAX_SYNCHRONISATION_NS &&
corrected >= ptp->min_synchronisation_ns) {
ngood++;
last_good = i;
}
}
if (ngood == 0) {
netif_warn(efx, drv, efx->net_dev,
"PTP no suitable synchronisations %dns\n",
ptp->base_sync_ns);
"PTP no suitable synchronisations\n");
return -EAGAIN;
}
/* Average minimum this synchronisation */
ptp->last_sync_ns = DIV_ROUND_UP(total, ngood);
if (!ptp->base_sync_valid || (ptp->last_sync_ns < ptp->base_sync_ns)) {
ptp->base_sync_valid = true;
ptp->base_sync_ns = ptp->last_sync_ns;
}
/* Convert the NIC time into kernel time. No correction is required-
* this time is the output of a firmware process.
*/
mc_time = ptp->nic_to_kernel_time(ptp->timeset[last_good].major,
ptp->timeset[last_good].minor, 0);
/* Calculate delay from actual PPS to last_time */
delta.tv_nsec =
ptp->timeset[last_good].nanoseconds +
delta = ktime_to_timespec(mc_time);
delta.tv_nsec +=
last_time->ts_real.tv_nsec -
(ptp->timeset[last_good].host_start & MC_NANOSECOND_MASK);
@ -621,9 +790,10 @@ static int efx_ptp_xmit_skb(struct efx_nic *efx, struct sk_buff *skb)
goto fail;
memset(&timestamps, 0, sizeof(timestamps));
timestamps.hwtstamp = ktime_set(
MCDI_DWORD(txtime, PTP_OUT_TRANSMIT_SECONDS),
MCDI_DWORD(txtime, PTP_OUT_TRANSMIT_NANOSECONDS));
timestamps.hwtstamp = ptp_data->nic_to_kernel_time(
MCDI_DWORD(txtime, PTP_OUT_TRANSMIT_MAJOR),
MCDI_DWORD(txtime, PTP_OUT_TRANSMIT_MINOR),
ptp_data->ts_corrections.tx);
skb_tstamp_tx(skb, &timestamps);
@ -641,6 +811,9 @@ static void efx_ptp_drop_time_expired_events(struct efx_nic *efx)
struct list_head *cursor;
struct list_head *next;
if (ptp->rx_ts_inline)
return;
/* Drop time-expired events */
spin_lock_bh(&ptp->evt_lock);
if (!list_empty(&ptp->evt_list)) {
@ -674,6 +847,8 @@ static enum ptp_packet_state efx_ptp_match_rx(struct efx_nic *efx,
struct efx_ptp_match *match;
enum ptp_packet_state rc = PTP_PACKET_STATE_UNMATCHED;
WARN_ON_ONCE(ptp->rx_ts_inline);
spin_lock_bh(&ptp->evt_lock);
evts_waiting = !list_empty(&ptp->evt_list);
spin_unlock_bh(&ptp->evt_lock);
@ -715,13 +890,10 @@ static enum ptp_packet_state efx_ptp_match_rx(struct efx_nic *efx,
/* Process any queued receive events and corresponding packets
*
* q is returned with all the packets that are ready for delivery.
* true is returned if at least one of those packets requires
* synchronisation.
*/
static bool efx_ptp_process_events(struct efx_nic *efx, struct sk_buff_head *q)
static void efx_ptp_process_events(struct efx_nic *efx, struct sk_buff_head *q)
{
struct efx_ptp_data *ptp = efx->ptp_data;
bool rc = false;
struct sk_buff *skb;
while ((skb = skb_dequeue(&ptp->rxq))) {
@ -732,7 +904,6 @@ static bool efx_ptp_process_events(struct efx_nic *efx, struct sk_buff_head *q)
__skb_queue_tail(q, skb);
} else if (efx_ptp_match_rx(efx, skb) ==
PTP_PACKET_STATE_MATCHED) {
rc = true;
__skb_queue_tail(q, skb);
} else if (time_after(jiffies, match->expiry)) {
match->state = PTP_PACKET_STATE_TIMED_OUT;
@ -746,8 +917,6 @@ static bool efx_ptp_process_events(struct efx_nic *efx, struct sk_buff_head *q)
break;
}
}
return rc;
}
/* Complete processing of a received packet */
@ -896,8 +1065,6 @@ static void efx_ptp_pps_worker(struct work_struct *work)
ptp_clock_event(ptp->phc_clock, &ptp_evt);
}
/* Process any pending transmissions and timestamp any received packets.
*/
static void efx_ptp_worker(struct work_struct *work)
{
struct efx_ptp_data *ptp_data =
@ -915,17 +1082,30 @@ static void efx_ptp_worker(struct work_struct *work)
efx_ptp_drop_time_expired_events(efx);
__skb_queue_head_init(&tempq);
if (efx_ptp_process_events(efx, &tempq) ||
!skb_queue_empty(&ptp_data->txq)) {
efx_ptp_process_events(efx, &tempq);
while ((skb = skb_dequeue(&ptp_data->txq)))
efx_ptp_xmit_skb(efx, skb);
}
while ((skb = skb_dequeue(&ptp_data->txq)))
efx_ptp_xmit_skb(efx, skb);
while ((skb = __skb_dequeue(&tempq)))
efx_ptp_process_rx(efx, skb);
}
static const struct ptp_clock_info efx_phc_clock_info = {
.owner = THIS_MODULE,
.name = "sfc",
.max_adj = MAX_PPB,
.n_alarm = 0,
.n_ext_ts = 0,
.n_per_out = 0,
.pps = 1,
.adjfreq = efx_phc_adjfreq,
.adjtime = efx_phc_adjtime,
.gettime = efx_phc_gettime,
.settime = efx_phc_settime,
.enable = efx_phc_enable,
};
/* Initialise PTP state. */
int efx_ptp_probe(struct efx_nic *efx, struct efx_channel *channel)
{
@ -940,6 +1120,7 @@ int efx_ptp_probe(struct efx_nic *efx, struct efx_channel *channel)
ptp->efx = efx;
ptp->channel = channel;
ptp->rx_ts_inline = efx_nic_rev(efx) >= EFX_REV_HUNT_A0;
rc = efx_nic_alloc_buffer(efx, &ptp->start, sizeof(int), GFP_KERNEL);
if (rc != 0)
@ -964,33 +1145,32 @@ int efx_ptp_probe(struct efx_nic *efx, struct efx_channel *channel)
list_add(&ptp->rx_evts[pos].link, &ptp->evt_free_list);
ptp->evt_overflow = false;
ptp->phc_clock_info.owner = THIS_MODULE;
snprintf(ptp->phc_clock_info.name,
sizeof(ptp->phc_clock_info.name),
"%pm", efx->net_dev->perm_addr);
ptp->phc_clock_info.max_adj = MAX_PPB;
ptp->phc_clock_info.n_alarm = 0;
ptp->phc_clock_info.n_ext_ts = 0;
ptp->phc_clock_info.n_per_out = 0;
ptp->phc_clock_info.pps = 1;
ptp->phc_clock_info.adjfreq = efx_phc_adjfreq;
ptp->phc_clock_info.adjtime = efx_phc_adjtime;
ptp->phc_clock_info.gettime = efx_phc_gettime;
ptp->phc_clock_info.settime = efx_phc_settime;
ptp->phc_clock_info.enable = efx_phc_enable;
ptp->phc_clock = ptp_clock_register(&ptp->phc_clock_info,
&efx->pci_dev->dev);
if (IS_ERR(ptp->phc_clock)) {
rc = PTR_ERR(ptp->phc_clock);
/* Get the NIC PTP attributes and set up time conversions */
rc = efx_ptp_get_attributes(efx);
if (rc < 0)
goto fail3;
}
INIT_WORK(&ptp->pps_work, efx_ptp_pps_worker);
ptp->pps_workwq = create_singlethread_workqueue("sfc_pps");
if (!ptp->pps_workwq) {
rc = -ENOMEM;
goto fail4;
/* Get the timestamp corrections */
rc = efx_ptp_get_timestamp_corrections(efx);
if (rc < 0)
goto fail3;
if (efx->mcdi->fn_flags &
(1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_PRIMARY)) {
ptp->phc_clock_info = efx_phc_clock_info;
ptp->phc_clock = ptp_clock_register(&ptp->phc_clock_info,
&efx->pci_dev->dev);
if (IS_ERR(ptp->phc_clock)) {
rc = PTR_ERR(ptp->phc_clock);
goto fail3;
}
INIT_WORK(&ptp->pps_work, efx_ptp_pps_worker);
ptp->pps_workwq = create_singlethread_workqueue("sfc_pps");
if (!ptp->pps_workwq) {
rc = -ENOMEM;
goto fail4;
}
}
ptp->nic_ts_enabled = false;
@ -1039,10 +1219,12 @@ void efx_ptp_remove(struct efx_nic *efx)
skb_queue_purge(&efx->ptp_data->rxq);
skb_queue_purge(&efx->ptp_data->txq);
ptp_clock_unregister(efx->ptp_data->phc_clock);
if (efx->ptp_data->phc_clock) {
destroy_workqueue(efx->ptp_data->pps_workwq);
ptp_clock_unregister(efx->ptp_data->phc_clock);
}
destroy_workqueue(efx->ptp_data->workwq);
destroy_workqueue(efx->ptp_data->pps_workwq);
efx_nic_free_buffer(efx, &efx->ptp_data->start);
kfree(efx->ptp_data);
@ -1133,14 +1315,8 @@ static bool efx_ptp_rx(struct efx_channel *channel, struct sk_buff *skb)
/* Does this packet require timestamping? */
if (ntohs(*(__be16 *)&skb->data[PTP_DPORT_OFFSET]) == PTP_EVENT_PORT) {
struct skb_shared_hwtstamps *timestamps;
match->state = PTP_PACKET_STATE_UNMATCHED;
/* Clear all timestamps held: filled in later */
timestamps = skb_hwtstamps(skb);
memset(timestamps, 0, sizeof(*timestamps));
/* We expect the sequence number to be in the same position in
* the packet for PTP V1 and V2
*/
@ -1185,8 +1361,13 @@ int efx_ptp_tx(struct efx_nic *efx, struct sk_buff *skb)
return NETDEV_TX_OK;
}
static int efx_ptp_change_mode(struct efx_nic *efx, bool enable_wanted,
unsigned int new_mode)
int efx_ptp_get_mode(struct efx_nic *efx)
{
return efx->ptp_data->mode;
}
int efx_ptp_change_mode(struct efx_nic *efx, bool enable_wanted,
unsigned int new_mode)
{
if ((enable_wanted != efx->ptp_data->enabled) ||
(enable_wanted && (efx->ptp_data->mode != new_mode))) {
@ -1230,8 +1411,6 @@ static int efx_ptp_change_mode(struct efx_nic *efx, bool enable_wanted,
static int efx_ptp_ts_init(struct efx_nic *efx, struct hwtstamp_config *init)
{
bool enable_wanted = false;
unsigned int new_mode;
int rc;
if (init->flags)
@ -1241,63 +1420,20 @@ static int efx_ptp_ts_init(struct efx_nic *efx, struct hwtstamp_config *init)
(init->tx_type != HWTSTAMP_TX_ON))
return -ERANGE;
new_mode = efx->ptp_data->mode;
/* Determine whether any PTP HW operations are required */
switch (init->rx_filter) {
case HWTSTAMP_FILTER_NONE:
break;
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
init->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
new_mode = MC_CMD_PTP_MODE_V1;
enable_wanted = true;
break;
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
/* Although these three are accepted only IPV4 packets will be
* timestamped
*/
init->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
new_mode = MC_CMD_PTP_MODE_V2_ENHANCED;
enable_wanted = true;
break;
case HWTSTAMP_FILTER_PTP_V2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
/* Non-IP + IPv6 timestamping not supported */
return -ERANGE;
break;
default:
return -ERANGE;
}
if (init->tx_type != HWTSTAMP_TX_OFF)
enable_wanted = true;
/* Old versions of the firmware do not support the improved
* UUID filtering option (SF bug 33070). If the firmware does
* not accept the enhanced mode, fall back to the standard PTP
* v2 UUID filtering.
*/
rc = efx_ptp_change_mode(efx, enable_wanted, new_mode);
if ((rc != 0) && (new_mode == MC_CMD_PTP_MODE_V2_ENHANCED))
rc = efx_ptp_change_mode(efx, enable_wanted, MC_CMD_PTP_MODE_V2);
if (rc != 0)
rc = efx->type->ptp_set_ts_config(efx, init);
if (rc)
return rc;
efx->ptp_data->config = *init;
return 0;
}
void efx_ptp_get_ts_info(struct efx_nic *efx, struct ethtool_ts_info *ts_info)
{
struct efx_ptp_data *ptp = efx->ptp_data;
struct efx_nic *primary = efx->primary;
ASSERT_RTNL();
if (!ptp)
return;
@ -1305,15 +1441,11 @@ void efx_ptp_get_ts_info(struct efx_nic *efx, struct ethtool_ts_info *ts_info)
ts_info->so_timestamping |= (SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE);
ts_info->phc_index = ptp_clock_index(ptp->phc_clock);
if (primary && primary->ptp_data && primary->ptp_data->phc_clock)
ts_info->phc_index =
ptp_clock_index(primary->ptp_data->phc_clock);
ts_info->tx_types = 1 << HWTSTAMP_TX_OFF | 1 << HWTSTAMP_TX_ON;
ts_info->rx_filters = (1 << HWTSTAMP_FILTER_NONE |
1 << HWTSTAMP_FILTER_PTP_V1_L4_EVENT |
1 << HWTSTAMP_FILTER_PTP_V1_L4_SYNC |
1 << HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ |
1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT |
1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC |
1 << HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ);
ts_info->rx_filters = ptp->efx->type->hwtstamp_filters;
}
int efx_ptp_set_ts_config(struct efx_nic *efx, struct ifreq *ifr)
@ -1364,6 +1496,9 @@ static void ptp_event_rx(struct efx_nic *efx, struct efx_ptp_data *ptp)
{
struct efx_ptp_event_rx *evt = NULL;
if (WARN_ON_ONCE(ptp->rx_ts_inline))
return;
if (ptp->evt_frag_idx != 3) {
ptp_event_failure(efx, 3);
return;
@ -1382,9 +1517,10 @@ static void ptp_event_rx(struct efx_nic *efx, struct efx_ptp_data *ptp)
MCDI_EVENT_SRC) << 8) |
(EFX_QWORD_FIELD(ptp->evt_frags[0],
MCDI_EVENT_SRC) << 16));
evt->hwtimestamp = ktime_set(
evt->hwtimestamp = efx->ptp_data->nic_to_kernel_time(
EFX_QWORD_FIELD(ptp->evt_frags[0], MCDI_EVENT_DATA),
EFX_QWORD_FIELD(ptp->evt_frags[1], MCDI_EVENT_DATA));
EFX_QWORD_FIELD(ptp->evt_frags[1], MCDI_EVENT_DATA),
ptp->ts_corrections.rx);
evt->expiry = jiffies + msecs_to_jiffies(PKT_EVENT_LIFETIME_MS);
list_add_tail(&evt->link, &ptp->evt_list);
@ -1459,6 +1595,93 @@ void efx_ptp_event(struct efx_nic *efx, efx_qword_t *ev)
}
}
void efx_time_sync_event(struct efx_channel *channel, efx_qword_t *ev)
{
channel->sync_timestamp_major = MCDI_EVENT_FIELD(*ev, PTP_TIME_MAJOR);
channel->sync_timestamp_minor =
MCDI_EVENT_FIELD(*ev, PTP_TIME_MINOR_26_19) << 19;
/* if sync events have been disabled then we want to silently ignore
* this event, so throw away result.
*/
(void) cmpxchg(&channel->sync_events_state, SYNC_EVENTS_REQUESTED,
SYNC_EVENTS_VALID);
}
/* make some assumptions about the time representation rather than abstract it,
* since we currently only support one type of inline timestamping and only on
* EF10.
*/
#define MINOR_TICKS_PER_SECOND 0x8000000
/* Fuzz factor for sync events to be out of order with RX events */
#define FUZZ (MINOR_TICKS_PER_SECOND / 10)
#define EXPECTED_SYNC_EVENTS_PER_SECOND 4
static inline u32 efx_rx_buf_timestamp_minor(struct efx_nic *efx, const u8 *eh)
{
#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
return __le32_to_cpup((const __le32 *)(eh + efx->rx_packet_ts_offset));
#else
const u8 *data = eh + efx->rx_packet_ts_offset;
return (u32)data[0] |
(u32)data[1] << 8 |
(u32)data[2] << 16 |
(u32)data[3] << 24;
#endif
}
void __efx_rx_skb_attach_timestamp(struct efx_channel *channel,
struct sk_buff *skb)
{
struct efx_nic *efx = channel->efx;
u32 pkt_timestamp_major, pkt_timestamp_minor;
u32 diff, carry;
struct skb_shared_hwtstamps *timestamps;
pkt_timestamp_minor = (efx_rx_buf_timestamp_minor(efx,
skb_mac_header(skb)) +
(u32) efx->ptp_data->ts_corrections.rx) &
(MINOR_TICKS_PER_SECOND - 1);
/* get the difference between the packet and sync timestamps,
* modulo one second
*/
diff = (pkt_timestamp_minor - channel->sync_timestamp_minor) &
(MINOR_TICKS_PER_SECOND - 1);
/* do we roll over a second boundary and need to carry the one? */
carry = channel->sync_timestamp_minor + diff > MINOR_TICKS_PER_SECOND ?
1 : 0;
if (diff <= MINOR_TICKS_PER_SECOND / EXPECTED_SYNC_EVENTS_PER_SECOND +
FUZZ) {
/* packet is ahead of the sync event by a quarter of a second or
* less (allowing for fuzz)
*/
pkt_timestamp_major = channel->sync_timestamp_major + carry;
} else if (diff >= MINOR_TICKS_PER_SECOND - FUZZ) {
/* packet is behind the sync event but within the fuzz factor.
* This means the RX packet and sync event crossed as they were
* placed on the event queue, which can sometimes happen.
*/
pkt_timestamp_major = channel->sync_timestamp_major - 1 + carry;
} else {
/* it's outside tolerance in both directions. this might be
* indicative of us missing sync events for some reason, so
* we'll call it an error rather than risk giving a bogus
* timestamp.
*/
netif_vdbg(efx, drv, efx->net_dev,
"packet timestamp %x too far from sync event %x:%x\n",
pkt_timestamp_minor, channel->sync_timestamp_major,
channel->sync_timestamp_minor);
return;
}
/* attach the timestamps to the skb */
timestamps = skb_hwtstamps(skb);
timestamps->hwtstamp =
efx_ptp_s27_to_ktime(pkt_timestamp_major, pkt_timestamp_minor);
}
static int efx_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta)
{
struct efx_ptp_data *ptp_data = container_of(ptp,
@ -1494,18 +1717,20 @@ static int efx_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta)
static int efx_phc_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
u32 nic_major, nic_minor;
struct efx_ptp_data *ptp_data = container_of(ptp,
struct efx_ptp_data,
phc_clock_info);
struct efx_nic *efx = ptp_data->efx;
struct timespec delta_ts = ns_to_timespec(delta);
MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_ADJUST_LEN);
efx->ptp_data->ns_to_nic_time(delta, &nic_major, &nic_minor);
MCDI_SET_DWORD(inbuf, PTP_IN_OP, MC_CMD_PTP_OP_ADJUST);
MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0);
MCDI_SET_QWORD(inbuf, PTP_IN_ADJUST_FREQ, ptp_data->current_adjfreq);
MCDI_SET_DWORD(inbuf, PTP_IN_ADJUST_SECONDS, (u32)delta_ts.tv_sec);
MCDI_SET_DWORD(inbuf, PTP_IN_ADJUST_NANOSECONDS, (u32)delta_ts.tv_nsec);
MCDI_SET_DWORD(inbuf, PTP_IN_ADJUST_MAJOR, nic_major);
MCDI_SET_DWORD(inbuf, PTP_IN_ADJUST_MINOR, nic_minor);
return efx_mcdi_rpc(efx, MC_CMD_PTP, inbuf, sizeof(inbuf),
NULL, 0, NULL);
}
@ -1519,6 +1744,7 @@ static int efx_phc_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_READ_NIC_TIME_LEN);
MCDI_DECLARE_BUF(outbuf, MC_CMD_PTP_OUT_READ_NIC_TIME_LEN);
int rc;
ktime_t kt;
MCDI_SET_DWORD(inbuf, PTP_IN_OP, MC_CMD_PTP_OP_READ_NIC_TIME);
MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0);
@ -1528,8 +1754,10 @@ static int efx_phc_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
if (rc != 0)
return rc;
ts->tv_sec = MCDI_DWORD(outbuf, PTP_OUT_READ_NIC_TIME_SECONDS);
ts->tv_nsec = MCDI_DWORD(outbuf, PTP_OUT_READ_NIC_TIME_NANOSECONDS);
kt = ptp_data->nic_to_kernel_time(
MCDI_DWORD(outbuf, PTP_OUT_READ_NIC_TIME_MAJOR),
MCDI_DWORD(outbuf, PTP_OUT_READ_NIC_TIME_MINOR), 0);
*ts = ktime_to_timespec(kt);
return 0;
}
@ -1595,9 +1823,15 @@ void efx_ptp_start_datapath(struct efx_nic *efx)
{
if (efx_ptp_restart(efx))
netif_err(efx, drv, efx->net_dev, "Failed to restart PTP.\n");
/* re-enable timestamping if it was previously enabled */
if (efx->type->ptp_set_ts_sync_events)
efx->type->ptp_set_ts_sync_events(efx, true, true);
}
void efx_ptp_stop_datapath(struct efx_nic *efx)
{
/* temporarily disable timestamping */
if (efx->type->ptp_set_ts_sync_events)
efx->type->ptp_set_ts_sync_events(efx, false, true);
efx_ptp_stop(efx);
}

View File

@ -476,14 +476,18 @@ static struct sk_buff *efx_rx_mk_skb(struct efx_channel *channel,
struct sk_buff *skb;
/* Allocate an SKB to store the headers */
skb = netdev_alloc_skb(efx->net_dev, hdr_len + EFX_PAGE_SKB_ALIGN);
skb = netdev_alloc_skb(efx->net_dev,
efx->rx_ip_align + efx->rx_prefix_size +
hdr_len);
if (unlikely(skb == NULL))
return NULL;
EFX_BUG_ON_PARANOID(rx_buf->len < hdr_len);
skb_reserve(skb, EFX_PAGE_SKB_ALIGN);
memcpy(__skb_put(skb, hdr_len), eh, hdr_len);
memcpy(skb->data + efx->rx_ip_align, eh - efx->rx_prefix_size,
efx->rx_prefix_size + hdr_len);
skb_reserve(skb, efx->rx_ip_align + efx->rx_prefix_size);
__skb_put(skb, hdr_len);
/* Append the remaining page(s) onto the frag list */
if (rx_buf->len > hdr_len) {
@ -620,6 +624,8 @@ static void efx_rx_deliver(struct efx_channel *channel, u8 *eh,
if (likely(rx_buf->flags & EFX_RX_PKT_CSUMMED))
skb->ip_summed = CHECKSUM_UNNECESSARY;
efx_rx_skb_attach_timestamp(channel, skb);
if (channel->type->receive_skb)
if (channel->type->receive_skb(channel, skb))
return;

View File

@ -116,6 +116,54 @@ out:
return rc ? rc : rc2;
}
/**************************************************************************
*
* PTP
*
**************************************************************************
*/
static void siena_ptp_write_host_time(struct efx_nic *efx, u32 host_time)
{
_efx_writed(efx, cpu_to_le32(host_time),
FR_CZ_MC_TREG_SMEM + MC_SMEM_P0_PTP_TIME_OFST);
}
static int siena_ptp_set_ts_config(struct efx_nic *efx,
struct hwtstamp_config *init)
{
int rc;
switch (init->rx_filter) {
case HWTSTAMP_FILTER_NONE:
/* if TX timestamping is still requested then leave PTP on */
return efx_ptp_change_mode(efx,
init->tx_type != HWTSTAMP_TX_OFF,
efx_ptp_get_mode(efx));
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
init->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
return efx_ptp_change_mode(efx, true, MC_CMD_PTP_MODE_V1);
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
init->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
rc = efx_ptp_change_mode(efx, true,
MC_CMD_PTP_MODE_V2_ENHANCED);
/* bug 33070 - old versions of the firmware do not support the
* improved UUID filtering option. Similarly old versions of the
* application do not expect it to be enabled. If the firmware
* does not accept the enhanced mode, fall back to the standard
* PTP v2 UUID filtering. */
if (rc != 0)
rc = efx_ptp_change_mode(efx, true, MC_CMD_PTP_MODE_V2);
return rc;
default:
return -ERANGE;
}
}
/**************************************************************************
*
* Device reset
@ -837,19 +885,6 @@ fail:
#endif /* CONFIG_SFC_MTD */
/**************************************************************************
*
* PTP
*
**************************************************************************
*/
static void siena_ptp_write_host_time(struct efx_nic *efx, u32 host_time)
{
_efx_writed(efx, cpu_to_le32(host_time),
FR_CZ_MC_TREG_SMEM + MC_SMEM_P0_PTP_TIME_OFST);
}
/**************************************************************************
*
* Revision-dependent attributes used by efx.c and nic.c
@ -942,6 +977,7 @@ const struct efx_nic_type siena_a0_nic_type = {
.mtd_sync = efx_mcdi_mtd_sync,
#endif
.ptp_write_host_time = siena_ptp_write_host_time,
.ptp_set_ts_config = siena_ptp_set_ts_config,
.revision = EFX_REV_SIENA_A0,
.txd_ptr_tbl_base = FR_BZ_TX_DESC_PTR_TBL,
@ -960,4 +996,11 @@ const struct efx_nic_type siena_a0_nic_type = {
NETIF_F_RXHASH | NETIF_F_NTUPLE),
.mcdi_max_ver = 1,
.max_rx_ip_filters = FR_BZ_RX_FILTER_TBL0_ROWS,
.hwtstamp_filters = (1 << HWTSTAMP_FILTER_NONE |
1 << HWTSTAMP_FILTER_PTP_V1_L4_EVENT |
1 << HWTSTAMP_FILTER_PTP_V1_L4_SYNC |
1 << HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ |
1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT |
1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC |
1 << HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ),
};