1128 lines
31 KiB
Diff
1128 lines
31 KiB
Diff
From fe8ca107fdb5dee6da75828cfcad689de344532b Mon Sep 17 00:00:00 2001
|
|
From: Yonglong Liu <liuyonglong@huawei.com>
|
|
Date: Sat, 8 Oct 2022 11:34:55 +0800
|
|
Subject: [PATCH 261/283] net: hns3: add support for Hisilicon ptp sync device
|
|
|
|
driver inclusion
|
|
category: feature
|
|
bugzilla: https://gitee.com/src-openeuler/kernel/issues/I8EN49
|
|
CVE: NA
|
|
|
|
----------------------------------------------------------------------
|
|
|
|
The hns3 driver provide ptp driver to get 1588 clock from
|
|
ethernet, but only the first PF on main chip can support this,
|
|
so, if getting ptp time from other chip, may have some bus
|
|
latency. The PTP sync device use to eliminate the bus latency.
|
|
|
|
Signed-off-by: Yonglong Liu <liuyonglong@huawei.com>
|
|
Signed-off-by: Xiaodong Li <lixiaodong67@huawei.com>
|
|
|
|
Conflicts:
|
|
arch/arm64/configs/openeuler_defconfig
|
|
drivers/ptp/Kconfig
|
|
drivers/ptp/Makefile
|
|
---
|
|
MAINTAINERS | 5 +
|
|
arch/arm64/configs/openeuler_defconfig | 1 +
|
|
drivers/ptp/Kconfig | 11 +
|
|
drivers/ptp/Makefile | 1 +
|
|
drivers/ptp/ptp_hisi.c | 1028 ++++++++++++++++++++++++
|
|
5 files changed, 1046 insertions(+)
|
|
create mode 100644 drivers/ptp/ptp_hisi.c
|
|
|
|
diff --git a/MAINTAINERS b/MAINTAINERS
|
|
index 6ec54a17a7c8..2be5c4406f3e 100644
|
|
--- a/MAINTAINERS
|
|
+++ b/MAINTAINERS
|
|
@@ -6718,6 +6718,11 @@ S: Maintained
|
|
F: Documentation/scsi/hisi_raid.rst
|
|
F: drivers/scsi/hisi_raid/
|
|
|
|
+HISILICON HNS3 PTP SYNC DRIVER
|
|
+M: Yonglong Liu <liuyonglong@huawei.com>
|
|
+S: Supported
|
|
+F: drivers/ptp/ptp_hisi.c
|
|
+
|
|
HMM - Heterogeneous Memory Management
|
|
M: Jérôme Glisse <jglisse@redhat.com>
|
|
L: linux-mm@kvack.org
|
|
diff --git a/arch/arm64/configs/openeuler_defconfig b/arch/arm64/configs/openeuler_defconfig
|
|
index 7505b04f928c..fd345bbc7362 100644
|
|
--- a/arch/arm64/configs/openeuler_defconfig
|
|
+++ b/arch/arm64/configs/openeuler_defconfig
|
|
@@ -3276,6 +3276,7 @@ CONFIG_PPS_CLIENT_GPIO=m
|
|
#
|
|
CONFIG_PTP_1588_CLOCK=y
|
|
CONFIG_DP83640_PHY=m
|
|
+CONFIG_PTP_HISI=m
|
|
CONFIG_PINCTRL=y
|
|
CONFIG_PINMUX=y
|
|
CONFIG_PINCONF=y
|
|
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
|
|
index d137c480db46..e4fcadc22243 100644
|
|
--- a/drivers/ptp/Kconfig
|
|
+++ b/drivers/ptp/Kconfig
|
|
@@ -118,4 +118,15 @@ config PTP_1588_CLOCK_KVM
|
|
To compile this driver as a module, choose M here: the module
|
|
will be called ptp_kvm.
|
|
|
|
+config PTP_HISI
|
|
+ tristate "HiSilicon PTP sync platform driver"
|
|
+ help
|
|
+ PTP sync driver work on multichip system, eliminates the bus latency
|
|
+ between multichip, and provide a higher precision clock source. But
|
|
+ the clock source of PTP sync device is from the RTC of HNS3 ethernet
|
|
+ device, so, if you want the PTP sync device works, you must enable
|
|
+ HNS3 driver also.
|
|
+
|
|
+ If unsure, say N.
|
|
+
|
|
endmenu
|
|
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
|
|
index 19efa9cfa950..0368dd837921 100644
|
|
--- a/drivers/ptp/Makefile
|
|
+++ b/drivers/ptp/Makefile
|
|
@@ -10,3 +10,4 @@ obj-$(CONFIG_PTP_1588_CLOCK_IXP46X) += ptp_ixp46x.o
|
|
obj-$(CONFIG_PTP_1588_CLOCK_PCH) += ptp_pch.o
|
|
obj-$(CONFIG_PTP_1588_CLOCK_KVM) += ptp_kvm.o
|
|
obj-$(CONFIG_PTP_1588_CLOCK_QORIQ) += ptp_qoriq.o
|
|
+obj-$(CONFIG_PTP_HISI) += ptp_hisi.o
|
|
diff --git a/drivers/ptp/ptp_hisi.c b/drivers/ptp/ptp_hisi.c
|
|
new file mode 100644
|
|
index 000000000000..1fcff3c9a90b
|
|
--- /dev/null
|
|
+++ b/drivers/ptp/ptp_hisi.c
|
|
@@ -0,0 +1,1028 @@
|
|
+// SPDX-License-Identifier: GPL-2.0+
|
|
+// Copyright (c) 2022 Hisilicon Limited.
|
|
+#include <linux/mm.h>
|
|
+#include <linux/acpi.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/err.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/ptp_clock_kernel.h>
|
|
+#include <linux/net_tstamp.h>
|
|
+#include <linux/debugfs.h>
|
|
+#include <linux/uaccess.h>
|
|
+
|
|
+#ifndef PTP_CLOCK_NAME_LEN
|
|
+#define PTP_CLOCK_NAME_LEN 32
|
|
+#endif
|
|
+
|
|
+#define HISI_PTP_VERSION "22.10.2"
|
|
+
|
|
+#define HISI_PTP_NAME "hisi_ptp"
|
|
+#define HISI_PTP_INT_NAME_LEN 32
|
|
+
|
|
+#define HISI_PTP_DBGFS_STS_LEN 2048
|
|
+#define HISI_PTP_DBGFS_REG_LEN 0x10000
|
|
+
|
|
+#define HISI_RES_T_PERI_SC 0
|
|
+#define HISI_RES_N_NET_SC 0
|
|
+#define HISI_RES_N_IO_SC 1
|
|
+
|
|
+#define HISI_PTP_INIT_DONE 0
|
|
+
|
|
+/* peri subctrl reg offset */
|
|
+#define PERI_SC_PTP_RESET_REQ 0xE18
|
|
+#define PERI_SC_PTP_RESET_DREQ 0xE1C
|
|
+#define PERI_SC_LOCAL_TIMER_COMP_HIGH_ADDR 0x5000
|
|
+#define PERI_SC_LOCAL_TIMER_COMP_LOW_ADDR 0x5004
|
|
+#define PERI_SC_BAUD_VALUE_ADDR 0x5008
|
|
+#define PERI_SC_LOCAL_CNT_EN_ADDR 0x500C
|
|
+#define PERI_SC_SYNC_ERR_COMP_HIGH_ADDR 0x5010
|
|
+#define PERI_SC_SYNC_ERR_COMP_LOW_ADDR 0x5014
|
|
+#define PERI_SC_CRC_EN_ADDR 0x5018
|
|
+#define PERI_SC_ONE_CYCLE_NUM_ADDR 0x5020
|
|
+#define PERI_SC_SYNC_ERR_CLR_ADDR 0x5024
|
|
+#define PERI_SC_RX_SHIFT_EN_ADDR 0x5028
|
|
+#define PERI_SC_TIMEL_CY_NUM_ADDR 0x502C
|
|
+#define PERI_SC_INT_PTP_SYNC_ERR_ADDR 0x5044
|
|
+#define PERI_SC_INT_PTP_SYNC_ERR_MASK_ADDR 0x5048
|
|
+#define PERI_SC_INT_ORIGIN 0x504C
|
|
+#define PERI_SC_CRC_ERR_COUNT 0x5050
|
|
+#define PERI_SC_CRC_INT_CONTRL_ADDR 0x5054
|
|
+#define PERI_SC_CAPTURE_PTP_TIME_COMP_HIGH 0x5058
|
|
+#define PERI_SC_CAPTURE_PTP_TIME_COMP_LOW 0x505C
|
|
+#define PERI_SC_CAPTURE_SYSTEM_COUNTER_BIN_HIGH 0x5060
|
|
+#define PERI_SC_CAPTURE_SYSTEM_COUNTER_BIN_LOW 0x5064
|
|
+#define PERI_SC_CAPTURE_VLD 0x5068
|
|
+#define PERI_SC_LOCAL_TIME_LOW_ADDR 0x5070
|
|
+#define PERI_SC_LOCAL_TIME_HIGH_ADDR 0x5074
|
|
+
|
|
+/* net subctrl reg offset */
|
|
+#define NET_SC_PTP_BAUD_VALUE_ADDR 0x2008
|
|
+#define NET_SC_PTP_COUNTER_EN_ADDR 0x200C
|
|
+#define NET_SC_PTP_NORMAL_MODE_EN 0x2010
|
|
+#define NET_SC_PTP_WIRE_DELAY_CAL_EN 0x2014
|
|
+#define NET_SC_SAMPLE_DELAY_CFG_ADDR 0x2018
|
|
+#define NET_SC_PTP_TX_DFXBUS0_ADDR 0x201C
|
|
+#define NET_SC_PTP_TX_DFXBUS1_ADDR 0x2020
|
|
+#define NET_SC_PTP_TX_DFXBUS2_ADDR 0x2024
|
|
+#define NET_SC_PTP_TX_DFXBUS3_ADDR 0x2028
|
|
+
|
|
+/* io subctrl reg offset */
|
|
+#define IO_SC_PTP_BAUD_VALUE_ADDR 0x2008
|
|
+#define IO_SC_PTP_COUNTER_EN_ADDR 0x200C
|
|
+#define IO_SC_PTP_NORMAL_MODE_EN 0x2010
|
|
+#define IO_SC_PTP_WIRE_DELAY_CAL_EN 0x2014
|
|
+#define IO_SC_SAMPLE_DELAY_CFG_ADDR 0x2018
|
|
+#define IO_SC_PTP_TX_DFXBUS0_ADDR 0x201C
|
|
+#define IO_SC_PTP_TX_DFXBUS1_ADDR 0x2020
|
|
+#define IO_SC_PTP_TX_DFXBUS2_ADDR 0x2024
|
|
+#define IO_SC_PTP_TX_DFXBUS3_ADDR 0x2028
|
|
+
|
|
+/* default values */
|
|
+#define HISI_DEF_BAUD 0x1388
|
|
+#define HISI_DEF_TIME_COMP 0xB2432
|
|
+#define HISI_DEF_ERR_COMP 0xFFFFFFFF
|
|
+#define HISI_DEF_ONE_CYCLE_NUM 0x50
|
|
+
|
|
+#define HISI_PTP_TX_IDLE_MASK GENMASK(26, 23)
|
|
+
|
|
+#define HISI_PTP_RX_CRC_INT_EN BIT(0)
|
|
+#define HISI_PTP_RX_CRC_CLR BIT(1)
|
|
+#define HISI_PTP_RX_CRC_CLR_AND_EN \
|
|
+ (HISI_PTP_RX_CRC_INT_EN | HISI_PTP_RX_CRC_INT_EN)
|
|
+#define HISI_PTP_RX_CRC_CLR_AND_DISABLE HISI_PTP_RX_CRC_CLR
|
|
+
|
|
+#define HISI_PTP_SUP_CHK_CNT 32
|
|
+/* suppress check window and suppress time, unit: ms */
|
|
+#define HISI_PTP_SUP_CHK_THR 10
|
|
+#define HISI_PTP_SUP_TIME 100
|
|
+
|
|
+enum HISI_PTP_TX_MODE {
|
|
+ HISI_PTP_CAL_MODE,
|
|
+ HISI_PTP_NORMAL_MODE,
|
|
+};
|
|
+
|
|
+struct hisi_ptp_rx {
|
|
+ struct list_head node;
|
|
+ char name[HISI_PTP_INT_NAME_LEN];
|
|
+ struct device *dev;
|
|
+ u64 time_comp; /* internal wire time compensation value */
|
|
+ int irq;
|
|
+ void __iomem *base;
|
|
+};
|
|
+
|
|
+struct hisi_ptp_tx {
|
|
+ struct device *dev;
|
|
+ void __iomem *base;
|
|
+ void __iomem *io_sc_base;
|
|
+};
|
|
+
|
|
+struct hisi_ptp_pdev {
|
|
+ struct list_head ptp_rx_list;
|
|
+ struct hisi_ptp_tx *ptp_tx;
|
|
+ u32 tx_cnt;
|
|
+ u32 rx_total;
|
|
+ u32 rx_cnt;
|
|
+ unsigned long flag;
|
|
+ void __iomem *rx_base; /* peri subctl base of chip 0 */
|
|
+ u32 irq_cnt;
|
|
+ unsigned long last_jiffies; /* record last irq jiffies */
|
|
+ struct timer_list suppress_timer;
|
|
+ struct ptp_clock *clock;
|
|
+ struct ptp_clock_info info;
|
|
+ rwlock_t rw_lock;
|
|
+ struct dentry *dbgfs_root;
|
|
+};
|
|
+
|
|
+struct hisi_ptp_reg {
|
|
+ const char *name;
|
|
+ u32 offset;
|
|
+};
|
|
+
|
|
+static struct hisi_ptp_pdev g_ptpdev;
|
|
+
|
|
+static uint err_threshold = HISI_DEF_ERR_COMP;
|
|
+module_param(err_threshold, uint, 0644);
|
|
+MODULE_PARM_DESC(err_threshold, "PTP time sync error threshold");
|
|
+
|
|
+static struct hisi_ptp_pdev *hisi_ptp_get_pdev(struct ptp_clock_info *info)
|
|
+{
|
|
+ struct hisi_ptp_pdev *ptp =
|
|
+ container_of(info, struct hisi_ptp_pdev, info);
|
|
+ return ptp;
|
|
+}
|
|
+
|
|
+/* This function should call under rw_lock */
|
|
+static void hisi_ptp_disable(struct hisi_ptp_pdev *ptp)
|
|
+{
|
|
+ struct hisi_ptp_rx *rx;
|
|
+ void __iomem *base;
|
|
+
|
|
+ /* disable tx */
|
|
+ if (ptp->ptp_tx && ptp->ptp_tx->base) {
|
|
+ base = ptp->ptp_tx->base;
|
|
+ writel(0, base + NET_SC_PTP_COUNTER_EN_ADDR);
|
|
+ }
|
|
+
|
|
+ /* disable all totem rx and interrupt */
|
|
+ list_for_each_entry(rx, &ptp->ptp_rx_list, node) {
|
|
+ base = rx->base;
|
|
+ writel(0, base + PERI_SC_RX_SHIFT_EN_ADDR);
|
|
+ writel(1, base + PERI_SC_INT_PTP_SYNC_ERR_MASK_ADDR);
|
|
+ writel(HISI_PTP_RX_CRC_CLR_AND_DISABLE,
|
|
+ base + PERI_SC_CRC_INT_CONTRL_ADDR);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* This function should call under rw_lock */
|
|
+static void hisi_ptp_unmask_irq(struct hisi_ptp_pdev *ptp)
|
|
+{
|
|
+ struct hisi_ptp_rx *rx;
|
|
+ void __iomem *base;
|
|
+
|
|
+ /* clear CRC errors and unmask all totem interrupt */
|
|
+ list_for_each_entry(rx, &ptp->ptp_rx_list, node) {
|
|
+ base = rx->base;
|
|
+ writel(HISI_PTP_RX_CRC_INT_EN,
|
|
+ base + PERI_SC_CRC_INT_CONTRL_ADDR);
|
|
+ writel(0, base + PERI_SC_INT_PTP_SYNC_ERR_MASK_ADDR);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* This function should call under rw_lock */
|
|
+static void hisi_ptp_wait_and_enable(struct hisi_ptp_pdev *ptp)
|
|
+{
|
|
+#define HISI_PTP_TX_IDLE_WAIT_CNT 20
|
|
+ void __iomem *nimbus_base;
|
|
+ struct hisi_ptp_rx *rx;
|
|
+ void __iomem *base;
|
|
+ int delay_cnt = 0;
|
|
+
|
|
+ if (!ptp->ptp_tx || !ptp->ptp_tx->base)
|
|
+ return;
|
|
+
|
|
+ /* wait for tx idle */
|
|
+ nimbus_base = ptp->ptp_tx->base;
|
|
+ while (delay_cnt++ < HISI_PTP_TX_IDLE_WAIT_CNT) {
|
|
+ u32 dfx_bus0 = readl(nimbus_base + NET_SC_PTP_TX_DFXBUS0_ADDR);
|
|
+
|
|
+ /* wait bit26:23 to 0 */
|
|
+ if ((dfx_bus0 & HISI_PTP_TX_IDLE_MASK) == 0)
|
|
+ break;
|
|
+
|
|
+ udelay(1);
|
|
+ }
|
|
+
|
|
+ /* enable all totem interrupt and rx */
|
|
+ list_for_each_entry(rx, &ptp->ptp_rx_list, node) {
|
|
+ base = rx->base;
|
|
+ writel(1, base + PERI_SC_SYNC_ERR_CLR_ADDR);
|
|
+ writel(0, base + PERI_SC_SYNC_ERR_CLR_ADDR);
|
|
+ writel(1, base + PERI_SC_INT_PTP_SYNC_ERR_ADDR);
|
|
+ writel(1, base + PERI_SC_RX_SHIFT_EN_ADDR);
|
|
+ }
|
|
+
|
|
+ /* enable tx */
|
|
+ writel(1, nimbus_base + NET_SC_PTP_COUNTER_EN_ADDR);
|
|
+
|
|
+ hisi_ptp_unmask_irq(ptp);
|
|
+}
|
|
+
|
|
+/* This function should call under rw_lock */
|
|
+static bool hisi_ptp_need_suppress(struct hisi_ptp_pdev *ptp)
|
|
+{
|
|
+ if (time_is_before_jiffies(ptp->last_jiffies +
|
|
+ msecs_to_jiffies(HISI_PTP_SUP_CHK_THR))) {
|
|
+ ptp->last_jiffies = jiffies;
|
|
+ ptp->irq_cnt = 0;
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (ptp->irq_cnt++ < HISI_PTP_SUP_CHK_CNT)
|
|
+ return false;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static irqreturn_t hisi_ptp_irq_handle(int irq, void *data)
|
|
+{
|
|
+ struct hisi_ptp_pdev *ptp = (struct hisi_ptp_pdev *)data;
|
|
+
|
|
+ dev_dbg(ptp->ptp_tx->dev, "ptp time sync error, irq:%d\n", irq);
|
|
+
|
|
+ write_lock(&ptp->rw_lock);
|
|
+
|
|
+ hisi_ptp_disable(ptp);
|
|
+
|
|
+ if (hisi_ptp_need_suppress(ptp)) {
|
|
+ mod_timer(&ptp->suppress_timer,
|
|
+ jiffies + msecs_to_jiffies(HISI_PTP_SUP_TIME));
|
|
+ write_unlock(&ptp->rw_lock);
|
|
+ return IRQ_HANDLED;
|
|
+ }
|
|
+
|
|
+ hisi_ptp_wait_and_enable(ptp);
|
|
+
|
|
+ write_unlock(&ptp->rw_lock);
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static int hisi_ptp_get_rx_resource(struct platform_device *pdev,
|
|
+ struct hisi_ptp_pdev *ptp)
|
|
+{
|
|
+ struct hisi_ptp_rx *rx;
|
|
+ struct resource *peri;
|
|
+ unsigned long flags;
|
|
+ u32 rx_total = 0;
|
|
+ bool is_base_rx;
|
|
+ int ret;
|
|
+
|
|
+ ret = device_property_read_u32(&pdev->dev, "rx_num", &rx_total);
|
|
+ if (ret) {
|
|
+ dev_err(&pdev->dev, "failed to read rx total property\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ rx = devm_kzalloc(&pdev->dev, sizeof(struct hisi_ptp_rx), GFP_KERNEL);
|
|
+ if (!rx)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ peri = platform_get_resource(pdev, IORESOURCE_MEM, HISI_RES_T_PERI_SC);
|
|
+ if (!peri) {
|
|
+ dev_err(&pdev->dev, "failed to get rx peri resource\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ rx->base = devm_ioremap(&pdev->dev, peri->start, resource_size(peri));
|
|
+ if (!rx->base) {
|
|
+ dev_err(&pdev->dev, "failed to remap rx peri resource\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ rx->irq = platform_get_irq(pdev, 0);
|
|
+ if (rx->irq < 0) {
|
|
+ dev_err(&pdev->dev, "failed to get irq, ret = %d\n", rx->irq);
|
|
+ return rx->irq;
|
|
+ }
|
|
+ snprintf(rx->name, HISI_PTP_INT_NAME_LEN, "%s-%d", HISI_PTP_NAME,
|
|
+ rx->irq);
|
|
+ ret = devm_request_irq(&pdev->dev, rx->irq, hisi_ptp_irq_handle, 0,
|
|
+ rx->name, ptp);
|
|
+ if (ret) {
|
|
+ dev_err(&pdev->dev, "failed to request irq(%d), ret = %d\n",
|
|
+ rx->irq, ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ is_base_rx = device_property_present(&pdev->dev, "base_rx");
|
|
+
|
|
+ rx->dev = &pdev->dev;
|
|
+
|
|
+ write_lock_irqsave(&ptp->rw_lock, flags);
|
|
+
|
|
+ if (is_base_rx)
|
|
+ ptp->rx_base = rx->base;
|
|
+
|
|
+ ptp->rx_cnt++;
|
|
+
|
|
+ /* use the first rx device to init the global rx_total */
|
|
+ if (ptp->rx_total == 0)
|
|
+ ptp->rx_total = rx_total;
|
|
+
|
|
+ if (ptp->rx_total != rx_total || ptp->rx_cnt > ptp->rx_total) {
|
|
+ write_unlock_irqrestore(&ptp->rw_lock, flags);
|
|
+ dev_err(&pdev->dev,
|
|
+ "failed to probe rx device, please check the asl file!\n");
|
|
+ dev_err(&pdev->dev,
|
|
+ "rx_total:%u, current rx_total:%u, rx_cnt:%u\n",
|
|
+ ptp->rx_total, rx_total, ptp->rx_cnt);
|
|
+
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ list_add_tail(&rx->node, &ptp->ptp_rx_list);
|
|
+
|
|
+ write_unlock_irqrestore(&ptp->rw_lock, flags);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int hisi_ptp_get_tx_resource(struct platform_device *pdev,
|
|
+ struct hisi_ptp_pdev *ptp)
|
|
+{
|
|
+ struct hisi_ptp_tx *tx;
|
|
+ struct resource *mem;
|
|
+ unsigned long flags;
|
|
+
|
|
+ write_lock_irqsave(&ptp->rw_lock, flags);
|
|
+ /* use have only one tx device */
|
|
+ if (ptp->tx_cnt) {
|
|
+ write_unlock_irqrestore(&ptp->rw_lock, flags);
|
|
+ dev_err(&pdev->dev,
|
|
+ "failed to probe tx device, more than one tx device found, please check the asl file!\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ write_unlock_irqrestore(&ptp->rw_lock, flags);
|
|
+
|
|
+ tx = devm_kzalloc(&pdev->dev, sizeof(struct hisi_ptp_tx), GFP_KERNEL);
|
|
+ if (!tx)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, HISI_RES_N_NET_SC);
|
|
+ if (!mem) {
|
|
+ dev_err(&pdev->dev, "failed to get tx net sc resource\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ tx->base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
|
|
+ if (!tx->base) {
|
|
+ dev_err(&pdev->dev, "failed to remap tx net sc resource\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, HISI_RES_N_IO_SC);
|
|
+ if (!mem) {
|
|
+ dev_err(&pdev->dev, "failed to get tx nimbus io sc resource\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ tx->io_sc_base = devm_ioremap(&pdev->dev, mem->start,
|
|
+ resource_size(mem));
|
|
+ if (!tx->io_sc_base) {
|
|
+ dev_err(&pdev->dev, "failed to remap tx nimbus io resource\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ tx->dev = &pdev->dev;
|
|
+
|
|
+ write_lock_irqsave(&ptp->rw_lock, flags);
|
|
+ ptp->tx_cnt++;
|
|
+ ptp->ptp_tx = tx;
|
|
+ write_unlock_irqrestore(&ptp->rw_lock, flags);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void hisi_ptp_cal_time_start(struct hisi_ptp_pdev *ptp)
|
|
+{
|
|
+ void __iomem *io_sc_base;
|
|
+ struct hisi_ptp_rx *rx;
|
|
+ void __iomem *base;
|
|
+
|
|
+ /* config all rx to enter calculation mode. */
|
|
+ list_for_each_entry(rx, &ptp->ptp_rx_list, node) {
|
|
+ base = rx->base;
|
|
+ writel(1, base + PERI_SC_PTP_RESET_REQ);
|
|
+ writel(1, base + PERI_SC_PTP_RESET_DREQ);
|
|
+ writel(0, base + PERI_SC_LOCAL_TIMER_COMP_HIGH_ADDR);
|
|
+ writel(0, base + PERI_SC_LOCAL_TIMER_COMP_LOW_ADDR);
|
|
+ writel(1, base + PERI_SC_CRC_EN_ADDR);
|
|
+ writel(0, base + PERI_SC_LOCAL_CNT_EN_ADDR);
|
|
+ writel(1, base + PERI_SC_RX_SHIFT_EN_ADDR);
|
|
+ }
|
|
+
|
|
+ /* config tx to enter calculation mode. */
|
|
+ base = ptp->ptp_tx->base;
|
|
+ io_sc_base = ptp->ptp_tx->io_sc_base;
|
|
+ writel(HISI_PTP_CAL_MODE, io_sc_base + IO_SC_PTP_NORMAL_MODE_EN);
|
|
+ writel(HISI_PTP_CAL_MODE, base + NET_SC_PTP_NORMAL_MODE_EN);
|
|
+
|
|
+ writel(HISI_DEF_BAUD, io_sc_base + IO_SC_PTP_BAUD_VALUE_ADDR);
|
|
+ writel(1, io_sc_base + IO_SC_PTP_COUNTER_EN_ADDR);
|
|
+ writel(0, io_sc_base + IO_SC_PTP_WIRE_DELAY_CAL_EN);
|
|
+ writel(1, io_sc_base + IO_SC_PTP_WIRE_DELAY_CAL_EN);
|
|
+}
|
|
+
|
|
+static void hisi_ptp_cal_time_get(struct hisi_ptp_pdev *ptp)
|
|
+{
|
|
+#define HISI_PTP_MAX_WAIT_CNT 60
|
|
+ struct hisi_ptp_rx *rx;
|
|
+ void __iomem *base;
|
|
+ int cnt;
|
|
+ u32 rd_l;
|
|
+ u32 rd_h;
|
|
+ u32 td_l;
|
|
+ u32 td_h;
|
|
+ u64 rd;
|
|
+ u64 td;
|
|
+
|
|
+ list_for_each_entry(rx, &ptp->ptp_rx_list, node) {
|
|
+ base = rx->base;
|
|
+ rx->time_comp = HISI_DEF_TIME_COMP;
|
|
+
|
|
+ cnt = 0;
|
|
+ do {
|
|
+ if (readl(base + PERI_SC_CAPTURE_VLD) == 0) {
|
|
+ mdelay(1);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ rd_h = readl(base +
|
|
+ PERI_SC_CAPTURE_SYSTEM_COUNTER_BIN_HIGH);
|
|
+ rd_l = readl(base +
|
|
+ PERI_SC_CAPTURE_SYSTEM_COUNTER_BIN_LOW);
|
|
+ td_h = readl(base + PERI_SC_CAPTURE_PTP_TIME_COMP_HIGH);
|
|
+ td_l = readl(base + PERI_SC_CAPTURE_PTP_TIME_COMP_LOW);
|
|
+
|
|
+ rd = (u64)rd_h << 32 | rd_l;
|
|
+ td = (u64)td_h << 32 | td_l;
|
|
+
|
|
+ if (!rd || !td || rd < td) {
|
|
+ mdelay(1);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ rx->time_comp = rd - td;
|
|
+ break;
|
|
+ } while (cnt++ <= HISI_PTP_MAX_WAIT_CNT);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void hisi_ptp_cal_time_end(struct hisi_ptp_pdev *ptp)
|
|
+{
|
|
+ void __iomem *io_sc_base;
|
|
+ struct hisi_ptp_rx *rx;
|
|
+ void __iomem *base;
|
|
+
|
|
+ /* config all rx to exit calculation mode. */
|
|
+ list_for_each_entry(rx, &ptp->ptp_rx_list, node) {
|
|
+ base = rx->base;
|
|
+ writel(0, base + PERI_SC_RX_SHIFT_EN_ADDR);
|
|
+ }
|
|
+
|
|
+ /* config tx to exit calculation mode. */
|
|
+ base = ptp->ptp_tx->base;
|
|
+ io_sc_base = ptp->ptp_tx->io_sc_base;
|
|
+
|
|
+ writel(0, io_sc_base + IO_SC_PTP_COUNTER_EN_ADDR);
|
|
+ writel(HISI_PTP_NORMAL_MODE, io_sc_base + IO_SC_PTP_NORMAL_MODE_EN);
|
|
+ writel(HISI_PTP_NORMAL_MODE, base + NET_SC_PTP_NORMAL_MODE_EN);
|
|
+}
|
|
+
|
|
+/* This function should call under rw_lock */
|
|
+static void hisi_ptp_cal_time_comp(struct hisi_ptp_pdev *ptp)
|
|
+{
|
|
+ hisi_ptp_cal_time_start(ptp);
|
|
+ hisi_ptp_cal_time_get(ptp);
|
|
+ hisi_ptp_cal_time_end(ptp);
|
|
+}
|
|
+
|
|
+/* This function should call under rw_lock */
|
|
+static void hisi_ptp_peri_rx_init(struct hisi_ptp_pdev *ptp)
|
|
+{
|
|
+ struct hisi_ptp_rx *rx;
|
|
+ void __iomem *base;
|
|
+
|
|
+ list_for_each_entry(rx, &ptp->ptp_rx_list, node) {
|
|
+ base = rx->base;
|
|
+ writel(1, base + PERI_SC_CRC_EN_ADDR);
|
|
+ writel(upper_32_bits(rx->time_comp),
|
|
+ base + PERI_SC_LOCAL_TIMER_COMP_HIGH_ADDR);
|
|
+ writel(lower_32_bits(rx->time_comp),
|
|
+ base + PERI_SC_LOCAL_TIMER_COMP_LOW_ADDR);
|
|
+ writel(err_threshold,
|
|
+ base + PERI_SC_SYNC_ERR_COMP_LOW_ADDR);
|
|
+ writel(1, base + PERI_SC_CRC_INT_CONTRL_ADDR);
|
|
+ writel(0, base + PERI_SC_SYNC_ERR_CLR_ADDR);
|
|
+ writel(1, base + PERI_SC_LOCAL_CNT_EN_ADDR);
|
|
+ writel(1, base + PERI_SC_RX_SHIFT_EN_ADDR);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* This function should call under rw_lock */
|
|
+static void hisi_ptp_net_tx_init(struct hisi_ptp_pdev *ptp)
|
|
+{
|
|
+ void __iomem *base;
|
|
+
|
|
+ base = ptp->ptp_tx->base;
|
|
+ writel(1, base + NET_SC_PTP_COUNTER_EN_ADDR);
|
|
+}
|
|
+
|
|
+static int hisi_ptp_adjfine(struct ptp_clock_info *ptp_info, long delta)
|
|
+{
|
|
+ return -EOPNOTSUPP;
|
|
+}
|
|
+
|
|
+static int hisi_ptp_adjtime(struct ptp_clock_info *ptp_info, s64 delta)
|
|
+{
|
|
+ return -EOPNOTSUPP;
|
|
+}
|
|
+
|
|
+static int hisi_ptp_settime(struct ptp_clock_info *ptp_info,
|
|
+ const struct timespec64 *ts)
|
|
+{
|
|
+ return -EOPNOTSUPP;
|
|
+}
|
|
+
|
|
+static int hisi_ptp_gettime(struct ptp_clock_info *ptp_info,
|
|
+ struct timespec64 *ts,
|
|
+ struct ptp_system_timestamp *sts)
|
|
+{
|
|
+ struct hisi_ptp_pdev *ptp = hisi_ptp_get_pdev(ptp_info);
|
|
+ unsigned long flags;
|
|
+ u32 hi = UINT_MAX;
|
|
+ u32 lo = UINT_MAX;
|
|
+ u64 ns;
|
|
+
|
|
+ read_lock_irqsave(&ptp->rw_lock, flags);
|
|
+
|
|
+ if (ptp->rx_base) {
|
|
+ hi = readl(ptp->rx_base + PERI_SC_LOCAL_TIME_HIGH_ADDR);
|
|
+ lo = readl(ptp->rx_base + PERI_SC_LOCAL_TIME_LOW_ADDR);
|
|
+ }
|
|
+
|
|
+ read_unlock_irqrestore(&ptp->rw_lock, flags);
|
|
+
|
|
+ ns = (u64)hi * NSEC_PER_SEC + lo;
|
|
+ *ts = ns_to_timespec64(ns);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int hisi_ptp_create_clock(struct hisi_ptp_pdev *ptp)
|
|
+{
|
|
+ dev_info(ptp->ptp_tx->dev, "register ptp clock\n");
|
|
+
|
|
+ snprintf(ptp->info.name, PTP_CLOCK_NAME_LEN, "%s", HISI_PTP_NAME);
|
|
+ ptp->info.owner = THIS_MODULE;
|
|
+ ptp->info.adjfine = hisi_ptp_adjfine;
|
|
+ ptp->info.adjtime = hisi_ptp_adjtime;
|
|
+ ptp->info.settime64 = hisi_ptp_settime;
|
|
+ ptp->info.gettimex64 = hisi_ptp_gettime;
|
|
+ ptp->clock = ptp_clock_register(&ptp->info, ptp->ptp_tx->dev);
|
|
+ if (IS_ERR(ptp->clock)) {
|
|
+ dev_err(ptp->ptp_tx->dev,
|
|
+ "failed to register ptp clock, ret = %ld\n",
|
|
+ PTR_ERR(ptp->clock));
|
|
+ return PTR_ERR(ptp->clock);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void hisi_ptp_timer(struct timer_list *t)
|
|
+{
|
|
+ struct hisi_ptp_pdev *ptp = from_timer(ptp, t, suppress_timer);
|
|
+ unsigned long flags;
|
|
+
|
|
+ write_lock_irqsave(&ptp->rw_lock, flags);
|
|
+
|
|
+ dev_dbg(ptp->ptp_tx->dev, "ptp timer timeout handler.\n");
|
|
+
|
|
+ ptp->last_jiffies = jiffies;
|
|
+ ptp->irq_cnt = 0;
|
|
+
|
|
+ hisi_ptp_wait_and_enable(ptp);
|
|
+
|
|
+ write_unlock_irqrestore(&ptp->rw_lock, flags);
|
|
+}
|
|
+
|
|
+static int hisi_ptp_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct hisi_ptp_pdev *ptp = &g_ptpdev;
|
|
+ unsigned long flags;
|
|
+ const char *type;
|
|
+ int ret;
|
|
+
|
|
+ dev_info(&pdev->dev, "ptp probe start\n");
|
|
+
|
|
+ ret = device_property_read_string(&pdev->dev, "type", &type);
|
|
+ if (ret) {
|
|
+ dev_err(&pdev->dev, "failed to read device type, ret = %d\n",
|
|
+ ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ if (!memcmp(type, "rx", strlen("rx"))) {
|
|
+ ret = hisi_ptp_get_rx_resource(pdev, ptp);
|
|
+ } else if (!memcmp(type, "tx", strlen("tx"))) {
|
|
+ ret = hisi_ptp_get_tx_resource(pdev, ptp);
|
|
+ } else {
|
|
+ dev_err(&pdev->dev,
|
|
+ "failed to probe unknown device, type: %s\n",
|
|
+ type);
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ write_lock_irqsave(&ptp->rw_lock, flags);
|
|
+
|
|
+ if (ptp->rx_total == 0 || ptp->rx_total != ptp->rx_cnt ||
|
|
+ ptp->tx_cnt != 1) {
|
|
+ write_unlock_irqrestore(&ptp->rw_lock, flags);
|
|
+ dev_info(&pdev->dev,
|
|
+ "waiting for devices...rx total:%u, now:%u. tx total:1, now:%u\n",
|
|
+ ptp->rx_total, ptp->rx_cnt, ptp->tx_cnt);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (!ptp->rx_base) {
|
|
+ write_unlock_irqrestore(&ptp->rw_lock, flags);
|
|
+ dev_err(&pdev->dev,
|
|
+ "failed to probe, no base rx device, please check the asl file!\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ hisi_ptp_disable(ptp);
|
|
+ hisi_ptp_cal_time_comp(ptp);
|
|
+ hisi_ptp_peri_rx_init(ptp);
|
|
+ hisi_ptp_net_tx_init(ptp);
|
|
+ hisi_ptp_unmask_irq(ptp);
|
|
+
|
|
+ write_unlock_irqrestore(&ptp->rw_lock, flags);
|
|
+
|
|
+ ret = hisi_ptp_create_clock(ptp);
|
|
+ if (ret) {
|
|
+ write_lock_irqsave(&ptp->rw_lock, flags);
|
|
+ hisi_ptp_disable(ptp);
|
|
+ write_unlock_irqrestore(&ptp->rw_lock, flags);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ set_bit(HISI_PTP_INIT_DONE, &ptp->flag);
|
|
+
|
|
+ dev_info(&pdev->dev, "ptp probe end\n");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int hisi_ptp_remove(struct platform_device *pdev)
|
|
+{
|
|
+ struct hisi_ptp_pdev *ptp = &g_ptpdev;
|
|
+ struct hisi_ptp_rx *rx;
|
|
+ unsigned long flags;
|
|
+
|
|
+ if (test_and_clear_bit(HISI_PTP_INIT_DONE, &ptp->flag)) {
|
|
+ ptp_clock_unregister(ptp->clock);
|
|
+ ptp->clock = NULL;
|
|
+
|
|
+ write_lock_irqsave(&ptp->rw_lock, flags);
|
|
+ hisi_ptp_disable(ptp);
|
|
+ write_unlock_irqrestore(&ptp->rw_lock, flags);
|
|
+
|
|
+ dev_info(&pdev->dev, "unregister ptp clock\n");
|
|
+ }
|
|
+
|
|
+ write_lock_irqsave(&ptp->rw_lock, flags);
|
|
+ if (ptp->ptp_tx && ptp->ptp_tx->dev == &pdev->dev) {
|
|
+ ptp->tx_cnt--;
|
|
+ ptp->ptp_tx = NULL;
|
|
+ dev_info(&pdev->dev, "remove tx ptp device\n");
|
|
+ } else {
|
|
+ list_for_each_entry(rx, &ptp->ptp_rx_list, node) {
|
|
+ if (rx->dev == &pdev->dev) {
|
|
+ ptp->rx_cnt--;
|
|
+ list_del(&rx->node);
|
|
+ dev_info(&pdev->dev, "remove rx ptp device\n");
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ write_unlock_irqrestore(&ptp->rw_lock, flags);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct acpi_device_id hisi_ptp_acpi_match[] = {
|
|
+ { "HISI0411", 0 },
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(acpi, hisi_ptp_acpi_match);
|
|
+
|
|
+static struct platform_driver hisi_ptp_driver = {
|
|
+ .probe = hisi_ptp_probe,
|
|
+ .remove = hisi_ptp_remove,
|
|
+ .driver = {
|
|
+ .name = HISI_PTP_NAME,
|
|
+ .acpi_match_table = ACPI_PTR(hisi_ptp_acpi_match),
|
|
+ },
|
|
+};
|
|
+
|
|
+static ssize_t hisi_ptp_dbg_read_state(struct file *filp, char __user *buf,
|
|
+ size_t cnt, loff_t *ppos)
|
|
+{
|
|
+ struct hisi_ptp_pdev *ptp = filp->private_data;
|
|
+ struct hisi_ptp_rx *rx;
|
|
+ unsigned long flags;
|
|
+ ssize_t size = 0;
|
|
+ char *read_buf;
|
|
+ int pos = 0;
|
|
+ int len;
|
|
+
|
|
+ if (*ppos < 0)
|
|
+ return -EINVAL;
|
|
+ if (cnt <= 0)
|
|
+ return 0;
|
|
+ if (!access_ok(buf, cnt))
|
|
+ return -EFAULT;
|
|
+
|
|
+ read_buf = kvzalloc(HISI_PTP_DBGFS_STS_LEN, GFP_KERNEL);
|
|
+ if (!read_buf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ len = HISI_PTP_DBGFS_STS_LEN;
|
|
+
|
|
+ write_lock_irqsave(&ptp->rw_lock, flags);
|
|
+ pos += scnprintf(read_buf + pos, len - pos, "error threshold: %#x\n",
|
|
+ err_threshold);
|
|
+ pos += scnprintf(read_buf + pos, len - pos, "tx count: %u\n",
|
|
+ ptp->tx_cnt);
|
|
+ pos += scnprintf(read_buf + pos, len - pos, "rx total: %u\n",
|
|
+ ptp->rx_total);
|
|
+ pos += scnprintf(read_buf + pos, len - pos, "rx count: %u\n",
|
|
+ ptp->rx_cnt);
|
|
+ pos += scnprintf(read_buf + pos, len - pos, "irq count: %u\n",
|
|
+ ptp->irq_cnt);
|
|
+ pos += scnprintf(read_buf + pos, len - pos, "irq last jiffies: %lu\n",
|
|
+ ptp->last_jiffies);
|
|
+
|
|
+ list_for_each_entry(rx, &ptp->ptp_rx_list, node) {
|
|
+ pos += scnprintf(read_buf + pos, len - pos, "name: %s\n",
|
|
+ rx->name);
|
|
+ pos += scnprintf(read_buf + pos, len - pos, "time comp: %#llx\n",
|
|
+ rx->time_comp);
|
|
+ pos += scnprintf(read_buf + pos, len - pos, "irq: %d\n",
|
|
+ rx->irq);
|
|
+ }
|
|
+ write_unlock_irqrestore(&ptp->rw_lock, flags);
|
|
+
|
|
+ size = simple_read_from_buffer(buf, cnt, ppos, read_buf,
|
|
+ strlen(read_buf));
|
|
+
|
|
+ kvfree(read_buf);
|
|
+
|
|
+ return size;
|
|
+}
|
|
+
|
|
+static const struct hisi_ptp_reg hisi_ptp_tx_reg[] = {
|
|
+ {"NET_SC_PTP_BAUD_VALUE_ADDR ",
|
|
+ NET_SC_PTP_BAUD_VALUE_ADDR},
|
|
+ {"NET_SC_PTP_COUNTER_EN_ADDR ",
|
|
+ NET_SC_PTP_COUNTER_EN_ADDR},
|
|
+ {"NET_SC_PTP_NORMAL_MODE_EN ",
|
|
+ NET_SC_PTP_NORMAL_MODE_EN},
|
|
+ {"NET_SC_PTP_WIRE_DELAY_CAL_EN",
|
|
+ NET_SC_PTP_WIRE_DELAY_CAL_EN},
|
|
+ {"NET_SC_SAMPLE_DELAY_CFG_ADDR",
|
|
+ NET_SC_SAMPLE_DELAY_CFG_ADDR},
|
|
+ {"NET_SC_PTP_TX_DFXBUS0_ADDR ",
|
|
+ NET_SC_PTP_TX_DFXBUS0_ADDR},
|
|
+ {"NET_SC_PTP_TX_DFXBUS1_ADDR ",
|
|
+ NET_SC_PTP_TX_DFXBUS1_ADDR},
|
|
+ {"NET_SC_PTP_TX_DFXBUS2_ADDR ",
|
|
+ NET_SC_PTP_TX_DFXBUS2_ADDR},
|
|
+ {"NET_SC_PTP_TX_DFXBUS3_ADDR ",
|
|
+ NET_SC_PTP_TX_DFXBUS3_ADDR}
|
|
+};
|
|
+
|
|
+static const struct hisi_ptp_reg hisi_ptp_tx_io_reg[] = {
|
|
+ {"IO_SC_PTP_BAUD_VALUE_ADDR ",
|
|
+ IO_SC_PTP_BAUD_VALUE_ADDR},
|
|
+ {"IO_SC_PTP_COUNTER_EN_ADDR ",
|
|
+ IO_SC_PTP_COUNTER_EN_ADDR},
|
|
+ {"IO_SC_PTP_NORMAL_MODE_EN ",
|
|
+ IO_SC_PTP_NORMAL_MODE_EN},
|
|
+ {"IO_SC_PTP_WIRE_DELAY_CAL_EN",
|
|
+ IO_SC_PTP_WIRE_DELAY_CAL_EN},
|
|
+ {"IO_SC_SAMPLE_DELAY_CFG_ADDR",
|
|
+ IO_SC_SAMPLE_DELAY_CFG_ADDR},
|
|
+ {"IO_SC_PTP_TX_DFXBUS0_ADDR ",
|
|
+ IO_SC_PTP_TX_DFXBUS0_ADDR},
|
|
+ {"IO_SC_PTP_TX_DFXBUS1_ADDR ",
|
|
+ IO_SC_PTP_TX_DFXBUS1_ADDR},
|
|
+ {"IO_SC_PTP_TX_DFXBUS2_ADDR ",
|
|
+ IO_SC_PTP_TX_DFXBUS2_ADDR},
|
|
+ {"IO_SC_PTP_TX_DFXBUS3_ADDR ",
|
|
+ IO_SC_PTP_TX_DFXBUS3_ADDR}
|
|
+};
|
|
+
|
|
+static const struct hisi_ptp_reg hisi_ptp_rx_reg[] = {
|
|
+ {"PERI_SC_LOCAL_TIMER_COMP_HIGH_ADDR ",
|
|
+ PERI_SC_LOCAL_TIMER_COMP_HIGH_ADDR},
|
|
+ {"PERI_SC_LOCAL_TIMER_COMP_LOW_ADDR ",
|
|
+ PERI_SC_LOCAL_TIMER_COMP_LOW_ADDR},
|
|
+ {"PERI_SC_BAUD_VALUE_ADDR ",
|
|
+ PERI_SC_BAUD_VALUE_ADDR},
|
|
+ {"PERI_SC_LOCAL_CNT_EN_ADDR ",
|
|
+ PERI_SC_LOCAL_CNT_EN_ADDR},
|
|
+ {"PERI_SC_SYNC_ERR_COMP_HIGH_ADDR ",
|
|
+ PERI_SC_SYNC_ERR_COMP_HIGH_ADDR},
|
|
+ {"PERI_SC_SYNC_ERR_COMP_LOW_ADDR ",
|
|
+ PERI_SC_SYNC_ERR_COMP_LOW_ADDR},
|
|
+ {"PERI_SC_CRC_EN_ADDR ",
|
|
+ PERI_SC_CRC_EN_ADDR},
|
|
+ {"PERI_SC_ONE_CYCLE_NUM_ADDR ",
|
|
+ PERI_SC_ONE_CYCLE_NUM_ADDR},
|
|
+ {"PERI_SC_SYNC_ERR_CLR_ADDR ",
|
|
+ PERI_SC_SYNC_ERR_CLR_ADDR},
|
|
+ {"PERI_SC_RX_SHIFT_EN_ADDR ",
|
|
+ PERI_SC_RX_SHIFT_EN_ADDR},
|
|
+ {"PERI_SC_TIMEL_CY_NUM_ADDR ",
|
|
+ PERI_SC_TIMEL_CY_NUM_ADDR},
|
|
+ {"PERI_SC_INT_PTP_SYNC_ERR_ADDR ",
|
|
+ PERI_SC_INT_PTP_SYNC_ERR_ADDR},
|
|
+ {"PERI_SC_INT_PTP_SYNC_ERR_MASK_ADDR ",
|
|
+ PERI_SC_INT_PTP_SYNC_ERR_MASK_ADDR},
|
|
+ {"PERI_SC_INT_ORIGIN ",
|
|
+ PERI_SC_INT_ORIGIN},
|
|
+ {"PERI_SC_CRC_ERR_COUNT ",
|
|
+ PERI_SC_CRC_ERR_COUNT},
|
|
+ {"PERI_SC_CRC_INT_CONTRL_ADDR ",
|
|
+ PERI_SC_CRC_INT_CONTRL_ADDR},
|
|
+ {"PERI_SC_CAPTURE_PTP_TIME_COMP_HIGH ",
|
|
+ PERI_SC_CAPTURE_PTP_TIME_COMP_HIGH},
|
|
+ {"PERI_SC_CAPTURE_PTP_TIME_COMP_LOW ",
|
|
+ PERI_SC_CAPTURE_PTP_TIME_COMP_LOW},
|
|
+ {"PERI_SC_CAPTURE_SYSTEM_COUNTER_BIN_HIGH",
|
|
+ PERI_SC_CAPTURE_SYSTEM_COUNTER_BIN_HIGH},
|
|
+ {"PERI_SC_CAPTURE_SYSTEM_COUNTER_BIN_LOW ",
|
|
+ PERI_SC_CAPTURE_SYSTEM_COUNTER_BIN_LOW},
|
|
+ {"PERI_SC_CAPTURE_VLD ",
|
|
+ PERI_SC_CAPTURE_VLD},
|
|
+ {"PERI_SC_LOCAL_TIME_LOW_ADDR ",
|
|
+ PERI_SC_LOCAL_TIME_LOW_ADDR},
|
|
+ {"PERI_SC_LOCAL_TIME_HIGH_ADDR ",
|
|
+ PERI_SC_LOCAL_TIME_HIGH_ADDR}
|
|
+};
|
|
+
|
|
+static void hisi_ptp_dump_reg(void __iomem *base,
|
|
+ const struct hisi_ptp_reg *reg, int reg_len,
|
|
+ char *buf, int len, int *pos)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < reg_len; i++)
|
|
+ *pos += scnprintf(buf + *pos, len - *pos, "%s : 0x%08x\n",
|
|
+ reg[i].name, readl(base + reg[i].offset));
|
|
+}
|
|
+
|
|
+static ssize_t hisi_ptp_dbg_read_reg(struct file *filp, char __user *buf,
|
|
+ size_t cnt, loff_t *ppos)
|
|
+{
|
|
+ struct hisi_ptp_pdev *ptp = filp->private_data;
|
|
+ struct hisi_ptp_rx *rx;
|
|
+ unsigned long flags;
|
|
+ ssize_t size = 0;
|
|
+ char *read_buf;
|
|
+ int pos = 0;
|
|
+ int len;
|
|
+
|
|
+ if (*ppos < 0)
|
|
+ return -EINVAL;
|
|
+ if (cnt <= 0)
|
|
+ return 0;
|
|
+ if (!access_ok(buf, cnt))
|
|
+ return -EFAULT;
|
|
+
|
|
+ read_buf = kvzalloc(HISI_PTP_DBGFS_REG_LEN, GFP_KERNEL);
|
|
+ if (!read_buf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ len = HISI_PTP_DBGFS_REG_LEN;
|
|
+
|
|
+ write_lock_irqsave(&ptp->rw_lock, flags);
|
|
+ if (ptp->ptp_tx && ptp->ptp_tx->base)
|
|
+ hisi_ptp_dump_reg(ptp->ptp_tx->base, hisi_ptp_tx_reg,
|
|
+ ARRAY_SIZE(hisi_ptp_tx_reg),
|
|
+ read_buf, len, &pos);
|
|
+
|
|
+ if (ptp->ptp_tx && ptp->ptp_tx->io_sc_base)
|
|
+ hisi_ptp_dump_reg(ptp->ptp_tx->io_sc_base, hisi_ptp_tx_io_reg,
|
|
+ ARRAY_SIZE(hisi_ptp_tx_io_reg),
|
|
+ read_buf, len, &pos);
|
|
+
|
|
+ list_for_each_entry(rx, &ptp->ptp_rx_list, node)
|
|
+ hisi_ptp_dump_reg(rx->base, hisi_ptp_rx_reg,
|
|
+ ARRAY_SIZE(hisi_ptp_rx_reg),
|
|
+ read_buf, len, &pos);
|
|
+
|
|
+ write_unlock_irqrestore(&ptp->rw_lock, flags);
|
|
+
|
|
+ size = simple_read_from_buffer(buf, cnt, ppos, read_buf,
|
|
+ strlen(read_buf));
|
|
+
|
|
+ kvfree(read_buf);
|
|
+
|
|
+ return size;
|
|
+}
|
|
+
|
|
+static const struct file_operations hisi_ptp_dbg_state_ops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .open = simple_open,
|
|
+ .read = hisi_ptp_dbg_read_state,
|
|
+};
|
|
+
|
|
+static const struct file_operations hisi_ptp_dbg_reg_ops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .open = simple_open,
|
|
+ .read = hisi_ptp_dbg_read_reg,
|
|
+};
|
|
+
|
|
+static void hisi_ptp_dbgfs_init(struct hisi_ptp_pdev *ptp)
|
|
+{
|
|
+ ptp->dbgfs_root = debugfs_create_dir(HISI_PTP_NAME, NULL);
|
|
+ debugfs_create_file("state", 0400, ptp->dbgfs_root, ptp,
|
|
+ &hisi_ptp_dbg_state_ops);
|
|
+ debugfs_create_file("reg", 0400, ptp->dbgfs_root, ptp,
|
|
+ &hisi_ptp_dbg_reg_ops);
|
|
+}
|
|
+
|
|
+static void hisi_ptp_dbgfs_uninit(struct hisi_ptp_pdev *ptp)
|
|
+{
|
|
+ debugfs_remove_recursive(ptp->dbgfs_root);
|
|
+}
|
|
+
|
|
+static int __init hisi_ptp_module_init(void)
|
|
+{
|
|
+ struct hisi_ptp_pdev *ptp = &g_ptpdev;
|
|
+ int ret;
|
|
+
|
|
+ memset(ptp, 0, sizeof(struct hisi_ptp_pdev));
|
|
+ rwlock_init(&ptp->rw_lock);
|
|
+ INIT_LIST_HEAD(&ptp->ptp_rx_list);
|
|
+
|
|
+ timer_setup(&ptp->suppress_timer, hisi_ptp_timer, 0);
|
|
+
|
|
+ ret = platform_driver_register(&hisi_ptp_driver);
|
|
+ if (ret) {
|
|
+ del_timer_sync(&ptp->suppress_timer);
|
|
+ pr_err("failed to register ptp platform driver, ret = %d\n",
|
|
+ ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ hisi_ptp_dbgfs_init(ptp);
|
|
+
|
|
+ pr_info("hisi ptp platform driver inited, version: %s\n",
|
|
+ HISI_PTP_VERSION);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+module_init(hisi_ptp_module_init);
|
|
+
|
|
+static void __exit hisi_ptp_module_exit(void)
|
|
+{
|
|
+ struct hisi_ptp_pdev *ptp = &g_ptpdev;
|
|
+
|
|
+ pr_info("hisi ptp platform driver exit\n");
|
|
+
|
|
+ hisi_ptp_dbgfs_uninit(ptp);
|
|
+
|
|
+ platform_driver_unregister(&hisi_ptp_driver);
|
|
+
|
|
+ if (ptp->suppress_timer.function)
|
|
+ del_timer_sync(&ptp->suppress_timer);
|
|
+
|
|
+ memset(ptp, 0, sizeof(struct hisi_ptp_pdev));
|
|
+}
|
|
+module_exit(hisi_ptp_module_exit);
|
|
+
|
|
+MODULE_DESCRIPTION("HiSilicon PTP driver");
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_VERSION(HISI_PTP_VERSION);
|
|
--
|
|
2.27.0
|
|
|