aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/octeon/octeon_mgmt.c
diff options
context:
space:
mode:
authorChad Reese <kreese@caviumnetworks.com>2012-08-21 14:45:07 -0400
committerDavid Daney <david.daney@cavium.com>2012-08-31 14:49:18 -0400
commit3d305850261dfbf815eb7a0f0b768d4e1a11485a (patch)
tree450d288a21c6a50e68de38f5818c2badfa71cb2c /drivers/net/ethernet/octeon/octeon_mgmt.c
parenteeae05aa21695703e1979999a9a4a861447045c9 (diff)
netdev: octeon_mgmt: Add hardware timestamp support.
Octeon cn6XXX models have timestamp support on the mgmt ports, so hook it up. Signed-off-by: Chad Reese <kreese@caviumnetworks.com> Signed-off-by: David Daney <david.daney@cavium.com> Acked-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/octeon/octeon_mgmt.c')
-rw-r--r--drivers/net/ethernet/octeon/octeon_mgmt.c157
1 files changed, 152 insertions, 5 deletions
diff --git a/drivers/net/ethernet/octeon/octeon_mgmt.c b/drivers/net/ethernet/octeon/octeon_mgmt.c
index c4df1ab13b6..687a6a0c714 100644
--- a/drivers/net/ethernet/octeon/octeon_mgmt.c
+++ b/drivers/net/ethernet/octeon/octeon_mgmt.c
@@ -10,6 +10,7 @@
10#include <linux/dma-mapping.h> 10#include <linux/dma-mapping.h>
11#include <linux/etherdevice.h> 11#include <linux/etherdevice.h>
12#include <linux/capability.h> 12#include <linux/capability.h>
13#include <linux/net_tstamp.h>
13#include <linux/interrupt.h> 14#include <linux/interrupt.h>
14#include <linux/netdevice.h> 15#include <linux/netdevice.h>
15#include <linux/spinlock.h> 16#include <linux/spinlock.h>
@@ -114,6 +115,7 @@ struct octeon_mgmt {
114 u64 agl_prt_ctl; 115 u64 agl_prt_ctl;
115 int port; 116 int port;
116 int irq; 117 int irq;
118 bool has_rx_tstamp;
117 u64 *tx_ring; 119 u64 *tx_ring;
118 dma_addr_t tx_ring_handle; 120 dma_addr_t tx_ring_handle;
119 unsigned int tx_next; 121 unsigned int tx_next;
@@ -238,6 +240,28 @@ static void octeon_mgmt_rx_fill_ring(struct net_device *netdev)
238 } 240 }
239} 241}
240 242
243static ktime_t ptp_to_ktime(u64 ptptime)
244{
245 ktime_t ktimebase;
246 u64 ptpbase;
247 unsigned long flags;
248
249 local_irq_save(flags);
250 /* Fill the icache with the code */
251 ktime_get_real();
252 /* Flush all pending operations */
253 mb();
254 /* Read the time and PTP clock as close together as
255 * possible. It is important that this sequence take the same
256 * amount of time to reduce jitter
257 */
258 ktimebase = ktime_get_real();
259 ptpbase = cvmx_read_csr(CVMX_MIO_PTP_CLOCK_HI);
260 local_irq_restore(flags);
261
262 return ktime_sub_ns(ktimebase, ptpbase - ptptime);
263}
264
241static void octeon_mgmt_clean_tx_buffers(struct octeon_mgmt *p) 265static void octeon_mgmt_clean_tx_buffers(struct octeon_mgmt *p)
242{ 266{
243 union cvmx_mixx_orcnt mix_orcnt; 267 union cvmx_mixx_orcnt mix_orcnt;
@@ -277,6 +301,20 @@ static void octeon_mgmt_clean_tx_buffers(struct octeon_mgmt *p)
277 301
278 dma_unmap_single(p->dev, re.s.addr, re.s.len, 302 dma_unmap_single(p->dev, re.s.addr, re.s.len,
279 DMA_TO_DEVICE); 303 DMA_TO_DEVICE);
304
305 /* Read the hardware TX timestamp if one was recorded */
306 if (unlikely(re.s.tstamp)) {
307 struct skb_shared_hwtstamps ts;
308 /* Read the timestamp */
309 u64 ns = cvmx_read_csr(CVMX_MIXX_TSTAMP(p->port));
310 /* Remove the timestamp from the FIFO */
311 cvmx_write_csr(CVMX_MIXX_TSCTL(p->port), 0);
312 /* Tell the kernel about the timestamp */
313 ts.syststamp = ptp_to_ktime(ns);
314 ts.hwtstamp = ns_to_ktime(ns);
315 skb_tstamp_tx(skb, &ts);
316 }
317
280 dev_kfree_skb_any(skb); 318 dev_kfree_skb_any(skb);
281 cleaned++; 319 cleaned++;
282 320
@@ -377,6 +415,16 @@ static int octeon_mgmt_receive_one(struct octeon_mgmt *p)
377 /* A good packet, send it up. */ 415 /* A good packet, send it up. */
378 skb_put(skb, re.s.len); 416 skb_put(skb, re.s.len);
379good: 417good:
418 /* Process the RX timestamp if it was recorded */
419 if (p->has_rx_tstamp) {
420 /* The first 8 bytes are the timestamp */
421 u64 ns = *(u64 *)skb->data;
422 struct skb_shared_hwtstamps *ts;
423 ts = skb_hwtstamps(skb);
424 ts->hwtstamp = ns_to_ktime(ns);
425 ts->syststamp = ptp_to_ktime(ns);
426 __skb_pull(skb, 8);
427 }
380 skb->protocol = eth_type_trans(skb, netdev); 428 skb->protocol = eth_type_trans(skb, netdev);
381 netdev->stats.rx_packets++; 429 netdev->stats.rx_packets++;
382 netdev->stats.rx_bytes += skb->len; 430 netdev->stats.rx_bytes += skb->len;
@@ -661,18 +709,114 @@ static irqreturn_t octeon_mgmt_interrupt(int cpl, void *dev_id)
661 return IRQ_HANDLED; 709 return IRQ_HANDLED;
662} 710}
663 711
664static int octeon_mgmt_ioctl(struct net_device *netdev, 712static int octeon_mgmt_ioctl_hwtstamp(struct net_device *netdev,
665 struct ifreq *rq, int cmd) 713 struct ifreq *rq, int cmd)
666{ 714{
667 struct octeon_mgmt *p = netdev_priv(netdev); 715 struct octeon_mgmt *p = netdev_priv(netdev);
716 struct hwtstamp_config config;
717 union cvmx_mio_ptp_clock_cfg ptp;
718 union cvmx_agl_gmx_rxx_frm_ctl rxx_frm_ctl;
719 bool have_hw_timestamps = false;
720
721 if (copy_from_user(&config, rq->ifr_data, sizeof(config)))
722 return -EFAULT;
668 723
669 if (!netif_running(netdev)) 724 if (config.flags) /* reserved for future extensions */
670 return -EINVAL; 725 return -EINVAL;
671 726
672 if (!p->phydev) 727 /* Check the status of hardware for tiemstamps */
728 if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
729 /* Get the current state of the PTP clock */
730 ptp.u64 = cvmx_read_csr(CVMX_MIO_PTP_CLOCK_CFG);
731 if (!ptp.s.ext_clk_en) {
732 /* The clock has not been configured to use an
733 * external source. Program it to use the main clock
734 * reference.
735 */
736 u64 clock_comp = (NSEC_PER_SEC << 32) / octeon_get_io_clock_rate();
737 if (!ptp.s.ptp_en)
738 cvmx_write_csr(CVMX_MIO_PTP_CLOCK_COMP, clock_comp);
739 pr_info("PTP Clock: Using sclk reference at %lld Hz\n",
740 (NSEC_PER_SEC << 32) / clock_comp);
741 } else {
742 /* The clock is already programmed to use a GPIO */
743 u64 clock_comp = cvmx_read_csr(CVMX_MIO_PTP_CLOCK_COMP);
744 pr_info("PTP Clock: Using GPIO %d at %lld Hz\n",
745 ptp.s.ext_clk_in,
746 (NSEC_PER_SEC << 32) / clock_comp);
747 }
748
749 /* Enable the clock if it wasn't done already */
750 if (!ptp.s.ptp_en) {
751 ptp.s.ptp_en = 1;
752 cvmx_write_csr(CVMX_MIO_PTP_CLOCK_CFG, ptp.u64);
753 }
754 have_hw_timestamps = true;
755 }
756
757 if (!have_hw_timestamps)
673 return -EINVAL; 758 return -EINVAL;
674 759
675 return phy_mii_ioctl(p->phydev, rq, cmd); 760 switch (config.tx_type) {
761 case HWTSTAMP_TX_OFF:
762 case HWTSTAMP_TX_ON:
763 break;
764 default:
765 return -ERANGE;
766 }
767
768 switch (config.rx_filter) {
769 case HWTSTAMP_FILTER_NONE:
770 p->has_rx_tstamp = false;
771 rxx_frm_ctl.u64 = cvmx_read_csr(p->agl + AGL_GMX_RX_FRM_CTL);
772 rxx_frm_ctl.s.ptp_mode = 0;
773 cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_CTL, rxx_frm_ctl.u64);
774 break;
775 case HWTSTAMP_FILTER_ALL:
776 case HWTSTAMP_FILTER_SOME:
777 case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
778 case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
779 case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
780 case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
781 case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
782 case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
783 case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
784 case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
785 case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
786 case HWTSTAMP_FILTER_PTP_V2_EVENT:
787 case HWTSTAMP_FILTER_PTP_V2_SYNC:
788 case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
789 p->has_rx_tstamp = have_hw_timestamps;
790 config.rx_filter = HWTSTAMP_FILTER_ALL;
791 if (p->has_rx_tstamp) {
792 rxx_frm_ctl.u64 = cvmx_read_csr(p->agl + AGL_GMX_RX_FRM_CTL);
793 rxx_frm_ctl.s.ptp_mode = 1;
794 cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_CTL, rxx_frm_ctl.u64);
795 }
796 break;
797 default:
798 return -ERANGE;
799 }
800
801 if (copy_to_user(rq->ifr_data, &config, sizeof(config)))
802 return -EFAULT;
803
804 return 0;
805}
806
807static int octeon_mgmt_ioctl(struct net_device *netdev,
808 struct ifreq *rq, int cmd)
809{
810 struct octeon_mgmt *p = netdev_priv(netdev);
811
812 switch (cmd) {
813 case SIOCSHWTSTAMP:
814 return octeon_mgmt_ioctl_hwtstamp(netdev, rq, cmd);
815 default:
816 if (p->phydev)
817 return phy_mii_ioctl(p->phydev, rq, cmd);
818 return -EINVAL;
819 }
676} 820}
677 821
678static void octeon_mgmt_disable_link(struct octeon_mgmt *p) 822static void octeon_mgmt_disable_link(struct octeon_mgmt *p)
@@ -1052,6 +1196,7 @@ static int octeon_mgmt_open(struct net_device *netdev)
1052 /* Enable packet I/O. */ 1196 /* Enable packet I/O. */
1053 1197
1054 rxx_frm_ctl.u64 = 0; 1198 rxx_frm_ctl.u64 = 0;
1199 rxx_frm_ctl.s.ptp_mode = p->has_rx_tstamp ? 1 : 0;
1055 rxx_frm_ctl.s.pre_align = 1; 1200 rxx_frm_ctl.s.pre_align = 1;
1056 /* 1201 /*
1057 * When set, disables the length check for non-min sized pkts 1202 * When set, disables the length check for non-min sized pkts
@@ -1155,6 +1300,7 @@ static int octeon_mgmt_xmit(struct sk_buff *skb, struct net_device *netdev)
1155 int rv = NETDEV_TX_BUSY; 1300 int rv = NETDEV_TX_BUSY;
1156 1301
1157 re.d64 = 0; 1302 re.d64 = 0;
1303 re.s.tstamp = ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) != 0);
1158 re.s.len = skb->len; 1304 re.s.len = skb->len;
1159 re.s.addr = dma_map_single(p->dev, skb->data, 1305 re.s.addr = dma_map_single(p->dev, skb->data,
1160 skb->len, 1306 skb->len,
@@ -1293,6 +1439,7 @@ static int __devinit octeon_mgmt_probe(struct platform_device *pdev)
1293 1439
1294 p->netdev = netdev; 1440 p->netdev = netdev;
1295 p->dev = &pdev->dev; 1441 p->dev = &pdev->dev;
1442 p->has_rx_tstamp = false;
1296 1443
1297 data = of_get_property(pdev->dev.of_node, "cell-index", &len); 1444 data = of_get_property(pdev->dev.of_node, "cell-index", &len);
1298 if (data && len == sizeof(*data)) { 1445 if (data && len == sizeof(*data)) {