blob: 4e5811a3ebcdb695e614f8ba90b39f9ece97d920 [file] [log] [blame]
/*
**************************************************************************
* Copyright (c) 2014-2020, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
/*
* nss_phy_if.c
* NSS physical interface functions
*/
#include "nss_tx_rx_common.h"
#include "nss_tstamp.h"
#if defined(NSS_HAL_IPQ807x_SUPPORT) || defined(NSS_HAL_IPQ60XX_SUPPORT)
#include <nss_dp_api_if.h>
#endif
#define NSS_PHYS_IF_TX_TIMEOUT 3000 /* 3 Seconds */
/*
* NSS phys_if modes
*/
#define NSS_PHYS_IF_MODE0 0 /* phys_if mode 0 */
#define NSS_PHYS_IF_MODE1 1 /* phys_if mode 1 */
#define NSS_PHYS_IF_MODE2 2 /* phys_if mode 2 */
/*
* Private data structure for phys_if interface
*/
static struct nss_phys_if_pvt {
struct semaphore sem;
struct completion complete;
int response;
} phif;
static int nss_phys_if_sem_init_done;
/*
* nss_phys_if_update_driver_stats()
* Snoop the extended message and update driver statistics.
*/
static void nss_phys_if_update_driver_stats(struct nss_ctx_instance *nss_ctx, uint32_t id, struct nss_phys_if_stats *stats)
{
struct nss_top_instance *nss_top = nss_ctx->nss_top;
uint64_t *top_stats = &(nss_top->stats_gmac[id][0]);
spin_lock_bh(&nss_top->stats_lock);
top_stats[NSS_GMAC_STATS_TOTAL_TICKS] += stats->estats.gmac_total_ticks;
if (unlikely(top_stats[NSS_GMAC_STATS_WORST_CASE_TICKS] < stats->estats.gmac_worst_case_ticks)) {
top_stats[NSS_GMAC_STATS_WORST_CASE_TICKS] = stats->estats.gmac_worst_case_ticks;
}
top_stats[NSS_GMAC_STATS_ITERATIONS] += stats->estats.gmac_iterations;
spin_unlock_bh(&nss_top->stats_lock);
}
/*
* nss_phys_if_msg_handler()
* Handle NSS -> HLOS messages for physical interface/gmacs
*/
static void nss_phys_if_msg_handler(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm,
__attribute__((unused))void *app_data)
{
struct nss_phys_if_msg *nim = (struct nss_phys_if_msg *)ncm;
nss_phys_if_msg_callback_t cb;
/*
* Sanity check the message type
*/
if (ncm->type > NSS_PHYS_IF_MAX_MSG_TYPES) {
nss_warning("%px: message type out of range: %d", nss_ctx, ncm->type);
return;
}
if (!NSS_IS_IF_TYPE(PHYSICAL, ncm->interface)) {
nss_warning("%px: response for another interface: %d", nss_ctx, ncm->interface);
return;
}
if (nss_cmn_get_msg_len(ncm) > sizeof(struct nss_phys_if_msg)) {
nss_warning("%px: message length too big: %d", nss_ctx, nss_cmn_get_msg_len(ncm));
return;
}
/*
* Messages value that are within the base class are handled by the base class.
*/
if (ncm->type < NSS_IF_MAX_MSG_TYPES) {
return nss_if_msg_handler(nss_ctx, ncm, app_data);
}
/*
* Log failures
*/
nss_core_log_msg_failures(nss_ctx, ncm);
/*
* Snoop messages for local driver and handle deprecated interfaces.
*/
switch (nim->cm.type) {
case NSS_PHYS_IF_EXTENDED_STATS_SYNC:
/*
* To create the old API gmac statistics, we use the new extended GMAC stats.
*/
nss_phys_if_update_driver_stats(nss_ctx, ncm->interface, &nim->msg.stats);
nss_top_main.data_plane_ops->data_plane_stats_sync(&nim->msg.stats, ncm->interface);
break;
}
/*
* Update the callback and app_data for NOTIFY messages, IPv4 sends all notify messages
* to the same callback/app_data.
*/
if (ncm->response == NSS_CMN_RESPONSE_NOTIFY) {
ncm->cb = (nss_ptr_t)nss_ctx->nss_top->phys_if_msg_callback[ncm->interface];
ncm->app_data = (nss_ptr_t)nss_ctx->subsys_dp_register[ncm->interface].ndev;
}
/*
* Do we have a callback?
*/
if (!ncm->cb) {
return;
}
/*
* Callback
*/
cb = (nss_phys_if_msg_callback_t)ncm->cb;
cb((void *)ncm->app_data, nim);
}
/*
* nss_phys_if_callback
* Callback to handle the completion of NSS ->HLOS messages.
*/
static void nss_phys_if_callback(void *app_data, struct nss_phys_if_msg *nim)
{
if(nim->cm.response != NSS_CMN_RESPONSE_ACK) {
nss_warning("phys_if Error response %d\n", nim->cm.response);
phif.response = NSS_TX_FAILURE;
complete(&phif.complete);
return;
}
phif.response = NSS_TX_SUCCESS;
complete(&phif.complete);
}
/*
* nss_phys_if_buf()
* Send packet to physical interface owned by NSS
*/
nss_tx_status_t nss_phys_if_buf(struct nss_ctx_instance *nss_ctx, struct sk_buff *os_buf, uint32_t if_num)
{
nss_trace("%px: Phys If Tx packet, id:%d, data=%px", nss_ctx, if_num, os_buf->data);
#ifdef NSS_DRV_TSTAMP_ENABLE
/*
* If we need the packet to be timestamped by GMAC Hardware at Tx
* send the packet to tstamp NSS module
*/
if (unlikely(skb_shinfo(os_buf)->tx_flags & SKBTX_HW_TSTAMP)) {
/* try PHY Driver hook for transmit timestamping firstly */
#if defined(NSS_HAL_IPQ807x_SUPPORT) || defined(NSS_HAL_IPQ60XX_SUPPORT)
nss_phy_tstamp_tx_buf(os_buf->dev, os_buf);
#endif
if (!(skb_shinfo(os_buf)->tx_flags & SKBTX_IN_PROGRESS))
return nss_tstamp_tx_buf(nss_ctx, os_buf, if_num);
}
#endif
return nss_core_send_packet(nss_ctx, os_buf, if_num, H2N_BIT_FLAG_BUFFER_REUSABLE);
}
/*
* nss_phys_if_msg()
*/
nss_tx_status_t nss_phys_if_msg(struct nss_ctx_instance *nss_ctx, struct nss_phys_if_msg *nim)
{
struct nss_cmn_msg *ncm = &nim->cm;
struct net_device *dev;
NSS_VERIFY_CTX_MAGIC(nss_ctx);
/*
* Sanity check the message
*/
if (!NSS_IS_IF_TYPE(PHYSICAL, ncm->interface)) {
nss_warning("%px: tx request for another interface: %d", nss_ctx, ncm->interface);
return NSS_TX_FAILURE;
}
if (ncm->type > NSS_PHYS_IF_MAX_MSG_TYPES) {
nss_warning("%px: message type out of range: %d", nss_ctx, ncm->type);
return NSS_TX_FAILURE;
}
dev = nss_ctx->subsys_dp_register[ncm->interface].ndev;
if (!dev) {
nss_warning("%px: Unregister physical interface %d: no context", nss_ctx, ncm->interface);
return NSS_TX_FAILURE_BAD_PARAM;
}
return nss_core_send_cmd(nss_ctx, nim, sizeof(*nim), NSS_NBUF_PAYLOAD_SIZE);
}
/*
* nss_phys_if_tx_msg_sync()
* Send a message to physical interface & wait for the response.
*/
nss_tx_status_t nss_phys_if_msg_sync(struct nss_ctx_instance *nss_ctx, struct nss_phys_if_msg *nim)
{
nss_tx_status_t status;
int ret = 0;
down(&phif.sem);
status = nss_phys_if_msg(nss_ctx, nim);
if(status != NSS_TX_SUCCESS)
{
nss_warning("%px: nss_phys_if_msg failed\n", nss_ctx);
up(&phif.sem);
return status;
}
ret = wait_for_completion_timeout(&phif.complete, msecs_to_jiffies(NSS_PHYS_IF_TX_TIMEOUT));
if(!ret)
{
nss_warning("%px: phys_if tx failed due to timeout\n", nss_ctx);
phif.response = NSS_TX_FAILURE;
}
status = phif.response;
up(&phif.sem);
return status;
}
/*
**********************************
Register/Unregister/Miscellaneous APIs
**********************************
*/
/*
* nss_phys_if_register()
*/
struct nss_ctx_instance *nss_phys_if_register(uint32_t if_num,
nss_phys_if_rx_callback_t rx_callback,
nss_phys_if_msg_callback_t msg_callback,
struct net_device *netdev,
uint32_t features)
{
uint8_t id = nss_top_main.phys_if_handler_id[if_num];
struct nss_ctx_instance *nss_ctx = &nss_top_main.nss[id];
nss_assert(nss_ctx);
nss_assert(if_num <= NSS_MAX_PHYSICAL_INTERFACES);
nss_core_register_subsys_dp(nss_ctx, if_num, rx_callback, NULL, NULL, netdev, features);
nss_top_main.phys_if_msg_callback[if_num] = msg_callback;
nss_ctx->phys_if_mtu[if_num] = ETH_DATA_LEN;
return nss_ctx;
}
/*
* nss_phys_if_unregister()
*/
void nss_phys_if_unregister(uint32_t if_num)
{
uint8_t id = nss_top_main.phys_if_handler_id[if_num];
struct nss_ctx_instance *nss_ctx = &nss_top_main.nss[id];
nss_assert(nss_ctx);
nss_assert(if_num < NSS_MAX_PHYSICAL_INTERFACES);
nss_core_unregister_subsys_dp(nss_ctx, if_num);
nss_top_main.phys_if_msg_callback[if_num] = NULL;
nss_top_main.nss[0].phys_if_mtu[if_num] = 0;
nss_top_main.nss[1].phys_if_mtu[if_num] = 0;
}
/*
* nss_phys_if_register_handler()
*/
void nss_phys_if_register_handler(struct nss_ctx_instance *nss_ctx, uint32_t if_num)
{
uint32_t ret;
ret = nss_core_register_handler(nss_ctx, if_num, nss_phys_if_msg_handler, NULL);
if (ret != NSS_CORE_STATUS_SUCCESS) {
nss_warning("Message handler FAILED to be registered for interface %d", if_num);
return;
}
if(!nss_phys_if_sem_init_done) {
sema_init(&phif.sem, 1);
init_completion(&phif.complete);
nss_phys_if_sem_init_done = 1;
}
}
/*
* nss_phys_if_open()
* Send open command to physical interface
*/
nss_tx_status_t nss_phys_if_open(struct nss_ctx_instance *nss_ctx, uint32_t tx_desc_ring, uint32_t rx_desc_ring, uint32_t mode, uint32_t if_num, uint32_t bypass_nw_process)
{
struct nss_phys_if_msg nim;
struct nss_if_open *nio;
NSS_VERIFY_CTX_MAGIC(nss_ctx);
nss_info("%px: Phys If Open, id:%d, TxDesc: %x, RxDesc: %x\n", nss_ctx, if_num, tx_desc_ring, rx_desc_ring);
nss_cmn_msg_init(&nim.cm, if_num, NSS_PHYS_IF_OPEN,
sizeof(struct nss_if_open), nss_phys_if_callback, NULL);
nio = &nim.msg.if_msg.open;
nio->tx_desc_ring = tx_desc_ring;
nio->rx_desc_ring = rx_desc_ring;
if (mode == NSS_PHYS_IF_MODE0) {
nio->rx_forward_if = NSS_ETH_RX_INTERFACE;
nio->alignment_mode = NSS_IF_DATA_ALIGN_2BYTE;
} else if (mode == NSS_PHYS_IF_MODE1) {
nio->rx_forward_if = NSS_SJACK_INTERFACE;
nio->alignment_mode = NSS_IF_DATA_ALIGN_4BYTE;
} else if (mode == NSS_PHYS_IF_MODE2) {
nio->rx_forward_if = NSS_PORTID_INTERFACE;
nio->alignment_mode = NSS_IF_DATA_ALIGN_2BYTE;
} else {
nss_info("%px: Phys If Open, unknown mode %d\n", nss_ctx, mode);
return NSS_TX_FAILURE;
}
/*
* If Network processing in NSS is bypassed
* update next hop and alignment accordingly
*/
if (bypass_nw_process) {
nio->rx_forward_if = NSS_N2H_INTERFACE;
nio->alignment_mode = NSS_IF_DATA_ALIGN_2BYTE;
}
return nss_phys_if_msg_sync(nss_ctx, &nim);
}
/*
* nss_phys_if_close()
* Send close command to physical interface
*/
nss_tx_status_t nss_phys_if_close(struct nss_ctx_instance *nss_ctx, uint32_t if_num)
{
struct nss_phys_if_msg nim;
NSS_VERIFY_CTX_MAGIC(nss_ctx);
nss_info("%px: Phys If Close, id:%d \n", nss_ctx, if_num);
nss_cmn_msg_init(&nim.cm, if_num, NSS_PHYS_IF_CLOSE,
sizeof(struct nss_if_close), nss_phys_if_callback, NULL);
return nss_phys_if_msg_sync(nss_ctx, &nim);
}
/*
* nss_phys_if_link_state()
* Send link state to physical interface
*/
nss_tx_status_t nss_phys_if_link_state(struct nss_ctx_instance *nss_ctx, uint32_t link_state, uint32_t if_num)
{
struct nss_phys_if_msg nim;
struct nss_if_link_state_notify *nils;
NSS_VERIFY_CTX_MAGIC(nss_ctx);
nss_info("%px: Phys If Link State, id:%d, State: %x\n", nss_ctx, if_num, link_state);
nss_cmn_msg_init(&nim.cm, if_num, NSS_PHYS_IF_LINK_STATE_NOTIFY,
sizeof(struct nss_if_link_state_notify), nss_phys_if_callback, NULL);
nils = &nim.msg.if_msg.link_state_notify;
nils->state = link_state;
return nss_phys_if_msg_sync(nss_ctx, &nim);
}
/*
* nss_phys_if_mac_addr()
* Send a MAC address to physical interface
*/
nss_tx_status_t nss_phys_if_mac_addr(struct nss_ctx_instance *nss_ctx, uint8_t *addr, uint32_t if_num)
{
struct nss_phys_if_msg nim;
struct nss_if_mac_address_set *nmas;
NSS_VERIFY_CTX_MAGIC(nss_ctx);
nss_info("%px: Phys If MAC Address, id:%d\n", nss_ctx, if_num);
nss_assert(addr != 0);
nss_cmn_msg_init(&nim.cm, if_num, NSS_PHYS_IF_MAC_ADDR_SET,
sizeof(struct nss_if_mac_address_set), nss_phys_if_callback, NULL);
nmas = &nim.msg.if_msg.mac_address_set;
memcpy(nmas->mac_addr, addr, ETH_ALEN);
return nss_phys_if_msg_sync(nss_ctx, &nim);
}
/*
* nss_phys_if_change_mtu()
* Send a MTU change command
*/
nss_tx_status_t nss_phys_if_change_mtu(struct nss_ctx_instance *nss_ctx, uint32_t mtu, uint32_t if_num)
{
struct nss_phys_if_msg nim;
struct nss_if_mtu_change *nimc;
uint16_t mtu_sz, max_mtu;
int i;
nss_tx_status_t status;
/*
* We disallow MTU changes for low memory profiles in order to keep the buffer size constant
*/
#ifdef NSS_FIXED_BUFFER_SIZE
if (mtu > ETH_DATA_LEN) {
nss_info_always("MTU change beyond 1500 restricted for low memory profile \n");
return NSS_TX_FAILURE;
}
#endif
NSS_VERIFY_CTX_MAGIC(nss_ctx);
nss_info("%px: Phys If Change MTU, id:%d, mtu=%d\n", nss_ctx, if_num, mtu);
nss_cmn_msg_init(&nim.cm, if_num, NSS_PHYS_IF_MTU_CHANGE,
sizeof(struct nss_if_mtu_change), nss_phys_if_callback, NULL);
nimc = &nim.msg.if_msg.mtu_change;
nimc->min_buf_size = mtu;
status = nss_phys_if_msg_sync(nss_ctx, &nim);
if (status != NSS_TX_SUCCESS) {
return status;
}
/*
* Update the mtu and max_buf_size accordingly
*/
nss_ctx->phys_if_mtu[if_num] = (uint16_t)mtu;
/*
* Loop through MTU values of all Physical
* interfaces and get the maximum one of all
*/
max_mtu = nss_ctx->phys_if_mtu[0];
for (i = 1; i < NSS_MAX_PHYSICAL_INTERFACES; i++) {
if (max_mtu < nss_ctx->phys_if_mtu[i]) {
max_mtu = nss_ctx->phys_if_mtu[i];
}
}
mtu_sz = nss_top_main.data_plane_ops->data_plane_get_mtu_sz(max_mtu);
/*
* We need to ensure the max_buf_size for 256MB profile stays
* constant at NSS_EMPTY_BUFFER_SIZE. We do this by disallowing changes
* to it due to MTU changes. Also, NSS_EMPTY_BUFFER_SIZE includes the
* PAD and ETH_HLEN, and is aligned to SMP_CACHE_BYTES
*/
#ifndef NSS_FIXED_BUFFER_SIZE
nss_ctx->max_buf_size = ((mtu_sz + ETH_HLEN + SMP_CACHE_BYTES - 1) & ~(SMP_CACHE_BYTES - 1)) + NSS_NBUF_ETH_EXTRA + NSS_NBUF_PAD_EXTRA;
/*
* max_buf_size should not be lesser than NSS_NBUF_PAYLOAD_SIZE
*/
if (nss_ctx->max_buf_size < NSS_NBUF_PAYLOAD_SIZE) {
nss_ctx->max_buf_size = NSS_NBUF_PAYLOAD_SIZE;
}
#else
nss_ctx->max_buf_size = NSS_EMPTY_BUFFER_SIZE;
#endif
#if (NSS_SKB_REUSE_SUPPORT == 1)
if (nss_ctx->max_buf_size > nss_core_get_max_reuse())
nss_core_set_max_reuse(ALIGN(nss_ctx->max_buf_size * 2, PAGE_SIZE));
#endif
nss_info("Current mtu:%u mtu_sz:%u max_buf_size:%d\n", mtu, mtu_sz, nss_ctx->max_buf_size);
if (mtu_sz > nss_ctx->nss_top->prev_mtu_sz) {
/* If crypto is enabled on platform
* Send the flush payloads message
*/
if (nss_ctx->nss_top->crypto_enabled) {
if (nss_n2h_flush_payloads(nss_ctx) != NSS_TX_SUCCESS) {
nss_info("Unable to send flush payloads command to NSS\n");
}
}
}
nss_ctx->nss_top->prev_mtu_sz = mtu_sz;
return status;
}
/*
* nss_phys_if_vsi_assign()
* Send a vsi assign to physical interface
*/
nss_tx_status_t nss_phys_if_vsi_assign(struct nss_ctx_instance *nss_ctx, uint32_t vsi, uint32_t if_num)
{
struct nss_phys_if_msg nim;
NSS_VERIFY_CTX_MAGIC(nss_ctx);
nss_info("%px: Phys If VSI Assign, id:%d\n", nss_ctx, if_num);
memset(&nim, 0, sizeof(struct nss_phys_if_msg));
nss_cmn_msg_init(&nim.cm, if_num, NSS_PHYS_IF_VSI_ASSIGN,
sizeof(struct nss_if_vsi_assign), nss_phys_if_callback, NULL);
nim.msg.if_msg.vsi_assign.vsi = vsi;
return nss_phys_if_msg_sync(nss_ctx, &nim);
}
/*
* nss_phys_if_vsi_unassign()
* Send a vsi unassign to physical interface
*/
nss_tx_status_t nss_phys_if_vsi_unassign(struct nss_ctx_instance *nss_ctx, uint32_t vsi, uint32_t if_num)
{
struct nss_phys_if_msg nim;
NSS_VERIFY_CTX_MAGIC(nss_ctx);
nss_info("%px: Phys If VSI Unassign, id:%d\n", nss_ctx, if_num);
memset(&nim, 0, sizeof(struct nss_phys_if_msg));
nss_cmn_msg_init(&nim.cm, if_num, NSS_PHYS_IF_VSI_UNASSIGN,
sizeof(struct nss_if_vsi_unassign), nss_phys_if_callback, NULL);
nim.msg.if_msg.vsi_unassign.vsi = vsi;
return nss_phys_if_msg_sync(nss_ctx, &nim);
}
/*
* nss_phys_if_pause_on_off()
* Send a pause enabled/disabled message to GMAC
*/
nss_tx_status_t nss_phys_if_pause_on_off(struct nss_ctx_instance *nss_ctx, uint32_t pause_on, uint32_t if_num)
{
struct nss_phys_if_msg nim;
struct nss_if_pause_on_off *nipe;
NSS_VERIFY_CTX_MAGIC(nss_ctx);
nss_info("%px: phys if pause is set to %d, id:%d\n", nss_ctx, pause_on, if_num);
nss_cmn_msg_init(&nim.cm, if_num, NSS_PHYS_IF_PAUSE_ON_OFF,
sizeof(struct nss_if_pause_on_off), nss_phys_if_callback, NULL);
nipe = &nim.msg.if_msg.pause_on_off;
nipe->pause_on = pause_on;
return nss_phys_if_msg_sync(nss_ctx, &nim);
}
/*
* nss_phys_if_reset_nexthop()
* De-configures nexthop for an interface
*/
nss_tx_status_t nss_phys_if_reset_nexthop(struct nss_ctx_instance *nss_ctx, uint32_t if_num)
{
struct nss_phys_if_msg nim;
NSS_VERIFY_CTX_MAGIC(nss_ctx);
nss_cmn_msg_init(&nim.cm, if_num, NSS_PHYS_IF_RESET_NEXTHOP,
0, nss_phys_if_callback, NULL);
return nss_phys_if_msg_sync(nss_ctx, &nim);
}
EXPORT_SYMBOL(nss_phys_if_reset_nexthop);
/*
* nss_phys_if_set_nexthop()
* Configures nexthop for an interface
*/
nss_tx_status_t nss_phys_if_set_nexthop(struct nss_ctx_instance *nss_ctx, uint32_t if_num, uint32_t nexthop)
{
struct nss_phys_if_msg nim;
NSS_VERIFY_CTX_MAGIC(nss_ctx);
if (nexthop >= NSS_MAX_NET_INTERFACES) {
nss_warning("%px: Invalid nexthop interface number: %d", nss_ctx, nexthop);
return NSS_TX_FAILURE_BAD_PARAM;
}
nss_info("%px: Phys If nexthop will be set to %d, id:%d\n", nss_ctx, nexthop, if_num);
nss_cmn_msg_init(&nim.cm, if_num, NSS_PHYS_IF_SET_NEXTHOP,
sizeof(struct nss_if_set_nexthop), nss_phys_if_callback, NULL);
nim.msg.if_msg.set_nexthop.nexthop = nexthop;
return nss_phys_if_msg_sync(nss_ctx, &nim);
}
EXPORT_SYMBOL(nss_phys_if_set_nexthop);
/*
* nss_get_state()
* Return the NSS initialization state
*/
nss_state_t nss_get_state(void *ctx)
{
return nss_cmn_get_state(ctx);
}
EXPORT_SYMBOL(nss_get_state);