net: mscc: PTP Hardware Clock (PHC) support
This patch adds support for PTP Hardware Clock (PHC) to the Ocelot switch for both PTP 1-step and 2-step modes. Signed-off-by: Antoine Tenart <antoine.tenart@bootlin.com> Acked-by: Richard Cochran <richardcochran@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:

committed by
David S. Miller

parent
1f0239de58
commit
4e3b0468e6
@@ -14,6 +14,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/ptp_clock_kernel.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <net/arp.h>
|
||||
@@ -538,7 +539,7 @@ static int ocelot_port_stop(struct net_device *dev)
|
||||
*/
|
||||
static int ocelot_gen_ifh(u32 *ifh, struct frame_info *info)
|
||||
{
|
||||
ifh[0] = IFH_INJ_BYPASS;
|
||||
ifh[0] = IFH_INJ_BYPASS | ((0x1ff & info->rew_op) << 21);
|
||||
ifh[1] = (0xf00 & info->port) >> 8;
|
||||
ifh[2] = (0xff & info->port) << 24;
|
||||
ifh[3] = (info->tag_type << 16) | info->vid;
|
||||
@@ -548,6 +549,7 @@ static int ocelot_gen_ifh(u32 *ifh, struct frame_info *info)
|
||||
|
||||
static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct skb_shared_info *shinfo = skb_shinfo(skb);
|
||||
struct ocelot_port *port = netdev_priv(dev);
|
||||
struct ocelot *ocelot = port->ocelot;
|
||||
u32 val, ifh[IFH_LEN];
|
||||
@@ -566,6 +568,14 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
info.port = BIT(port->chip_port);
|
||||
info.tag_type = IFH_TAG_TYPE_C;
|
||||
info.vid = skb_vlan_tag_get(skb);
|
||||
|
||||
/* Check if timestamping is needed */
|
||||
if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
|
||||
info.rew_op = port->ptp_cmd;
|
||||
if (port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
|
||||
info.rew_op |= (port->ts_id % 4) << 3;
|
||||
}
|
||||
|
||||
ocelot_gen_ifh(ifh, &info);
|
||||
|
||||
for (i = 0; i < IFH_LEN; i++)
|
||||
@@ -596,11 +606,58 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
|
||||
dev->stats.tx_packets++;
|
||||
dev->stats.tx_bytes += skb->len;
|
||||
dev_kfree_skb_any(skb);
|
||||
|
||||
if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP &&
|
||||
port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
|
||||
struct ocelot_skb *oskb =
|
||||
kzalloc(sizeof(struct ocelot_skb), GFP_ATOMIC);
|
||||
|
||||
if (unlikely(!oskb))
|
||||
goto out;
|
||||
|
||||
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
|
||||
|
||||
oskb->skb = skb;
|
||||
oskb->id = port->ts_id % 4;
|
||||
port->ts_id++;
|
||||
|
||||
list_add_tail(&oskb->head, &port->skbs);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
out:
|
||||
dev_kfree_skb_any(skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
void ocelot_get_hwtimestamp(struct ocelot *ocelot, struct timespec64 *ts)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
|
||||
|
||||
/* Read current PTP time to get seconds */
|
||||
val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
|
||||
|
||||
val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
|
||||
val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_SAVE);
|
||||
ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
|
||||
ts->tv_sec = ocelot_read_rix(ocelot, PTP_PIN_TOD_SEC_LSB, TOD_ACC_PIN);
|
||||
|
||||
/* Read packet HW timestamp from FIFO */
|
||||
val = ocelot_read(ocelot, SYS_PTP_TXSTAMP);
|
||||
ts->tv_nsec = SYS_PTP_TXSTAMP_PTP_TXSTAMP(val);
|
||||
|
||||
/* Sec has incremented since the ts was registered */
|
||||
if ((ts->tv_sec & 0x1) != !!(val & SYS_PTP_TXSTAMP_PTP_TXSTAMP_SEC))
|
||||
ts->tv_sec--;
|
||||
|
||||
spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(ocelot_get_hwtimestamp);
|
||||
|
||||
static int ocelot_mc_unsync(struct net_device *dev, const unsigned char *addr)
|
||||
{
|
||||
struct ocelot_port *port = netdev_priv(dev);
|
||||
@@ -917,6 +974,97 @@ static int ocelot_get_port_parent_id(struct net_device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ocelot_hwstamp_get(struct ocelot_port *port, struct ifreq *ifr)
|
||||
{
|
||||
struct ocelot *ocelot = port->ocelot;
|
||||
|
||||
return copy_to_user(ifr->ifr_data, &ocelot->hwtstamp_config,
|
||||
sizeof(ocelot->hwtstamp_config)) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
static int ocelot_hwstamp_set(struct ocelot_port *port, struct ifreq *ifr)
|
||||
{
|
||||
struct ocelot *ocelot = port->ocelot;
|
||||
struct hwtstamp_config cfg;
|
||||
|
||||
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
|
||||
return -EFAULT;
|
||||
|
||||
/* reserved for future extensions */
|
||||
if (cfg.flags)
|
||||
return -EINVAL;
|
||||
|
||||
/* Tx type sanity check */
|
||||
switch (cfg.tx_type) {
|
||||
case HWTSTAMP_TX_ON:
|
||||
port->ptp_cmd = IFH_REW_OP_TWO_STEP_PTP;
|
||||
break;
|
||||
case HWTSTAMP_TX_ONESTEP_SYNC:
|
||||
/* IFH_REW_OP_ONE_STEP_PTP updates the correctional field, we
|
||||
* need to update the origin time.
|
||||
*/
|
||||
port->ptp_cmd = IFH_REW_OP_ORIGIN_PTP;
|
||||
break;
|
||||
case HWTSTAMP_TX_OFF:
|
||||
port->ptp_cmd = 0;
|
||||
break;
|
||||
default:
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
mutex_lock(&ocelot->ptp_lock);
|
||||
|
||||
switch (cfg.rx_filter) {
|
||||
case HWTSTAMP_FILTER_NONE:
|
||||
break;
|
||||
case HWTSTAMP_FILTER_ALL:
|
||||
case HWTSTAMP_FILTER_SOME:
|
||||
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
|
||||
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
|
||||
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
|
||||
case HWTSTAMP_FILTER_NTP_ALL:
|
||||
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
|
||||
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
|
||||
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
|
||||
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
|
||||
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
|
||||
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
|
||||
case HWTSTAMP_FILTER_PTP_V2_EVENT:
|
||||
case HWTSTAMP_FILTER_PTP_V2_SYNC:
|
||||
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
|
||||
cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
|
||||
break;
|
||||
default:
|
||||
mutex_unlock(&ocelot->ptp_lock);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/* Commit back the result & save it */
|
||||
memcpy(&ocelot->hwtstamp_config, &cfg, sizeof(cfg));
|
||||
mutex_unlock(&ocelot->ptp_lock);
|
||||
|
||||
return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
static int ocelot_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||
{
|
||||
struct ocelot_port *port = netdev_priv(dev);
|
||||
struct ocelot *ocelot = port->ocelot;
|
||||
|
||||
/* The function is only used for PTP operations for now */
|
||||
if (!ocelot->ptp)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (cmd) {
|
||||
case SIOCSHWTSTAMP:
|
||||
return ocelot_hwstamp_set(port, ifr);
|
||||
case SIOCGHWTSTAMP:
|
||||
return ocelot_hwstamp_get(port, ifr);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct net_device_ops ocelot_port_netdev_ops = {
|
||||
.ndo_open = ocelot_port_open,
|
||||
.ndo_stop = ocelot_port_stop,
|
||||
@@ -933,6 +1081,7 @@ static const struct net_device_ops ocelot_port_netdev_ops = {
|
||||
.ndo_set_features = ocelot_set_features,
|
||||
.ndo_get_port_parent_id = ocelot_get_port_parent_id,
|
||||
.ndo_setup_tc = ocelot_setup_tc,
|
||||
.ndo_do_ioctl = ocelot_ioctl,
|
||||
};
|
||||
|
||||
static void ocelot_get_strings(struct net_device *netdev, u32 sset, u8 *data)
|
||||
@@ -1014,12 +1163,37 @@ static int ocelot_get_sset_count(struct net_device *dev, int sset)
|
||||
return ocelot->num_stats;
|
||||
}
|
||||
|
||||
static int ocelot_get_ts_info(struct net_device *dev,
|
||||
struct ethtool_ts_info *info)
|
||||
{
|
||||
struct ocelot_port *ocelot_port = netdev_priv(dev);
|
||||
struct ocelot *ocelot = ocelot_port->ocelot;
|
||||
|
||||
if (!ocelot->ptp)
|
||||
return ethtool_op_get_ts_info(dev, info);
|
||||
|
||||
info->phc_index = ocelot->ptp_clock ?
|
||||
ptp_clock_index(ocelot->ptp_clock) : -1;
|
||||
info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE |
|
||||
SOF_TIMESTAMPING_RX_SOFTWARE |
|
||||
SOF_TIMESTAMPING_SOFTWARE |
|
||||
SOF_TIMESTAMPING_TX_HARDWARE |
|
||||
SOF_TIMESTAMPING_RX_HARDWARE |
|
||||
SOF_TIMESTAMPING_RAW_HARDWARE;
|
||||
info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON) |
|
||||
BIT(HWTSTAMP_TX_ONESTEP_SYNC);
|
||||
info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ethtool_ops ocelot_ethtool_ops = {
|
||||
.get_strings = ocelot_get_strings,
|
||||
.get_ethtool_stats = ocelot_get_ethtool_stats,
|
||||
.get_sset_count = ocelot_get_sset_count,
|
||||
.get_link_ksettings = phy_ethtool_get_link_ksettings,
|
||||
.set_link_ksettings = phy_ethtool_set_link_ksettings,
|
||||
.get_ts_info = ocelot_get_ts_info,
|
||||
};
|
||||
|
||||
static int ocelot_port_attr_stp_state_set(struct ocelot_port *ocelot_port,
|
||||
@@ -1629,6 +1803,196 @@ struct notifier_block ocelot_switchdev_blocking_nb __read_mostly = {
|
||||
};
|
||||
EXPORT_SYMBOL(ocelot_switchdev_blocking_nb);
|
||||
|
||||
int ocelot_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts)
|
||||
{
|
||||
struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
|
||||
unsigned long flags;
|
||||
time64_t s;
|
||||
u32 val;
|
||||
s64 ns;
|
||||
|
||||
spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
|
||||
|
||||
val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
|
||||
val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
|
||||
val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_SAVE);
|
||||
ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
|
||||
|
||||
s = ocelot_read_rix(ocelot, PTP_PIN_TOD_SEC_MSB, TOD_ACC_PIN) & 0xffff;
|
||||
s <<= 32;
|
||||
s += ocelot_read_rix(ocelot, PTP_PIN_TOD_SEC_LSB, TOD_ACC_PIN);
|
||||
ns = ocelot_read_rix(ocelot, PTP_PIN_TOD_NSEC, TOD_ACC_PIN);
|
||||
|
||||
spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
|
||||
|
||||
/* Deal with negative values */
|
||||
if (ns >= 0x3ffffff0 && ns <= 0x3fffffff) {
|
||||
s--;
|
||||
ns &= 0xf;
|
||||
ns += 999999984;
|
||||
}
|
||||
|
||||
set_normalized_timespec64(ts, s, ns);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ocelot_ptp_gettime64);
|
||||
|
||||
static int ocelot_ptp_settime64(struct ptp_clock_info *ptp,
|
||||
const struct timespec64 *ts)
|
||||
{
|
||||
struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
|
||||
|
||||
val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
|
||||
val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
|
||||
val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_IDLE);
|
||||
|
||||
ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
|
||||
|
||||
ocelot_write_rix(ocelot, lower_32_bits(ts->tv_sec), PTP_PIN_TOD_SEC_LSB,
|
||||
TOD_ACC_PIN);
|
||||
ocelot_write_rix(ocelot, upper_32_bits(ts->tv_sec), PTP_PIN_TOD_SEC_MSB,
|
||||
TOD_ACC_PIN);
|
||||
ocelot_write_rix(ocelot, ts->tv_nsec, PTP_PIN_TOD_NSEC, TOD_ACC_PIN);
|
||||
|
||||
val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
|
||||
val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
|
||||
val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_LOAD);
|
||||
|
||||
ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
|
||||
|
||||
spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ocelot_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
||||
{
|
||||
if (delta > -(NSEC_PER_SEC / 2) && delta < (NSEC_PER_SEC / 2)) {
|
||||
struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
|
||||
|
||||
val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
|
||||
val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
|
||||
val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_IDLE);
|
||||
|
||||
ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
|
||||
|
||||
ocelot_write_rix(ocelot, 0, PTP_PIN_TOD_SEC_LSB, TOD_ACC_PIN);
|
||||
ocelot_write_rix(ocelot, 0, PTP_PIN_TOD_SEC_MSB, TOD_ACC_PIN);
|
||||
ocelot_write_rix(ocelot, delta, PTP_PIN_TOD_NSEC, TOD_ACC_PIN);
|
||||
|
||||
val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
|
||||
val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
|
||||
val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_DELTA);
|
||||
|
||||
ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
|
||||
|
||||
spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
|
||||
} else {
|
||||
/* Fall back using ocelot_ptp_settime64 which is not exact. */
|
||||
struct timespec64 ts;
|
||||
u64 now;
|
||||
|
||||
ocelot_ptp_gettime64(ptp, &ts);
|
||||
|
||||
now = ktime_to_ns(timespec64_to_ktime(ts));
|
||||
ts = ns_to_timespec64(now + delta);
|
||||
|
||||
ocelot_ptp_settime64(ptp, &ts);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ocelot_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
|
||||
{
|
||||
struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
|
||||
u32 unit = 0, direction = 0;
|
||||
unsigned long flags;
|
||||
u64 adj = 0;
|
||||
|
||||
spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
|
||||
|
||||
if (!scaled_ppm)
|
||||
goto disable_adj;
|
||||
|
||||
if (scaled_ppm < 0) {
|
||||
direction = PTP_CFG_CLK_ADJ_CFG_DIR;
|
||||
scaled_ppm = -scaled_ppm;
|
||||
}
|
||||
|
||||
adj = PSEC_PER_SEC << 16;
|
||||
do_div(adj, scaled_ppm);
|
||||
do_div(adj, 1000);
|
||||
|
||||
/* If the adjustment value is too large, use ns instead */
|
||||
if (adj >= (1L << 30)) {
|
||||
unit = PTP_CFG_CLK_ADJ_FREQ_NS;
|
||||
do_div(adj, 1000);
|
||||
}
|
||||
|
||||
/* Still too big */
|
||||
if (adj >= (1L << 30))
|
||||
goto disable_adj;
|
||||
|
||||
ocelot_write(ocelot, unit | adj, PTP_CLK_CFG_ADJ_FREQ);
|
||||
ocelot_write(ocelot, PTP_CFG_CLK_ADJ_CFG_ENA | direction,
|
||||
PTP_CLK_CFG_ADJ_CFG);
|
||||
|
||||
spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
|
||||
return 0;
|
||||
|
||||
disable_adj:
|
||||
ocelot_write(ocelot, 0, PTP_CLK_CFG_ADJ_CFG);
|
||||
|
||||
spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ptp_clock_info ocelot_ptp_clock_info = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "ocelot ptp",
|
||||
.max_adj = 0x7fffffff,
|
||||
.n_alarm = 0,
|
||||
.n_ext_ts = 0,
|
||||
.n_per_out = 0,
|
||||
.n_pins = 0,
|
||||
.pps = 0,
|
||||
.gettime64 = ocelot_ptp_gettime64,
|
||||
.settime64 = ocelot_ptp_settime64,
|
||||
.adjtime = ocelot_ptp_adjtime,
|
||||
.adjfine = ocelot_ptp_adjfine,
|
||||
};
|
||||
|
||||
static int ocelot_init_timestamp(struct ocelot *ocelot)
|
||||
{
|
||||
ocelot->ptp_info = ocelot_ptp_clock_info;
|
||||
ocelot->ptp_clock = ptp_clock_register(&ocelot->ptp_info, ocelot->dev);
|
||||
if (IS_ERR(ocelot->ptp_clock))
|
||||
return PTR_ERR(ocelot->ptp_clock);
|
||||
/* Check if PHC support is missing at the configuration level */
|
||||
if (!ocelot->ptp_clock)
|
||||
return 0;
|
||||
|
||||
ocelot_write(ocelot, SYS_PTP_CFG_PTP_STAMP_WID(30), SYS_PTP_CFG);
|
||||
ocelot_write(ocelot, 0xffffffff, ANA_TABLES_PTP_ID_LOW);
|
||||
ocelot_write(ocelot, 0xffffffff, ANA_TABLES_PTP_ID_HIGH);
|
||||
|
||||
ocelot_write(ocelot, PTP_CFG_MISC_PTP_EN, PTP_CFG_MISC);
|
||||
|
||||
/* There is no device reconfiguration, PTP Rx stamping is always
|
||||
* enabled.
|
||||
*/
|
||||
ocelot->hwtstamp_config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ocelot_probe_port(struct ocelot *ocelot, u8 port,
|
||||
void __iomem *regs,
|
||||
struct phy_device *phy)
|
||||
@@ -1661,6 +2025,8 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port,
|
||||
ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr, ocelot_port->pvid,
|
||||
ENTRYTYPE_LOCKED);
|
||||
|
||||
INIT_LIST_HEAD(&ocelot_port->skbs);
|
||||
|
||||
err = register_netdev(dev);
|
||||
if (err) {
|
||||
dev_err(ocelot->dev, "register_netdev failed\n");
|
||||
@@ -1684,7 +2050,7 @@ EXPORT_SYMBOL(ocelot_probe_port);
|
||||
int ocelot_init(struct ocelot *ocelot)
|
||||
{
|
||||
u32 port;
|
||||
int i, cpu = ocelot->num_phys_ports;
|
||||
int i, ret, cpu = ocelot->num_phys_ports;
|
||||
char queue_name[32];
|
||||
|
||||
ocelot->lags = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
|
||||
@@ -1699,6 +2065,8 @@ int ocelot_init(struct ocelot *ocelot)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&ocelot->stats_lock);
|
||||
mutex_init(&ocelot->ptp_lock);
|
||||
spin_lock_init(&ocelot->ptp_clock_lock);
|
||||
snprintf(queue_name, sizeof(queue_name), "%s-stats",
|
||||
dev_name(ocelot->dev));
|
||||
ocelot->stats_queue = create_singlethread_workqueue(queue_name);
|
||||
@@ -1812,16 +2180,43 @@ int ocelot_init(struct ocelot *ocelot)
|
||||
INIT_DELAYED_WORK(&ocelot->stats_work, ocelot_check_stats_work);
|
||||
queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work,
|
||||
OCELOT_STATS_CHECK_DELAY);
|
||||
|
||||
if (ocelot->ptp) {
|
||||
ret = ocelot_init_timestamp(ocelot);
|
||||
if (ret) {
|
||||
dev_err(ocelot->dev,
|
||||
"Timestamp initialization failed\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ocelot_init);
|
||||
|
||||
void ocelot_deinit(struct ocelot *ocelot)
|
||||
{
|
||||
struct list_head *pos, *tmp;
|
||||
struct ocelot_port *port;
|
||||
struct ocelot_skb *entry;
|
||||
int i;
|
||||
|
||||
cancel_delayed_work(&ocelot->stats_work);
|
||||
destroy_workqueue(ocelot->stats_queue);
|
||||
mutex_destroy(&ocelot->stats_lock);
|
||||
ocelot_ace_deinit();
|
||||
|
||||
for (i = 0; i < ocelot->num_phys_ports; i++) {
|
||||
port = ocelot->ports[i];
|
||||
|
||||
list_for_each_safe(pos, tmp, &port->skbs) {
|
||||
entry = list_entry(pos, struct ocelot_skb, head);
|
||||
|
||||
list_del(pos);
|
||||
dev_kfree_skb_any(entry->skb);
|
||||
kfree(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(ocelot_deinit);
|
||||
|
||||
|
Reference in New Issue
Block a user