Commit 30a02921 authored by Jakub Kicinski's avatar Jakub Kicinski Committed by David S. Miller

nfp: allow multi-stage NSP configuration

NSP commands may be slow to respond, we should try to avoid doing
a command-per-item when user requested to change multiple parameters
for instance with an ethtool .set_settings() command.

Introduce a way of internal NSP code to carry state in NSP structure
and add start/finish calls to perform the initialization and kick off
of the configuration request, with potentially many parameters being
modified in between.

nfp_eth_set_mod_enable() will make use of the new code internally,
other "set" functions to follow.
Signed-off-by: default avatarJakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: default avatarSimon Horman <simon.horman@netronome.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ce22f5a2
...@@ -52,6 +52,14 @@ const char *nfp_hwinfo_lookup(struct nfp_cpp *cpp, const char *lookup); ...@@ -52,6 +52,14 @@ const char *nfp_hwinfo_lookup(struct nfp_cpp *cpp, const char *lookup);
struct nfp_nsp; struct nfp_nsp;
struct nfp_cpp *nfp_nsp_cpp(struct nfp_nsp *state);
bool nfp_nsp_config_modified(struct nfp_nsp *state);
void nfp_nsp_config_set_modified(struct nfp_nsp *state, bool modified);
void *nfp_nsp_config_entries(struct nfp_nsp *state);
unsigned int nfp_nsp_config_idx(struct nfp_nsp *state);
void nfp_nsp_config_set_state(struct nfp_nsp *state, void *entries,
unsigned int idx);
void nfp_nsp_config_clear_state(struct nfp_nsp *state);
int nfp_nsp_read_eth_table(struct nfp_nsp *state, void *buf, unsigned int size); int nfp_nsp_read_eth_table(struct nfp_nsp *state, void *buf, unsigned int size);
int nfp_nsp_write_eth_table(struct nfp_nsp *state, int nfp_nsp_write_eth_table(struct nfp_nsp *state,
const void *buf, unsigned int size); const void *buf, unsigned int size);
......
...@@ -104,8 +104,51 @@ struct nfp_nsp { ...@@ -104,8 +104,51 @@ struct nfp_nsp {
u16 major; u16 major;
u16 minor; u16 minor;
} ver; } ver;
/* Eth table config state */
bool modified;
unsigned int idx;
void *entries;
}; };
struct nfp_cpp *nfp_nsp_cpp(struct nfp_nsp *state)
{
return state->cpp;
}
bool nfp_nsp_config_modified(struct nfp_nsp *state)
{
return state->modified;
}
void nfp_nsp_config_set_modified(struct nfp_nsp *state, bool modified)
{
state->modified = modified;
}
void *nfp_nsp_config_entries(struct nfp_nsp *state)
{
return state->entries;
}
unsigned int nfp_nsp_config_idx(struct nfp_nsp *state)
{
return state->idx;
}
void
nfp_nsp_config_set_state(struct nfp_nsp *state, void *entries, unsigned int idx)
{
state->entries = entries;
state->idx = idx;
}
void nfp_nsp_config_clear_state(struct nfp_nsp *state)
{
state->entries = NULL;
state->idx = 0;
}
static int nfp_nsp_check(struct nfp_nsp *state) static int nfp_nsp_check(struct nfp_nsp *state)
{ {
struct nfp_cpp *cpp = state->cpp; struct nfp_cpp *cpp = state->cpp;
......
...@@ -136,4 +136,8 @@ struct nfp_eth_table * ...@@ -136,4 +136,8 @@ struct nfp_eth_table *
__nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp); __nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp);
int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, bool enable); int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, bool enable);
struct nfp_nsp *nfp_eth_config_start(struct nfp_cpp *cpp, unsigned int idx);
int nfp_eth_config_commit_end(struct nfp_nsp *nsp);
void nfp_eth_config_cleanup_end(struct nfp_nsp *nsp);
#endif #endif
...@@ -268,63 +268,115 @@ __nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp) ...@@ -268,63 +268,115 @@ __nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp)
return NULL; return NULL;
} }
/** struct nfp_nsp *nfp_eth_config_start(struct nfp_cpp *cpp, unsigned int idx)
* nfp_eth_set_mod_enable() - set PHY module enable control bit
* @cpp: NFP CPP handle
* @idx: NFP chip-wide port index
* @enable: Desired state
*
* Enable or disable PHY module (this usually means setting the TX lanes
* disable bits).
*
* Return: 0 or -ERRNO.
*/
int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, bool enable)
{ {
struct eth_table_entry *entries; struct eth_table_entry *entries;
struct nfp_nsp *nsp; struct nfp_nsp *nsp;
u64 reg;
int ret; int ret;
entries = kzalloc(NSP_ETH_TABLE_SIZE, GFP_KERNEL); entries = kzalloc(NSP_ETH_TABLE_SIZE, GFP_KERNEL);
if (!entries) if (!entries)
return -ENOMEM; return ERR_PTR(-ENOMEM);
nsp = nfp_nsp_open(cpp); nsp = nfp_nsp_open(cpp);
if (IS_ERR(nsp)) { if (IS_ERR(nsp)) {
kfree(entries); kfree(entries);
return PTR_ERR(nsp); return nsp;
} }
ret = nfp_nsp_read_eth_table(nsp, entries, NSP_ETH_TABLE_SIZE); ret = nfp_nsp_read_eth_table(nsp, entries, NSP_ETH_TABLE_SIZE);
if (ret < 0) { if (ret < 0) {
nfp_err(cpp, "reading port table failed %d\n", ret); nfp_err(cpp, "reading port table failed %d\n", ret);
goto exit_close_nsp; goto err;
} }
if (!(entries[idx].port & NSP_ETH_PORT_LANES_MASK)) { if (!(entries[idx].port & NSP_ETH_PORT_LANES_MASK)) {
nfp_warn(cpp, "trying to set port state on disabled port %d\n", nfp_warn(cpp, "trying to set port state on disabled port %d\n",
idx); idx);
ret = -EINVAL; goto err;
goto exit_close_nsp;
} }
/* Check if we are already in requested state */ nfp_nsp_config_set_state(nsp, entries, idx);
reg = le64_to_cpu(entries[idx].state); return nsp;
if (enable == FIELD_GET(NSP_ETH_CTRL_ENABLED, reg)) {
ret = 0; err:
goto exit_close_nsp; nfp_nsp_close(nsp);
} kfree(entries);
return ERR_PTR(-EIO);
}
reg = le64_to_cpu(entries[idx].control); void nfp_eth_config_cleanup_end(struct nfp_nsp *nsp)
reg &= ~NSP_ETH_CTRL_ENABLED; {
reg |= FIELD_PREP(NSP_ETH_CTRL_ENABLED, enable); struct eth_table_entry *entries = nfp_nsp_config_entries(nsp);
entries[idx].control = cpu_to_le64(reg);
ret = nfp_nsp_write_eth_table(nsp, entries, NSP_ETH_TABLE_SIZE); nfp_nsp_config_set_modified(nsp, false);
exit_close_nsp: nfp_nsp_config_clear_state(nsp);
nfp_nsp_close(nsp); nfp_nsp_close(nsp);
kfree(entries); kfree(entries);
}
/**
* nfp_eth_config_commit_end() - perform recorded configuration changes
* @nsp: NFP NSP handle returned from nfp_eth_config_start()
*
* Perform the configuration which was requested with __nfp_eth_set_*()
* helpers and recorded in @nsp state. If device was already configured
* as requested or no __nfp_eth_set_*() operations were made no NSP command
* will be performed.
*
* Return:
* 0 - configuration successful;
* 1 - no changes were needed;
* -ERRNO - configuration failed.
*/
int nfp_eth_config_commit_end(struct nfp_nsp *nsp)
{
struct eth_table_entry *entries = nfp_nsp_config_entries(nsp);
int ret = 1;
if (nfp_nsp_config_modified(nsp)) {
ret = nfp_nsp_write_eth_table(nsp, entries, NSP_ETH_TABLE_SIZE);
ret = ret < 0 ? ret : 0;
}
nfp_eth_config_cleanup_end(nsp);
return ret;
}
/**
* nfp_eth_set_mod_enable() - set PHY module enable control bit
* @cpp: NFP CPP handle
* @idx: NFP chip-wide port index
* @enable: Desired state
*
* Enable or disable PHY module (this usually means setting the TX lanes
* disable bits).
*
* Return: 0 or -ERRNO.
*/
int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, bool enable)
{
struct eth_table_entry *entries;
struct nfp_nsp *nsp;
u64 reg;
nsp = nfp_eth_config_start(cpp, idx);
if (IS_ERR(nsp))
return PTR_ERR(nsp);
entries = nfp_nsp_config_entries(nsp);
/* Check if we are already in requested state */
reg = le64_to_cpu(entries[idx].state);
if (enable != FIELD_GET(NSP_ETH_CTRL_ENABLED, reg)) {
reg = le64_to_cpu(entries[idx].control);
reg &= ~NSP_ETH_CTRL_ENABLED;
reg |= FIELD_PREP(NSP_ETH_CTRL_ENABLED, enable);
entries[idx].control = cpu_to_le64(reg);
nfp_nsp_config_set_modified(nsp, true);
}
return ret < 0 ? ret : 0; return nfp_eth_config_commit_end(nsp);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment