Commit 5b1bf3f6 authored by David S. Miller's avatar David S. Miller

Merge branch 'FDB-VLAN-and-PTP-fixes-for-SJA1105-DSA'

Vladimir Oltean says:

====================
FDB, VLAN and PTP fixes for SJA1105 DSA

This patchset is an assortment of fixes for the net-next version of the
sja1105 DSA driver:
- Avoid a kernel panic when the driver fails to probe or unregisters
- Finish Arnd Bermann's idea of compiling PTP support as part of the
  main DSA driver and not separately
- Better handling of initial port-based VLAN as well as VLANs for
  dsa_8021q FDB entries
- Fix address learning for the SJA1105 P/Q/R/S family
- Make static FDB entries persistent across switch resets
- Fix reporting of statically-added FDB entries in 'bridge fdb show'
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents c881e10e d7637782
...@@ -10,5 +10,5 @@ sja1105-objs := \ ...@@ -10,5 +10,5 @@ sja1105-objs := \
sja1105_dynamic_config.o \ sja1105_dynamic_config.o \
ifdef CONFIG_NET_DSA_SJA1105_PTP ifdef CONFIG_NET_DSA_SJA1105_PTP
obj-$(CONFIG_NET_DSA_SJA1105) += sja1105_ptp.o sja1105-objs += sja1105_ptp.o
endif endif
...@@ -3,6 +3,98 @@ ...@@ -3,6 +3,98 @@
*/ */
#include "sja1105.h" #include "sja1105.h"
/* In the dynamic configuration interface, the switch exposes a register-like
* view of some of the static configuration tables.
* Many times the field organization of the dynamic tables is abbreviated (not
* all fields are dynamically reconfigurable) and different from the static
* ones, but the key reason for having it is that we can spare a switch reset
* for settings that can be changed dynamically.
*
* This file creates a per-switch-family abstraction called
* struct sja1105_dynamic_table_ops and two operations that work with it:
* - sja1105_dynamic_config_write
* - sja1105_dynamic_config_read
*
* Compared to the struct sja1105_table_ops from sja1105_static_config.c,
* the dynamic accessors work with a compound buffer:
*
* packed_buf
*
* |
* V
* +-----------------------------------------+------------------+
* | ENTRY BUFFER | COMMAND BUFFER |
* +-----------------------------------------+------------------+
*
* <----------------------- packed_size ------------------------>
*
* The ENTRY BUFFER may or may not have the same layout, or size, as its static
* configuration table entry counterpart. When it does, the same packing
* function is reused (bar exceptional cases - see
* sja1105pqrs_dyn_l2_lookup_entry_packing).
*
* The reason for the COMMAND BUFFER being at the end is to be able to send
* a dynamic write command through a single SPI burst. By the time the switch
* reacts to the command, the ENTRY BUFFER is already populated with the data
* sent by the core.
*
* The COMMAND BUFFER is always SJA1105_SIZE_DYN_CMD bytes (one 32-bit word) in
* size.
*
* Sometimes the ENTRY BUFFER does not really exist (when the number of fields
* that can be reconfigured is small), then the switch repurposes some of the
* unused 32 bits of the COMMAND BUFFER to hold ENTRY data.
*
* The key members of struct sja1105_dynamic_table_ops are:
* - .entry_packing: A function that deals with packing an ENTRY structure
* into an SPI buffer, or retrieving an ENTRY structure
* from one.
* The @packed_buf pointer it's given does always point to
* the ENTRY portion of the buffer.
* - .cmd_packing: A function that deals with packing/unpacking the COMMAND
* structure to/from the SPI buffer.
* It is given the same @packed_buf pointer as .entry_packing,
* so most of the time, the @packed_buf points *behind* the
* COMMAND offset inside the buffer.
* To access the COMMAND portion of the buffer, the function
* knows its correct offset.
* Giving both functions the same pointer is handy because in
* extreme cases (see sja1105pqrs_dyn_l2_lookup_entry_packing)
* the .entry_packing is able to jump to the COMMAND portion,
* or vice-versa (sja1105pqrs_l2_lookup_cmd_packing).
* - .access: A bitmap of:
* OP_READ: Set if the hardware manual marks the ENTRY portion of the
* dynamic configuration table buffer as R (readable) after
* an SPI read command (the switch will populate the buffer).
* OP_WRITE: Set if the manual marks the ENTRY portion of the dynamic
* table buffer as W (writable) after an SPI write command
* (the switch will read the fields provided in the buffer).
* OP_DEL: Set if the manual says the VALIDENT bit is supported in the
* COMMAND portion of this dynamic config buffer (i.e. the
* specified entry can be invalidated through a SPI write
* command).
* OP_SEARCH: Set if the manual says that the index of an entry can
* be retrieved in the COMMAND portion of the buffer based
* on its ENTRY portion, as a result of a SPI write command.
* Only the TCAM-based FDB table on SJA1105 P/Q/R/S supports
* this.
* - .max_entry_count: The number of entries, counting from zero, that can be
* reconfigured through the dynamic interface. If a static
* table can be reconfigured at all dynamically, this
* number always matches the maximum number of supported
* static entries.
* - .packed_size: The length in bytes of the compound ENTRY + COMMAND BUFFER.
* Note that sometimes the compound buffer may contain holes in
* it (see sja1105_vlan_lookup_cmd_packing). The @packed_buf is
* contiguous however, so @packed_size includes any unused
* bytes.
* - .addr: The base SPI address at which the buffer must be written to the
* switch's memory. When looking at the hardware manual, this must
* always match the lowest documented address for the ENTRY, and not
* that of the COMMAND, since the other 32-bit words will follow along
* at the correct addresses.
*/
#define SJA1105_SIZE_DYN_CMD 4 #define SJA1105_SIZE_DYN_CMD 4
#define SJA1105ET_SIZE_MAC_CONFIG_DYN_ENTRY \ #define SJA1105ET_SIZE_MAC_CONFIG_DYN_ENTRY \
...@@ -57,13 +149,11 @@ sja1105pqrs_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, ...@@ -57,13 +149,11 @@ sja1105pqrs_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
{ {
u8 *p = buf + SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY; u8 *p = buf + SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY;
const int size = SJA1105_SIZE_DYN_CMD; const int size = SJA1105_SIZE_DYN_CMD;
u64 lockeds = 0;
u64 hostcmd; u64 hostcmd;
sja1105_packing(p, &cmd->valid, 31, 31, size, op); sja1105_packing(p, &cmd->valid, 31, 31, size, op);
sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op); sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op);
sja1105_packing(p, &cmd->errors, 29, 29, size, op); sja1105_packing(p, &cmd->errors, 29, 29, size, op);
sja1105_packing(p, &lockeds, 28, 28, size, op);
sja1105_packing(p, &cmd->valident, 27, 27, size, op); sja1105_packing(p, &cmd->valident, 27, 27, size, op);
/* VALIDENT is supposed to indicate "keep or not", but in SJA1105 E/T, /* VALIDENT is supposed to indicate "keep or not", but in SJA1105 E/T,
...@@ -113,6 +203,64 @@ sja1105pqrs_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, ...@@ -113,6 +203,64 @@ sja1105pqrs_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY, op); SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY, op);
} }
/* The switch is so retarded that it makes our command/entry abstraction
* crumble apart.
*
* On P/Q/R/S, the switch tries to say whether a FDB entry
* is statically programmed or dynamically learned via a flag called LOCKEDS.
* The hardware manual says about this fiels:
*
* On write will specify the format of ENTRY.
* On read the flag will be found cleared at times the VALID flag is found
* set. The flag will also be found cleared in response to a read having the
* MGMTROUTE flag set. In response to a read with the MGMTROUTE flag
* cleared, the flag be set if the most recent access operated on an entry
* that was either loaded by configuration or through dynamic reconfiguration
* (as opposed to automatically learned entries).
*
* The trouble with this flag is that it's part of the *command* to access the
* dynamic interface, and not part of the *entry* retrieved from it.
* Otherwise said, for a sja1105_dynamic_config_read, LOCKEDS is supposed to be
* an output from the switch into the command buffer, and for a
* sja1105_dynamic_config_write, the switch treats LOCKEDS as an input
* (hence we can write either static, or automatically learned entries, from
* the core).
* But the manual contradicts itself in the last phrase where it says that on
* read, LOCKEDS will be set to 1 for all FDB entries written through the
* dynamic interface (therefore, the value of LOCKEDS from the
* sja1105_dynamic_config_write is not really used for anything, it'll store a
* 1 anyway).
* This means you can't really write a FDB entry with LOCKEDS=0 (automatically
* learned) into the switch, which kind of makes sense.
* As for reading through the dynamic interface, it doesn't make too much sense
* to put LOCKEDS into the command, since the switch will inevitably have to
* ignore it (otherwise a command would be like "read the FDB entry 123, but
* only if it's dynamically learned" <- well how am I supposed to know?) and
* just use it as an output buffer for its findings. But guess what... that's
* what the entry buffer is for!
* Unfortunately, what really breaks this abstraction is the fact that it
* wasn't designed having the fact in mind that the switch can output
* entry-related data as writeback through the command buffer.
* However, whether a FDB entry is statically or dynamically learned *is* part
* of the entry and not the command data, no matter what the switch thinks.
* In order to do that, we'll need to wrap around the
* sja1105pqrs_l2_lookup_entry_packing from sja1105_static_config.c, and take
* a peek outside of the caller-supplied @buf (the entry buffer), to reach the
* command buffer.
*/
static size_t
sja1105pqrs_dyn_l2_lookup_entry_packing(void *buf, void *entry_ptr,
enum packing_op op)
{
struct sja1105_l2_lookup_entry *entry = entry_ptr;
u8 *cmd = buf + SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY;
const int size = SJA1105_SIZE_DYN_CMD;
sja1105_packing(cmd, &entry->lockeds, 28, 28, size, op);
return sja1105pqrs_l2_lookup_entry_packing(buf, entry_ptr, op);
}
static void static void
sja1105et_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, sja1105et_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
enum packing_op op) enum packing_op op)
...@@ -393,7 +541,7 @@ struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = { ...@@ -393,7 +541,7 @@ struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = {
/* SJA1105P/Q/R/S: Second generation */ /* SJA1105P/Q/R/S: Second generation */
struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = { struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = {
[BLK_IDX_L2_LOOKUP] = { [BLK_IDX_L2_LOOKUP] = {
.entry_packing = sja1105pqrs_l2_lookup_entry_packing, .entry_packing = sja1105pqrs_dyn_l2_lookup_entry_packing,
.cmd_packing = sja1105pqrs_l2_lookup_cmd_packing, .cmd_packing = sja1105pqrs_l2_lookup_cmd_packing,
.access = (OP_READ | OP_WRITE | OP_DEL | OP_SEARCH), .access = (OP_READ | OP_WRITE | OP_DEL | OP_SEARCH),
.max_entry_count = SJA1105_MAX_L2_LOOKUP_COUNT, .max_entry_count = SJA1105_MAX_L2_LOOKUP_COUNT,
......
This diff is collapsed.
...@@ -77,7 +77,6 @@ int sja1105_get_ts_info(struct dsa_switch *ds, int port, ...@@ -77,7 +77,6 @@ int sja1105_get_ts_info(struct dsa_switch *ds, int port,
info->phc_index = ptp_clock_index(priv->clock); info->phc_index = ptp_clock_index(priv->clock);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(sja1105_get_ts_info);
int sja1105et_ptp_cmd(const void *ctx, const void *data) int sja1105et_ptp_cmd(const void *ctx, const void *data)
{ {
...@@ -95,7 +94,6 @@ int sja1105et_ptp_cmd(const void *ctx, const void *data) ...@@ -95,7 +94,6 @@ int sja1105et_ptp_cmd(const void *ctx, const void *data)
return sja1105_spi_send_packed_buf(priv, SPI_WRITE, regs->ptp_control, return sja1105_spi_send_packed_buf(priv, SPI_WRITE, regs->ptp_control,
buf, SJA1105_SIZE_PTP_CMD); buf, SJA1105_SIZE_PTP_CMD);
} }
EXPORT_SYMBOL_GPL(sja1105et_ptp_cmd);
int sja1105pqrs_ptp_cmd(const void *ctx, const void *data) int sja1105pqrs_ptp_cmd(const void *ctx, const void *data)
{ {
...@@ -113,7 +111,6 @@ int sja1105pqrs_ptp_cmd(const void *ctx, const void *data) ...@@ -113,7 +111,6 @@ int sja1105pqrs_ptp_cmd(const void *ctx, const void *data)
return sja1105_spi_send_packed_buf(priv, SPI_WRITE, regs->ptp_control, return sja1105_spi_send_packed_buf(priv, SPI_WRITE, regs->ptp_control,
buf, SJA1105_SIZE_PTP_CMD); buf, SJA1105_SIZE_PTP_CMD);
} }
EXPORT_SYMBOL_GPL(sja1105pqrs_ptp_cmd);
/* The switch returns partial timestamps (24 bits for SJA1105 E/T, which wrap /* The switch returns partial timestamps (24 bits for SJA1105 E/T, which wrap
* around in 0.135 seconds, and 32 bits for P/Q/R/S, wrapping around in 34.35 * around in 0.135 seconds, and 32 bits for P/Q/R/S, wrapping around in 34.35
...@@ -146,7 +143,6 @@ u64 sja1105_tstamp_reconstruct(struct sja1105_private *priv, u64 now, ...@@ -146,7 +143,6 @@ u64 sja1105_tstamp_reconstruct(struct sja1105_private *priv, u64 now,
return ts_reconstructed; return ts_reconstructed;
} }
EXPORT_SYMBOL_GPL(sja1105_tstamp_reconstruct);
/* Reads the SPI interface for an egress timestamp generated by the switch /* Reads the SPI interface for an egress timestamp generated by the switch
* for frames sent using management routes. * for frames sent using management routes.
...@@ -219,7 +215,6 @@ int sja1105_ptpegr_ts_poll(struct sja1105_private *priv, int port, u64 *ts) ...@@ -219,7 +215,6 @@ int sja1105_ptpegr_ts_poll(struct sja1105_private *priv, int port, u64 *ts)
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(sja1105_ptpegr_ts_poll);
int sja1105_ptp_reset(struct sja1105_private *priv) int sja1105_ptp_reset(struct sja1105_private *priv)
{ {
...@@ -240,7 +235,6 @@ int sja1105_ptp_reset(struct sja1105_private *priv) ...@@ -240,7 +235,6 @@ int sja1105_ptp_reset(struct sja1105_private *priv)
return rc; return rc;
} }
EXPORT_SYMBOL_GPL(sja1105_ptp_reset);
static int sja1105_ptp_gettime(struct ptp_clock_info *ptp, static int sja1105_ptp_gettime(struct ptp_clock_info *ptp,
struct timespec64 *ts) struct timespec64 *ts)
...@@ -387,18 +381,13 @@ int sja1105_ptp_clock_register(struct sja1105_private *priv) ...@@ -387,18 +381,13 @@ int sja1105_ptp_clock_register(struct sja1105_private *priv)
return sja1105_ptp_reset(priv); return sja1105_ptp_reset(priv);
} }
EXPORT_SYMBOL_GPL(sja1105_ptp_clock_register);
void sja1105_ptp_clock_unregister(struct sja1105_private *priv) void sja1105_ptp_clock_unregister(struct sja1105_private *priv)
{ {
if (IS_ERR_OR_NULL(priv->clock)) if (IS_ERR_OR_NULL(priv->clock))
return; return;
cancel_delayed_work_sync(&priv->refresh_work);
ptp_clock_unregister(priv->clock); ptp_clock_unregister(priv->clock);
priv->clock = NULL; priv->clock = NULL;
} }
EXPORT_SYMBOL_GPL(sja1105_ptp_clock_unregister);
MODULE_AUTHOR("Vladimir Oltean <olteanv@gmail.com>");
MODULE_DESCRIPTION("SJA1105 PHC Driver");
MODULE_LICENSE("GPL v2");
...@@ -100,7 +100,6 @@ int sja1105_spi_send_packed_buf(const struct sja1105_private *priv, ...@@ -100,7 +100,6 @@ int sja1105_spi_send_packed_buf(const struct sja1105_private *priv,
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(sja1105_spi_send_packed_buf);
/* If @rw is: /* If @rw is:
* - SPI_WRITE: creates and sends an SPI write message at absolute * - SPI_WRITE: creates and sends an SPI write message at absolute
...@@ -136,7 +135,6 @@ int sja1105_spi_send_int(const struct sja1105_private *priv, ...@@ -136,7 +135,6 @@ int sja1105_spi_send_int(const struct sja1105_private *priv,
return rc; return rc;
} }
EXPORT_SYMBOL_GPL(sja1105_spi_send_int);
/* Should be used if a @packed_buf larger than SJA1105_SIZE_SPI_MSG_MAXLEN /* Should be used if a @packed_buf larger than SJA1105_SIZE_SPI_MSG_MAXLEN
* must be sent/received. Splitting the buffer into chunks and assembling * must be sent/received. Splitting the buffer into chunks and assembling
......
...@@ -35,7 +35,6 @@ void sja1105_pack(void *buf, const u64 *val, int start, int end, size_t len) ...@@ -35,7 +35,6 @@ void sja1105_pack(void *buf, const u64 *val, int start, int end, size_t len)
} }
dump_stack(); dump_stack();
} }
EXPORT_SYMBOL_GPL(sja1105_pack);
void sja1105_unpack(const void *buf, u64 *val, int start, int end, size_t len) void sja1105_unpack(const void *buf, u64 *val, int start, int end, size_t len)
{ {
...@@ -53,7 +52,6 @@ void sja1105_unpack(const void *buf, u64 *val, int start, int end, size_t len) ...@@ -53,7 +52,6 @@ void sja1105_unpack(const void *buf, u64 *val, int start, int end, size_t len)
start, end); start, end);
dump_stack(); dump_stack();
} }
EXPORT_SYMBOL_GPL(sja1105_unpack);
void sja1105_packing(void *buf, u64 *val, int start, int end, void sja1105_packing(void *buf, u64 *val, int start, int end,
size_t len, enum packing_op op) size_t len, enum packing_op op)
...@@ -76,7 +74,6 @@ void sja1105_packing(void *buf, u64 *val, int start, int end, ...@@ -76,7 +74,6 @@ void sja1105_packing(void *buf, u64 *val, int start, int end,
} }
dump_stack(); dump_stack();
} }
EXPORT_SYMBOL_GPL(sja1105_packing);
/* Little-endian Ethernet CRC32 of data packed as big-endian u32 words */ /* Little-endian Ethernet CRC32 of data packed as big-endian u32 words */
u32 sja1105_crc32(const void *buf, size_t len) u32 sja1105_crc32(const void *buf, size_t len)
...@@ -233,11 +230,20 @@ sja1105pqrs_l2_lookup_params_entry_packing(void *buf, void *entry_ptr, ...@@ -233,11 +230,20 @@ sja1105pqrs_l2_lookup_params_entry_packing(void *buf, void *entry_ptr,
{ {
const size_t size = SJA1105PQRS_SIZE_L2_LOOKUP_PARAMS_ENTRY; const size_t size = SJA1105PQRS_SIZE_L2_LOOKUP_PARAMS_ENTRY;
struct sja1105_l2_lookup_params_entry *entry = entry_ptr; struct sja1105_l2_lookup_params_entry *entry = entry_ptr;
int offset, i;
for (i = 0, offset = 58; i < 5; i++, offset += 11)
sja1105_packing(buf, &entry->maxaddrp[i],
offset + 10, offset + 0, size, op);
sja1105_packing(buf, &entry->maxage, 57, 43, size, op); sja1105_packing(buf, &entry->maxage, 57, 43, size, op);
sja1105_packing(buf, &entry->start_dynspc, 42, 33, size, op);
sja1105_packing(buf, &entry->drpnolearn, 32, 28, size, op);
sja1105_packing(buf, &entry->shared_learn, 27, 27, size, op); sja1105_packing(buf, &entry->shared_learn, 27, 27, size, op);
sja1105_packing(buf, &entry->no_enf_hostprt, 26, 26, size, op); sja1105_packing(buf, &entry->no_enf_hostprt, 26, 26, size, op);
sja1105_packing(buf, &entry->no_mgmt_learn, 25, 25, size, op); sja1105_packing(buf, &entry->no_mgmt_learn, 25, 25, size, op);
sja1105_packing(buf, &entry->use_static, 24, 24, size, op);
sja1105_packing(buf, &entry->owr_dyn, 23, 23, size, op);
sja1105_packing(buf, &entry->learn_once, 22, 22, size, op);
return size; return size;
} }
......
...@@ -132,7 +132,7 @@ struct sja1105_l2_lookup_entry { ...@@ -132,7 +132,7 @@ struct sja1105_l2_lookup_entry {
u64 mask_vlanid; u64 mask_vlanid;
u64 mask_macaddr; u64 mask_macaddr;
u64 iotag; u64 iotag;
bool lockeds; u64 lockeds;
union { union {
/* LOCKEDS=1: Static FDB entries */ /* LOCKEDS=1: Static FDB entries */
struct { struct {
...@@ -151,6 +151,7 @@ struct sja1105_l2_lookup_entry { ...@@ -151,6 +151,7 @@ struct sja1105_l2_lookup_entry {
}; };
struct sja1105_l2_lookup_params_entry { struct sja1105_l2_lookup_params_entry {
u64 maxaddrp[5]; /* P/Q/R/S only */
u64 start_dynspc; /* P/Q/R/S only */ u64 start_dynspc; /* P/Q/R/S only */
u64 drpnolearn; /* P/Q/R/S only */ u64 drpnolearn; /* P/Q/R/S only */
u64 use_static; /* P/Q/R/S only */ u64 use_static; /* P/Q/R/S only */
......
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