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:
commit
7ed2a0d0d4
10 changed files with 796 additions and 216 deletions
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 |
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**************************************************************************
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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(×tamps, 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, ×tamps);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -116,6 +116,54 @@ static int siena_test_chip(struct efx_nic *efx, struct efx_self_tests *tests)
|
|||
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 @@ static int siena_mtd_probe(struct efx_nic *efx)
|
|||
|
||||
#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),
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue