diff options
Diffstat (limited to 'drivers/net/ethernet/cadence/macb_ptp.c')
| -rw-r--r-- | drivers/net/ethernet/cadence/macb_ptp.c | 186 |
1 files changed, 68 insertions, 118 deletions
diff --git a/drivers/net/ethernet/cadence/macb_ptp.c b/drivers/net/ethernet/cadence/macb_ptp.c index a6dc47edc4cf..c9e77819196e 100644 --- a/drivers/net/ethernet/cadence/macb_ptp.c +++ b/drivers/net/ethernet/cadence/macb_ptp.c @@ -1,22 +1,11 @@ -/** +// SPDX-License-Identifier: GPL-2.0-only +/* * 1588 PTP support for Cadence GEM device. * - * Copyright (C) 2017 Cadence Design Systems - http://www.cadence.com + * Copyright (C) 2017 Cadence Design Systems - https://www.cadence.com * * Authors: Rafal Ozieblo <rafalo@cadence.com> * Bartosz Folta <bfolta@cadence.com> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 of - * the License as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <linux/kernel.h> #include <linux/types.h> @@ -39,17 +28,20 @@ static struct macb_dma_desc_ptp *macb_ptp_desc(struct macb *bp, struct macb_dma_desc *desc) { - if (bp->hw_dma_cap == HW_DMA_CAP_PTP) - return (struct macb_dma_desc_ptp *) - ((u8 *)desc + sizeof(struct macb_dma_desc)); - if (bp->hw_dma_cap == HW_DMA_CAP_64B_PTP) + if (!macb_dma_ptp(bp)) + return NULL; + + if (macb_dma64(bp)) return (struct macb_dma_desc_ptp *) ((u8 *)desc + sizeof(struct macb_dma_desc) + sizeof(struct macb_dma_desc_64)); - return NULL; + else + return (struct macb_dma_desc_ptp *) + ((u8 *)desc + sizeof(struct macb_dma_desc)); } -static int gem_tsu_get_time(struct ptp_clock_info *ptp, struct timespec64 *ts) +static int gem_tsu_get_time(struct ptp_clock_info *ptp, struct timespec64 *ts, + struct ptp_system_timestamp *sts) { struct macb *bp = container_of(ptp, struct macb, ptp_clock_info); unsigned long flags; @@ -57,7 +49,9 @@ static int gem_tsu_get_time(struct ptp_clock_info *ptp, struct timespec64 *ts) u32 secl, sech; spin_lock_irqsave(&bp->tsu_clk_lock, flags); + ptp_read_system_prets(sts); first = gem_readl(bp, TN); + ptp_read_system_postts(sts); secl = gem_readl(bp, TSL); sech = gem_readl(bp, TSH); second = gem_readl(bp, TN); @@ -67,7 +61,9 @@ static int gem_tsu_get_time(struct ptp_clock_info *ptp, struct timespec64 *ts) /* if so, use later read & re-read seconds * (assume all done within 1s) */ + ptp_read_system_prets(sts); ts->tv_nsec = gem_readl(bp, TN); + ptp_read_system_postts(sts); secl = gem_readl(bp, TSL); sech = gem_readl(bp, TSH); } else { @@ -115,7 +111,10 @@ static int gem_tsu_incr_set(struct macb *bp, struct tsu_incr *incr_spec) * to take effect. */ spin_lock_irqsave(&bp->tsu_clk_lock, flags); - gem_writel(bp, TISUBN, GEM_BF(SUBNSINCR, incr_spec->sub_ns)); + /* RegBit[15:0] = Subns[23:8]; RegBit[31:24] = Subns[7:0] */ + gem_writel(bp, TISUBN, GEM_BF(SUBNSINCRL, incr_spec->sub_ns) | + GEM_BF(SUBNSINCRH, (incr_spec->sub_ns >> + GEM_SUBNSINCRL_SIZE))); gem_writel(bp, TI, GEM_BF(NSINCR, incr_spec->ns)); spin_unlock_irqrestore(&bp->tsu_clk_lock, flags); @@ -146,7 +145,7 @@ static int gem_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) * (temp / USEC_PER_SEC) + 0.5 */ adj += (USEC_PER_SEC >> 1); - adj >>= GEM_SUBNSINCR_SIZE; /* remove fractions */ + adj >>= PPM_FRACTION; /* remove fractions */ adj = div_u64(adj, USEC_PER_SEC); adj = neg_adj ? (word - adj) : (word + adj); @@ -169,7 +168,7 @@ static int gem_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) } if (delta > TSU_NSEC_MAX_VAL) { - gem_tsu_get_time(&bp->ptp_clock_info, &now); + gem_tsu_get_time(&bp->ptp_clock_info, &now, NULL); now = timespec64_add(now, then); gem_tsu_set_time(&bp->ptp_clock_info, @@ -200,7 +199,7 @@ static const struct ptp_clock_info gem_ptp_caps_template = { .pps = 1, .adjfine = gem_ptp_adjfine, .adjtime = gem_ptp_adjtime, - .gettime64 = gem_tsu_get_time, + .gettimex64 = gem_tsu_get_time, .settime64 = gem_tsu_set_time, .enable = gem_ptp_enable, }; @@ -259,7 +258,9 @@ static int gem_hw_timestamp(struct macb *bp, u32 dma_desc_ts_1, * The timestamp only contains lower few bits of seconds, * so add value from 1588 timer */ - gem_tsu_get_time(&bp->ptp_clock_info, &tsu); + gem_tsu_get_time(&bp->ptp_clock_info, &tsu, NULL); + + ts->tv_sec |= ((~GEM_DMA_SEC_MASK) & tsu.tv_sec); /* If the top bit is set in the timestamp, * but not in 1588 timer, it has rolled over, @@ -269,8 +270,6 @@ static int gem_hw_timestamp(struct macb *bp, u32 dma_desc_ts_1, !(tsu.tv_sec & (GEM_DMA_SEC_TOP >> 1))) ts->tv_sec -= GEM_DMA_SEC_TOP; - ts->tv_sec += ((~GEM_DMA_SEC_MASK) & tsu.tv_sec); - return 0; } @@ -283,82 +282,51 @@ void gem_ptp_rxstamp(struct macb *bp, struct sk_buff *skb, if (GEM_BFEXT(DMA_RXVALID, desc->addr)) { desc_ptp = macb_ptp_desc(bp, desc); + /* Unlikely but check */ + if (!desc_ptp) { + dev_warn_ratelimited(&bp->pdev->dev, + "Timestamp not supported in BD\n"); + return; + } gem_hw_timestamp(bp, desc_ptp->ts_1, desc_ptp->ts_2, &ts); memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps)); shhwtstamps->hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); } } -static void gem_tstamp_tx(struct macb *bp, struct sk_buff *skb, - struct macb_dma_desc_ptp *desc_ptp) +void gem_ptp_txstamp(struct macb *bp, struct sk_buff *skb, + struct macb_dma_desc *desc) { struct skb_shared_hwtstamps shhwtstamps; - struct timespec64 ts; - - gem_hw_timestamp(bp, desc_ptp->ts_1, desc_ptp->ts_2, &ts); - memset(&shhwtstamps, 0, sizeof(shhwtstamps)); - shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); - skb_tstamp_tx(skb, &shhwtstamps); -} - -int gem_ptp_txstamp(struct macb_queue *queue, struct sk_buff *skb, - struct macb_dma_desc *desc) -{ - unsigned long tail = READ_ONCE(queue->tx_ts_tail); - unsigned long head = queue->tx_ts_head; struct macb_dma_desc_ptp *desc_ptp; - struct gem_tx_ts *tx_timestamp; + struct timespec64 ts; - if (!GEM_BFEXT(DMA_TXVALID, desc->ctrl)) - return -EINVAL; + if (!GEM_BFEXT(DMA_TXVALID, desc->ctrl)) { + dev_warn_ratelimited(&bp->pdev->dev, + "Timestamp not set in TX BD as expected\n"); + return; + } - if (CIRC_SPACE(head, tail, PTP_TS_BUFFER_SIZE) == 0) - return -ENOMEM; + desc_ptp = macb_ptp_desc(bp, desc); + /* Unlikely but check */ + if (!desc_ptp) { + dev_warn_ratelimited(&bp->pdev->dev, + "Timestamp not supported in BD\n"); + return; + } - skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; - desc_ptp = macb_ptp_desc(queue->bp, desc); - tx_timestamp = &queue->tx_timestamps[head]; - tx_timestamp->skb = skb; /* ensure ts_1/ts_2 is loaded after ctrl (TX_USED check) */ dma_rmb(); - tx_timestamp->desc_ptp.ts_1 = desc_ptp->ts_1; - tx_timestamp->desc_ptp.ts_2 = desc_ptp->ts_2; - /* move head */ - smp_store_release(&queue->tx_ts_head, - (head + 1) & (PTP_TS_BUFFER_SIZE - 1)); - - schedule_work(&queue->tx_ts_task); - return 0; -} + gem_hw_timestamp(bp, desc_ptp->ts_1, desc_ptp->ts_2, &ts); -static void gem_tx_timestamp_flush(struct work_struct *work) -{ - struct macb_queue *queue = - container_of(work, struct macb_queue, tx_ts_task); - unsigned long head, tail; - struct gem_tx_ts *tx_ts; - - /* take current head */ - head = smp_load_acquire(&queue->tx_ts_head); - tail = queue->tx_ts_tail; - - while (CIRC_CNT(head, tail, PTP_TS_BUFFER_SIZE)) { - tx_ts = &queue->tx_timestamps[tail]; - gem_tstamp_tx(queue->bp, tx_ts->skb, &tx_ts->desc_ptp); - /* cleanup */ - dev_kfree_skb_any(tx_ts->skb); - /* remove old tail */ - smp_store_release(&queue->tx_ts_tail, - (tail + 1) & (PTP_TS_BUFFER_SIZE - 1)); - tail = queue->tx_ts_tail; - } + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); + shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); + skb_tstamp_tx(skb, &shhwtstamps); } void gem_ptp_init(struct net_device *dev) { struct macb *bp = netdev_priv(dev); - struct macb_queue *queue; - unsigned int q; bp->ptp_clock_info = gem_ptp_caps_template; @@ -378,11 +346,6 @@ void gem_ptp_init(struct net_device *dev) } spin_lock_init(&bp->tsu_clk_lock); - for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { - queue->tx_ts_head = 0; - queue->tx_ts_tail = 0; - INIT_WORK(&queue->tx_ts_task, gem_tx_timestamp_flush); - } gem_ptp_init_tsu(bp); @@ -413,22 +376,19 @@ static int gem_ptp_set_ts_mode(struct macb *bp, return 0; } -int gem_get_hwtst(struct net_device *dev, struct ifreq *rq) +int gem_get_hwtst(struct net_device *dev, + struct kernel_hwtstamp_config *tstamp_config) { - struct hwtstamp_config *tstamp_config; struct macb *bp = netdev_priv(dev); - tstamp_config = &bp->tstamp_config; - if ((bp->hw_dma_cap & HW_DMA_CAP_PTP) == 0) + *tstamp_config = bp->tstamp_config; + if (!macb_dma_ptp(bp)) return -EOPNOTSUPP; - if (copy_to_user(rq->ifr_data, tstamp_config, sizeof(*tstamp_config))) - return -EFAULT; - else - return 0; + return 0; } -static int gem_ptp_set_one_step_sync(struct macb *bp, u8 enable) +static void gem_ptp_set_one_step_sync(struct macb *bp, u8 enable) { u32 reg_val; @@ -438,38 +398,29 @@ static int gem_ptp_set_one_step_sync(struct macb *bp, u8 enable) macb_writel(bp, NCR, reg_val | MACB_BIT(OSSMODE)); else macb_writel(bp, NCR, reg_val & ~MACB_BIT(OSSMODE)); - - return 0; } -int gem_set_hwtst(struct net_device *dev, struct ifreq *ifr, int cmd) +int gem_set_hwtst(struct net_device *dev, + struct kernel_hwtstamp_config *tstamp_config, + struct netlink_ext_ack *extack) { enum macb_bd_control tx_bd_control = TSTAMP_DISABLED; enum macb_bd_control rx_bd_control = TSTAMP_DISABLED; - struct hwtstamp_config *tstamp_config; struct macb *bp = netdev_priv(dev); u32 regval; - tstamp_config = &bp->tstamp_config; - if ((bp->hw_dma_cap & HW_DMA_CAP_PTP) == 0) + if (!macb_dma_ptp(bp)) return -EOPNOTSUPP; - if (copy_from_user(tstamp_config, ifr->ifr_data, - sizeof(*tstamp_config))) - return -EFAULT; - - /* reserved for future extensions */ - if (tstamp_config->flags) - return -EINVAL; - switch (tstamp_config->tx_type) { case HWTSTAMP_TX_OFF: break; case HWTSTAMP_TX_ONESTEP_SYNC: - if (gem_ptp_set_one_step_sync(bp, 1) != 0) - return -ERANGE; - /* fall through */ + gem_ptp_set_one_step_sync(bp, 1); + tx_bd_control = TSTAMP_ALL_FRAMES; + break; case HWTSTAMP_TX_ON: + gem_ptp_set_one_step_sync(bp, 0); tx_bd_control = TSTAMP_ALL_FRAMES; break; default: @@ -507,12 +458,11 @@ int gem_set_hwtst(struct net_device *dev, struct ifreq *ifr, int cmd) return -ERANGE; } + bp->tstamp_config = *tstamp_config; + if (gem_ptp_set_ts_mode(bp, tx_bd_control, rx_bd_control) != 0) return -ERANGE; - if (copy_to_user(ifr->ifr_data, tstamp_config, sizeof(*tstamp_config))) - return -EFAULT; - else - return 0; + return 0; } |
