From 391fd6caf5cfc74a15a79c1237de0a2d84dfd398 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:33 -0600 Subject: [PATCH 01/13] net: ethernet: ti: cpts: switch to readl/writel_relaxed() Switch to readl/writel_relaxed() APIs, because this is recommended API and the CPTS IP is reused on Keystone 2 SoCs where LE/BE modes are supported. Signed-off-by: Grygorii Strashko Acked-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpts.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index 85a55b4ff8c0..a42c44910af1 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -33,8 +33,8 @@ #ifdef CONFIG_TI_CPTS -#define cpts_read32(c, r) __raw_readl(&c->reg->r) -#define cpts_write32(c, v, r) __raw_writel(v, &c->reg->r) +#define cpts_read32(c, r) readl_relaxed(&c->reg->r) +#define cpts_write32(c, v, r) writel_relaxed(v, &c->reg->r) static int event_expired(struct cpts_event *event) { From c8395d4e1d4ffbc9d8aa61f534c82e8deed72cfd Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:34 -0600 Subject: [PATCH 02/13] net: ethernet: ti: allow cpts to be built separately TI CPTS IP is used as part of TI OMAP CPSW driver, but it's also present as part of NETCP on TI Keystone 2 SoCs. So, It's required to enable build of CPTS for both this drivers and this can be achieved by allowing CPTS to be built separately. Hence, allow cpts to be built separately and convert it to be a module as both CPSW and NETCP drives can be built as modules. Signed-off-by: Grygorii Strashko Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/Kconfig | 2 +- drivers/net/ethernet/ti/Makefile | 3 ++- drivers/net/ethernet/ti/cpsw.c | 22 +++++++++++++++++----- drivers/net/ethernet/ti/cpts.c | 16 ++++++++-------- drivers/net/ethernet/ti/cpts.h | 18 ++++++++++++++---- 5 files changed, 42 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig index 9904d740d528..ff7f518a0702 100644 --- a/drivers/net/ethernet/ti/Kconfig +++ b/drivers/net/ethernet/ti/Kconfig @@ -74,7 +74,7 @@ config TI_CPSW will be called cpsw. config TI_CPTS - bool "TI Common Platform Time Sync (CPTS) Support" + tristate "TI Common Platform Time Sync (CPTS) Support" depends on TI_CPSW select PTP_1588_CLOCK ---help--- diff --git a/drivers/net/ethernet/ti/Makefile b/drivers/net/ethernet/ti/Makefile index d420d9413e4a..1e7c10bf8713 100644 --- a/drivers/net/ethernet/ti/Makefile +++ b/drivers/net/ethernet/ti/Makefile @@ -12,8 +12,9 @@ obj-$(CONFIG_TI_DAVINCI_MDIO) += davinci_mdio.o obj-$(CONFIG_TI_DAVINCI_CPDMA) += davinci_cpdma.o obj-$(CONFIG_TI_CPSW_PHY_SEL) += cpsw-phy-sel.o obj-$(CONFIG_TI_CPSW_ALE) += cpsw_ale.o +obj-$(CONFIG_TI_CPTS) += cpts.o obj-$(CONFIG_TI_CPSW) += ti_cpsw.o -ti_cpsw-y := cpsw.o cpts.o +ti_cpsw-y := cpsw.o obj-$(CONFIG_TI_KEYSTONE_NETCP) += keystone_netcp.o keystone_netcp-y := netcp_core.o diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index f373a4b44857..8fdb27498160 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1594,7 +1594,7 @@ fail: return NETDEV_TX_BUSY; } -#ifdef CONFIG_TI_CPTS +#if IS_ENABLED(CONFIG_TI_CPTS) static void cpsw_hwtstamp_v1(struct cpsw_common *cpsw) { @@ -1742,7 +1742,16 @@ static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr) return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; } +#else +static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr) +{ + return -EOPNOTSUPP; +} +static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr) +{ + return -EOPNOTSUPP; +} #endif /*CONFIG_TI_CPTS*/ static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd) @@ -1755,12 +1764,10 @@ static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd) return -EINVAL; switch (cmd) { -#ifdef CONFIG_TI_CPTS case SIOCSHWTSTAMP: return cpsw_hwtstamp_set(dev, req); case SIOCGHWTSTAMP: return cpsw_hwtstamp_get(dev, req); -#endif } if (!cpsw->slaves[slave_no].phy) @@ -2100,10 +2107,10 @@ static void cpsw_set_msglevel(struct net_device *ndev, u32 value) priv->msg_enable = value; } +#if IS_ENABLED(CONFIG_TI_CPTS) static int cpsw_get_ts_info(struct net_device *ndev, struct ethtool_ts_info *info) { -#ifdef CONFIG_TI_CPTS struct cpsw_common *cpsw = ndev_to_cpsw(ndev); info->so_timestamping = @@ -2120,7 +2127,12 @@ static int cpsw_get_ts_info(struct net_device *ndev, info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | (1 << HWTSTAMP_FILTER_PTP_V2_EVENT); + return 0; +} #else +static int cpsw_get_ts_info(struct net_device *ndev, + struct ethtool_ts_info *info) +{ info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE | @@ -2128,9 +2140,9 @@ static int cpsw_get_ts_info(struct net_device *ndev, info->phc_index = -1; info->tx_types = 0; info->rx_filters = 0; -#endif return 0; } +#endif static int cpsw_get_link_ksettings(struct net_device *ndev, struct ethtool_link_ksettings *ecmd) diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index a42c44910af1..8cb0369f0d1a 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -31,8 +31,6 @@ #include "cpts.h" -#ifdef CONFIG_TI_CPTS - #define cpts_read32(c, r) readl_relaxed(&c->reg->r) #define cpts_write32(c, v, r) writel_relaxed(v, &c->reg->r) @@ -334,6 +332,7 @@ void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb) memset(ssh, 0, sizeof(*ssh)); ssh->hwtstamp = ns_to_ktime(ns); } +EXPORT_SYMBOL_GPL(cpts_rx_timestamp); void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) { @@ -349,13 +348,11 @@ void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) ssh.hwtstamp = ns_to_ktime(ns); skb_tstamp_tx(skb, &ssh); } - -#endif /*CONFIG_TI_CPTS*/ +EXPORT_SYMBOL_GPL(cpts_tx_timestamp); int cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift) { -#ifdef CONFIG_TI_CPTS int err, i; unsigned long flags; @@ -391,18 +388,21 @@ int cpts_register(struct device *dev, struct cpts *cpts, schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD); cpts->phc_index = ptp_clock_index(cpts->clock); -#endif return 0; } +EXPORT_SYMBOL_GPL(cpts_register); void cpts_unregister(struct cpts *cpts) { -#ifdef CONFIG_TI_CPTS if (cpts->clock) { ptp_clock_unregister(cpts->clock); cancel_delayed_work_sync(&cpts->overflow_work); } if (cpts->refclk) cpts_clk_release(cpts); -#endif } +EXPORT_SYMBOL_GPL(cpts_unregister); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TI CPTS driver"); +MODULE_AUTHOR("Richard Cochran "); diff --git a/drivers/net/ethernet/ti/cpts.h b/drivers/net/ethernet/ti/cpts.h index 69a46b92c7d6..416ba2c0d99a 100644 --- a/drivers/net/ethernet/ti/cpts.h +++ b/drivers/net/ethernet/ti/cpts.h @@ -111,7 +111,7 @@ struct cpts { struct cpsw_cpts __iomem *reg; int tx_enable; int rx_enable; -#ifdef CONFIG_TI_CPTS +#if IS_ENABLED(CONFIG_TI_CPTS) struct ptp_clock_info info; struct ptp_clock *clock; spinlock_t lock; /* protects time registers */ @@ -127,9 +127,11 @@ struct cpts { #endif }; -#ifdef CONFIG_TI_CPTS +#if IS_ENABLED(CONFIG_TI_CPTS) void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb); void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb); +int cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift); +void cpts_unregister(struct cpts *cpts); #else static inline void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb) { @@ -137,9 +139,17 @@ static inline void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb) static inline void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) { } + +static inline int +cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift) +{ + return 0; +} + +static inline void cpts_unregister(struct cpts *cpts) +{ +} #endif -int cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift); -void cpts_unregister(struct cpts *cpts); #endif From b63ba58ee9957261b39209f3031ff06714cf13cb Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:35 -0600 Subject: [PATCH 03/13] net: ethernet: ti: cpsw: minimize direct access to struct cpts This will provide more flexibility in changing CPTS internals and also required for further changes. Signed-off-by: Grygorii Strashko Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 28 ++++++++++++------------ drivers/net/ethernet/ti/cpts.h | 39 ++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 8fdb27498160..7599895d29b8 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1562,7 +1562,7 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb, } if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && - cpsw->cpts->tx_enable) + cpts_is_tx_enabled(cpsw->cpts)) skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; skb_tx_timestamp(skb); @@ -1601,7 +1601,8 @@ static void cpsw_hwtstamp_v1(struct cpsw_common *cpsw) struct cpsw_slave *slave = &cpsw->slaves[cpsw->data.active_slave]; u32 ts_en, seq_id; - if (!cpsw->cpts->tx_enable && !cpsw->cpts->rx_enable) { + if (!cpts_is_tx_enabled(cpsw->cpts) && + !cpts_is_rx_enabled(cpsw->cpts)) { slave_write(slave, 0, CPSW1_TS_CTL); return; } @@ -1609,10 +1610,10 @@ static void cpsw_hwtstamp_v1(struct cpsw_common *cpsw) seq_id = (30 << CPSW_V1_SEQ_ID_OFS_SHIFT) | ETH_P_1588; ts_en = EVENT_MSG_BITS << CPSW_V1_MSG_TYPE_OFS; - if (cpsw->cpts->tx_enable) + if (cpts_is_tx_enabled(cpsw->cpts)) ts_en |= CPSW_V1_TS_TX_EN; - if (cpsw->cpts->rx_enable) + if (cpts_is_rx_enabled(cpsw->cpts)) ts_en |= CPSW_V1_TS_RX_EN; slave_write(slave, ts_en, CPSW1_TS_CTL); @@ -1635,20 +1636,20 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv) case CPSW_VERSION_2: ctrl &= ~CTRL_V2_ALL_TS_MASK; - if (cpsw->cpts->tx_enable) + if (cpts_is_tx_enabled(cpsw->cpts)) ctrl |= CTRL_V2_TX_TS_BITS; - if (cpsw->cpts->rx_enable) + if (cpts_is_rx_enabled(cpsw->cpts)) ctrl |= CTRL_V2_RX_TS_BITS; break; case CPSW_VERSION_3: default: ctrl &= ~CTRL_V3_ALL_TS_MASK; - if (cpsw->cpts->tx_enable) + if (cpts_is_tx_enabled(cpsw->cpts)) ctrl |= CTRL_V3_TX_TS_BITS; - if (cpsw->cpts->rx_enable) + if (cpts_is_rx_enabled(cpsw->cpts)) ctrl |= CTRL_V3_RX_TS_BITS; break; } @@ -1684,7 +1685,7 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr) switch (cfg.rx_filter) { case HWTSTAMP_FILTER_NONE: - cpts->rx_enable = 0; + cpts_rx_enable(cpts, 0); break; case HWTSTAMP_FILTER_ALL: case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: @@ -1700,14 +1701,14 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr) case HWTSTAMP_FILTER_PTP_V2_EVENT: case HWTSTAMP_FILTER_PTP_V2_SYNC: case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: - cpts->rx_enable = 1; + cpts_rx_enable(cpts, 1); cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; break; default: return -ERANGE; } - cpts->tx_enable = cfg.tx_type == HWTSTAMP_TX_ON; + cpts_tx_enable(cpts, cfg.tx_type == HWTSTAMP_TX_ON); switch (cpsw->version) { case CPSW_VERSION_1: @@ -1736,8 +1737,9 @@ static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr) return -EOPNOTSUPP; cfg.flags = 0; - cfg.tx_type = cpts->tx_enable ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; - cfg.rx_filter = (cpts->rx_enable ? + cfg.tx_type = cpts_is_tx_enabled(cpts) ? + HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; + cfg.rx_filter = (cpts_is_rx_enabled(cpts) ? HWTSTAMP_FILTER_PTP_V2_EVENT : HWTSTAMP_FILTER_NONE); return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; diff --git a/drivers/net/ethernet/ti/cpts.h b/drivers/net/ethernet/ti/cpts.h index 416ba2c0d99a..29a1e80c6a2b 100644 --- a/drivers/net/ethernet/ti/cpts.h +++ b/drivers/net/ethernet/ti/cpts.h @@ -132,6 +132,27 @@ void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb); void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb); int cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift); void cpts_unregister(struct cpts *cpts); + +static inline void cpts_rx_enable(struct cpts *cpts, int enable) +{ + cpts->rx_enable = enable; +} + +static inline bool cpts_is_rx_enabled(struct cpts *cpts) +{ + return !!cpts->rx_enable; +} + +static inline void cpts_tx_enable(struct cpts *cpts, int enable) +{ + cpts->tx_enable = enable; +} + +static inline bool cpts_is_tx_enabled(struct cpts *cpts) +{ + return !!cpts->tx_enable; +} + #else static inline void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb) { @@ -149,6 +170,24 @@ cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift) static inline void cpts_unregister(struct cpts *cpts) { } + +static inline void cpts_rx_enable(struct cpts *cpts, int enable) +{ +} + +static inline bool cpts_is_rx_enabled(struct cpts *cpts) +{ + return false; +} + +static inline void cpts_tx_enable(struct cpts *cpts, int enable) +{ +} + +static inline bool cpts_is_tx_enabled(struct cpts *cpts) +{ + return false; +} #endif From fd123a9414766e73fa704547e7f03595741b7de1 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:36 -0600 Subject: [PATCH 04/13] net: ethernet: ti: cpts: fix unbalanced clk api usage in cpts_register/unregister There are two issues with TI CPTS code which are reproducible when TI CPSW ethX device passes few up/down iterations: - cpts refclk prepare counter continuously incremented after each up/down iteration; - devm_clk_get(dev, "cpts") is called many times. Hence, fix these issues by using clk_disable_unprepare() in cpts_clk_release() and skipping devm_clk_get() if cpts refclk has been acquired already. Signed-off-by: Grygorii Strashko Acked-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpts.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index 8cb0369f0d1a..61198f154a04 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -230,18 +230,20 @@ static void cpts_overflow_check(struct work_struct *work) static void cpts_clk_init(struct device *dev, struct cpts *cpts) { - cpts->refclk = devm_clk_get(dev, "cpts"); - if (IS_ERR(cpts->refclk)) { - dev_err(dev, "Failed to get cpts refclk\n"); - cpts->refclk = NULL; - return; + if (!cpts->refclk) { + cpts->refclk = devm_clk_get(dev, "cpts"); + if (IS_ERR(cpts->refclk)) { + dev_err(dev, "Failed to get cpts refclk\n"); + cpts->refclk = NULL; + return; + } } clk_prepare_enable(cpts->refclk); } static void cpts_clk_release(struct cpts *cpts) { - clk_disable(cpts->refclk); + clk_disable_unprepare(cpts->refclk); } static int cpts_match(struct sk_buff *skb, unsigned int ptp_class, From 6c691405bcaf8f1a7413416cc757756cfb060875 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:37 -0600 Subject: [PATCH 05/13] net: ethernet: ti: cpts: fix registration order The ptp clock registered before spinlock, which is protecting it, and before timecounter and cyclecounter initialization in cpts_register(). So, ensure that ptp clock is registered the last, after everything else is done. Signed-off-by: Grygorii Strashko Acked-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpts.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index 61198f154a04..3dda6d5ba0aa 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -356,15 +356,8 @@ int cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift) { int err, i; - unsigned long flags; cpts->info = cpts_info; - cpts->clock = ptp_clock_register(&cpts->info, dev); - if (IS_ERR(cpts->clock)) { - err = PTR_ERR(cpts->clock); - cpts->clock = NULL; - return err; - } spin_lock_init(&cpts->lock); cpts->cc.read = cpts_systim_read; @@ -382,15 +375,26 @@ int cpts_register(struct device *dev, struct cpts *cpts, cpts_write32(cpts, CPTS_EN, control); cpts_write32(cpts, TS_PEND_EN, int_enable); - spin_lock_irqsave(&cpts->lock, flags); timecounter_init(&cpts->tc, &cpts->cc, ktime_to_ns(ktime_get_real())); - spin_unlock_irqrestore(&cpts->lock, flags); INIT_DELAYED_WORK(&cpts->overflow_work, cpts_overflow_check); + + cpts->clock = ptp_clock_register(&cpts->info, dev); + if (IS_ERR(cpts->clock)) { + err = PTR_ERR(cpts->clock); + cpts->clock = NULL; + goto err_ptp; + } + cpts->phc_index = ptp_clock_index(cpts->clock); + schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD); - cpts->phc_index = ptp_clock_index(cpts->clock); return 0; + +err_ptp: + if (cpts->refclk) + cpts_clk_release(cpts); + return err; } EXPORT_SYMBOL_GPL(cpts_register); From 8fcd68914e447af006f9597a2688754517fabfe9 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:38 -0600 Subject: [PATCH 06/13] net: ethernet: ti: cpts: disable cpts when unregistered The cpts now is left enabled after unregistration. Hence, disable it in cpts_unregister(). Signed-off-by: Grygorii Strashko Acked-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpts.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index 3dda6d5ba0aa..d3c1ac5e9336 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -404,6 +404,10 @@ void cpts_unregister(struct cpts *cpts) ptp_clock_unregister(cpts->clock); cancel_delayed_work_sync(&cpts->overflow_work); } + + cpts_write32(cpts, 0, int_enable); + cpts_write32(cpts, 0, control); + if (cpts->refclk) cpts_clk_release(cpts); } From e4439fa838eb78b20f82665110b478018268833e Mon Sep 17 00:00:00 2001 From: WingMan Kwok Date: Tue, 6 Dec 2016 18:00:39 -0600 Subject: [PATCH 07/13] net: ethernet: ti: cpts: clean up event list if event pool is empty When a CPTS user does not exit gracefully by disabling cpts timestamping and leaving a joined multicast group, the system continues to receive and timestamps the ptp packets which eventually occupy all the event list entries. When this happns, the added code tries to remove some list entries which are expired. Signed-off-by: WingMan Kwok Signed-off-by: Grygorii Strashko Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpts.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index d3c1ac5e9336..7ab1fa73c0c0 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -57,6 +57,26 @@ static int cpts_fifo_pop(struct cpts *cpts, u32 *high, u32 *low) return -1; } +static int cpts_purge_events(struct cpts *cpts) +{ + struct list_head *this, *next; + struct cpts_event *event; + int removed = 0; + + list_for_each_safe(this, next, &cpts->events) { + event = list_entry(this, struct cpts_event, list); + if (event_expired(event)) { + list_del_init(&event->list); + list_add(&event->list, &cpts->pool); + ++removed; + } + } + + if (removed) + pr_debug("cpts: event pool cleaned up %d\n", removed); + return removed ? 0 : -1; +} + /* * Returns zero if matching event type was found. */ @@ -69,10 +89,12 @@ static int cpts_fifo_read(struct cpts *cpts, int match) for (i = 0; i < CPTS_FIFO_DEPTH; i++) { if (cpts_fifo_pop(cpts, &hi, &lo)) break; - if (list_empty(&cpts->pool)) { - pr_err("cpts: event pool is empty\n"); + + if (list_empty(&cpts->pool) && cpts_purge_events(cpts)) { + pr_err("cpts: event pool empty\n"); return -1; } + event = list_first_entry(&cpts->pool, struct cpts_event, list); event->tmo = jiffies + 2; event->high = hi; From 2a79df3ee9679dd1433ef9baa87ec395355eb0b4 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:40 -0600 Subject: [PATCH 08/13] net: ethernet: ti: cpts: drop excessive writes to CTRL and INT_EN regs CPTS module and IRQs are always enabled when CPTS is registered, before starting overflow check work, and disabled during deregistration, when overflow check work has been canceled already. So, It doesn't require to (re)enable CPTS module and IRQs in cpts_overflow_check(). Signed-off-by: Grygorii Strashko Acked-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpts.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index 7ab1fa73c0c0..fe1bb7f02f2e 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -243,8 +243,6 @@ static void cpts_overflow_check(struct work_struct *work) struct timespec64 ts; struct cpts *cpts = container_of(work, struct cpts, overflow_work.work); - cpts_write32(cpts, CPTS_EN, control); - cpts_write32(cpts, TS_PEND_EN, int_enable); cpts_ptp_gettime(&cpts->info, &ts); pr_debug("cpts overflow check at %lld.%09lu\n", ts.tv_sec, ts.tv_nsec); schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD); From 8a2c9a5ab4b933efd611af0a1313ec7892f5df1a Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:41 -0600 Subject: [PATCH 09/13] net: ethernet: ti: cpts: rework initialization/deinitialization The current implementation CPTS initialization and deinitialization (represented by cpts_register/unregister()) does too many static initialization from .ndo_open(), which is reasonable to do once at probe time instead, and also require caller to allocate memory for struct cpts, which is internal for CPTS driver in general. This patch splits CPTS initialization and deinitialization on two parts: - static initializtion cpts_create()/cpts_release() which expected to be executed when parent driver is probed/removed; - dynamic part cpts_register/unregister() which expected to be executed when network device is opened/closed. As result, current code of CPTS parent driver - CPSW - will be simplified (and it also will allow simplify adding support for Keystone 2 devices in the future), plus more initialization errors will be catched earlier. In addition, this change allows to clean up cpts.h for the case when CPTS is disabled. Signed-off-by: Grygorii Strashko Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 24 +++++---- drivers/net/ethernet/ti/cpts.c | 99 ++++++++++++++++++++-------------- drivers/net/ethernet/ti/cpts.h | 26 +++++++-- 3 files changed, 92 insertions(+), 57 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 7599895d29b8..a9a835420281 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1487,9 +1487,7 @@ static int cpsw_ndo_open(struct net_device *ndev) if (ret < 0) goto err_cleanup; - if (cpts_register(cpsw->dev, cpsw->cpts, - cpsw->data.cpts_clock_mult, - cpsw->data.cpts_clock_shift)) + if (cpts_register(cpsw->cpts)) dev_err(priv->dev, "error registering cpts device\n"); } @@ -2796,6 +2794,7 @@ static int cpsw_probe(struct platform_device *pdev) struct cpdma_params dma_params; struct cpsw_ale_params ale_params; void __iomem *ss_regs; + void __iomem *cpts_regs; struct resource *res, *ss_res; const struct of_device_id *of_id; struct gpio_descs *mode; @@ -2823,12 +2822,6 @@ static int cpsw_probe(struct platform_device *pdev) priv->dev = &ndev->dev; priv->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG); cpsw->rx_packet_max = max(rx_packet_max, 128); - cpsw->cpts = devm_kzalloc(&pdev->dev, sizeof(struct cpts), GFP_KERNEL); - if (!cpsw->cpts) { - dev_err(&pdev->dev, "error allocating cpts\n"); - ret = -ENOMEM; - goto clean_ndev_ret; - } mode = devm_gpiod_get_array_optional(&pdev->dev, "mode", GPIOD_OUT_LOW); if (IS_ERR(mode)) { @@ -2916,7 +2909,7 @@ static int cpsw_probe(struct platform_device *pdev) switch (cpsw->version) { case CPSW_VERSION_1: cpsw->host_port_regs = ss_regs + CPSW1_HOST_PORT_OFFSET; - cpsw->cpts->reg = ss_regs + CPSW1_CPTS_OFFSET; + cpts_regs = ss_regs + CPSW1_CPTS_OFFSET; cpsw->hw_stats = ss_regs + CPSW1_HW_STATS; dma_params.dmaregs = ss_regs + CPSW1_CPDMA_OFFSET; dma_params.txhdp = ss_regs + CPSW1_STATERAM_OFFSET; @@ -2930,7 +2923,7 @@ static int cpsw_probe(struct platform_device *pdev) case CPSW_VERSION_3: case CPSW_VERSION_4: cpsw->host_port_regs = ss_regs + CPSW2_HOST_PORT_OFFSET; - cpsw->cpts->reg = ss_regs + CPSW2_CPTS_OFFSET; + cpts_regs = ss_regs + CPSW2_CPTS_OFFSET; cpsw->hw_stats = ss_regs + CPSW2_HW_STATS; dma_params.dmaregs = ss_regs + CPSW2_CPDMA_OFFSET; dma_params.txhdp = ss_regs + CPSW2_STATERAM_OFFSET; @@ -2997,6 +2990,14 @@ static int cpsw_probe(struct platform_device *pdev) goto clean_dma_ret; } + cpsw->cpts = cpts_create(cpsw->dev, cpts_regs, + cpsw->data.cpts_clock_mult, + cpsw->data.cpts_clock_shift); + if (IS_ERR(cpsw->cpts)) { + ret = PTR_ERR(cpsw->cpts); + goto clean_ale_ret; + } + ndev->irq = platform_get_irq(pdev, 1); if (ndev->irq < 0) { dev_err(priv->dev, "error getting irq resource\n"); @@ -3112,6 +3113,7 @@ static int cpsw_remove(struct platform_device *pdev) unregister_netdev(cpsw->slaves[1].ndev); unregister_netdev(ndev); + cpts_release(cpsw->cpts); cpsw_ale_destroy(cpsw->ale); cpdma_ctlr_destroy(cpsw->dma); cpsw_remove_dt(pdev); diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index fe1bb7f02f2e..31cd83fa10ce 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -248,24 +248,6 @@ static void cpts_overflow_check(struct work_struct *work) schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD); } -static void cpts_clk_init(struct device *dev, struct cpts *cpts) -{ - if (!cpts->refclk) { - cpts->refclk = devm_clk_get(dev, "cpts"); - if (IS_ERR(cpts->refclk)) { - dev_err(dev, "Failed to get cpts refclk\n"); - cpts->refclk = NULL; - return; - } - } - clk_prepare_enable(cpts->refclk); -} - -static void cpts_clk_release(struct cpts *cpts) -{ - clk_disable_unprepare(cpts->refclk); -} - static int cpts_match(struct sk_buff *skb, unsigned int ptp_class, u16 ts_seqid, u8 ts_msgtype) { @@ -372,34 +354,23 @@ void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) } EXPORT_SYMBOL_GPL(cpts_tx_timestamp); -int cpts_register(struct device *dev, struct cpts *cpts, - u32 mult, u32 shift) +int cpts_register(struct cpts *cpts) { int err, i; - cpts->info = cpts_info; - spin_lock_init(&cpts->lock); - - cpts->cc.read = cpts_systim_read; - cpts->cc.mask = CLOCKSOURCE_MASK(32); - cpts->cc_mult = mult; - cpts->cc.mult = mult; - cpts->cc.shift = shift; - INIT_LIST_HEAD(&cpts->events); INIT_LIST_HEAD(&cpts->pool); for (i = 0; i < CPTS_MAX_EVENTS; i++) list_add(&cpts->pool_data[i].list, &cpts->pool); - cpts_clk_init(dev, cpts); + clk_enable(cpts->refclk); + cpts_write32(cpts, CPTS_EN, control); cpts_write32(cpts, TS_PEND_EN, int_enable); timecounter_init(&cpts->tc, &cpts->cc, ktime_to_ns(ktime_get_real())); - INIT_DELAYED_WORK(&cpts->overflow_work, cpts_overflow_check); - - cpts->clock = ptp_clock_register(&cpts->info, dev); + cpts->clock = ptp_clock_register(&cpts->info, cpts->dev); if (IS_ERR(cpts->clock)) { err = PTR_ERR(cpts->clock); cpts->clock = NULL; @@ -412,27 +383,73 @@ int cpts_register(struct device *dev, struct cpts *cpts, return 0; err_ptp: - if (cpts->refclk) - cpts_clk_release(cpts); + clk_disable(cpts->refclk); return err; } EXPORT_SYMBOL_GPL(cpts_register); void cpts_unregister(struct cpts *cpts) { - if (cpts->clock) { - ptp_clock_unregister(cpts->clock); - cancel_delayed_work_sync(&cpts->overflow_work); - } + if (WARN_ON(!cpts->clock)) + return; + + cancel_delayed_work_sync(&cpts->overflow_work); + + ptp_clock_unregister(cpts->clock); + cpts->clock = NULL; cpts_write32(cpts, 0, int_enable); cpts_write32(cpts, 0, control); - if (cpts->refclk) - cpts_clk_release(cpts); + clk_disable(cpts->refclk); } EXPORT_SYMBOL_GPL(cpts_unregister); +struct cpts *cpts_create(struct device *dev, void __iomem *regs, + u32 mult, u32 shift) +{ + struct cpts *cpts; + + cpts = devm_kzalloc(dev, sizeof(*cpts), GFP_KERNEL); + if (!cpts) + return ERR_PTR(-ENOMEM); + + cpts->dev = dev; + cpts->reg = (struct cpsw_cpts __iomem *)regs; + spin_lock_init(&cpts->lock); + INIT_DELAYED_WORK(&cpts->overflow_work, cpts_overflow_check); + + cpts->refclk = devm_clk_get(dev, "cpts"); + if (IS_ERR(cpts->refclk)) { + dev_err(dev, "Failed to get cpts refclk\n"); + return ERR_PTR(PTR_ERR(cpts->refclk)); + } + + clk_prepare(cpts->refclk); + + cpts->cc.read = cpts_systim_read; + cpts->cc.mask = CLOCKSOURCE_MASK(32); + cpts->cc.shift = shift; + cpts->cc_mult = mult; + cpts->cc.mult = mult; + cpts->info = cpts_info; + + return cpts; +} +EXPORT_SYMBOL_GPL(cpts_create); + +void cpts_release(struct cpts *cpts) +{ + if (!cpts) + return; + + if (WARN_ON(!cpts->refclk)) + return; + + clk_unprepare(cpts->refclk); +} +EXPORT_SYMBOL_GPL(cpts_release); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("TI CPTS driver"); MODULE_AUTHOR("Richard Cochran "); diff --git a/drivers/net/ethernet/ti/cpts.h b/drivers/net/ethernet/ti/cpts.h index 29a1e80c6a2b..e7d857c24891 100644 --- a/drivers/net/ethernet/ti/cpts.h +++ b/drivers/net/ethernet/ti/cpts.h @@ -20,6 +20,8 @@ #ifndef _TI_CPTS_H_ #define _TI_CPTS_H_ +#if IS_ENABLED(CONFIG_TI_CPTS) + #include #include #include @@ -108,10 +110,10 @@ struct cpts_event { }; struct cpts { + struct device *dev; struct cpsw_cpts __iomem *reg; int tx_enable; int rx_enable; -#if IS_ENABLED(CONFIG_TI_CPTS) struct ptp_clock_info info; struct ptp_clock *clock; spinlock_t lock; /* protects time registers */ @@ -124,14 +126,15 @@ struct cpts { struct list_head events; struct list_head pool; struct cpts_event pool_data[CPTS_MAX_EVENTS]; -#endif }; -#if IS_ENABLED(CONFIG_TI_CPTS) void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb); void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb); -int cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift); +int cpts_register(struct cpts *cpts); void cpts_unregister(struct cpts *cpts); +struct cpts *cpts_create(struct device *dev, void __iomem *regs, + u32 mult, u32 shift); +void cpts_release(struct cpts *cpts); static inline void cpts_rx_enable(struct cpts *cpts, int enable) { @@ -154,6 +157,8 @@ static inline bool cpts_is_tx_enabled(struct cpts *cpts) } #else +struct cpts; + static inline void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb) { } @@ -161,8 +166,19 @@ static inline void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) { } +static inline +struct cpts *cpts_create(struct device *dev, void __iomem *regs, + u32 mult, u32 shift) +{ + return NULL; +} + +static inline void cpts_release(struct cpts *cpts) +{ +} + static inline int -cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift) +cpts_register(struct cpts *cpts) { return 0; } From 4a88fb9565d418cf1c2bf41be7cfe156717b9c85 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:42 -0600 Subject: [PATCH 10/13] net: ethernet: ti: cpts: move dt props parsing to cpts driver Move DT properties parsing into CPTS driver to simplify CPSW code and CPTS driver porting on other SoC in the future (like Keystone 2) - with this change it will not be required to add the same DT parsing code in Keystone 2 NETCP driver. Signed-off-by: Grygorii Strashko Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 16 +--------------- drivers/net/ethernet/ti/cpsw.h | 2 -- drivers/net/ethernet/ti/cpts.c | 34 ++++++++++++++++++++++++++++++---- drivers/net/ethernet/ti/cpts.h | 5 +++-- 4 files changed, 34 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index a9a835420281..b62d958c5fae 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -2524,18 +2524,6 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } data->active_slave = prop; - if (of_property_read_u32(node, "cpts_clock_mult", &prop)) { - dev_err(&pdev->dev, "Missing cpts_clock_mult property in the DT.\n"); - return -EINVAL; - } - data->cpts_clock_mult = prop; - - if (of_property_read_u32(node, "cpts_clock_shift", &prop)) { - dev_err(&pdev->dev, "Missing cpts_clock_shift property in the DT.\n"); - return -EINVAL; - } - data->cpts_clock_shift = prop; - data->slave_data = devm_kzalloc(&pdev->dev, data->slaves * sizeof(struct cpsw_slave_data), GFP_KERNEL); @@ -2990,9 +2978,7 @@ static int cpsw_probe(struct platform_device *pdev) goto clean_dma_ret; } - cpsw->cpts = cpts_create(cpsw->dev, cpts_regs, - cpsw->data.cpts_clock_mult, - cpsw->data.cpts_clock_shift); + cpsw->cpts = cpts_create(cpsw->dev, cpts_regs, cpsw->dev->of_node); if (IS_ERR(cpsw->cpts)) { ret = PTR_ERR(cpsw->cpts); goto clean_ale_ret; diff --git a/drivers/net/ethernet/ti/cpsw.h b/drivers/net/ethernet/ti/cpsw.h index 16b54c6f32c2..6c3037aa2cd3 100644 --- a/drivers/net/ethernet/ti/cpsw.h +++ b/drivers/net/ethernet/ti/cpsw.h @@ -31,8 +31,6 @@ struct cpsw_platform_data { u32 channels; /* number of cpdma channels (symmetric) */ u32 slaves; /* number of slave cpgmac ports */ u32 active_slave; /* time stamping, ethtool and SIOCGMIIPHY slave */ - u32 cpts_clock_mult; /* convert input clock ticks to nanoseconds */ - u32 cpts_clock_shift; /* convert input clock ticks to nanoseconds */ u32 ale_entries; /* ale table size */ u32 bd_ram_size; /*buffer descriptor ram size */ u32 mac_control; /* Mac control register */ diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index 31cd83fa10ce..cb844ed6dcdd 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -405,10 +405,31 @@ void cpts_unregister(struct cpts *cpts) } EXPORT_SYMBOL_GPL(cpts_unregister); +static int cpts_of_parse(struct cpts *cpts, struct device_node *node) +{ + int ret = -EINVAL; + u32 prop; + + if (of_property_read_u32(node, "cpts_clock_mult", &prop)) + goto of_error; + cpts->cc.mult = prop; + + if (of_property_read_u32(node, "cpts_clock_shift", &prop)) + goto of_error; + cpts->cc.shift = prop; + + return 0; + +of_error: + dev_err(cpts->dev, "CPTS: Missing property in the DT.\n"); + return ret; +} + struct cpts *cpts_create(struct device *dev, void __iomem *regs, - u32 mult, u32 shift) + struct device_node *node) { struct cpts *cpts; + int ret; cpts = devm_kzalloc(dev, sizeof(*cpts), GFP_KERNEL); if (!cpts) @@ -419,6 +440,10 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs, spin_lock_init(&cpts->lock); INIT_DELAYED_WORK(&cpts->overflow_work, cpts_overflow_check); + ret = cpts_of_parse(cpts, node); + if (ret) + return ERR_PTR(ret); + cpts->refclk = devm_clk_get(dev, "cpts"); if (IS_ERR(cpts->refclk)) { dev_err(dev, "Failed to get cpts refclk\n"); @@ -429,9 +454,10 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs, cpts->cc.read = cpts_systim_read; cpts->cc.mask = CLOCKSOURCE_MASK(32); - cpts->cc.shift = shift; - cpts->cc_mult = mult; - cpts->cc.mult = mult; + /* save cc.mult original value as it can be modified + * by cpts_ptp_adjfreq(). + */ + cpts->cc_mult = cpts->cc.mult; cpts->info = cpts_info; return cpts; diff --git a/drivers/net/ethernet/ti/cpts.h b/drivers/net/ethernet/ti/cpts.h index e7d857c24891..5da23af7a9a6 100644 --- a/drivers/net/ethernet/ti/cpts.h +++ b/drivers/net/ethernet/ti/cpts.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -133,7 +134,7 @@ void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb); int cpts_register(struct cpts *cpts); void cpts_unregister(struct cpts *cpts); struct cpts *cpts_create(struct device *dev, void __iomem *regs, - u32 mult, u32 shift); + struct device_node *node); void cpts_release(struct cpts *cpts); static inline void cpts_rx_enable(struct cpts *cpts, int enable) @@ -168,7 +169,7 @@ static inline void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) static inline struct cpts *cpts_create(struct device *dev, void __iomem *regs, - u32 mult, u32 shift) + struct device_node *node) { return NULL; } From 5304121adae9fc59f4b640f82200868112edd0bd Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Tue, 6 Dec 2016 18:00:43 -0600 Subject: [PATCH 11/13] clocksource: export the clocks_calc_mult_shift to use by timestamp code The CPSW CPTS driver is capable of doing timestamping on tx/rx packets and requires to know mult and shift factors for timestamp conversion from raw value to nanoseconds (ptp clock). Now these mult and shift factors are calculated manually and provided through DT, which makes very hard to support of a lot number of platforms, especially if CPTS refclk is not the same for some kind of boards and depends on efuse settings (Keystone 2 platforms). Hence, export clocks_calc_mult_shift() to allow drivers like CPSW CPTS (and other ptp drivesr) to benefit from automaitc calculation of mult and shift factors. Cc: John Stultz Signed-off-by: Murali Karicheri Signed-off-by: Grygorii Strashko Acked-by: Thomas Gleixner Signed-off-by: David S. Miller --- kernel/time/clocksource.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 7e4fad75acaa..150242ccfcd2 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -89,6 +89,7 @@ clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 maxsec) *mult = tmp; *shift = sft; } +EXPORT_SYMBOL_GPL(clocks_calc_mult_shift); /*[Clocksource internal variables]--------- * curr_clocksource: From 88f0f0b0bebfbe086dda853f591ae3a014b19e40 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:44 -0600 Subject: [PATCH 12/13] net: ethernet: ti: cpts: calc mult and shift from refclk freq The cyclecounter mult and shift values can be calculated based on the CPTS rfclk frequency and timekeepnig framework provides required algos and API's. Hence, calc mult and shift basing on CPTS rfclk frequency if both cpts_clock_shift and cpts_clock_mult properties are not provided in DT (the basis of calculation algorithm is borrowed from __clocksource_update_freq_scale() commit 7d2f944a2b83 ("clocksource: Provide a generic mult/shift factor calculation")). After this change cpts_clock_shift and cpts_clock_mult DT properties will become optional. Cc: John Stultz Cc: Thomas Gleixner Signed-off-by: Grygorii Strashko Signed-off-by: David S. Miller --- .../devicetree/bindings/net/cpsw.txt | 8 +-- drivers/net/ethernet/ti/cpts.c | 50 ++++++++++++++++--- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt index 5ad439f30135..ebda7c93453a 100644 --- a/Documentation/devicetree/bindings/net/cpsw.txt +++ b/Documentation/devicetree/bindings/net/cpsw.txt @@ -20,8 +20,6 @@ Required properties: - slaves : Specifies number for slaves - active_slave : Specifies the slave to use for time stamping, ethtool and SIOCGMIIPHY -- cpts_clock_mult : Numerator to convert input clock ticks into nanoseconds -- cpts_clock_shift : Denominator to convert input clock ticks into nanoseconds Optional properties: - ti,hwmods : Must be "cpgmac0" @@ -35,7 +33,11 @@ Optional properties: For example in dra72x-evm, pcf gpio has to be driven low so that cpsw slave 0 and phy data lines are connected via mux. - +- cpts_clock_mult : Numerator to convert input clock ticks into nanoseconds +- cpts_clock_shift : Denominator to convert input clock ticks into nanoseconds + Mult and shift will be calculated basing on CPTS + rftclk frequency if both cpts_clock_shift and + cpts_clock_mult properties are not provided. Slave Properties: Required properties: diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index cb844ed6dcdd..ebd413a1cf56 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -405,18 +405,52 @@ void cpts_unregister(struct cpts *cpts) } EXPORT_SYMBOL_GPL(cpts_unregister); +static void cpts_calc_mult_shift(struct cpts *cpts) +{ + u64 frac, maxsec, ns; + u32 freq; + + freq = clk_get_rate(cpts->refclk); + + /* Calc the maximum number of seconds which we can run before + * wrapping around. + */ + maxsec = cpts->cc.mask; + do_div(maxsec, freq); + /* limit conversation rate to 10 sec as higher values will produce + * too small mult factors and so reduce the conversion accuracy + */ + if (maxsec > 10) + maxsec = 10; + + if (cpts->cc.mult || cpts->cc.shift) + return; + + clocks_calc_mult_shift(&cpts->cc.mult, &cpts->cc.shift, + freq, NSEC_PER_SEC, maxsec); + + frac = 0; + ns = cyclecounter_cyc2ns(&cpts->cc, freq, cpts->cc.mask, &frac); + + dev_info(cpts->dev, + "CPTS: ref_clk_freq:%u calc_mult:%u calc_shift:%u error:%lld nsec/sec\n", + freq, cpts->cc.mult, cpts->cc.shift, (ns - NSEC_PER_SEC)); +} + static int cpts_of_parse(struct cpts *cpts, struct device_node *node) { int ret = -EINVAL; u32 prop; - if (of_property_read_u32(node, "cpts_clock_mult", &prop)) - goto of_error; - cpts->cc.mult = prop; + if (!of_property_read_u32(node, "cpts_clock_mult", &prop)) + cpts->cc.mult = prop; - if (of_property_read_u32(node, "cpts_clock_shift", &prop)) - goto of_error; - cpts->cc.shift = prop; + if (!of_property_read_u32(node, "cpts_clock_shift", &prop)) + cpts->cc.shift = prop; + + if ((cpts->cc.mult && !cpts->cc.shift) || + (!cpts->cc.mult && cpts->cc.shift)) + goto of_error; return 0; @@ -454,11 +488,13 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs, cpts->cc.read = cpts_systim_read; cpts->cc.mask = CLOCKSOURCE_MASK(32); + cpts->info = cpts_info; + + cpts_calc_mult_shift(cpts); /* save cc.mult original value as it can be modified * by cpts_ptp_adjfreq(). */ cpts->cc_mult = cpts->cc.mult; - cpts->info = cpts_info; return cpts; } From 20138cf9efc1a761a17839581b0130d8e4209882 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Dec 2016 18:00:45 -0600 Subject: [PATCH 13/13] net: ethernet: ti: cpts: fix overflow check period The CPTS drivers uses 8sec period for overflow checking with assumption that CPTS retclk will not exceed 500MHz. But that's not true on some TI platforms (Kesytone 2). As result, it is possible that CPTS counter will overflow more than once between two readings. Hence, fix it by selecting overflow check period dynamically as max_sec_before_overflow/2, where max_sec_before_overflow = max_counter_val / rftclk_freq. Cc: John Stultz Cc: Thomas Gleixner Signed-off-by: Grygorii Strashko Acked-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpts.c | 10 +++++++--- drivers/net/ethernet/ti/cpts.h | 4 +--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index ebd413a1cf56..0c0d48e5bea4 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -245,7 +245,7 @@ static void cpts_overflow_check(struct work_struct *work) cpts_ptp_gettime(&cpts->info, &ts); pr_debug("cpts overflow check at %lld.%09lu\n", ts.tv_sec, ts.tv_nsec); - schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD); + schedule_delayed_work(&cpts->overflow_work, cpts->ov_check_period); } static int cpts_match(struct sk_buff *skb, unsigned int ptp_class, @@ -378,8 +378,7 @@ int cpts_register(struct cpts *cpts) } cpts->phc_index = ptp_clock_index(cpts->clock); - schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD); - + schedule_delayed_work(&cpts->overflow_work, cpts->ov_check_period); return 0; err_ptp: @@ -423,6 +422,11 @@ static void cpts_calc_mult_shift(struct cpts *cpts) if (maxsec > 10) maxsec = 10; + /* Calc overflow check period (maxsec / 2) */ + cpts->ov_check_period = (HZ * maxsec) / 2; + dev_info(cpts->dev, "cpts: overflow check period %lu (jiffies)\n", + cpts->ov_check_period); + if (cpts->cc.mult || cpts->cc.shift) return; diff --git a/drivers/net/ethernet/ti/cpts.h b/drivers/net/ethernet/ti/cpts.h index 5da23af7a9a6..c96eca2b1b46 100644 --- a/drivers/net/ethernet/ti/cpts.h +++ b/drivers/net/ethernet/ti/cpts.h @@ -97,9 +97,6 @@ enum { CPTS_EV_TX, /* Ethernet Transmit Event */ }; -/* This covers any input clock up to about 500 MHz. */ -#define CPTS_OVERFLOW_PERIOD (HZ * 8) - #define CPTS_FIFO_DEPTH 16 #define CPTS_MAX_EVENTS 32 @@ -127,6 +124,7 @@ struct cpts { struct list_head events; struct list_head pool; struct cpts_event pool_data[CPTS_MAX_EVENTS]; + unsigned long ov_check_period; }; void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb);