Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,72 @@
# SPDX-License-Identifier: GPL-2.0
#
# Renesas network device configuration
#
config NET_VENDOR_RENESAS
bool "Renesas devices"
default y
help
If you have a network (Ethernet) card belonging to this class, say Y.
Note that the answer to this question doesn't directly affect the
kernel: saying N will just cause the configurator to skip all
the questions about Renesas devices. If you say Y, you will be asked
for your specific device in the following questions.
if NET_VENDOR_RENESAS
config SH_ETH
tristate "Renesas SuperH Ethernet support"
depends on ARCH_RENESAS || SUPERH || COMPILE_TEST
select CRC32
select MII
select MDIO_BITBANG
select PHYLIB
help
Renesas SuperH Ethernet device driver.
config RAVB
tristate "Renesas Ethernet AVB support"
depends on ARCH_RENESAS || COMPILE_TEST
depends on PTP_1588_CLOCK_OPTIONAL
select CRC32
select MII
select MDIO_BITBANG
select PAGE_POOL
select PHYLIB
select RESET_CONTROLLER
help
Renesas Ethernet AVB device driver.
config RENESAS_ETHER_SWITCH
tristate "Renesas Ethernet Switch support"
depends on ARCH_RENESAS || COMPILE_TEST
depends on PTP_1588_CLOCK
select CRC32
select MII
select PHYLINK
select RENESAS_GEN4_PTP
help
Renesas Ethernet Switch device driver.
config RENESAS_GEN4_PTP
tristate "Renesas R-Car Gen4 gPTP support" if COMPILE_TEST
depends on PTP_1588_CLOCK
select CRC32
select MII
select PHYLIB
help
Renesas R-Car Gen4 gPTP device driver.
config RTSN
tristate "Renesas Ethernet-TSN support"
depends on ARCH_RENESAS || COMPILE_TEST
depends on PTP_1588_CLOCK
select CRC32
select PHYLIB
select RENESAS_GEN4_PTP
help
Renesas Ethernet-TSN device driver.
endif # NET_VENDOR_RENESAS
@@ -0,0 +1,16 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the Renesas network device drivers
#
obj-$(CONFIG_SH_ETH) += sh_eth.o
ravb-objs := ravb_main.o ravb_ptp.o
obj-$(CONFIG_RAVB) += ravb.o
rswitch-objs := rswitch_main.o rswitch_l2.o
obj-$(CONFIG_RENESAS_ETHER_SWITCH) += rswitch.o
obj-$(CONFIG_RENESAS_GEN4_PTP) += rcar_gen4_ptp.o
obj-$(CONFIG_RTSN) += rtsn.o
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,341 @@
// SPDX-License-Identifier: GPL-2.0+
/* PTP 1588 clock using the Renesas Ethernet AVB
*
* Copyright (C) 2013-2015 Renesas Electronics Corporation
* Copyright (C) 2015 Renesas Solutions Corp.
* Copyright (C) 2015-2016 Cogent Embedded, Inc. <source@cogentembedded.com>
*/
#include "ravb.h"
static int ravb_ptp_tcr_request(struct ravb_private *priv, u32 request)
{
struct net_device *ndev = priv->ndev;
int error;
error = ravb_wait(ndev, GCCR, GCCR_TCR, GCCR_TCR_NOREQ);
if (error)
return error;
ravb_modify(ndev, GCCR, request, request);
return ravb_wait(ndev, GCCR, GCCR_TCR, GCCR_TCR_NOREQ);
}
/* Caller must hold the lock */
static int ravb_ptp_time_read(struct ravb_private *priv, struct timespec64 *ts)
{
struct net_device *ndev = priv->ndev;
int error;
error = ravb_ptp_tcr_request(priv, GCCR_TCR_CAPTURE);
if (error)
return error;
ts->tv_nsec = ravb_read(ndev, GCT0);
ts->tv_sec = ravb_read(ndev, GCT1) |
((s64)ravb_read(ndev, GCT2) << 32);
return 0;
}
/* Caller must hold the lock */
static int ravb_ptp_time_write(struct ravb_private *priv,
const struct timespec64 *ts)
{
struct net_device *ndev = priv->ndev;
int error;
u32 gccr;
error = ravb_ptp_tcr_request(priv, GCCR_TCR_RESET);
if (error)
return error;
gccr = ravb_read(ndev, GCCR);
if (gccr & GCCR_LTO)
return -EBUSY;
ravb_write(ndev, ts->tv_nsec, GTO0);
ravb_write(ndev, ts->tv_sec, GTO1);
ravb_write(ndev, (ts->tv_sec >> 32) & 0xffff, GTO2);
ravb_write(ndev, gccr | GCCR_LTO, GCCR);
return 0;
}
/* Caller must hold the lock */
static int ravb_ptp_update_compare(struct ravb_private *priv, u32 ns)
{
struct net_device *ndev = priv->ndev;
/* When the comparison value (GPTC.PTCV) is in range of
* [x-1 to x+1] (x is the configured increment value in
* GTI.TIV), it may happen that a comparison match is
* not detected when the timer wraps around.
*/
u32 gti_ns_plus_1 = (priv->ptp.current_addend >> 20) + 1;
u32 gccr;
if (ns < gti_ns_plus_1)
ns = gti_ns_plus_1;
else if (ns > 0 - gti_ns_plus_1)
ns = 0 - gti_ns_plus_1;
gccr = ravb_read(ndev, GCCR);
if (gccr & GCCR_LPTC)
return -EBUSY;
ravb_write(ndev, ns, GPTC);
ravb_write(ndev, gccr | GCCR_LPTC, GCCR);
return 0;
}
/* PTP clock operations */
static int ravb_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
{
struct ravb_private *priv = container_of(ptp, struct ravb_private,
ptp.info);
struct net_device *ndev = priv->ndev;
unsigned long flags;
u32 addend;
u32 gccr;
addend = (u32)adjust_by_scaled_ppm(priv->ptp.default_addend,
scaled_ppm);
spin_lock_irqsave(&priv->lock, flags);
priv->ptp.current_addend = addend;
gccr = ravb_read(ndev, GCCR);
if (gccr & GCCR_LTI) {
spin_unlock_irqrestore(&priv->lock, flags);
return -EBUSY;
}
ravb_write(ndev, addend & GTI_TIV, GTI);
ravb_write(ndev, gccr | GCCR_LTI, GCCR);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int ravb_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
struct ravb_private *priv = container_of(ptp, struct ravb_private,
ptp.info);
struct timespec64 ts;
unsigned long flags;
int error;
spin_lock_irqsave(&priv->lock, flags);
error = ravb_ptp_time_read(priv, &ts);
if (!error) {
u64 now = ktime_to_ns(timespec64_to_ktime(ts));
ts = ns_to_timespec64(now + delta);
error = ravb_ptp_time_write(priv, &ts);
}
spin_unlock_irqrestore(&priv->lock, flags);
return error;
}
static int ravb_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts)
{
struct ravb_private *priv = container_of(ptp, struct ravb_private,
ptp.info);
unsigned long flags;
int error;
spin_lock_irqsave(&priv->lock, flags);
error = ravb_ptp_time_read(priv, ts);
spin_unlock_irqrestore(&priv->lock, flags);
return error;
}
static int ravb_ptp_settime64(struct ptp_clock_info *ptp,
const struct timespec64 *ts)
{
struct ravb_private *priv = container_of(ptp, struct ravb_private,
ptp.info);
unsigned long flags;
int error;
spin_lock_irqsave(&priv->lock, flags);
error = ravb_ptp_time_write(priv, ts);
spin_unlock_irqrestore(&priv->lock, flags);
return error;
}
static int ravb_ptp_extts(struct ptp_clock_info *ptp,
struct ptp_extts_request *req, int on)
{
struct ravb_private *priv = container_of(ptp, struct ravb_private,
ptp.info);
const struct ravb_hw_info *info = priv->info;
struct net_device *ndev = priv->ndev;
unsigned long flags;
if (req->index)
return -EINVAL;
if (priv->ptp.extts[req->index] == on)
return 0;
priv->ptp.extts[req->index] = on;
spin_lock_irqsave(&priv->lock, flags);
if (!info->irq_en_dis)
ravb_modify(ndev, GIC, GIC_PTCE, on ? GIC_PTCE : 0);
else if (on)
ravb_write(ndev, GIE_PTCS, GIE);
else
ravb_write(ndev, GID_PTCD, GID);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int ravb_ptp_perout(struct ptp_clock_info *ptp,
struct ptp_perout_request *req, int on)
{
struct ravb_private *priv = container_of(ptp, struct ravb_private,
ptp.info);
const struct ravb_hw_info *info = priv->info;
struct net_device *ndev = priv->ndev;
struct ravb_ptp_perout *perout;
unsigned long flags;
int error = 0;
if (req->index)
return -EINVAL;
if (on) {
u64 start_ns;
u64 period_ns;
start_ns = req->start.sec * NSEC_PER_SEC + req->start.nsec;
period_ns = req->period.sec * NSEC_PER_SEC + req->period.nsec;
if (start_ns > U32_MAX) {
netdev_warn(ndev,
"ptp: start value (nsec) is over limit. Maximum size of start is only 32 bits\n");
return -ERANGE;
}
if (period_ns > U32_MAX) {
netdev_warn(ndev,
"ptp: period value (nsec) is over limit. Maximum size of period is only 32 bits\n");
return -ERANGE;
}
spin_lock_irqsave(&priv->lock, flags);
perout = &priv->ptp.perout[req->index];
perout->target = (u32)start_ns;
perout->period = (u32)period_ns;
error = ravb_ptp_update_compare(priv, (u32)start_ns);
if (!error) {
/* Unmask interrupt */
if (!info->irq_en_dis)
ravb_modify(ndev, GIC, GIC_PTME, GIC_PTME);
else
ravb_write(ndev, GIE_PTMS0, GIE);
}
} else {
spin_lock_irqsave(&priv->lock, flags);
perout = &priv->ptp.perout[req->index];
perout->period = 0;
/* Mask interrupt */
if (!info->irq_en_dis)
ravb_modify(ndev, GIC, GIC_PTME, 0);
else
ravb_write(ndev, GID_PTMD0, GID);
}
spin_unlock_irqrestore(&priv->lock, flags);
return error;
}
static int ravb_ptp_enable(struct ptp_clock_info *ptp,
struct ptp_clock_request *req, int on)
{
switch (req->type) {
case PTP_CLK_REQ_EXTTS:
return ravb_ptp_extts(ptp, &req->extts, on);
case PTP_CLK_REQ_PEROUT:
return ravb_ptp_perout(ptp, &req->perout, on);
default:
return -EOPNOTSUPP;
}
}
static const struct ptp_clock_info ravb_ptp_info = {
.owner = THIS_MODULE,
.name = "ravb clock",
.max_adj = 50000000,
.n_ext_ts = N_EXT_TS,
.n_per_out = N_PER_OUT,
.supported_extts_flags = PTP_RISING_EDGE | PTP_FALLING_EDGE,
.adjfine = ravb_ptp_adjfine,
.adjtime = ravb_ptp_adjtime,
.gettime64 = ravb_ptp_gettime64,
.settime64 = ravb_ptp_settime64,
.enable = ravb_ptp_enable,
};
/* Caller must hold the lock */
void ravb_ptp_interrupt(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
u32 gis = ravb_read(ndev, GIS);
gis &= ravb_read(ndev, GIC);
if (gis & GIS_PTCF) {
struct ptp_clock_event event;
event.type = PTP_CLOCK_EXTTS;
event.index = 0;
event.timestamp = ravb_read(ndev, GCPT);
ptp_clock_event(priv->ptp.clock, &event);
}
if (gis & GIS_PTMF) {
struct ravb_ptp_perout *perout = priv->ptp.perout;
if (perout->period) {
perout->target += perout->period;
ravb_ptp_update_compare(priv, perout->target);
}
}
ravb_write(ndev, ~(gis | GIS_RESERVED), GIS);
}
void ravb_ptp_init(struct net_device *ndev, struct platform_device *pdev)
{
struct ravb_private *priv = netdev_priv(ndev);
unsigned long flags;
priv->ptp.info = ravb_ptp_info;
priv->ptp.default_addend = ravb_read(ndev, GTI);
priv->ptp.current_addend = priv->ptp.default_addend;
spin_lock_irqsave(&priv->lock, flags);
ravb_wait(ndev, GCCR, GCCR_TCR, GCCR_TCR_NOREQ);
ravb_modify(ndev, GCCR, GCCR_TCSS, GCCR_TCSS_ADJGPTP);
spin_unlock_irqrestore(&priv->lock, flags);
priv->ptp.clock = ptp_clock_register(&priv->ptp.info, &pdev->dev);
}
void ravb_ptp_stop(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
ravb_write(ndev, 0, GIC);
ravb_write(ndev, 0, GIS);
ptp_clock_unregister(priv->ptp.clock);
}
@@ -0,0 +1,219 @@
// SPDX-License-Identifier: GPL-2.0
/* Renesas R-Car Gen4 gPTP device driver
*
* Copyright (C) 2022 Renesas Electronics Corporation
*/
#include <linux/err.h>
#include <linux/etherdevice.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/slab.h>
#include "rcar_gen4_ptp.h"
#define PTPTMEC_REG 0x0010
#define PTPTMDC_REG 0x0014
#define PTPTIVC0_REG 0x0020
#define PTPTOVC00_REG 0x0030
#define PTPTOVC10_REG 0x0034
#define PTPTOVC20_REG 0x0038
#define PTPGPTPTM00_REG 0x0050
#define PTPGPTPTM10_REG 0x0054
#define PTPGPTPTM20_REG 0x0058
struct rcar_gen4_ptp_private {
void __iomem *addr;
struct ptp_clock *clock;
struct ptp_clock_info info;
spinlock_t lock; /* For multiple registers access */
s64 default_addend;
bool initialized;
};
#define ptp_to_priv(ptp) container_of(ptp, struct rcar_gen4_ptp_private, info)
static int rcar_gen4_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
{
struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);
s64 addend = ptp_priv->default_addend;
bool neg_adj = scaled_ppm < 0;
s64 diff;
if (neg_adj)
scaled_ppm = -scaled_ppm;
diff = div_s64(addend * scaled_ppm_to_ppb(scaled_ppm), NSEC_PER_SEC);
addend = neg_adj ? addend - diff : addend + diff;
iowrite32(addend, ptp_priv->addr + PTPTIVC0_REG);
return 0;
}
static void _rcar_gen4_ptp_gettime(struct ptp_clock_info *ptp,
struct timespec64 *ts)
{
struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);
lockdep_assert_held(&ptp_priv->lock);
ts->tv_nsec = ioread32(ptp_priv->addr + PTPGPTPTM00_REG);
ts->tv_sec = ioread32(ptp_priv->addr + PTPGPTPTM10_REG) |
((s64)ioread32(ptp_priv->addr + PTPGPTPTM20_REG) << 32);
}
static int rcar_gen4_ptp_gettime(struct ptp_clock_info *ptp,
struct timespec64 *ts)
{
struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);
unsigned long flags;
spin_lock_irqsave(&ptp_priv->lock, flags);
_rcar_gen4_ptp_gettime(ptp, ts);
spin_unlock_irqrestore(&ptp_priv->lock, flags);
return 0;
}
/* Caller must hold the lock */
static void _rcar_gen4_ptp_settime(struct ptp_clock_info *ptp,
const struct timespec64 *ts)
{
struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);
iowrite32(1, ptp_priv->addr + PTPTMDC_REG);
iowrite32(0, ptp_priv->addr + PTPTOVC20_REG);
iowrite32(0, ptp_priv->addr + PTPTOVC10_REG);
iowrite32(0, ptp_priv->addr + PTPTOVC00_REG);
iowrite32(1, ptp_priv->addr + PTPTMEC_REG);
iowrite32(ts->tv_sec >> 32, ptp_priv->addr + PTPTOVC20_REG);
iowrite32(ts->tv_sec, ptp_priv->addr + PTPTOVC10_REG);
iowrite32(ts->tv_nsec, ptp_priv->addr + PTPTOVC00_REG);
}
static int rcar_gen4_ptp_settime(struct ptp_clock_info *ptp,
const struct timespec64 *ts)
{
struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);
unsigned long flags;
spin_lock_irqsave(&ptp_priv->lock, flags);
_rcar_gen4_ptp_settime(ptp, ts);
spin_unlock_irqrestore(&ptp_priv->lock, flags);
return 0;
}
static int rcar_gen4_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
struct rcar_gen4_ptp_private *ptp_priv = ptp_to_priv(ptp);
struct timespec64 ts;
unsigned long flags;
s64 now;
spin_lock_irqsave(&ptp_priv->lock, flags);
_rcar_gen4_ptp_gettime(ptp, &ts);
now = ktime_to_ns(timespec64_to_ktime(ts));
ts = ns_to_timespec64(now + delta);
_rcar_gen4_ptp_settime(ptp, &ts);
spin_unlock_irqrestore(&ptp_priv->lock, flags);
return 0;
}
static int rcar_gen4_ptp_enable(struct ptp_clock_info *ptp,
struct ptp_clock_request *rq, int on)
{
return -EOPNOTSUPP;
}
static struct ptp_clock_info rcar_gen4_ptp_info = {
.owner = THIS_MODULE,
.name = "rcar_gen4_ptp",
.max_adj = 50000000,
.adjfine = rcar_gen4_ptp_adjfine,
.adjtime = rcar_gen4_ptp_adjtime,
.gettime64 = rcar_gen4_ptp_gettime,
.settime64 = rcar_gen4_ptp_settime,
.enable = rcar_gen4_ptp_enable,
};
static s64 rcar_gen4_ptp_rate_to_increment(u32 rate)
{
/* Timer increment in ns.
* bit[31:27] - integer
* bit[26:0] - decimal
* increment[ns] = perid[ns] * 2^27 => (1ns * 2^27) / rate[hz]
*/
return div_s64(1000000000LL << 27, rate);
}
int rcar_gen4_ptp_register(struct rcar_gen4_ptp_private *ptp_priv, u32 rate)
{
if (ptp_priv->initialized)
return 0;
spin_lock_init(&ptp_priv->lock);
ptp_priv->default_addend = rcar_gen4_ptp_rate_to_increment(rate);
iowrite32(ptp_priv->default_addend, ptp_priv->addr + PTPTIVC0_REG);
ptp_priv->clock = ptp_clock_register(&ptp_priv->info, NULL);
if (IS_ERR(ptp_priv->clock))
return PTR_ERR(ptp_priv->clock);
iowrite32(0x01, ptp_priv->addr + PTPTMEC_REG);
ptp_priv->initialized = true;
return 0;
}
EXPORT_SYMBOL_GPL(rcar_gen4_ptp_register);
int rcar_gen4_ptp_unregister(struct rcar_gen4_ptp_private *ptp_priv)
{
iowrite32(1, ptp_priv->addr + PTPTMDC_REG);
return ptp_clock_unregister(ptp_priv->clock);
}
EXPORT_SYMBOL_GPL(rcar_gen4_ptp_unregister);
struct rcar_gen4_ptp_private *rcar_gen4_ptp_alloc(struct platform_device *pdev,
void __iomem *addr)
{
struct rcar_gen4_ptp_private *ptp;
ptp = devm_kzalloc(&pdev->dev, sizeof(*ptp), GFP_KERNEL);
if (!ptp)
return NULL;
ptp->info = rcar_gen4_ptp_info;
ptp->addr = addr;
return ptp;
}
EXPORT_SYMBOL_GPL(rcar_gen4_ptp_alloc);
int rcar_gen4_ptp_clock_index(struct rcar_gen4_ptp_private *priv)
{
if (!priv->initialized)
return -1;
return ptp_clock_index(priv->clock);
}
EXPORT_SYMBOL_GPL(rcar_gen4_ptp_clock_index);
void rcar_gen4_ptp_gettime64(struct rcar_gen4_ptp_private *priv,
struct timespec64 *ts)
{
if (!priv->initialized)
return;
priv->info.gettime64(&priv->info, ts);
}
EXPORT_SYMBOL_GPL(rcar_gen4_ptp_gettime64);
MODULE_AUTHOR("Yoshihiro Shimoda");
MODULE_DESCRIPTION("Renesas R-Car Gen4 gPTP driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Renesas R-Car Gen4 gPTP device driver
*
* Copyright (C) 2022 Renesas Electronics Corporation
*/
#ifndef __RCAR_GEN4_PTP_H__
#define __RCAR_GEN4_PTP_H__
struct rcar_gen4_ptp_private;
int rcar_gen4_ptp_register(struct rcar_gen4_ptp_private *ptp_priv, u32 rate);
int rcar_gen4_ptp_unregister(struct rcar_gen4_ptp_private *ptp_priv);
struct rcar_gen4_ptp_private *rcar_gen4_ptp_alloc(struct platform_device *pdev,
void __iomem *addr);
int rcar_gen4_ptp_clock_index(struct rcar_gen4_ptp_private *priv);
void rcar_gen4_ptp_gettime64(struct rcar_gen4_ptp_private *priv,
struct timespec64 *ts);
#endif /* #ifndef __RCAR_GEN4_PTP_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,315 @@
// SPDX-License-Identifier: GPL-2.0
/* Renesas Ethernet Switch device driver
*
* Copyright (C) 2025 - 2026 Renesas Electronics Corporation
*/
#include <linux/err.h>
#include <linux/etherdevice.h>
#include <linux/if_bridge.h>
#include <linux/kernel.h>
#include <net/switchdev.h>
#include "rswitch.h"
#include "rswitch_l2.h"
static bool rdev_for_l2_offload(struct rswitch_device *rdev)
{
return rdev->priv->offload_brdev &&
rdev->brdev == rdev->priv->offload_brdev &&
(test_bit(rdev->port, rdev->priv->opened_ports));
}
static void rswitch_change_l2_hw_offloading(struct rswitch_device *rdev,
bool start, bool learning)
{
u32 bits = learning ? FWPC0_MACSSA | FWPC0_MACHLA | FWPC0_MACHMA : FWPC0_MACDSA;
u32 clear = start ? 0 : bits;
u32 set = start ? bits : 0;
if ((learning && rdev->learning_offloaded == start) ||
(!learning && rdev->forwarding_offloaded == start))
return;
rswitch_modify(rdev->priv->addr, FWPC0(rdev->port), clear, set);
if (learning)
rdev->learning_offloaded = start;
else
rdev->forwarding_offloaded = start;
netdev_info(rdev->ndev, "%s hw %s\n", start ? "starting" : "stopping",
learning ? "learning" : "forwarding");
}
static void rswitch_update_l2_hw_learning(struct rswitch_private *priv)
{
struct rswitch_device *rdev;
bool learning_needed;
rswitch_for_all_ports(priv, rdev) {
if (rdev_for_l2_offload(rdev))
learning_needed = rdev->learning_requested;
else
learning_needed = false;
rswitch_change_l2_hw_offloading(rdev, learning_needed, true);
}
}
static void rswitch_update_l2_hw_forwarding(struct rswitch_private *priv)
{
struct rswitch_device *rdev;
bool new_forwarding_offload;
unsigned int fwd_mask;
/* calculate fwd_mask with zeroes in bits corresponding to ports that
* shall participate in hardware forwarding
*/
fwd_mask = GENMASK(RSWITCH_NUM_AGENTS - 1, 0);
rswitch_for_all_ports(priv, rdev) {
if (rdev_for_l2_offload(rdev) && rdev->forwarding_requested)
fwd_mask &= ~BIT(rdev->port);
}
rswitch_for_all_ports(priv, rdev) {
new_forwarding_offload = (rdev_for_l2_offload(rdev) && rdev->forwarding_requested);
if (new_forwarding_offload || rdev->forwarding_offloaded) {
/* Update allowed offload destinations even for ports
* with L2 offload enabled earlier.
*
* Do not allow L2 forwarding to self for hw port.
*/
iowrite32(FIELD_PREP(FWCP2_LTWFW_MASK, fwd_mask | BIT(rdev->port)),
priv->addr + FWPC2(rdev->port));
}
if (new_forwarding_offload && !rdev->forwarding_offloaded)
rswitch_change_l2_hw_offloading(rdev, true, false);
else if (!new_forwarding_offload && rdev->forwarding_offloaded)
rswitch_change_l2_hw_offloading(rdev, false, false);
}
}
void rswitch_update_l2_offload(struct rswitch_private *priv)
{
rswitch_update_l2_hw_learning(priv);
rswitch_update_l2_hw_forwarding(priv);
}
static void rswitch_update_offload_brdev(struct rswitch_private *priv)
{
struct net_device *offload_brdev = NULL;
struct rswitch_device *rdev, *rdev2;
rswitch_for_all_ports(priv, rdev) {
if (!rdev->brdev)
continue;
rswitch_for_all_ports(priv, rdev2) {
if (rdev2 == rdev)
break;
if (rdev2->brdev == rdev->brdev) {
offload_brdev = rdev->brdev;
break;
}
}
if (offload_brdev)
break;
}
if (offload_brdev == priv->offload_brdev)
dev_dbg(&priv->pdev->dev,
"changing l2 offload from %s to %s\n",
netdev_name(priv->offload_brdev),
netdev_name(offload_brdev));
else if (offload_brdev)
dev_dbg(&priv->pdev->dev, "starting l2 offload for %s\n",
netdev_name(offload_brdev));
else if (!offload_brdev)
dev_dbg(&priv->pdev->dev, "stopping l2 offload for %s\n",
netdev_name(priv->offload_brdev));
priv->offload_brdev = offload_brdev;
rswitch_update_l2_offload(priv);
}
static bool rswitch_port_check(const struct net_device *ndev)
{
return is_rdev(ndev);
}
static void rswitch_port_update_brdev(struct net_device *ndev,
struct net_device *brdev)
{
struct rswitch_device *rdev;
if (!is_rdev(ndev))
return;
rdev = netdev_priv(ndev);
rdev->brdev = brdev;
rswitch_update_offload_brdev(rdev->priv);
}
static int rswitch_port_update_stp_state(struct net_device *ndev, u8 stp_state)
{
struct rswitch_device *rdev;
if (!is_rdev(ndev))
return -ENODEV;
rdev = netdev_priv(ndev);
rdev->learning_requested = (stp_state == BR_STATE_LEARNING ||
stp_state == BR_STATE_FORWARDING);
rdev->forwarding_requested = (stp_state == BR_STATE_FORWARDING);
rswitch_update_l2_offload(rdev->priv);
return 0;
}
static int rswitch_netdevice_event(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
struct netdev_notifier_changeupper_info *info;
struct net_device *brdev;
if (!rswitch_port_check(ndev))
return NOTIFY_DONE;
if (event != NETDEV_CHANGEUPPER)
return NOTIFY_DONE;
info = ptr;
if (netif_is_bridge_master(info->upper_dev)) {
brdev = info->linking ? info->upper_dev : NULL;
rswitch_port_update_brdev(ndev, brdev);
}
return NOTIFY_OK;
}
static int rswitch_update_ageing_time(struct net_device *ndev, clock_t time)
{
struct rswitch_device *rdev = netdev_priv(ndev);
u32 reg_val;
if (!is_rdev(ndev))
return -ENODEV;
if (!FIELD_FIT(FWMACAGC_MACAGT, time))
return -EINVAL;
reg_val = FIELD_PREP(FWMACAGC_MACAGT, time);
reg_val |= FWMACAGC_MACAGE | FWMACAGC_MACAGSL;
iowrite32(reg_val, rdev->priv->addr + FWMACAGC);
return 0;
}
static int rswitch_port_attr_set(struct net_device *ndev, const void *ctx,
const struct switchdev_attr *attr,
struct netlink_ext_ack *extack)
{
switch (attr->id) {
case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
return rswitch_port_update_stp_state(ndev, attr->u.stp_state);
case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
return rswitch_update_ageing_time(ndev, attr->u.ageing_time);
default:
return -EOPNOTSUPP;
}
}
static int rswitch_switchdev_event(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct net_device *ndev = switchdev_notifier_info_to_dev(ptr);
int ret;
if (event == SWITCHDEV_PORT_ATTR_SET) {
ret = switchdev_handle_port_attr_set(ndev, ptr,
rswitch_port_check,
rswitch_port_attr_set);
return notifier_from_errno(ret);
}
if (!rswitch_port_check(ndev))
return NOTIFY_DONE;
return notifier_from_errno(-EOPNOTSUPP);
}
static int rswitch_switchdev_blocking_event(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct net_device *ndev = switchdev_notifier_info_to_dev(ptr);
int ret;
switch (event) {
case SWITCHDEV_PORT_OBJ_ADD:
return -EOPNOTSUPP;
case SWITCHDEV_PORT_OBJ_DEL:
return -EOPNOTSUPP;
case SWITCHDEV_PORT_ATTR_SET:
ret = switchdev_handle_port_attr_set(ndev, ptr,
rswitch_port_check,
rswitch_port_attr_set);
break;
default:
if (!rswitch_port_check(ndev))
return NOTIFY_DONE;
ret = -EOPNOTSUPP;
}
return notifier_from_errno(ret);
}
static struct notifier_block rswitch_netdevice_nb = {
.notifier_call = rswitch_netdevice_event,
};
static struct notifier_block rswitch_switchdev_nb = {
.notifier_call = rswitch_switchdev_event,
};
static struct notifier_block rswitch_switchdev_blocking_nb = {
.notifier_call = rswitch_switchdev_blocking_event,
};
int rswitch_register_notifiers(void)
{
int ret;
ret = register_netdevice_notifier(&rswitch_netdevice_nb);
if (ret)
goto register_netdevice_notifier_failed;
ret = register_switchdev_notifier(&rswitch_switchdev_nb);
if (ret)
goto register_switchdev_notifier_failed;
ret = register_switchdev_blocking_notifier(&rswitch_switchdev_blocking_nb);
if (ret)
goto register_switchdev_blocking_notifier_failed;
return 0;
register_switchdev_blocking_notifier_failed:
unregister_switchdev_notifier(&rswitch_switchdev_nb);
register_switchdev_notifier_failed:
unregister_netdevice_notifier(&rswitch_netdevice_nb);
register_netdevice_notifier_failed:
return ret;
}
void rswitch_unregister_notifiers(void)
{
unregister_switchdev_blocking_notifier(&rswitch_switchdev_blocking_nb);
unregister_switchdev_notifier(&rswitch_switchdev_nb);
unregister_netdevice_notifier(&rswitch_netdevice_nb);
}
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Renesas Ethernet Switch device driver
*
* Copyright (C) 2025 Renesas Electronics Corporation
*/
#ifndef __RSWITCH_L2_H__
#define __RSWITCH_L2_H__
void rswitch_update_l2_offload(struct rswitch_private *priv);
int rswitch_register_notifiers(void);
void rswitch_unregister_notifiers(void);
#endif /* #ifndef __RSWITCH_L2_H__ */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,464 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Renesas Ethernet-TSN device driver
*
* Copyright (C) 2022 Renesas Electronics Corporation
* Copyright (C) 2023 Niklas Söderlund <niklas.soderlund@ragnatech.se>
*/
#ifndef __RTSN_H__
#define __RTSN_H__
#include <linux/types.h>
#define AXIBMI 0x0000
#define TSNMHD 0x1000
#define RMSO 0x2000
#define RMRO 0x3800
enum rtsn_reg {
AXIWC = AXIBMI + 0x0000,
AXIRC = AXIBMI + 0x0004,
TDPC0 = AXIBMI + 0x0010,
TFT = AXIBMI + 0x0090,
TATLS0 = AXIBMI + 0x00a0,
TATLS1 = AXIBMI + 0x00a4,
TATLR = AXIBMI + 0x00a8,
RATLS0 = AXIBMI + 0x00b0,
RATLS1 = AXIBMI + 0x00b4,
RATLR = AXIBMI + 0x00b8,
TSA0 = AXIBMI + 0x00c0,
TSS0 = AXIBMI + 0x00c4,
TRCR0 = AXIBMI + 0x0140,
RIDAUAS0 = AXIBMI + 0x0180,
RR = AXIBMI + 0x0200,
TATS = AXIBMI + 0x0210,
TATSR0 = AXIBMI + 0x0214,
TATSR1 = AXIBMI + 0x0218,
TATSR2 = AXIBMI + 0x021c,
RATS = AXIBMI + 0x0220,
RATSR0 = AXIBMI + 0x0224,
RATSR1 = AXIBMI + 0x0228,
RATSR2 = AXIBMI + 0x022c,
RIDASM0 = AXIBMI + 0x0240,
RIDASAM0 = AXIBMI + 0x0244,
RIDACAM0 = AXIBMI + 0x0248,
EIS0 = AXIBMI + 0x0300,
EIE0 = AXIBMI + 0x0304,
EID0 = AXIBMI + 0x0308,
EIS1 = AXIBMI + 0x0310,
EIE1 = AXIBMI + 0x0314,
EID1 = AXIBMI + 0x0318,
TCEIS0 = AXIBMI + 0x0340,
TCEIE0 = AXIBMI + 0x0344,
TCEID0 = AXIBMI + 0x0348,
RFSEIS0 = AXIBMI + 0x04c0,
RFSEIE0 = AXIBMI + 0x04c4,
RFSEID0 = AXIBMI + 0x04c8,
RFEIS0 = AXIBMI + 0x0540,
RFEIE0 = AXIBMI + 0x0544,
RFEID0 = AXIBMI + 0x0548,
RCEIS0 = AXIBMI + 0x05c0,
RCEIE0 = AXIBMI + 0x05c4,
RCEID0 = AXIBMI + 0x05c8,
RIDAOIS = AXIBMI + 0x0640,
RIDAOIE = AXIBMI + 0x0644,
RIDAOID = AXIBMI + 0x0648,
TSFEIS = AXIBMI + 0x06c0,
TSFEIE = AXIBMI + 0x06c4,
TSFEID = AXIBMI + 0x06c8,
TSCEIS = AXIBMI + 0x06d0,
TSCEIE = AXIBMI + 0x06d4,
TSCEID = AXIBMI + 0x06d8,
DIS = AXIBMI + 0x0b00,
DIE = AXIBMI + 0x0b04,
DID = AXIBMI + 0x0b08,
TDIS0 = AXIBMI + 0x0b10,
TDIE0 = AXIBMI + 0x0b14,
TDID0 = AXIBMI + 0x0b18,
RDIS0 = AXIBMI + 0x0b90,
RDIE0 = AXIBMI + 0x0b94,
RDID0 = AXIBMI + 0x0b98,
TSDIS = AXIBMI + 0x0c10,
TSDIE = AXIBMI + 0x0c14,
TSDID = AXIBMI + 0x0c18,
GPOUT = AXIBMI + 0x6000,
OCR = TSNMHD + 0x0000,
OSR = TSNMHD + 0x0004,
SWR = TSNMHD + 0x0008,
SIS = TSNMHD + 0x000c,
GIS = TSNMHD + 0x0010,
GIE = TSNMHD + 0x0014,
GID = TSNMHD + 0x0018,
TIS1 = TSNMHD + 0x0020,
TIE1 = TSNMHD + 0x0024,
TID1 = TSNMHD + 0x0028,
TIS2 = TSNMHD + 0x0030,
TIE2 = TSNMHD + 0x0034,
TID2 = TSNMHD + 0x0038,
RIS = TSNMHD + 0x0040,
RIE = TSNMHD + 0x0044,
RID = TSNMHD + 0x0048,
TGC1 = TSNMHD + 0x0050,
TGC2 = TSNMHD + 0x0054,
TFS0 = TSNMHD + 0x0060,
TCF0 = TSNMHD + 0x0070,
TCR1 = TSNMHD + 0x0080,
TCR2 = TSNMHD + 0x0084,
TCR3 = TSNMHD + 0x0088,
TCR4 = TSNMHD + 0x008c,
TMS0 = TSNMHD + 0x0090,
TSR1 = TSNMHD + 0x00b0,
TSR2 = TSNMHD + 0x00b4,
TSR3 = TSNMHD + 0x00b8,
TSR4 = TSNMHD + 0x00bc,
TSR5 = TSNMHD + 0x00c0,
RGC = TSNMHD + 0x00d0,
RDFCR = TSNMHD + 0x00d4,
RCFCR = TSNMHD + 0x00d8,
REFCNCR = TSNMHD + 0x00dc,
RSR1 = TSNMHD + 0x00e0,
RSR2 = TSNMHD + 0x00e4,
RSR3 = TSNMHD + 0x00e8,
TCIS = TSNMHD + 0x01e0,
TCIE = TSNMHD + 0x01e4,
TCID = TSNMHD + 0x01e8,
TPTPC = TSNMHD + 0x01f0,
TTML = TSNMHD + 0x01f4,
TTJ = TSNMHD + 0x01f8,
TCC = TSNMHD + 0x0200,
TCS = TSNMHD + 0x0204,
TGS = TSNMHD + 0x020c,
TACST0 = TSNMHD + 0x0210,
TACST1 = TSNMHD + 0x0214,
TACST2 = TSNMHD + 0x0218,
TALIT0 = TSNMHD + 0x0220,
TALIT1 = TSNMHD + 0x0224,
TALIT2 = TSNMHD + 0x0228,
TAEN0 = TSNMHD + 0x0230,
TAEN1 = TSNMHD + 0x0234,
TASFE = TSNMHD + 0x0240,
TACLL0 = TSNMHD + 0x0250,
TACLL1 = TSNMHD + 0x0254,
TACLL2 = TSNMHD + 0x0258,
CACC = TSNMHD + 0x0260,
CCS = TSNMHD + 0x0264,
CAIV0 = TSNMHD + 0x0270,
CAUL0 = TSNMHD + 0x0290,
TOCST0 = TSNMHD + 0x0300,
TOCST1 = TSNMHD + 0x0304,
TOCST2 = TSNMHD + 0x0308,
TOLIT0 = TSNMHD + 0x0310,
TOLIT1 = TSNMHD + 0x0314,
TOLIT2 = TSNMHD + 0x0318,
TOEN0 = TSNMHD + 0x0320,
TOEN1 = TSNMHD + 0x0324,
TOSFE = TSNMHD + 0x0330,
TCLR0 = TSNMHD + 0x0340,
TCLR1 = TSNMHD + 0x0344,
TCLR2 = TSNMHD + 0x0348,
TSMS = TSNMHD + 0x0350,
COCC = TSNMHD + 0x0360,
COIV0 = TSNMHD + 0x03b0,
COUL0 = TSNMHD + 0x03d0,
QSTMACU0 = TSNMHD + 0x0400,
QSTMACD0 = TSNMHD + 0x0404,
QSTMAMU0 = TSNMHD + 0x0408,
QSTMAMD0 = TSNMHD + 0x040c,
QSFTVL0 = TSNMHD + 0x0410,
QSFTVLM0 = TSNMHD + 0x0414,
QSFTMSD0 = TSNMHD + 0x0418,
QSFTGMI0 = TSNMHD + 0x041c,
QSFTLS = TSNMHD + 0x0600,
QSFTLIS = TSNMHD + 0x0604,
QSFTLIE = TSNMHD + 0x0608,
QSFTLID = TSNMHD + 0x060c,
QSMSMC = TSNMHD + 0x0610,
QSGTMC = TSNMHD + 0x0614,
QSEIS = TSNMHD + 0x0618,
QSEIE = TSNMHD + 0x061c,
QSEID = TSNMHD + 0x0620,
QGACST0 = TSNMHD + 0x0630,
QGACST1 = TSNMHD + 0x0634,
QGACST2 = TSNMHD + 0x0638,
QGALIT1 = TSNMHD + 0x0640,
QGALIT2 = TSNMHD + 0x0644,
QGAEN0 = TSNMHD + 0x0648,
QGAEN1 = TSNMHD + 0x074c,
QGIGS = TSNMHD + 0x0650,
QGGC = TSNMHD + 0x0654,
QGATL0 = TSNMHD + 0x0664,
QGATL1 = TSNMHD + 0x0668,
QGATL2 = TSNMHD + 0x066c,
QGOCST0 = TSNMHD + 0x0670,
QGOCST1 = TSNMHD + 0x0674,
QGOCST2 = TSNMHD + 0x0678,
QGOLIT0 = TSNMHD + 0x067c,
QGOLIT1 = TSNMHD + 0x0680,
QGOLIT2 = TSNMHD + 0x0684,
QGOEN0 = TSNMHD + 0x0688,
QGOEN1 = TSNMHD + 0x068c,
QGTRO = TSNMHD + 0x0690,
QGTR1 = TSNMHD + 0x0694,
QGTR2 = TSNMHD + 0x0698,
QGFSMS = TSNMHD + 0x069c,
QTMIS = TSNMHD + 0x06e0,
QTMIE = TSNMHD + 0x06e4,
QTMID = TSNMHD + 0x06e8,
QMEC = TSNMHD + 0x0700,
QMMC = TSNMHD + 0x0704,
QRFDC = TSNMHD + 0x0708,
QYFDC = TSNMHD + 0x070c,
QVTCMC0 = TSNMHD + 0x0710,
QMCBSC0 = TSNMHD + 0x0750,
QMCIRC0 = TSNMHD + 0x0790,
QMEBSC0 = TSNMHD + 0x07d0,
QMEIRC0 = TSNMHD + 0x0710,
QMCFC = TSNMHD + 0x0850,
QMEIS = TSNMHD + 0x0860,
QMEIE = TSNMHD + 0x0864,
QMEID = TSNMHD + 0x086c,
QSMFC0 = TSNMHD + 0x0870,
QMSPPC0 = TSNMHD + 0x08b0,
QMSRPC0 = TSNMHD + 0x08f0,
QGPPC0 = TSNMHD + 0x0930,
QGRPC0 = TSNMHD + 0x0950,
QMDPC0 = TSNMHD + 0x0970,
QMGPC0 = TSNMHD + 0x09b0,
QMYPC0 = TSNMHD + 0x09f0,
QMRPC0 = TSNMHD + 0x0a30,
MQSTMACU = TSNMHD + 0x0a70,
MQSTMACD = TSNMHD + 0x0a74,
MQSTMAMU = TSNMHD + 0x0a78,
MQSTMAMD = TSNMHD + 0x0a7c,
MQSFTVL = TSNMHD + 0x0a80,
MQSFTVLM = TSNMHD + 0x0a84,
MQSFTMSD = TSNMHD + 0x0a88,
MQSFTGMI = TSNMHD + 0x0a8c,
CFCR0 = RMSO + 0x0800,
FMSCR = RMSO + 0x0c10,
MMC = RMRO + 0x0000,
MPSM = RMRO + 0x0010,
MPIC = RMRO + 0x0014,
MTFFC = RMRO + 0x0020,
MTPFC = RMRO + 0x0024,
MTATC0 = RMRO + 0x0040,
MRGC = RMRO + 0x0080,
MRMAC0 = RMRO + 0x0084,
MRMAC1 = RMRO + 0x0088,
MRAFC = RMRO + 0x008c,
MRSCE = RMRO + 0x0090,
MRSCP = RMRO + 0x0094,
MRSCC = RMRO + 0x0098,
MRFSCE = RMRO + 0x009c,
MRFSCP = RMRO + 0x00a0,
MTRC = RMRO + 0x00a4,
MPFC = RMRO + 0x0100,
MLVC = RMRO + 0x0340,
MEEEC = RMRO + 0x0350,
MLBC = RMRO + 0x0360,
MGMR = RMRO + 0x0400,
MMPFTCT = RMRO + 0x0410,
MAPFTCT = RMRO + 0x0414,
MPFRCT = RMRO + 0x0418,
MFCICT = RMRO + 0x041c,
MEEECT = RMRO + 0x0420,
MEIS = RMRO + 0x0500,
MEIE = RMRO + 0x0504,
MEID = RMRO + 0x0508,
MMIS0 = RMRO + 0x0510,
MMIE0 = RMRO + 0x0514,
MMID0 = RMRO + 0x0518,
MMIS1 = RMRO + 0x0520,
MMIE1 = RMRO + 0x0524,
MMID1 = RMRO + 0x0528,
MMIS2 = RMRO + 0x0530,
MMIE2 = RMRO + 0x0534,
MMID2 = RMRO + 0x0538,
MXMS = RMRO + 0x0600,
};
/* AXIBMI */
#define RR_RATRR BIT(0)
#define RR_TATRR BIT(1)
#define RR_RST (RR_RATRR | RR_TATRR)
#define RR_RST_COMPLETE 0x03
#define AXIWC_DEFAULT 0xffff
#define AXIRC_DEFAULT 0xffff
#define TATLS0_TEDE BIT(1)
#define TATLS0_TATEN_SHIFT 24
#define TATLS0_TATEN(n) ((n) << TATLS0_TATEN_SHIFT)
#define TATLR_TATL BIT(31)
#define RATLS0_RETS BIT(2)
#define RATLS0_REDE BIT(3)
#define RATLS0_RATEN_SHIFT 24
#define RATLS0_RATEN(n) ((n) << RATLS0_RATEN_SHIFT)
#define RATLR_RATL BIT(31)
#define DIE_DID_TDICX(n) BIT((n))
#define DIE_DID_RDICX(n) BIT((n) + 8)
#define TDIE_TDID_TDX(n) BIT(n)
#define RDIE_RDID_RDX(n) BIT(n)
#define TDIS_TDS(n) BIT(n)
#define RDIS_RDS(n) BIT(n)
/* MHD */
#define OSR_OPS 0x07
#define SWR_SWR BIT(0)
#define TGC1_TQTM_SFM 0xff00
#define TGC1_STTV_DEFAULT 0x03
#define TMS_MFS_MAX 0x2800
/* RMAC System */
#define CFCR_SDID(n) ((n) << 16)
#define FMSCR_FMSIE(n) ((n) << 0)
/* RMAC */
#define MPIC_PIS_MASK GENMASK(1, 0)
#define MPIC_PIS_MII 0
#define MPIC_PIS_RMII 0x01
#define MPIC_PIS_GMII 0x02
#define MPIC_PIS_RGMII 0x03
#define MPIC_LSC_SHIFT 2
#define MPIC_LSC_MASK GENMASK(3, MPIC_LSC_SHIFT)
#define MPIC_LSC_10M (0 << MPIC_LSC_SHIFT)
#define MPIC_LSC_100M (0x01 << MPIC_LSC_SHIFT)
#define MPIC_LSC_1G (0x02 << MPIC_LSC_SHIFT)
#define MPIC_PSMCS_SHIFT 16
#define MPIC_PSMCS_MASK GENMASK(21, MPIC_PSMCS_SHIFT)
#define MPIC_PSMCS_DEFAULT (0x0a << MPIC_PSMCS_SHIFT)
#define MPIC_PSMHT_SHIFT 24
#define MPIC_PSMHT_MASK GENMASK(26, MPIC_PSMHT_SHIFT)
#define MPIC_PSMHT_DEFAULT (0x07 << MPIC_PSMHT_SHIFT)
#define MLVC_PASE BIT(8)
#define MLVC_PSE BIT(16)
#define MLVC_PLV BIT(17)
#define MPSM_PSME BIT(0)
#define MPSM_PSMAD BIT(1)
#define MPSM_PDA_SHIFT 3
#define MPSM_PDA_MASK GENMASK(7, 3)
#define MPSM_PDA(n) (((n) << MPSM_PDA_SHIFT) & MPSM_PDA_MASK)
#define MPSM_PRA_SHIFT 8
#define MPSM_PRA_MASK GENMASK(12, 8)
#define MPSM_PRA(n) (((n) << MPSM_PRA_SHIFT) & MPSM_PRA_MASK)
#define MPSM_PRD_SHIFT 16
#define MPSM_PRD_SET(n) ((n) << MPSM_PRD_SHIFT)
#define MPSM_PRD_GET(n) ((n) >> MPSM_PRD_SHIFT)
#define GPOUT_RDM BIT(13)
#define GPOUT_TDM BIT(14)
/* RTSN */
#define RTSN_INTERVAL_US 1000
#define RTSN_TIMEOUT_US 1000000
#define TX_NUM_CHAINS 1
#define RX_NUM_CHAINS 1
#define TX_CHAIN_SIZE 1024
#define RX_CHAIN_SIZE 1024
#define TX_CHAIN_IDX 0
#define RX_CHAIN_IDX 0
#define TX_CHAIN_ADDR_OFFSET (sizeof(struct rtsn_desc) * TX_CHAIN_IDX)
#define RX_CHAIN_ADDR_OFFSET (sizeof(struct rtsn_desc) * RX_CHAIN_IDX)
#define PKT_BUF_SZ 1584
#define RTSN_ALIGN 128
enum rtsn_mode {
OCR_OPC_DISABLE,
OCR_OPC_CONFIG,
OCR_OPC_OPERATION,
};
/* Descriptors */
enum RX_DS_CC_BIT {
RX_DS = 0x0fff, /* Data size */
RX_TR = 0x1000, /* Truncation indication */
RX_EI = 0x2000, /* Error indication */
RX_PS = 0xc000, /* Padding selection */
};
enum TX_FS_TAGL_BIT {
TX_DS = 0x0fff, /* Data size */
TX_TAGL = 0xf000, /* Frame tag LSBs */
};
enum DIE_DT {
/* HW/SW arbitration */
DT_FEMPTY_IS = 0x10,
DT_FEMPTY_IC = 0x20,
DT_FEMPTY_ND = 0x30,
DT_FEMPTY = 0x40,
DT_FEMPTY_START = 0x50,
DT_FEMPTY_MID = 0x60,
DT_FEMPTY_END = 0x70,
/* Frame data */
DT_FSINGLE = 0x80,
DT_FSTART = 0x90,
DT_FMID = 0xa0,
DT_FEND = 0xb0,
/* Chain control */
DT_LEMPTY = 0xc0,
DT_EEMPTY = 0xd0,
DT_LINK = 0xe0,
DT_EOS = 0xf0,
DT_MASK = 0xf0,
D_DIE = 0x08,
};
struct rtsn_desc {
__le16 info_ds;
__u8 info;
u8 die_dt;
__le32 dptr;
} __packed;
struct rtsn_ts_desc {
__le16 info_ds;
__u8 info;
u8 die_dt;
__le32 dptr;
__le32 ts_nsec;
__le32 ts_sec;
} __packed;
struct rtsn_ext_desc {
__le16 info_ds;
__u8 info;
u8 die_dt;
__le32 dptr;
__le64 info1;
} __packed;
struct rtsn_ext_ts_desc {
__le16 info_ds;
__u8 info;
u8 die_dt;
__le32 dptr;
__le64 info1;
__le32 ts_nsec;
__le32 ts_sec;
} __packed;
enum EXT_INFO_DS_BIT {
TXC = 0x4000,
};
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,567 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* SuperH Ethernet device driver
*
* Copyright (C) 2006-2012 Nobuhiro Iwamatsu
* Copyright (C) 2008-2012 Renesas Solutions Corp.
*/
#ifndef __SH_ETH_H__
#define __SH_ETH_H__
#define CARDNAME "sh-eth"
#define TX_TIMEOUT (5*HZ)
#define TX_RING_SIZE 64 /* Tx ring size */
#define RX_RING_SIZE 64 /* Rx ring size */
#define TX_RING_MIN 64
#define RX_RING_MIN 64
#define TX_RING_MAX 1024
#define RX_RING_MAX 1024
#define PKT_BUF_SZ 1538
#define SH_ETH_TSU_TIMEOUT_MS 500
#define SH_ETH_TSU_CAM_ENTRIES 32
enum {
/* IMPORTANT: To keep ethtool register dump working, add new
* register names immediately before SH_ETH_MAX_REGISTER_OFFSET.
*/
/* E-DMAC registers */
EDSR = 0,
EDMR,
EDTRR,
EDRRR,
EESR,
EESIPR,
TDLAR,
TDFAR,
TDFXR,
TDFFR,
RDLAR,
RDFAR,
RDFXR,
RDFFR,
TRSCER,
RMFCR,
TFTR,
FDR,
RMCR,
EDOCR,
TFUCR,
RFOCR,
RMIIMODE,
FCFTR,
RPADIR,
TRIMD,
RBWAR,
TBRAR,
/* Ether registers */
ECMR,
ECSR,
ECSIPR,
PIR,
PSR,
RDMLR,
PIPR,
RFLR,
IPGR,
APR,
MPR,
PFTCR,
PFRCR,
RFCR,
RFCF,
TPAUSER,
TPAUSECR,
BCFR,
BCFRR,
GECMR,
BCULR,
MAHR,
MALR,
TROCR,
CDCR,
LCCR,
CNDCR,
CEFCR,
FRECR,
TSFRCR,
TLFRCR,
CERCR,
CEECR,
MAFCR,
RTRATE,
CSMR,
RMII_MII,
/* TSU Absolute address */
ARSTR,
TSU_CTRST,
TSU_FWEN0,
TSU_FWEN1,
TSU_FCM,
TSU_BSYSL0,
TSU_BSYSL1,
TSU_PRISL0,
TSU_PRISL1,
TSU_FWSL0,
TSU_FWSL1,
TSU_FWSLC,
TSU_QTAG0, /* Same as TSU_QTAGM0 */
TSU_QTAG1, /* Same as TSU_QTAGM1 */
TSU_QTAGM0,
TSU_QTAGM1,
TSU_FWSR,
TSU_FWINMK,
TSU_ADQT0,
TSU_ADQT1,
TSU_VTAG0,
TSU_VTAG1,
TSU_ADSBSY,
TSU_TEN,
TSU_POST1,
TSU_POST2,
TSU_POST3,
TSU_POST4,
TSU_ADRH0,
/* TSU_ADR{H,L}{0..31} are assumed to be contiguous */
TXNLCR0,
TXALCR0,
RXNLCR0,
RXALCR0,
FWNLCR0,
FWALCR0,
TXNLCR1,
TXALCR1,
RXNLCR1,
RXALCR1,
FWNLCR1,
FWALCR1,
/* This value must be written at last. */
SH_ETH_MAX_REGISTER_OFFSET,
};
enum {
SH_ETH_REG_GIGABIT,
SH_ETH_REG_FAST_RCAR,
SH_ETH_REG_FAST_SH4,
SH_ETH_REG_FAST_SH3_SH2
};
/* Driver's parameters */
#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_RENESAS)
#define SH_ETH_RX_ALIGN 32
#else
#define SH_ETH_RX_ALIGN 2
#endif
/* Register's bits
*/
/* EDSR : sh7734, sh7757, sh7763, r8a7740, and r7s72100 only */
enum EDSR_BIT {
EDSR_ENT = 0x01, EDSR_ENR = 0x02,
};
#define EDSR_ENALL (EDSR_ENT|EDSR_ENR)
/* GECMR : sh7734, sh7763 and r8a7740 only */
enum GECMR_BIT {
GECMR_10 = 0x0, GECMR_100 = 0x04, GECMR_1000 = 0x01,
};
/* EDMR */
enum EDMR_BIT {
EDMR_NBST = 0x80,
EDMR_EL = 0x40, /* Litte endian */
EDMR_DL1 = 0x20, EDMR_DL0 = 0x10,
EDMR_SRST_GETHER = 0x03,
EDMR_SRST_ETHER = 0x01,
};
/* EDTRR */
enum EDTRR_BIT {
EDTRR_TRNS_GETHER = 0x03,
EDTRR_TRNS_ETHER = 0x01,
};
/* EDRRR */
enum EDRRR_BIT {
EDRRR_R = 0x01,
};
/* TPAUSER */
enum TPAUSER_BIT {
TPAUSER_TPAUSE = 0x0000ffff,
TPAUSER_UNLIMITED = 0,
};
/* BCFR */
enum BCFR_BIT {
BCFR_RPAUSE = 0x0000ffff,
BCFR_UNLIMITED = 0,
};
/* PIR */
enum PIR_BIT {
PIR_MDI = 0x08, PIR_MDO = 0x04, PIR_MMD = 0x02, PIR_MDC = 0x01,
};
/* PSR */
enum PSR_BIT { PSR_LMON = 0x01, };
/* EESR */
enum EESR_BIT {
EESR_TWB1 = 0x80000000,
EESR_TWB = 0x40000000, /* same as TWB0 */
EESR_TC1 = 0x20000000,
EESR_TUC = 0x10000000,
EESR_ROC = 0x08000000,
EESR_TABT = 0x04000000,
EESR_RABT = 0x02000000,
EESR_RFRMER = 0x01000000, /* same as RFCOF */
EESR_ADE = 0x00800000,
EESR_ECI = 0x00400000,
EESR_FTC = 0x00200000, /* same as TC or TC0 */
EESR_TDE = 0x00100000,
EESR_TFE = 0x00080000, /* same as TFUF */
EESR_FRC = 0x00040000, /* same as FR */
EESR_RDE = 0x00020000,
EESR_RFE = 0x00010000,
EESR_CND = 0x00000800,
EESR_DLC = 0x00000400,
EESR_CD = 0x00000200,
EESR_TRO = 0x00000100,
EESR_RMAF = 0x00000080,
EESR_CEEF = 0x00000040,
EESR_CELF = 0x00000020,
EESR_RRF = 0x00000010,
EESR_RTLF = 0x00000008,
EESR_RTSF = 0x00000004,
EESR_PRE = 0x00000002,
EESR_CERF = 0x00000001,
};
#define EESR_RX_CHECK (EESR_FRC | /* Frame recv */ \
EESR_RMAF | /* Multicast address recv */ \
EESR_RRF | /* Bit frame recv */ \
EESR_RTLF | /* Long frame recv */ \
EESR_RTSF | /* Short frame recv */ \
EESR_PRE | /* PHY-LSI recv error */ \
EESR_CERF) /* Recv frame CRC error */
#define DEFAULT_TX_CHECK (EESR_FTC | EESR_CND | EESR_DLC | EESR_CD | \
EESR_TRO)
#define DEFAULT_EESR_ERR_CHECK (EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE | \
EESR_RDE | EESR_RFRMER | EESR_ADE | \
EESR_TFE | EESR_TDE)
/* EESIPR */
enum EESIPR_BIT {
EESIPR_TWB1IP = 0x80000000,
EESIPR_TWBIP = 0x40000000, /* same as TWB0IP */
EESIPR_TC1IP = 0x20000000,
EESIPR_TUCIP = 0x10000000,
EESIPR_ROCIP = 0x08000000,
EESIPR_TABTIP = 0x04000000,
EESIPR_RABTIP = 0x02000000,
EESIPR_RFCOFIP = 0x01000000,
EESIPR_ADEIP = 0x00800000,
EESIPR_ECIIP = 0x00400000,
EESIPR_FTCIP = 0x00200000, /* same as TC0IP */
EESIPR_TDEIP = 0x00100000,
EESIPR_TFUFIP = 0x00080000,
EESIPR_FRIP = 0x00040000,
EESIPR_RDEIP = 0x00020000,
EESIPR_RFOFIP = 0x00010000,
EESIPR_CNDIP = 0x00000800,
EESIPR_DLCIP = 0x00000400,
EESIPR_CDIP = 0x00000200,
EESIPR_TROIP = 0x00000100,
EESIPR_RMAFIP = 0x00000080,
EESIPR_CEEFIP = 0x00000040,
EESIPR_CELFIP = 0x00000020,
EESIPR_RRFIP = 0x00000010,
EESIPR_RTLFIP = 0x00000008,
EESIPR_RTSFIP = 0x00000004,
EESIPR_PREIP = 0x00000002,
EESIPR_CERFIP = 0x00000001,
};
/* FCFTR */
enum FCFTR_BIT {
FCFTR_RFF2 = 0x00040000, FCFTR_RFF1 = 0x00020000,
FCFTR_RFF0 = 0x00010000, FCFTR_RFD2 = 0x00000004,
FCFTR_RFD1 = 0x00000002, FCFTR_RFD0 = 0x00000001,
};
#define DEFAULT_FIFO_F_D_RFF (FCFTR_RFF2 | FCFTR_RFF1 | FCFTR_RFF0)
#define DEFAULT_FIFO_F_D_RFD (FCFTR_RFD2 | FCFTR_RFD1 | FCFTR_RFD0)
/* RMCR */
enum RMCR_BIT {
RMCR_RNC = 0x00000001,
};
/* ECMR */
enum ECMR_BIT {
ECMR_TRCCM = 0x04000000, ECMR_RCSC = 0x00800000,
ECMR_DPAD = 0x00200000, ECMR_RZPF = 0x00100000,
ECMR_ZPF = 0x00080000, ECMR_PFR = 0x00040000, ECMR_RXF = 0x00020000,
ECMR_TXF = 0x00010000, ECMR_MCT = 0x00002000, ECMR_PRCEF = 0x00001000,
ECMR_MPDE = 0x00000200, ECMR_RE = 0x00000040, ECMR_TE = 0x00000020,
ECMR_RTM = 0x00000010, ECMR_ILB = 0x00000008, ECMR_ELB = 0x00000004,
ECMR_DM = 0x00000002, ECMR_PRM = 0x00000001,
};
/* ECSR */
enum ECSR_BIT {
ECSR_BRCRX = 0x20, ECSR_PSRTO = 0x10,
ECSR_LCHNG = 0x04,
ECSR_MPD = 0x02, ECSR_ICD = 0x01,
};
#define DEFAULT_ECSR_INIT (ECSR_BRCRX | ECSR_PSRTO | ECSR_LCHNG | \
ECSR_ICD | ECSIPR_MPDIP)
/* ECSIPR */
enum ECSIPR_BIT {
ECSIPR_BRCRXIP = 0x20, ECSIPR_PSRTOIP = 0x10,
ECSIPR_LCHNGIP = 0x04,
ECSIPR_MPDIP = 0x02, ECSIPR_ICDIP = 0x01,
};
#define DEFAULT_ECSIPR_INIT (ECSIPR_BRCRXIP | ECSIPR_PSRTOIP | \
ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP)
/* APR */
enum APR_BIT {
APR_AP = 0x0000ffff,
};
/* MPR */
enum MPR_BIT {
MPR_MP = 0x0000ffff,
};
/* TRSCER */
enum TRSCER_BIT {
TRSCER_CNDCE = 0x00000800,
TRSCER_DLCCE = 0x00000400,
TRSCER_CDCE = 0x00000200,
TRSCER_TROCE = 0x00000100,
TRSCER_RMAFCE = 0x00000080,
TRSCER_RRFCE = 0x00000010,
TRSCER_RTLFCE = 0x00000008,
TRSCER_RTSFCE = 0x00000004,
TRSCER_PRECE = 0x00000002,
TRSCER_CERFCE = 0x00000001,
};
#define DEFAULT_TRSCER_ERR_MASK (TRSCER_RMAFCE | TRSCER_RRFCE | TRSCER_CDCE)
/* RPADIR */
enum RPADIR_BIT {
RPADIR_PADS = 0x1f0000, RPADIR_PADR = 0xffff,
};
/* FDR */
#define DEFAULT_FDR_INIT 0x00000707
/* ARSTR */
enum ARSTR_BIT { ARSTR_ARST = 0x00000001, };
/* TSU_FWEN0 */
enum TSU_FWEN0_BIT {
TSU_FWEN0_0 = 0x00000001,
};
/* TSU_ADSBSY */
enum TSU_ADSBSY_BIT {
TSU_ADSBSY_0 = 0x00000001,
};
/* TSU_TEN */
enum TSU_TEN_BIT {
TSU_TEN_0 = 0x80000000,
};
/* TSU_FWSL0 */
enum TSU_FWSL0_BIT {
TSU_FWSL0_FW50 = 0x1000, TSU_FWSL0_FW40 = 0x0800,
TSU_FWSL0_FW30 = 0x0400, TSU_FWSL0_FW20 = 0x0200,
TSU_FWSL0_FW10 = 0x0100, TSU_FWSL0_RMSA0 = 0x0010,
};
/* TSU_FWSLC */
enum TSU_FWSLC_BIT {
TSU_FWSLC_POSTENU = 0x2000, TSU_FWSLC_POSTENL = 0x1000,
TSU_FWSLC_CAMSEL03 = 0x0080, TSU_FWSLC_CAMSEL02 = 0x0040,
TSU_FWSLC_CAMSEL01 = 0x0020, TSU_FWSLC_CAMSEL00 = 0x0010,
TSU_FWSLC_CAMSEL13 = 0x0008, TSU_FWSLC_CAMSEL12 = 0x0004,
TSU_FWSLC_CAMSEL11 = 0x0002, TSU_FWSLC_CAMSEL10 = 0x0001,
};
/* TSU_VTAGn */
#define TSU_VTAG_ENABLE 0x80000000
#define TSU_VTAG_VID_MASK 0x00000fff
/* The sh ether Tx buffer descriptors.
* This structure should be 20 bytes.
*/
struct sh_eth_txdesc {
u32 status; /* TD0 */
u32 len; /* TD1 */
u32 addr; /* TD2 */
u32 pad0; /* padding data */
} __aligned(2) __packed;
/* Transmit descriptor 0 bits */
enum TD_STS_BIT {
TD_TACT = 0x80000000,
TD_TDLE = 0x40000000,
TD_TFP1 = 0x20000000,
TD_TFP0 = 0x10000000,
TD_TFE = 0x08000000,
TD_TWBI = 0x04000000,
};
#define TDF1ST TD_TFP1
#define TDFEND TD_TFP0
#define TD_TFP (TD_TFP1 | TD_TFP0)
/* Transmit descriptor 1 bits */
enum TD_LEN_BIT {
TD_TBL = 0xffff0000, /* transmit buffer length */
};
/* The sh ether Rx buffer descriptors.
* This structure should be 20 bytes.
*/
struct sh_eth_rxdesc {
u32 status; /* RD0 */
u32 len; /* RD1 */
u32 addr; /* RD2 */
u32 pad0; /* padding data */
} __aligned(2) __packed;
/* Receive descriptor 0 bits */
enum RD_STS_BIT {
RD_RACT = 0x80000000,
RD_RDLE = 0x40000000,
RD_RFP1 = 0x20000000,
RD_RFP0 = 0x10000000,
RD_RFE = 0x08000000,
RD_RFS10 = 0x00000200,
RD_RFS9 = 0x00000100,
RD_RFS8 = 0x00000080,
RD_RFS7 = 0x00000040,
RD_RFS6 = 0x00000020,
RD_RFS5 = 0x00000010,
RD_RFS4 = 0x00000008,
RD_RFS3 = 0x00000004,
RD_RFS2 = 0x00000002,
RD_RFS1 = 0x00000001,
};
#define RDF1ST RD_RFP1
#define RDFEND RD_RFP0
#define RD_RFP (RD_RFP1 | RD_RFP0)
/* Receive descriptor 1 bits */
enum RD_LEN_BIT {
RD_RFL = 0x0000ffff, /* receive frame length */
RD_RBL = 0xffff0000, /* receive buffer length */
};
/* This structure is used by each CPU dependency handling. */
struct sh_eth_cpu_data {
/* mandatory functions */
int (*soft_reset)(struct net_device *ndev);
/* optional functions */
void (*chip_reset)(struct net_device *ndev);
void (*set_duplex)(struct net_device *ndev);
void (*set_rate)(struct net_device *ndev);
/* mandatory initialize value */
int register_type;
u32 edtrr_trns;
u32 eesipr_value;
/* optional initialize value */
u32 ecsr_value;
u32 ecsipr_value;
u32 fdr_value;
u32 fcftr_value;
/* interrupt checking mask */
u32 tx_check;
u32 eesr_err_check;
/* Error mask */
u32 trscer_err_mask;
/* hardware features */
unsigned long irq_flags; /* IRQ configuration flags */
unsigned no_psr:1; /* EtherC DOES NOT have PSR */
unsigned apr:1; /* EtherC has APR */
unsigned mpr:1; /* EtherC has MPR */
unsigned tpauser:1; /* EtherC has TPAUSER */
unsigned gecmr:1; /* EtherC has GECMR */
unsigned bculr:1; /* EtherC has BCULR */
unsigned tsu:1; /* EtherC has TSU */
unsigned hw_swap:1; /* E-DMAC has DE bit in EDMR */
unsigned nbst:1; /* E-DMAC has NBST bit in EDMR */
unsigned rpadir:1; /* E-DMAC has RPADIR */
unsigned no_trimd:1; /* E-DMAC DOES NOT have TRIMD */
unsigned no_ade:1; /* E-DMAC DOES NOT have ADE bit in EESR */
unsigned no_xdfar:1; /* E-DMAC DOES NOT have RDFAR/TDFAR */
unsigned xdfar_rw:1; /* E-DMAC has writeable RDFAR/TDFAR */
unsigned csmr:1; /* E-DMAC has CSMR */
unsigned rx_csum:1; /* EtherC has ECMR.RCSC */
unsigned select_mii:1; /* EtherC has RMII_MII (MII select register) */
unsigned rmiimode:1; /* EtherC has RMIIMODE register */
unsigned rtrate:1; /* EtherC has RTRATE register */
unsigned magic:1; /* EtherC has ECMR.MPDE and ECSR.MPD */
unsigned no_tx_cntrs:1; /* EtherC DOES NOT have TX error counters */
unsigned cexcr:1; /* EtherC has CERCR/CEECR */
unsigned dual_port:1; /* Dual EtherC/E-DMAC */
};
struct sh_eth_private {
struct platform_device *pdev;
struct sh_eth_cpu_data *cd;
const u16 *reg_offset;
void __iomem *addr;
void __iomem *tsu_addr;
struct clk *clk;
u32 num_rx_ring;
u32 num_tx_ring;
dma_addr_t rx_desc_dma;
dma_addr_t tx_desc_dma;
struct sh_eth_rxdesc *rx_ring;
struct sh_eth_txdesc *tx_ring;
struct sk_buff **rx_skbuff;
struct sk_buff **tx_skbuff;
spinlock_t lock; /* Register access lock */
u32 cur_rx, dirty_rx; /* Producer/consumer ring indices */
u32 cur_tx, dirty_tx;
u32 rx_buf_sz; /* Based on MTU+slack. */
struct napi_struct napi;
bool irq_enabled;
/* MII transceiver section. */
u32 phy_id; /* PHY ID */
struct mii_bus *mii_bus; /* MDIO bus control */
int link;
phy_interface_t phy_interface;
int msg_enable;
int speed;
int duplex;
int port; /* for TSU */
int vlan_num_ids; /* for VLAN tag filter */
unsigned no_ether_link:1;
unsigned ether_link_active_low:1;
unsigned is_opened:1;
unsigned wol_enabled:1;
};
#endif /* #ifndef __SH_ETH_H__ */