@@ -12,6 +12,9 @@
#define TABLE_UPDATE_SLEEP_US 10
#define TABLE_UPDATE_TIMEOUT_US 100000
+#define IFH_EXTRACT_BITFIELD64(x, o, w) \
+ (((x) >> (o)) & GENMASK_ULL((w) - 1, 0))
+
struct ocelot_mact_entry {
u8 mac[ETH_ALEN];
u16 vid;
@@ -561,6 +564,166 @@ void ocelot_get_txtstamp(struct ocelot *ocelot)
}
EXPORT_SYMBOL(ocelot_get_txtstamp);
+static int ocelot_parse_xfh(u32 *_ifh, struct ocelot_frame_info *info)
+{
+ u8 llen, wlen;
+ u64 ifh[2];
+
+ ifh[0] = be64_to_cpu(((__force __be64 *)_ifh)[0]);
+ ifh[1] = be64_to_cpu(((__force __be64 *)_ifh)[1]);
+
+ wlen = IFH_EXTRACT_BITFIELD64(ifh[0], 7, 8);
+ llen = IFH_EXTRACT_BITFIELD64(ifh[0], 15, 6);
+
+ info->len = OCELOT_BUFFER_CELL_SZ * wlen + llen - 80;
+
+ info->timestamp = IFH_EXTRACT_BITFIELD64(ifh[0], 21, 32);
+
+ info->port = IFH_EXTRACT_BITFIELD64(ifh[1], 43, 4);
+
+ info->tag_type = IFH_EXTRACT_BITFIELD64(ifh[1], 16, 1);
+ info->vid = IFH_EXTRACT_BITFIELD64(ifh[1], 0, 12);
+
+ return 0;
+}
+
+static int ocelot_rx_frame_word(struct ocelot *ocelot, u8 grp, bool ifh,
+ u32 *rval)
+{
+ u32 val;
+ u32 bytes_valid;
+
+ val = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
+ if (val == XTR_NOT_READY) {
+ if (ifh)
+ return -EIO;
+
+ do {
+ val = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
+ } while (val == XTR_NOT_READY);
+ }
+
+ switch (val) {
+ case XTR_ABORT:
+ return -EIO;
+ case XTR_EOF_0:
+ case XTR_EOF_1:
+ case XTR_EOF_2:
+ case XTR_EOF_3:
+ case XTR_PRUNED:
+ bytes_valid = XTR_VALID_BYTES(val);
+ val = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
+ if (val == XTR_ESCAPE)
+ *rval = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
+ else
+ *rval = val;
+
+ return bytes_valid;
+ case XTR_ESCAPE:
+ *rval = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
+
+ return 4;
+ default:
+ *rval = val;
+
+ return 4;
+ }
+}
+
+int ocelot_xtr_poll_xfh(struct ocelot *ocelot, int grp,
+ struct ocelot_frame_info *info)
+{
+ u32 ifh[OCELOT_TAG_LEN / 4];
+ int i, err = 0;
+
+ for (i = 0; i < OCELOT_TAG_LEN / 4; i++) {
+ err = ocelot_rx_frame_word(ocelot, grp, true, &ifh[i]);
+ if (err != 4)
+ return (err < 0) ? err : -EIO;
+ }
+
+ ocelot_parse_xfh(ifh, info);
+
+ return 0;
+}
+
+int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp,
+ struct net_device *dev,
+ struct ocelot_frame_info *info,
+ struct sk_buff **nskb)
+{
+ struct skb_shared_hwtstamps *shhwtstamps;
+ u64 tod_in_ns, full_ts_in_ns;
+ struct timespec64 ts;
+ int sz, len, buf_len;
+ struct sk_buff *skb;
+ u32 val, *buf;
+ int err = 0;
+
+ skb = netdev_alloc_skb(dev, info->len);
+ if (unlikely(!skb)) {
+ netdev_err(dev, "Unable to allocate sk_buff\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ buf_len = info->len - ETH_FCS_LEN;
+ buf = (u32 *)skb_put(skb, buf_len);
+
+ len = 0;
+ do {
+ sz = ocelot_rx_frame_word(ocelot, grp, false, &val);
+ if (sz < 0) {
+ err = sz;
+ goto out;
+ }
+ *buf++ = val;
+ len += sz;
+ } while (len < buf_len);
+
+ /* Read the FCS */
+ sz = ocelot_rx_frame_word(ocelot, grp, false, &val);
+ if (sz < 0) {
+ err = sz;
+ goto out;
+ }
+
+ /* Update the statistics if part of the FCS was read before */
+ len -= ETH_FCS_LEN - sz;
+
+ if (unlikely(dev->features & NETIF_F_RXFCS)) {
+ buf = (u32 *)skb_put(skb, ETH_FCS_LEN);
+ *buf = val;
+ }
+
+ if (ocelot->ptp) {
+ ocelot_ptp_gettime64(&ocelot->ptp_info, &ts);
+
+ tod_in_ns = ktime_set(ts.tv_sec, ts.tv_nsec);
+ if ((tod_in_ns & 0xffffffff) < info->timestamp)
+ full_ts_in_ns = (((tod_in_ns >> 32) - 1) << 32) |
+ info->timestamp;
+ else
+ full_ts_in_ns = (tod_in_ns & GENMASK_ULL(63, 32)) |
+ info->timestamp;
+
+ shhwtstamps = skb_hwtstamps(skb);
+ memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
+ shhwtstamps->hwtstamp = full_ts_in_ns;
+ }
+
+ /* Everything we see on an interface that is in the HW bridge
+ * has already been forwarded.
+ */
+ if (ocelot->bridge_mask & BIT(info->port))
+ skb->offload_fwd_mark = 1;
+
+ skb->protocol = eth_type_trans(skb, dev);
+ *nskb = skb;
+out:
+ return err;
+}
+
/* Generate the IFH for frame injection
*
* The IFH is a 128bit-value
@@ -121,6 +121,12 @@ void ocelot_port_devlink_teardown(struct ocelot *ocelot, int port);
bool ocelot_can_inject(struct ocelot *ocelot, int grp);
void ocelot_port_inject_frame(struct ocelot *ocelot, int port, int grp,
u32 rew_op, struct sk_buff *skb);
+int ocelot_xtr_poll_xfh(struct ocelot *ocelot, int grp,
+ struct ocelot_frame_info *info);
+int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp,
+ struct net_device *dev,
+ struct ocelot_frame_info *info,
+ struct sk_buff **skb);
extern struct notifier_block ocelot_netdevice_nb;
extern struct notifier_block ocelot_switchdev_nb;
@@ -18,8 +18,6 @@
#include <soc/mscc/ocelot_hsio.h>
#include "ocelot.h"
-#define IFH_EXTRACT_BITFIELD64(x, o, w) (((x) >> (o)) & GENMASK_ULL((w) - 1, 0))
-
static const u32 ocelot_ana_regmap[] = {
REG(ANA_ADVLEARN, 0x009000),
REG(ANA_VLANMASK, 0x009004),
@@ -532,173 +530,37 @@ static int ocelot_chip_init(struct ocelot *ocelot, const struct ocelot_ops *ops)
return 0;
}
-static int ocelot_parse_ifh(u32 *_ifh, struct ocelot_frame_info *info)
-{
- u8 llen, wlen;
- u64 ifh[2];
-
- ifh[0] = be64_to_cpu(((__force __be64 *)_ifh)[0]);
- ifh[1] = be64_to_cpu(((__force __be64 *)_ifh)[1]);
-
- wlen = IFH_EXTRACT_BITFIELD64(ifh[0], 7, 8);
- llen = IFH_EXTRACT_BITFIELD64(ifh[0], 15, 6);
-
- info->len = OCELOT_BUFFER_CELL_SZ * wlen + llen - 80;
-
- info->timestamp = IFH_EXTRACT_BITFIELD64(ifh[0], 21, 32);
-
- info->port = IFH_EXTRACT_BITFIELD64(ifh[1], 43, 4);
-
- info->tag_type = IFH_EXTRACT_BITFIELD64(ifh[1], 16, 1);
- info->vid = IFH_EXTRACT_BITFIELD64(ifh[1], 0, 12);
-
- return 0;
-}
-
-static int ocelot_rx_frame_word(struct ocelot *ocelot, u8 grp, bool ifh,
- u32 *rval)
-{
- u32 val;
- u32 bytes_valid;
-
- val = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
- if (val == XTR_NOT_READY) {
- if (ifh)
- return -EIO;
-
- do {
- val = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
- } while (val == XTR_NOT_READY);
- }
-
- switch (val) {
- case XTR_ABORT:
- return -EIO;
- case XTR_EOF_0:
- case XTR_EOF_1:
- case XTR_EOF_2:
- case XTR_EOF_3:
- case XTR_PRUNED:
- bytes_valid = XTR_VALID_BYTES(val);
- val = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
- if (val == XTR_ESCAPE)
- *rval = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
- else
- *rval = val;
-
- return bytes_valid;
- case XTR_ESCAPE:
- *rval = ocelot_read_rix(ocelot, QS_XTR_RD, grp);
-
- return 4;
- default:
- *rval = val;
-
- return 4;
- }
-}
-
static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
{
struct ocelot *ocelot = arg;
- int i = 0, grp = 0;
- int err = 0;
+ int grp = 0, err;
while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)) {
- struct skb_shared_hwtstamps *shhwtstamps;
struct ocelot_frame_info info = {};
struct ocelot_port_private *priv;
struct ocelot_port *ocelot_port;
- u64 tod_in_ns, full_ts_in_ns;
struct net_device *dev;
- u32 ifh[4], val, *buf;
- struct timespec64 ts;
- int sz, len, buf_len;
struct sk_buff *skb;
- for (i = 0; i < OCELOT_TAG_LEN / 4; i++) {
- err = ocelot_rx_frame_word(ocelot, grp, true, &ifh[i]);
- if (err != 4)
- goto out;
- }
-
- /* At this point the IFH was read correctly, so it is safe to
- * presume that there is no error. The err needs to be reset
- * otherwise a frame could come in CPU queue between the while
- * condition and the check for error later on. And in that case
- * the new frame is just removed and not processed.
- */
- err = 0;
+ err = ocelot_xtr_poll_xfh(ocelot, grp, &info);
+ if (err)
+ break;
- ocelot_parse_ifh(ifh, &info);
+ if (WARN_ON(info.port >= ocelot->num_phys_ports))
+ goto out;
ocelot_port = ocelot->ports[info.port];
priv = container_of(ocelot_port, struct ocelot_port_private,
port);
dev = priv->dev;
- skb = netdev_alloc_skb(dev, info.len);
-
- if (unlikely(!skb)) {
- netdev_err(dev, "Unable to allocate sk_buff\n");
- err = -ENOMEM;
- goto out;
- }
- buf_len = info.len - ETH_FCS_LEN;
- buf = (u32 *)skb_put(skb, buf_len);
-
- len = 0;
- do {
- sz = ocelot_rx_frame_word(ocelot, grp, false, &val);
- if (sz < 0) {
- err = sz;
- goto out;
- }
- *buf++ = val;
- len += sz;
- } while (len < buf_len);
-
- /* Read the FCS */
- sz = ocelot_rx_frame_word(ocelot, grp, false, &val);
- if (sz < 0) {
- err = sz;
- goto out;
- }
-
- /* Update the statistics if part of the FCS was read before */
- len -= ETH_FCS_LEN - sz;
-
- if (unlikely(dev->features & NETIF_F_RXFCS)) {
- buf = (u32 *)skb_put(skb, ETH_FCS_LEN);
- *buf = val;
- }
-
- if (ocelot->ptp) {
- ocelot_ptp_gettime64(&ocelot->ptp_info, &ts);
-
- tod_in_ns = ktime_set(ts.tv_sec, ts.tv_nsec);
- if ((tod_in_ns & 0xffffffff) < info.timestamp)
- full_ts_in_ns = (((tod_in_ns >> 32) - 1) << 32) |
- info.timestamp;
- else
- full_ts_in_ns = (tod_in_ns & GENMASK_ULL(63, 32)) |
- info.timestamp;
-
- shhwtstamps = skb_hwtstamps(skb);
- memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
- shhwtstamps->hwtstamp = full_ts_in_ns;
- }
-
- /* Everything we see on an interface that is in the HW bridge
- * has already been forwarded.
- */
- if (ocelot->bridge_mask & BIT(info.port))
- skb->offload_fwd_mark = 1;
+ err = ocelot_xtr_poll_frame(ocelot, grp, dev, &info, &skb);
+ if (err)
+ break;
- skb->protocol = eth_type_trans(skb, dev);
if (!skb_defer_rx_timestamp(skb))
netif_rx(skb);
- dev->stats.rx_bytes += len;
+ dev->stats.rx_bytes += info.len;
dev->stats.rx_packets++;
}