diff options
Diffstat (limited to 'drivers/net/ethernet/ti/netcp_ethss.c')
-rw-r--r-- | drivers/net/ethernet/ti/netcp_ethss.c | 437 |
1 files changed, 428 insertions, 9 deletions
diff --git a/drivers/net/ethernet/ti/netcp_ethss.c b/drivers/net/ethernet/ti/netcp_ethss.c index 48cb04fb7e0c..c7e547e4f2b1 100644 --- a/drivers/net/ethernet/ti/netcp_ethss.c +++ b/drivers/net/ethernet/ti/netcp_ethss.c @@ -23,10 +23,13 @@ #include <linux/of_mdio.h> #include <linux/of_address.h> #include <linux/if_vlan.h> +#include <linux/ptp_classify.h> +#include <linux/net_tstamp.h> #include <linux/ethtool.h> #include "cpsw_ale.h" #include "netcp.h" +#include "cpts.h" #define NETCP_DRIVER_NAME "TI KeyStone Ethernet Driver" #define NETCP_DRIVER_VERSION "v1.0" @@ -51,6 +54,7 @@ #define GBE13_EMAC_OFFSET 0x100 #define GBE13_SLAVE_PORT2_OFFSET 0x200 #define GBE13_HW_STATS_OFFSET 0x300 +#define GBE13_CPTS_OFFSET 0x500 #define GBE13_ALE_OFFSET 0x600 #define GBE13_HOST_PORT_NUM 0 #define GBE13_NUM_ALE_ENTRIES 1024 @@ -74,6 +78,7 @@ #define GBENU_SLAVE_PORT_OFFSET 0x2000 #define GBENU_EMAC_OFFSET 0x2330 #define GBENU_HW_STATS_OFFSET 0x1a000 +#define GBENU_CPTS_OFFSET 0x1d000 #define GBENU_ALE_OFFSET 0x1e000 #define GBENU_HOST_PORT_NUM 0 #define GBENU_NUM_ALE_ENTRIES 1024 @@ -93,6 +98,7 @@ #define XGBE10_HOST_PORT_OFFSET 0x34 #define XGBE10_SLAVE_PORT_OFFSET 0x64 #define XGBE10_EMAC_OFFSET 0x400 +#define XGBE10_CPTS_OFFSET 0x600 #define XGBE10_ALE_OFFSET 0x700 #define XGBE10_HW_STATS_OFFSET 0x800 #define XGBE10_HOST_PORT_NUM 0 @@ -155,6 +161,7 @@ #define GBE_TX_QUEUE 648 #define GBE_TXHOOK_ORDER 0 +#define GBE_RXHOOK_ORDER 0 #define GBE_DEFAULT_ALE_AGEOUT 30 #define SLAVE_LINK_IS_XGMII(s) ((s)->link_interface >= XGMII_LINK_MAC_PHY) #define NETCP_LINK_STATE_INVALID -1 @@ -169,6 +176,56 @@ #define HOST_TX_PRI_MAP_DEFAULT 0x00000000 +#if IS_ENABLED(CONFIG_TI_CPTS) +/* Px_TS_CTL register fields */ +#define TS_RX_ANX_F_EN BIT(0) +#define TS_RX_VLAN_LT1_EN BIT(1) +#define TS_RX_VLAN_LT2_EN BIT(2) +#define TS_RX_ANX_D_EN BIT(3) +#define TS_TX_ANX_F_EN BIT(4) +#define TS_TX_VLAN_LT1_EN BIT(5) +#define TS_TX_VLAN_LT2_EN BIT(6) +#define TS_TX_ANX_D_EN BIT(7) +#define TS_LT2_EN BIT(8) +#define TS_RX_ANX_E_EN BIT(9) +#define TS_TX_ANX_E_EN BIT(10) +#define TS_MSG_TYPE_EN_SHIFT 16 +#define TS_MSG_TYPE_EN_MASK 0xffff + +/* Px_TS_SEQ_LTYPE register fields */ +#define TS_SEQ_ID_OFS_SHIFT 16 +#define TS_SEQ_ID_OFS_MASK 0x3f + +/* Px_TS_CTL_LTYPE2 register fields */ +#define TS_107 BIT(16) +#define TS_129 BIT(17) +#define TS_130 BIT(18) +#define TS_131 BIT(19) +#define TS_132 BIT(20) +#define TS_319 BIT(21) +#define TS_320 BIT(22) +#define TS_TTL_NONZERO BIT(23) +#define TS_UNI_EN BIT(24) +#define TS_UNI_EN_SHIFT 24 + +#define TS_TX_ANX_ALL_EN \ + (TS_TX_ANX_D_EN | TS_TX_ANX_E_EN | TS_TX_ANX_F_EN) + +#define TS_RX_ANX_ALL_EN \ + (TS_RX_ANX_D_EN | TS_RX_ANX_E_EN | TS_RX_ANX_F_EN) + +#define TS_CTL_DST_PORT TS_319 +#define TS_CTL_DST_PORT_SHIFT 21 + +#define TS_CTL_MADDR_ALL \ + (TS_107 | TS_129 | TS_130 | TS_131 | TS_132) + +#define TS_CTL_MADDR_SHIFT 16 + +/* The PTP event messages - Sync, Delay_Req, Pdelay_Req, and Pdelay_Resp. */ +#define EVENT_MSG_BITS (BIT(0) | BIT(1) | BIT(2) | BIT(3)) +#endif /* CONFIG_TI_CPTS */ + struct xgbe_ss_regs { u32 id_ver; u32 synce_count; @@ -616,6 +673,13 @@ struct gbe_hw_stats { #define GBE_MAX_HW_STAT_MODS 9 #define GBE_HW_STATS_REG_MAP_SZ 0x100 +struct ts_ctl { + int uni; + u8 dst_port_map; + u8 maddr_map; + u8 ts_mcast_type; +}; + struct gbe_slave { void __iomem *port_regs; void __iomem *emac_regs; @@ -630,6 +694,7 @@ struct gbe_slave { u32 mac_control; u8 phy_port_t; struct device_node *phy_node; + struct ts_ctl ts_ctl; struct list_head slave_list; }; @@ -655,6 +720,7 @@ struct gbe_priv { void __iomem *switch_regs; void __iomem *host_port_regs; void __iomem *ale_reg; + void __iomem *cpts_reg; void __iomem *sgmii_port_regs; void __iomem *sgmii_port34_regs; void __iomem *xgbe_serdes_regs; @@ -678,6 +744,9 @@ struct gbe_priv { int num_et_stats; /* Lock for updating the hwstats */ spinlock_t hw_stats_lock; + + int cpts_registered; + struct cpts *cpts; }; struct gbe_intf { @@ -1912,6 +1981,49 @@ static int keystone_set_link_ksettings(struct net_device *ndev, return phy_ethtool_ksettings_set(phy, cmd); } +#if IS_ENABLED(CONFIG_TI_CPTS) +static int keystone_get_ts_info(struct net_device *ndev, + struct ethtool_ts_info *info) +{ + struct netcp_intf *netcp = netdev_priv(ndev); + struct gbe_intf *gbe_intf; + + gbe_intf = netcp_module_get_intf_data(&gbe_module, netcp); + if (!gbe_intf || !gbe_intf->gbe_dev->cpts) + return -EINVAL; + + info->so_timestamping = + SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + info->phc_index = gbe_intf->gbe_dev->cpts->phc_index; + info->tx_types = + (1 << HWTSTAMP_TX_OFF) | + (1 << HWTSTAMP_TX_ON); + info->rx_filters = + (1 << HWTSTAMP_FILTER_NONE) | + (1 << HWTSTAMP_FILTER_PTP_V1_L4_EVENT) | + (1 << HWTSTAMP_FILTER_PTP_V2_EVENT); + return 0; +} +#else +static int keystone_get_ts_info(struct net_device *ndev, + struct ethtool_ts_info *info) +{ + info->so_timestamping = + SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE; + info->phc_index = -1; + info->tx_types = 0; + info->rx_filters = 0; + return 0; +} +#endif /* CONFIG_TI_CPTS */ + static const struct ethtool_ops keystone_ethtool_ops = { .get_drvinfo = keystone_get_drvinfo, .get_link = ethtool_op_get_link, @@ -1922,6 +2034,7 @@ static const struct ethtool_ops keystone_ethtool_ops = { .get_ethtool_stats = keystone_get_ethtool_stats, .get_link_ksettings = keystone_get_link_ksettings, .set_link_ksettings = keystone_set_link_ksettings, + .get_ts_info = keystone_get_ts_info, }; #define mac_hi(mac) (((mac)[0] << 0) | ((mac)[1] << 8) | \ @@ -2365,16 +2478,279 @@ static int gbe_del_vid(void *intf_priv, int vid) return 0; } +#if IS_ENABLED(CONFIG_TI_CPTS) +#define HAS_PHY_TXTSTAMP(p) ((p)->drv && (p)->drv->txtstamp) +#define HAS_PHY_RXTSTAMP(p) ((p)->drv && (p)->drv->rxtstamp) + +static void gbe_txtstamp(void *context, struct sk_buff *skb) +{ + struct gbe_intf *gbe_intf = context; + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + + cpts_tx_timestamp(gbe_dev->cpts, skb); +} + +static bool gbe_need_txtstamp(struct gbe_intf *gbe_intf, + const struct netcp_packet *p_info) +{ + struct sk_buff *skb = p_info->skb; + unsigned int class = ptp_classify_raw(skb); + + if (class == PTP_CLASS_NONE) + return false; + + switch (class) { + case PTP_CLASS_V1_IPV4: + case PTP_CLASS_V1_IPV6: + case PTP_CLASS_V2_IPV4: + case PTP_CLASS_V2_IPV6: + case PTP_CLASS_V2_L2: + case (PTP_CLASS_V2_VLAN | PTP_CLASS_L2): + case (PTP_CLASS_V2_VLAN | PTP_CLASS_IPV4): + case (PTP_CLASS_V2_VLAN | PTP_CLASS_IPV6): + return true; + } + + return false; +} + +static int gbe_txtstamp_mark_pkt(struct gbe_intf *gbe_intf, + struct netcp_packet *p_info) +{ + struct phy_device *phydev = p_info->skb->dev->phydev; + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + + if (!(skb_shinfo(p_info->skb)->tx_flags & SKBTX_HW_TSTAMP) || + !cpts_is_tx_enabled(gbe_dev->cpts)) + return 0; + + /* If phy has the txtstamp api, assume it will do it. + * We mark it here because skb_tx_timestamp() is called + * after all the txhooks are called. + */ + if (phydev && HAS_PHY_TXTSTAMP(phydev)) { + skb_shinfo(p_info->skb)->tx_flags |= SKBTX_IN_PROGRESS; + return 0; + } + + if (gbe_need_txtstamp(gbe_intf, p_info)) { + p_info->txtstamp = gbe_txtstamp; + p_info->ts_context = (void *)gbe_intf; + skb_shinfo(p_info->skb)->tx_flags |= SKBTX_IN_PROGRESS; + } + + return 0; +} + +static int gbe_rxtstamp(struct gbe_intf *gbe_intf, struct netcp_packet *p_info) +{ + struct phy_device *phydev = p_info->skb->dev->phydev; + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + + if (p_info->rxtstamp_complete) + return 0; + + if (phydev && HAS_PHY_RXTSTAMP(phydev)) { + p_info->rxtstamp_complete = true; + return 0; + } + + cpts_rx_timestamp(gbe_dev->cpts, p_info->skb); + p_info->rxtstamp_complete = true; + + return 0; +} + +static int gbe_hwtstamp_get(struct gbe_intf *gbe_intf, struct ifreq *ifr) +{ + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + struct cpts *cpts = gbe_dev->cpts; + struct hwtstamp_config cfg; + + if (!cpts) + return -EOPNOTSUPP; + + cfg.flags = 0; + cfg.tx_type = cpts_is_tx_enabled(cpts) ? + HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; + cfg.rx_filter = (cpts_is_rx_enabled(cpts) ? + cpts->rx_enable : HWTSTAMP_FILTER_NONE); + + return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; +} + +static void gbe_hwtstamp(struct gbe_intf *gbe_intf) +{ + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + struct gbe_slave *slave = gbe_intf->slave; + u32 ts_en, seq_id, ctl; + + if (!cpts_is_rx_enabled(gbe_dev->cpts) && + !cpts_is_tx_enabled(gbe_dev->cpts)) { + writel(0, GBE_REG_ADDR(slave, port_regs, ts_ctl)); + return; + } + + seq_id = (30 << TS_SEQ_ID_OFS_SHIFT) | ETH_P_1588; + ts_en = EVENT_MSG_BITS << TS_MSG_TYPE_EN_SHIFT; + ctl = ETH_P_1588 | TS_TTL_NONZERO | + (slave->ts_ctl.dst_port_map << TS_CTL_DST_PORT_SHIFT) | + (slave->ts_ctl.uni ? TS_UNI_EN : + slave->ts_ctl.maddr_map << TS_CTL_MADDR_SHIFT); + + if (cpts_is_tx_enabled(gbe_dev->cpts)) + ts_en |= (TS_TX_ANX_ALL_EN | TS_TX_VLAN_LT1_EN); + + if (cpts_is_rx_enabled(gbe_dev->cpts)) + ts_en |= (TS_RX_ANX_ALL_EN | TS_RX_VLAN_LT1_EN); + + writel(ts_en, GBE_REG_ADDR(slave, port_regs, ts_ctl)); + writel(seq_id, GBE_REG_ADDR(slave, port_regs, ts_seq_ltype)); + writel(ctl, GBE_REG_ADDR(slave, port_regs, ts_ctl_ltype2)); +} + +static int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *ifr) +{ + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + struct cpts *cpts = gbe_dev->cpts; + struct hwtstamp_config cfg; + + if (!cpts) + return -EOPNOTSUPP; + + if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) + return -EFAULT; + + /* reserved for future extensions */ + if (cfg.flags) + return -EINVAL; + + switch (cfg.tx_type) { + case HWTSTAMP_TX_OFF: + cpts_tx_enable(cpts, 0); + break; + case HWTSTAMP_TX_ON: + cpts_tx_enable(cpts, 1); + break; + default: + return -ERANGE; + } + + switch (cfg.rx_filter) { + case HWTSTAMP_FILTER_NONE: + cpts_rx_enable(cpts, 0); + break; + case HWTSTAMP_FILTER_ALL: + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + cpts_rx_enable(cpts, HWTSTAMP_FILTER_PTP_V1_L4_EVENT); + cfg.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; + break; + 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: + cpts_rx_enable(cpts, HWTSTAMP_FILTER_PTP_V2_EVENT); + cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + break; + default: + return -ERANGE; + } + + gbe_hwtstamp(gbe_intf); + + return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; +} + +static void gbe_register_cpts(struct gbe_priv *gbe_dev) +{ + if (!gbe_dev->cpts) + return; + + if (gbe_dev->cpts_registered > 0) + goto done; + + if (cpts_register(gbe_dev->cpts)) { + dev_err(gbe_dev->dev, "error registering cpts device\n"); + return; + } + +done: + ++gbe_dev->cpts_registered; +} + +static void gbe_unregister_cpts(struct gbe_priv *gbe_dev) +{ + if (!gbe_dev->cpts || (gbe_dev->cpts_registered <= 0)) + return; + + if (--gbe_dev->cpts_registered) + return; + + cpts_unregister(gbe_dev->cpts); +} +#else +static inline int gbe_txtstamp_mark_pkt(struct gbe_intf *gbe_intf, + struct netcp_packet *p_info) +{ + return 0; +} + +static inline int gbe_rxtstamp(struct gbe_intf *gbe_intf, + struct netcp_packet *p_info) +{ + return 0; +} + +static inline int gbe_hwtstamp(struct gbe_intf *gbe_intf, + struct ifreq *ifr, int cmd) +{ + return -EOPNOTSUPP; +} + +static inline void gbe_register_cpts(struct gbe_priv *gbe_dev) +{ +} + +static inline void gbe_unregister_cpts(struct gbe_priv *gbe_dev) +{ +} + +static inline int gbe_hwtstamp_get(struct gbe_intf *gbe_intf, struct ifreq *req) +{ + return -EOPNOTSUPP; +} + +static inline int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *req) +{ + return -EOPNOTSUPP; +} +#endif /* CONFIG_TI_CPTS */ + static int gbe_ioctl(void *intf_priv, struct ifreq *req, int cmd) { struct gbe_intf *gbe_intf = intf_priv; struct phy_device *phy = gbe_intf->slave->phy; - int ret = -EOPNOTSUPP; + + if (!phy || !phy->drv->hwtstamp) { + switch (cmd) { + case SIOCGHWTSTAMP: + return gbe_hwtstamp_get(gbe_intf, req); + case SIOCSHWTSTAMP: + return gbe_hwtstamp_set(gbe_intf, req); + } + } if (phy) - ret = phy_mii_ioctl(phy, req, cmd); + return phy_mii_ioctl(phy, req, cmd); - return ret; + return -EOPNOTSUPP; } static void netcp_ethss_timer(unsigned long arg) @@ -2410,12 +2786,20 @@ static void netcp_ethss_timer(unsigned long arg) add_timer(&gbe_dev->timer); } -static int gbe_tx_hook(int order, void *data, struct netcp_packet *p_info) +static int gbe_txhook(int order, void *data, struct netcp_packet *p_info) { struct gbe_intf *gbe_intf = data; p_info->tx_pipe = &gbe_intf->tx_pipe; - return 0; + + return gbe_txtstamp_mark_pkt(gbe_intf, p_info); +} + +static int gbe_rxhook(int order, void *data, struct netcp_packet *p_info) +{ + struct gbe_intf *gbe_intf = data; + + return gbe_rxtstamp(gbe_intf, p_info); } static int gbe_open(void *intf_priv, struct net_device *ndev) @@ -2465,11 +2849,14 @@ static int gbe_open(void *intf_priv, struct net_device *ndev) if (ret) goto fail; - netcp_register_txhook(netcp, GBE_TXHOOK_ORDER, gbe_tx_hook, - gbe_intf); + netcp_register_txhook(netcp, GBE_TXHOOK_ORDER, gbe_txhook, gbe_intf); + netcp_register_rxhook(netcp, GBE_RXHOOK_ORDER, gbe_rxhook, gbe_intf); slave->open = true; netcp_ethss_update_link_state(gbe_dev, slave, ndev); + + gbe_register_cpts(gbe_dev); + return 0; fail: @@ -2481,16 +2868,36 @@ static int gbe_close(void *intf_priv, struct net_device *ndev) { struct gbe_intf *gbe_intf = intf_priv; struct netcp_intf *netcp = netdev_priv(ndev); + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + + gbe_unregister_cpts(gbe_dev); gbe_slave_stop(gbe_intf); - netcp_unregister_txhook(netcp, GBE_TXHOOK_ORDER, gbe_tx_hook, - gbe_intf); + + netcp_unregister_rxhook(netcp, GBE_RXHOOK_ORDER, gbe_rxhook, gbe_intf); + netcp_unregister_txhook(netcp, GBE_TXHOOK_ORDER, gbe_txhook, gbe_intf); gbe_intf->slave->open = false; atomic_set(&gbe_intf->slave->link_state, NETCP_LINK_STATE_INVALID); return 0; } +#if IS_ENABLED(CONFIG_TI_CPTS) +static void init_slave_ts_ctl(struct gbe_slave *slave) +{ + slave->ts_ctl.uni = 1; + slave->ts_ctl.dst_port_map = + (TS_CTL_DST_PORT >> TS_CTL_DST_PORT_SHIFT) & 0x3; + slave->ts_ctl.maddr_map = + (TS_CTL_MADDR_ALL >> TS_CTL_MADDR_SHIFT) & 0x1f; +} + +#else +static void init_slave_ts_ctl(struct gbe_slave *slave) +{ +} +#endif /* CONFIG_TI_CPTS */ + static int init_slave(struct gbe_priv *gbe_dev, struct gbe_slave *slave, struct device_node *node) { @@ -2605,6 +3012,8 @@ static int init_slave(struct gbe_priv *gbe_dev, struct gbe_slave *slave, } atomic_set(&slave->link_state, NETCP_LINK_STATE_INVALID); + + init_slave_ts_ctl(slave); return 0; } @@ -2795,6 +3204,7 @@ static int set_xgbe_ethss10_priv(struct gbe_priv *gbe_dev, XGBE10_HW_STATS_OFFSET + (GBE_HW_STATS_REG_MAP_SZ * i); gbe_dev->ale_reg = gbe_dev->switch_regs + XGBE10_ALE_OFFSET; + gbe_dev->cpts_reg = gbe_dev->switch_regs + XGBE10_CPTS_OFFSET; gbe_dev->ale_ports = gbe_dev->max_num_ports; gbe_dev->host_port = XGBE10_HOST_PORT_NUM; gbe_dev->ale_entries = XGBE10_NUM_ALE_ENTRIES; @@ -2917,6 +3327,7 @@ static int set_gbe_ethss14_priv(struct gbe_priv *gbe_dev, (GBE_HW_STATS_REG_MAP_SZ * (i & 0x1)); } + gbe_dev->cpts_reg = gbe_dev->switch_regs + GBE13_CPTS_OFFSET; gbe_dev->ale_reg = gbe_dev->switch_regs + GBE13_ALE_OFFSET; gbe_dev->ale_ports = gbe_dev->max_num_ports; gbe_dev->host_port = GBE13_HOST_PORT_NUM; @@ -3006,6 +3417,7 @@ static int set_gbenu_ethss_priv(struct gbe_priv *gbe_dev, gbe_dev->hw_stats_regs[i] = gbe_dev->switch_regs + GBENU_HW_STATS_OFFSET + (GBENU_HW_STATS_REG_MAP_SZ * i); + gbe_dev->cpts_reg = gbe_dev->switch_regs + GBENU_CPTS_OFFSET; gbe_dev->ale_reg = gbe_dev->switch_regs + GBENU_ALE_OFFSET; gbe_dev->ale_ports = gbe_dev->max_num_ports; gbe_dev->host_port = GBENU_HOST_PORT_NUM; @@ -3187,6 +3599,12 @@ static int gbe_probe(struct netcp_device *netcp_device, struct device *dev, dev_dbg(gbe_dev->dev, "Created a gbe ale engine\n"); } + gbe_dev->cpts = cpts_create(gbe_dev->dev, gbe_dev->cpts_reg, node); + if (IS_ENABLED(CONFIG_TI_CPTS) && IS_ERR(gbe_dev->cpts)) { + ret = PTR_ERR(gbe_dev->cpts); + goto free_sec_ports; + } + /* initialize host port */ gbe_init_host_port(gbe_dev); @@ -3275,6 +3693,7 @@ static int gbe_remove(struct netcp_device *netcp_device, void *inst_priv) struct gbe_priv *gbe_dev = inst_priv; del_timer_sync(&gbe_dev->timer); + cpts_release(gbe_dev->cpts); cpsw_ale_stop(gbe_dev->ale); cpsw_ale_destroy(gbe_dev->ale); netcp_txpipe_close(&gbe_dev->tx_pipe); |