summaryrefslogtreecommitdiff
path: root/drivers/ptp
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-12-14 02:47:48 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2022-12-14 02:47:48 +0300
commit7e68dd7d07a28faa2e6574dd6b9dbd90cdeaae91 (patch)
treeae0427c5a3b905f24b3a44b510a9bcf35d9b67a3 /drivers/ptp
parent1ca06f1c1acecbe02124f14a37cce347b8c1a90c (diff)
parent7c4a6309e27f411743817fe74a832ec2d2798a4b (diff)
downloadlinux-7e68dd7d07a28faa2e6574dd6b9dbd90cdeaae91.tar.xz
Merge tag 'net-next-6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next
Pull networking updates from Paolo Abeni: "Core: - Allow live renaming when an interface is up - Add retpoline wrappers for tc, improving considerably the performances of complex queue discipline configurations - Add inet drop monitor support - A few GRO performance improvements - Add infrastructure for atomic dev stats, addressing long standing data races - De-duplicate common code between OVS and conntrack offloading infrastructure - A bunch of UBSAN_BOUNDS/FORTIFY_SOURCE improvements - Netfilter: introduce packet parser for tunneled packets - Replace IPVS timer-based estimators with kthreads to scale up the workload with the number of available CPUs - Add the helper support for connection-tracking OVS offload BPF: - Support for user defined BPF objects: the use case is to allocate own objects, build own object hierarchies and use the building blocks to build own data structures flexibly, for example, linked lists in BPF - Make cgroup local storage available to non-cgroup attached BPF programs - Avoid unnecessary deadlock detection and failures wrt BPF task storage helpers - A relevant bunch of BPF verifier fixes and improvements - Veristat tool improvements to support custom filtering, sorting, and replay of results - Add LLVM disassembler as default library for dumping JITed code - Lots of new BPF documentation for various BPF maps - Add bpf_rcu_read_{,un}lock() support for sleepable programs - Add RCU grace period chaining to BPF to wait for the completion of access from both sleepable and non-sleepable BPF programs - Add support storing struct task_struct objects as kptrs in maps - Improve helper UAPI by explicitly defining BPF_FUNC_xxx integer values - Add libbpf *_opts API-variants for bpf_*_get_fd_by_id() functions Protocols: - TCP: implement Protective Load Balancing across switch links - TCP: allow dynamically disabling TCP-MD5 static key, reverting back to fast[er]-path - UDP: Introduce optional per-netns hash lookup table - IPv6: simplify and cleanup sockets disposal - Netlink: support different type policies for each generic netlink operation - MPTCP: add MSG_FASTOPEN and FastOpen listener side support - MPTCP: add netlink notification support for listener sockets events - SCTP: add VRF support, allowing sctp sockets binding to VRF devices - Add bridging MAC Authentication Bypass (MAB) support - Extensions for Ethernet VPN bridging implementation to better support multicast scenarios - More work for Wi-Fi 7 support, comprising conversion of all the existing drivers to internal TX queue usage - IPSec: introduce a new offload type (packet offload) allowing complete header processing and crypto offloading - IPSec: extended ack support for more descriptive XFRM error reporting - RXRPC: increase SACK table size and move processing into a per-local endpoint kernel thread, reducing considerably the required locking - IEEE 802154: synchronous send frame and extended filtering support, initial support for scanning available 15.4 networks - Tun: bump the link speed from 10Mbps to 10Gbps - Tun/VirtioNet: implement UDP segmentation offload support Driver API: - PHY/SFP: improve power level switching between standard level 1 and the higher power levels - New API for netdev <-> devlink_port linkage - PTP: convert existing drivers to new frequency adjustment implementation - DSA: add support for rx offloading - Autoload DSA tagging driver when dynamically changing protocol - Add new PCP and APPTRUST attributes to Data Center Bridging - Add configuration support for 800Gbps link speed - Add devlink port function attribute to enable/disable RoCE and migratable - Extend devlink-rate to support strict prioriry and weighted fair queuing - Add devlink support to directly reading from region memory - New device tree helper to fetch MAC address from nvmem - New big TCP helper to simplify temporary header stripping New hardware / drivers: - Ethernet: - Marvel Octeon CNF95N and CN10KB Ethernet Switches - Marvel Prestera AC5X Ethernet Switch - WangXun 10 Gigabit NIC - Motorcomm yt8521 Gigabit Ethernet - Microchip ksz9563 Gigabit Ethernet Switch - Microsoft Azure Network Adapter - Linux Automation 10Base-T1L adapter - PHY: - Aquantia AQR112 and AQR412 - Motorcomm YT8531S - PTP: - Orolia ART-CARD - WiFi: - MediaTek Wi-Fi 7 (802.11be) devices - RealTek rtw8821cu, rtw8822bu, rtw8822cu and rtw8723du USB devices - Bluetooth: - Broadcom BCM4377/4378/4387 Bluetooth chipsets - Realtek RTL8852BE and RTL8723DS - Cypress.CYW4373A0 WiFi + Bluetooth combo device Drivers: - CAN: - gs_usb: bus error reporting support - kvaser_usb: listen only and bus error reporting support - Ethernet NICs: - Intel (100G): - extend action skbedit to RX queue mapping - implement devlink-rate support - support direct read from memory - nVidia/Mellanox (mlx5): - SW steering improvements, increasing rules update rate - Support for enhanced events compression - extend H/W offload packet manipulation capabilities - implement IPSec packet offload mode - nVidia/Mellanox (mlx4): - better big TCP support - Netronome Ethernet NICs (nfp): - IPsec offload support - add support for multicast filter - Broadcom: - RSS and PTP support improvements - AMD/SolarFlare: - netlink extened ack improvements - add basic flower matches to offload, and related stats - Virtual NICs: - ibmvnic: introduce affinity hint support - small / embedded: - FreeScale fec: add initial XDP support - Marvel mv643xx_eth: support MII/GMII/RGMII modes for Kirkwood - TI am65-cpsw: add suspend/resume support - Mediatek MT7986: add RX wireless wthernet dispatch support - Realtek 8169: enable GRO software interrupt coalescing per default - Ethernet high-speed switches: - Microchip (sparx5): - add support for Sparx5 TC/flower H/W offload via VCAP - Mellanox mlxsw: - add 802.1X and MAC Authentication Bypass offload support - add ip6gre support - Embedded Ethernet switches: - Mediatek (mtk_eth_soc): - improve PCS implementation, add DSA untag support - enable flow offload support - Renesas: - add rswitch R-Car Gen4 gPTP support - Microchip (lan966x): - add full XDP support - add TC H/W offload via VCAP - enable PTP on bridge interfaces - Microchip (ksz8): - add MTU support for KSZ8 series - Qualcomm 802.11ax WiFi (ath11k): - support configuring channel dwell time during scan - MediaTek WiFi (mt76): - enable Wireless Ethernet Dispatch (WED) offload support - add ack signal support - enable coredump support - remain_on_channel support - Intel WiFi (iwlwifi): - enable Wi-Fi 7 Extremely High Throughput (EHT) PHY capabilities - 320 MHz channels support - RealTek WiFi (rtw89): - new dynamic header firmware format support - wake-over-WLAN support" * tag 'net-next-6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (2002 commits) ipvs: fix type warning in do_div() on 32 bit net: lan966x: Remove a useless test in lan966x_ptp_add_trap() net: ipa: add IPA v4.7 support dt-bindings: net: qcom,ipa: Add SM6350 compatible bnxt: Use generic HBH removal helper in tx path IPv6/GRO: generic helper to remove temporary HBH/jumbo header in driver selftests: forwarding: Add bridge MDB test selftests: forwarding: Rename bridge_mdb test bridge: mcast: Support replacement of MDB port group entries bridge: mcast: Allow user space to specify MDB entry routing protocol bridge: mcast: Allow user space to add (*, G) with a source list and filter mode bridge: mcast: Add support for (*, G) with a source list and filter mode bridge: mcast: Avoid arming group timer when (S, G) corresponds to a source bridge: mcast: Add a flag for user installed source entries bridge: mcast: Expose __br_multicast_del_group_src() bridge: mcast: Expose br_multicast_new_group_src() bridge: mcast: Add a centralized error path bridge: mcast: Place netlink policy before validation functions bridge: mcast: Split (*, G) and (S, G) addition into different functions bridge: mcast: Do not derive entry type from its filter mode ...
Diffstat (limited to 'drivers/ptp')
-rw-r--r--drivers/ptp/ptp_clock.c5
-rw-r--r--drivers/ptp/ptp_dte.c5
-rw-r--r--drivers/ptp/ptp_idt82p33.c709
-rw-r--r--drivers/ptp/ptp_idt82p33.h21
-rw-r--r--drivers/ptp/ptp_kvm_common.c4
-rw-r--r--drivers/ptp/ptp_ocp.c567
-rw-r--r--drivers/ptp/ptp_pch.c19
-rw-r--r--drivers/ptp/ptp_vmw.c4
8 files changed, 1163 insertions, 171 deletions
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
index 51cae72bb6db..62d4d29e7c05 100644
--- a/drivers/ptp/ptp_clock.c
+++ b/drivers/ptp/ptp_clock.c
@@ -131,10 +131,7 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct __kernel_timex *tx)
long ppb = scaled_ppm_to_ppb(tx->freq);
if (ppb > ops->max_adj || ppb < -ops->max_adj)
return -ERANGE;
- if (ops->adjfine)
- err = ops->adjfine(ops, tx->freq);
- else
- err = ops->adjfreq(ops, ppb);
+ err = ops->adjfine(ops, tx->freq);
ptp->dialed_frequency = tx->freq;
} else if (tx->modes & ADJ_OFFSET) {
if (ops->adjphase) {
diff --git a/drivers/ptp/ptp_dte.c b/drivers/ptp/ptp_dte.c
index 8641fd060491..7cc5a00e625b 100644
--- a/drivers/ptp/ptp_dte.c
+++ b/drivers/ptp/ptp_dte.c
@@ -134,8 +134,9 @@ static s64 dte_read_nco_with_ovf(struct ptp_dte *ptp_dte)
return ns;
}
-static int ptp_dte_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
+static int ptp_dte_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
{
+ s32 ppb = scaled_ppm_to_ppb(scaled_ppm);
u32 nco_incr;
unsigned long flags;
struct ptp_dte *ptp_dte = container_of(ptp, struct ptp_dte, caps);
@@ -219,7 +220,7 @@ static const struct ptp_clock_info ptp_dte_caps = {
.n_ext_ts = 0,
.n_pins = 0,
.pps = 0,
- .adjfreq = ptp_dte_adjfreq,
+ .adjfine = ptp_dte_adjfine,
.adjtime = ptp_dte_adjtime,
.gettime64 = ptp_dte_gettime,
.settime64 = ptp_dte_settime,
diff --git a/drivers/ptp/ptp_idt82p33.c b/drivers/ptp/ptp_idt82p33.c
index 97c1be44e323..afc76c22271a 100644
--- a/drivers/ptp/ptp_idt82p33.c
+++ b/drivers/ptp/ptp_idt82p33.c
@@ -27,6 +27,8 @@ MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");
MODULE_FIRMWARE(FW_FILENAME);
+#define EXTTS_PERIOD_MS (95)
+
/* Module Parameters */
static u32 phase_snap_threshold = SNAP_THRESHOLD_NS;
module_param(phase_snap_threshold, uint, 0);
@@ -36,6 +38,8 @@ MODULE_PARM_DESC(phase_snap_threshold,
static char *firmware;
module_param(firmware, charp, 0);
+static struct ptp_pin_desc pin_config[MAX_PHC_PLL][MAX_TRIG_CLK];
+
static inline int idt82p33_read(struct idt82p33 *idt82p33, u16 regaddr,
u8 *buf, u16 count)
{
@@ -121,24 +125,270 @@ static int idt82p33_dpll_set_mode(struct idt82p33_channel *channel,
return 0;
}
-static int _idt82p33_gettime(struct idt82p33_channel *channel,
- struct timespec64 *ts)
+static int idt82p33_set_tod_trigger(struct idt82p33_channel *channel,
+ u8 trigger, bool write)
+{
+ struct idt82p33 *idt82p33 = channel->idt82p33;
+ int err;
+ u8 cfg;
+
+ if (trigger > WR_TRIG_SEL_MAX)
+ return -EINVAL;
+
+ err = idt82p33_read(idt82p33, channel->dpll_tod_trigger,
+ &cfg, sizeof(cfg));
+
+ if (err)
+ return err;
+
+ if (write == true)
+ trigger = (trigger << WRITE_TRIGGER_SHIFT) |
+ (cfg & READ_TRIGGER_MASK);
+ else
+ trigger = (trigger << READ_TRIGGER_SHIFT) |
+ (cfg & WRITE_TRIGGER_MASK);
+
+ return idt82p33_write(idt82p33, channel->dpll_tod_trigger,
+ &trigger, sizeof(trigger));
+}
+
+static int idt82p33_get_extts(struct idt82p33_channel *channel,
+ struct timespec64 *ts)
+{
+ struct idt82p33 *idt82p33 = channel->idt82p33;
+ u8 buf[TOD_BYTE_COUNT];
+ int err;
+
+ err = idt82p33_read(idt82p33, channel->dpll_tod_sts, buf, sizeof(buf));
+
+ if (err)
+ return err;
+
+ /* Since trigger is not self clearing itself, we have to poll tod_sts */
+ if (memcmp(buf, channel->extts_tod_sts, TOD_BYTE_COUNT) == 0)
+ return -EAGAIN;
+
+ memcpy(channel->extts_tod_sts, buf, TOD_BYTE_COUNT);
+
+ idt82p33_byte_array_to_timespec(ts, buf);
+
+ if (channel->discard_next_extts) {
+ channel->discard_next_extts = false;
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static int map_ref_to_tod_trig_sel(int ref, u8 *trigger)
+{
+ int err = 0;
+
+ switch (ref) {
+ case 0:
+ *trigger = HW_TOD_TRIG_SEL_IN12;
+ break;
+ case 1:
+ *trigger = HW_TOD_TRIG_SEL_IN13;
+ break;
+ case 2:
+ *trigger = HW_TOD_TRIG_SEL_IN14;
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+static bool is_one_shot(u8 mask)
+{
+ /* Treat single bit PLL masks as continuous trigger */
+ if ((mask == 1) || (mask == 2))
+ return false;
+ else
+ return true;
+}
+
+static int arm_tod_read_with_trigger(struct idt82p33_channel *channel, u8 trigger)
{
struct idt82p33 *idt82p33 = channel->idt82p33;
u8 buf[TOD_BYTE_COUNT];
+ int err;
+
+ /* Remember the current tod_sts before setting the trigger */
+ err = idt82p33_read(idt82p33, channel->dpll_tod_sts, buf, sizeof(buf));
+
+ if (err)
+ return err;
+
+ memcpy(channel->extts_tod_sts, buf, TOD_BYTE_COUNT);
+
+ err = idt82p33_set_tod_trigger(channel, trigger, false);
+
+ if (err)
+ dev_err(idt82p33->dev, "%s: err = %d", __func__, err);
+
+ return err;
+}
+
+static int idt82p33_extts_enable(struct idt82p33_channel *channel,
+ struct ptp_clock_request *rq, int on)
+{
+ u8 index = rq->extts.index;
+ struct idt82p33 *idt82p33;
+ u8 mask = 1 << index;
+ int err = 0;
+ u8 old_mask;
u8 trigger;
+ int ref;
+
+ idt82p33 = channel->idt82p33;
+ old_mask = idt82p33->extts_mask;
+
+ /* Reject requests with unsupported flags */
+ if (rq->extts.flags & ~(PTP_ENABLE_FEATURE |
+ PTP_RISING_EDGE |
+ PTP_FALLING_EDGE |
+ PTP_STRICT_FLAGS))
+ return -EOPNOTSUPP;
+
+ /* Reject requests to enable time stamping on falling edge */
+ if ((rq->extts.flags & PTP_ENABLE_FEATURE) &&
+ (rq->extts.flags & PTP_FALLING_EDGE))
+ return -EOPNOTSUPP;
+
+ if (index >= MAX_PHC_PLL)
+ return -EINVAL;
+
+ if (on) {
+ /* Return if it was already enabled */
+ if (idt82p33->extts_mask & mask)
+ return 0;
+
+ /* Use the pin configured for the channel */
+ ref = ptp_find_pin(channel->ptp_clock, PTP_PF_EXTTS, channel->plln);
+
+ if (ref < 0) {
+ dev_err(idt82p33->dev, "%s: No valid pin found for Pll%d!\n",
+ __func__, channel->plln);
+ return -EBUSY;
+ }
+
+ err = map_ref_to_tod_trig_sel(ref, &trigger);
+
+ if (err) {
+ dev_err(idt82p33->dev,
+ "%s: Unsupported ref %d!\n", __func__, ref);
+ return err;
+ }
+
+ err = arm_tod_read_with_trigger(&idt82p33->channel[index], trigger);
+
+ if (err == 0) {
+ idt82p33->extts_mask |= mask;
+ idt82p33->channel[index].tod_trigger = trigger;
+ idt82p33->event_channel[index] = channel;
+ idt82p33->extts_single_shot = is_one_shot(idt82p33->extts_mask);
+
+ if (old_mask)
+ return 0;
+
+ schedule_delayed_work(&idt82p33->extts_work,
+ msecs_to_jiffies(EXTTS_PERIOD_MS));
+ }
+ } else {
+ idt82p33->extts_mask &= ~mask;
+ idt82p33->extts_single_shot = is_one_shot(idt82p33->extts_mask);
+
+ if (idt82p33->extts_mask == 0)
+ cancel_delayed_work(&idt82p33->extts_work);
+ }
+
+ return err;
+}
+
+static int idt82p33_extts_check_channel(struct idt82p33 *idt82p33, u8 todn)
+{
+ struct idt82p33_channel *event_channel;
+ struct ptp_clock_event event;
+ struct timespec64 ts;
+ int err;
+
+ err = idt82p33_get_extts(&idt82p33->channel[todn], &ts);
+ if (err == 0) {
+ event_channel = idt82p33->event_channel[todn];
+ event.type = PTP_CLOCK_EXTTS;
+ event.index = todn;
+ event.timestamp = timespec64_to_ns(&ts);
+ ptp_clock_event(event_channel->ptp_clock,
+ &event);
+ }
+ return err;
+}
+
+static u8 idt82p33_extts_enable_mask(struct idt82p33_channel *channel,
+ u8 extts_mask, bool enable)
+{
+ struct idt82p33 *idt82p33 = channel->idt82p33;
+ u8 trigger = channel->tod_trigger;
+ u8 mask;
int err;
+ int i;
- trigger = TOD_TRIGGER(HW_TOD_WR_TRIG_SEL_MSB_TOD_CNFG,
- HW_TOD_RD_TRIG_SEL_LSB_TOD_STS);
+ if (extts_mask == 0)
+ return 0;
+ if (enable == false)
+ cancel_delayed_work_sync(&idt82p33->extts_work);
- err = idt82p33_write(idt82p33, channel->dpll_tod_trigger,
- &trigger, sizeof(trigger));
+ for (i = 0; i < MAX_PHC_PLL; i++) {
+ mask = 1 << i;
+
+ if ((extts_mask & mask) == 0)
+ continue;
+ if (enable) {
+ err = arm_tod_read_with_trigger(&idt82p33->channel[i], trigger);
+ if (err)
+ dev_err(idt82p33->dev,
+ "%s: Arm ToD read trigger failed, err = %d",
+ __func__, err);
+ } else {
+ err = idt82p33_extts_check_channel(idt82p33, i);
+ if (err == 0 && idt82p33->extts_single_shot)
+ /* trigger happened so we won't re-enable it */
+ extts_mask &= ~mask;
+ }
+ }
+
+ if (enable)
+ schedule_delayed_work(&idt82p33->extts_work,
+ msecs_to_jiffies(EXTTS_PERIOD_MS));
+
+ return extts_mask;
+}
+
+static int _idt82p33_gettime(struct idt82p33_channel *channel,
+ struct timespec64 *ts)
+{
+ struct idt82p33 *idt82p33 = channel->idt82p33;
+ u8 old_mask = idt82p33->extts_mask;
+ u8 buf[TOD_BYTE_COUNT];
+ u8 new_mask = 0;
+ int err;
+
+ /* Disable extts */
+ if (old_mask)
+ new_mask = idt82p33_extts_enable_mask(channel, old_mask, false);
+
+ err = idt82p33_set_tod_trigger(channel, HW_TOD_RD_TRIG_SEL_LSB_TOD_STS,
+ false);
if (err)
return err;
+ channel->discard_next_extts = true;
+
if (idt82p33->calculate_overhead_flag)
idt82p33->start_time = ktime_get_raw();
@@ -147,6 +397,10 @@ static int _idt82p33_gettime(struct idt82p33_channel *channel,
if (err)
return err;
+ /* Re-enable extts */
+ if (new_mask)
+ idt82p33_extts_enable_mask(channel, new_mask, true);
+
idt82p33_byte_array_to_timespec(ts, buf);
return 0;
@@ -165,19 +419,16 @@ static int _idt82p33_settime(struct idt82p33_channel *channel,
struct timespec64 local_ts = *ts;
char buf[TOD_BYTE_COUNT];
s64 dynamic_overhead_ns;
- unsigned char trigger;
int err;
u8 i;
- trigger = TOD_TRIGGER(HW_TOD_WR_TRIG_SEL_MSB_TOD_CNFG,
- HW_TOD_RD_TRIG_SEL_LSB_TOD_STS);
-
- err = idt82p33_write(idt82p33, channel->dpll_tod_trigger,
- &trigger, sizeof(trigger));
-
+ err = idt82p33_set_tod_trigger(channel, HW_TOD_WR_TRIG_SEL_MSB_TOD_CNFG,
+ true);
if (err)
return err;
+ channel->discard_next_extts = true;
+
if (idt82p33->calculate_overhead_flag) {
dynamic_overhead_ns = ktime_to_ns(ktime_get_raw())
- ktime_to_ns(idt82p33->start_time);
@@ -202,7 +453,8 @@ static int _idt82p33_settime(struct idt82p33_channel *channel,
return err;
}
-static int _idt82p33_adjtime(struct idt82p33_channel *channel, s64 delta_ns)
+static int _idt82p33_adjtime_immediate(struct idt82p33_channel *channel,
+ s64 delta_ns)
{
struct idt82p33 *idt82p33 = channel->idt82p33;
struct timespec64 ts;
@@ -226,6 +478,60 @@ static int _idt82p33_adjtime(struct idt82p33_channel *channel, s64 delta_ns)
return err;
}
+static int _idt82p33_adjtime_internal_triggered(struct idt82p33_channel *channel,
+ s64 delta_ns)
+{
+ struct idt82p33 *idt82p33 = channel->idt82p33;
+ char buf[TOD_BYTE_COUNT];
+ struct timespec64 ts;
+ const u8 delay_ns = 32;
+ s32 remainder;
+ s64 ns;
+ int err;
+
+ err = _idt82p33_gettime(channel, &ts);
+
+ if (err)
+ return err;
+
+ if (ts.tv_nsec > (NSEC_PER_SEC - 5 * NSEC_PER_MSEC)) {
+ /* Too close to miss next trigger, so skip it */
+ mdelay(6);
+ ns = (ts.tv_sec + 2) * NSEC_PER_SEC + delta_ns + delay_ns;
+ } else
+ ns = (ts.tv_sec + 1) * NSEC_PER_SEC + delta_ns + delay_ns;
+
+ ts = ns_to_timespec64(ns);
+ idt82p33_timespec_to_byte_array(&ts, buf);
+
+ /*
+ * Store the new time value.
+ */
+ err = idt82p33_write(idt82p33, channel->dpll_tod_cnfg, buf, sizeof(buf));
+ if (err)
+ return err;
+
+ /* Schedule to implement the workaround in one second */
+ (void)div_s64_rem(delta_ns, NSEC_PER_SEC, &remainder);
+ if (remainder != 0)
+ schedule_delayed_work(&channel->adjtime_work, HZ);
+
+ return idt82p33_set_tod_trigger(channel, HW_TOD_TRIG_SEL_TOD_PPS, true);
+}
+
+static void idt82p33_adjtime_workaround(struct work_struct *work)
+{
+ struct idt82p33_channel *channel = container_of(work,
+ struct idt82p33_channel,
+ adjtime_work.work);
+ struct idt82p33 *idt82p33 = channel->idt82p33;
+
+ mutex_lock(idt82p33->lock);
+ /* Workaround for TOD-to-output alignment issue */
+ _idt82p33_adjtime_internal_triggered(channel, 0);
+ mutex_unlock(idt82p33->lock);
+}
+
static int _idt82p33_adjfine(struct idt82p33_channel *channel, long scaled_ppm)
{
struct idt82p33 *idt82p33 = channel->idt82p33;
@@ -233,25 +539,22 @@ static int _idt82p33_adjfine(struct idt82p33_channel *channel, long scaled_ppm)
int err, i;
s64 fcw;
- if (scaled_ppm == channel->current_freq_ppb)
- return 0;
-
/*
- * Frequency Control Word unit is: 1.68 * 10^-10 ppm
+ * Frequency Control Word unit is: 1.6861512 * 10^-10 ppm
*
* adjfreq:
- * ppb * 10^9
- * FCW = ----------
- * 168
+ * ppb * 10^14
+ * FCW = -----------
+ * 16861512
*
* adjfine:
- * scaled_ppm * 5^12
- * FCW = -------------
- * 168 * 2^4
+ * scaled_ppm * 5^12 * 10^5
+ * FCW = ------------------------
+ * 16861512 * 2^4
*/
- fcw = scaled_ppm * 244140625ULL;
- fcw = div_s64(fcw, 2688);
+ fcw = scaled_ppm * 762939453125ULL;
+ fcw = div_s64(fcw, 8430756LL);
for (i = 0; i < 5; i++) {
buf[i] = fcw & 0xff;
@@ -266,26 +569,84 @@ static int _idt82p33_adjfine(struct idt82p33_channel *channel, long scaled_ppm)
err = idt82p33_write(idt82p33, channel->dpll_freq_cnfg,
buf, sizeof(buf));
- if (err == 0)
- channel->current_freq_ppb = scaled_ppm;
-
return err;
}
+/* ppb = scaled_ppm * 125 / 2^13 */
+static s32 idt82p33_ddco_scaled_ppm(long current_ppm, s32 ddco_ppb)
+{
+ s64 scaled_ppm = div_s64(((s64)ddco_ppb << 13), 125);
+ s64 max_scaled_ppm = div_s64(((s64)DCO_MAX_PPB << 13), 125);
+
+ current_ppm += scaled_ppm;
+
+ if (current_ppm > max_scaled_ppm)
+ current_ppm = max_scaled_ppm;
+ else if (current_ppm < -max_scaled_ppm)
+ current_ppm = -max_scaled_ppm;
+
+ return (s32)current_ppm;
+}
+
+static int idt82p33_stop_ddco(struct idt82p33_channel *channel)
+{
+ int err;
+
+ err = _idt82p33_adjfine(channel, channel->current_freq);
+ if (err)
+ return err;
+
+ channel->ddco = false;
+
+ return 0;
+}
+
+static int idt82p33_start_ddco(struct idt82p33_channel *channel, s32 delta_ns)
+{
+ s32 current_ppm = channel->current_freq;
+ u32 duration_ms = MSEC_PER_SEC;
+ s32 ppb;
+ int err;
+
+ /* If the ToD correction is less than 5 nanoseconds, then skip it.
+ * The error introduced by the ToD adjustment procedure would be bigger
+ * than the required ToD correction
+ */
+ if (abs(delta_ns) < DDCO_THRESHOLD_NS)
+ return 0;
+
+ /* For most cases, keep ddco duration 1 second */
+ ppb = delta_ns;
+ while (abs(ppb) > DCO_MAX_PPB) {
+ duration_ms *= 2;
+ ppb /= 2;
+ }
+
+ err = _idt82p33_adjfine(channel,
+ idt82p33_ddco_scaled_ppm(current_ppm, ppb));
+ if (err)
+ return err;
+
+ /* schedule the worker to cancel ddco */
+ ptp_schedule_worker(channel->ptp_clock,
+ msecs_to_jiffies(duration_ms) - 1);
+ channel->ddco = true;
+
+ return 0;
+}
+
static int idt82p33_measure_one_byte_write_overhead(
struct idt82p33_channel *channel, s64 *overhead_ns)
{
struct idt82p33 *idt82p33 = channel->idt82p33;
ktime_t start, stop;
+ u8 trigger = 0;
s64 total_ns;
- u8 trigger;
int err;
u8 i;
total_ns = 0;
*overhead_ns = 0;
- trigger = TOD_TRIGGER(HW_TOD_WR_TRIG_SEL_MSB_TOD_CNFG,
- HW_TOD_RD_TRIG_SEL_LSB_TOD_STS);
for (i = 0; i < MAX_MEASURMENT_COUNT; i++) {
@@ -307,8 +668,41 @@ static int idt82p33_measure_one_byte_write_overhead(
return err;
}
+static int idt82p33_measure_one_byte_read_overhead(
+ struct idt82p33_channel *channel, s64 *overhead_ns)
+{
+ struct idt82p33 *idt82p33 = channel->idt82p33;
+ ktime_t start, stop;
+ u8 trigger = 0;
+ s64 total_ns;
+ int err;
+ u8 i;
+
+ total_ns = 0;
+ *overhead_ns = 0;
+
+ for (i = 0; i < MAX_MEASURMENT_COUNT; i++) {
+
+ start = ktime_get_raw();
+
+ err = idt82p33_read(idt82p33, channel->dpll_tod_trigger,
+ &trigger, sizeof(trigger));
+
+ stop = ktime_get_raw();
+
+ if (err)
+ return err;
+
+ total_ns += ktime_to_ns(stop) - ktime_to_ns(start);
+ }
+
+ *overhead_ns = div_s64(total_ns, MAX_MEASURMENT_COUNT);
+
+ return err;
+}
+
static int idt82p33_measure_tod_write_9_byte_overhead(
- struct idt82p33_channel *channel)
+ struct idt82p33_channel *channel)
{
struct idt82p33 *idt82p33 = channel->idt82p33;
u8 buf[TOD_BYTE_COUNT];
@@ -368,7 +762,7 @@ static int idt82p33_measure_settime_gettime_gap_overhead(
static int idt82p33_measure_tod_write_overhead(struct idt82p33_channel *channel)
{
- s64 trailing_overhead_ns, one_byte_write_ns, gap_ns;
+ s64 trailing_overhead_ns, one_byte_write_ns, gap_ns, one_byte_read_ns;
struct idt82p33 *idt82p33 = channel->idt82p33;
int err;
@@ -388,12 +782,19 @@ static int idt82p33_measure_tod_write_overhead(struct idt82p33_channel *channel)
if (err)
return err;
+ err = idt82p33_measure_one_byte_read_overhead(channel,
+ &one_byte_read_ns);
+
+ if (err)
+ return err;
+
err = idt82p33_measure_tod_write_9_byte_overhead(channel);
if (err)
return err;
- trailing_overhead_ns = gap_ns - (2 * one_byte_write_ns);
+ trailing_overhead_ns = gap_ns - 2 * one_byte_write_ns
+ - one_byte_read_ns;
idt82p33->tod_write_overhead_ns -= trailing_overhead_ns;
@@ -462,6 +863,20 @@ static int idt82p33_sync_tod(struct idt82p33_channel *channel, bool enable)
&sync_cnfg, sizeof(sync_cnfg));
}
+static long idt82p33_work_handler(struct ptp_clock_info *ptp)
+{
+ struct idt82p33_channel *channel =
+ container_of(ptp, struct idt82p33_channel, caps);
+ struct idt82p33 *idt82p33 = channel->idt82p33;
+
+ mutex_lock(idt82p33->lock);
+ (void)idt82p33_stop_ddco(channel);
+ mutex_unlock(idt82p33->lock);
+
+ /* Return a negative value here to not reschedule */
+ return -1;
+}
+
static int idt82p33_output_enable(struct idt82p33_channel *channel,
bool enable, unsigned int outn)
{
@@ -480,40 +895,10 @@ static int idt82p33_output_enable(struct idt82p33_channel *channel,
return idt82p33_write(idt82p33, OUT_MUX_CNFG(outn), &val, sizeof(val));
}
-static int idt82p33_output_mask_enable(struct idt82p33_channel *channel,
- bool enable)
-{
- u16 mask;
- int err;
- u8 outn;
-
- mask = channel->output_mask;
- outn = 0;
-
- while (mask) {
- if (mask & 0x1) {
- err = idt82p33_output_enable(channel, enable, outn);
- if (err)
- return err;
- }
-
- mask >>= 0x1;
- outn++;
- }
-
- return 0;
-}
-
static int idt82p33_perout_enable(struct idt82p33_channel *channel,
bool enable,
struct ptp_perout_request *perout)
{
- unsigned int flags = perout->flags;
-
- /* Enable/disable output based on output_mask */
- if (flags == PEROUT_ENABLE_OUTPUT_MASK)
- return idt82p33_output_mask_enable(channel, enable);
-
/* Enable/disable individual output instead */
return idt82p33_output_enable(channel, enable, perout->index);
}
@@ -546,14 +931,15 @@ static void idt82p33_ptp_clock_unregister_all(struct idt82p33 *idt82p33)
u8 i;
for (i = 0; i < MAX_PHC_PLL; i++) {
-
channel = &idt82p33->channel[i];
-
+ cancel_delayed_work_sync(&channel->adjtime_work);
if (channel->ptp_clock)
ptp_clock_unregister(channel->ptp_clock);
}
}
+
+
static int idt82p33_enable(struct ptp_clock_info *ptp,
struct ptp_clock_request *rq, int on)
{
@@ -564,7 +950,8 @@ static int idt82p33_enable(struct ptp_clock_info *ptp,
mutex_lock(idt82p33->lock);
- if (rq->type == PTP_CLK_REQ_PEROUT) {
+ switch (rq->type) {
+ case PTP_CLK_REQ_PEROUT:
if (!on)
err = idt82p33_perout_enable(channel, false,
&rq->perout);
@@ -575,6 +962,12 @@ static int idt82p33_enable(struct ptp_clock_info *ptp,
else
err = idt82p33_perout_enable(channel, true,
&rq->perout);
+ break;
+ case PTP_CLK_REQ_EXTTS:
+ err = idt82p33_extts_enable(channel, rq, on);
+ break;
+ default:
+ break;
}
mutex_unlock(idt82p33->lock);
@@ -634,13 +1027,22 @@ static int idt82p33_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
struct idt82p33 *idt82p33 = channel->idt82p33;
int err;
+ if (channel->ddco == true)
+ return 0;
+
+ if (scaled_ppm == channel->current_freq)
+ return 0;
+
mutex_lock(idt82p33->lock);
err = _idt82p33_adjfine(channel, scaled_ppm);
+
+ if (err == 0)
+ channel->current_freq = scaled_ppm;
mutex_unlock(idt82p33->lock);
+
if (err)
dev_err(idt82p33->dev,
"Failed in %s with err %d!\n", __func__, err);
-
return err;
}
@@ -651,14 +1053,21 @@ static int idt82p33_adjtime(struct ptp_clock_info *ptp, s64 delta_ns)
struct idt82p33 *idt82p33 = channel->idt82p33;
int err;
+ if (channel->ddco == true)
+ return -EBUSY;
+
mutex_lock(idt82p33->lock);
if (abs(delta_ns) < phase_snap_threshold) {
+ err = idt82p33_start_ddco(channel, delta_ns);
mutex_unlock(idt82p33->lock);
- return 0;
+ return err;
}
- err = _idt82p33_adjtime(channel, delta_ns);
+ /* Use more accurate internal 1pps triggered write first */
+ err = _idt82p33_adjtime_internal_triggered(channel, delta_ns);
+ if (err && delta_ns > IMMEDIATE_SNAP_THRESHOLD_NS)
+ err = _idt82p33_adjtime_immediate(channel, delta_ns);
mutex_unlock(idt82p33->lock);
@@ -703,8 +1112,10 @@ static int idt82p33_settime(struct ptp_clock_info *ptp,
return err;
}
-static int idt82p33_channel_init(struct idt82p33_channel *channel, int index)
+static int idt82p33_channel_init(struct idt82p33 *idt82p33, u32 index)
{
+ struct idt82p33_channel *channel = &idt82p33->channel[index];
+
switch (index) {
case 0:
channel->dpll_tod_cnfg = DPLL1_TOD_CNFG;
@@ -730,22 +1141,60 @@ static int idt82p33_channel_init(struct idt82p33_channel *channel, int index)
return -EINVAL;
}
- channel->current_freq_ppb = 0;
+ channel->plln = index;
+ channel->current_freq = 0;
+ channel->idt82p33 = idt82p33;
+ INIT_DELAYED_WORK(&channel->adjtime_work, idt82p33_adjtime_workaround);
return 0;
}
-static void idt82p33_caps_init(struct ptp_clock_info *caps)
+static int idt82p33_verify_pin(struct ptp_clock_info *ptp, unsigned int pin,
+ enum ptp_pin_function func, unsigned int chan)
{
+ switch (func) {
+ case PTP_PF_NONE:
+ case PTP_PF_EXTTS:
+ break;
+ case PTP_PF_PEROUT:
+ case PTP_PF_PHYSYNC:
+ return -1;
+ }
+ return 0;
+}
+
+static void idt82p33_caps_init(u32 index, struct ptp_clock_info *caps,
+ struct ptp_pin_desc *pin_cfg, u8 max_pins)
+{
+ struct ptp_pin_desc *ppd;
+ int i;
+
caps->owner = THIS_MODULE;
caps->max_adj = DCO_MAX_PPB;
- caps->n_per_out = 11;
- caps->adjphase = idt82p33_adjwritephase;
+ caps->n_per_out = MAX_PER_OUT;
+ caps->n_ext_ts = MAX_PHC_PLL,
+ caps->n_pins = max_pins,
+ caps->adjphase = idt82p33_adjwritephase,
caps->adjfine = idt82p33_adjfine;
caps->adjtime = idt82p33_adjtime;
caps->gettime64 = idt82p33_gettime;
caps->settime64 = idt82p33_settime;
caps->enable = idt82p33_enable;
+ caps->verify = idt82p33_verify_pin;
+ caps->do_aux_work = idt82p33_work_handler;
+
+ snprintf(caps->name, sizeof(caps->name), "IDT 82P33 PLL%u", index);
+
+ caps->pin_config = pin_cfg;
+
+ for (i = 0; i < max_pins; ++i) {
+ ppd = &pin_cfg[i];
+
+ ppd->index = i;
+ ppd->func = PTP_PF_NONE;
+ ppd->chan = index;
+ snprintf(ppd->name, sizeof(ppd->name), "in%d", 12 + i);
+ }
}
static int idt82p33_enable_channel(struct idt82p33 *idt82p33, u32 index)
@@ -758,7 +1207,7 @@ static int idt82p33_enable_channel(struct idt82p33 *idt82p33, u32 index)
channel = &idt82p33->channel[index];
- err = idt82p33_channel_init(channel, index);
+ err = idt82p33_channel_init(idt82p33, index);
if (err) {
dev_err(idt82p33->dev,
"Channel_init failed in %s with err %d!\n",
@@ -766,11 +1215,8 @@ static int idt82p33_enable_channel(struct idt82p33 *idt82p33, u32 index)
return err;
}
- channel->idt82p33 = idt82p33;
-
- idt82p33_caps_init(&channel->caps);
- snprintf(channel->caps.name, sizeof(channel->caps.name),
- "IDT 82P33 PLL%u", index);
+ idt82p33_caps_init(index, &channel->caps,
+ pin_config[index], MAX_TRIG_CLK);
channel->ptp_clock = ptp_clock_register(&channel->caps, NULL);
@@ -805,17 +1251,46 @@ static int idt82p33_enable_channel(struct idt82p33 *idt82p33, u32 index)
return 0;
}
+static int idt82p33_reset(struct idt82p33 *idt82p33, bool cold)
+{
+ int err;
+ u8 cfg = SOFT_RESET_EN;
+
+ if (cold == true)
+ goto cold_reset;
+
+ err = idt82p33_read(idt82p33, REG_SOFT_RESET, &cfg, sizeof(cfg));
+ if (err) {
+ dev_err(idt82p33->dev,
+ "Soft reset failed with err %d!\n", err);
+ return err;
+ }
+
+ cfg |= SOFT_RESET_EN;
+
+cold_reset:
+ err = idt82p33_write(idt82p33, REG_SOFT_RESET, &cfg, sizeof(cfg));
+ if (err)
+ dev_err(idt82p33->dev,
+ "Cold reset failed with err %d!\n", err);
+ return err;
+}
+
static int idt82p33_load_firmware(struct idt82p33 *idt82p33)
{
+ char fname[128] = FW_FILENAME;
const struct firmware *fw;
struct idt82p33_fwrc *rec;
u8 loaddr, page, val;
int err;
s32 len;
- dev_dbg(idt82p33->dev, "requesting firmware '%s'\n", FW_FILENAME);
+ if (firmware) /* module parameter */
+ snprintf(fname, sizeof(fname), "%s", firmware);
- err = request_firmware(&fw, FW_FILENAME, idt82p33->dev);
+ dev_info(idt82p33->dev, "requesting firmware '%s'\n", fname);
+
+ err = request_firmware(&fw, fname, idt82p33->dev);
if (err) {
dev_err(idt82p33->dev,
@@ -863,6 +1338,46 @@ out:
return err;
}
+static void idt82p33_extts_check(struct work_struct *work)
+{
+ struct idt82p33 *idt82p33 = container_of(work, struct idt82p33,
+ extts_work.work);
+ struct idt82p33_channel *channel;
+ int err;
+ u8 mask;
+ int i;
+
+ if (idt82p33->extts_mask == 0)
+ return;
+
+ mutex_lock(idt82p33->lock);
+
+ for (i = 0; i < MAX_PHC_PLL; i++) {
+ mask = 1 << i;
+
+ if ((idt82p33->extts_mask & mask) == 0)
+ continue;
+
+ err = idt82p33_extts_check_channel(idt82p33, i);
+
+ if (err == 0) {
+ /* trigger clears itself, so clear the mask */
+ if (idt82p33->extts_single_shot) {
+ idt82p33->extts_mask &= ~mask;
+ } else {
+ /* Re-arm */
+ channel = &idt82p33->channel[i];
+ arm_tod_read_with_trigger(channel, channel->tod_trigger);
+ }
+ }
+ }
+
+ if (idt82p33->extts_mask)
+ schedule_delayed_work(&idt82p33->extts_work,
+ msecs_to_jiffies(EXTTS_PERIOD_MS));
+
+ mutex_unlock(idt82p33->lock);
+}
static int idt82p33_probe(struct platform_device *pdev)
{
@@ -885,25 +1400,33 @@ static int idt82p33_probe(struct platform_device *pdev)
idt82p33->pll_mask = DEFAULT_PLL_MASK;
idt82p33->channel[0].output_mask = DEFAULT_OUTPUT_MASK_PLL0;
idt82p33->channel[1].output_mask = DEFAULT_OUTPUT_MASK_PLL1;
+ idt82p33->extts_mask = 0;
+ INIT_DELAYED_WORK(&idt82p33->extts_work, idt82p33_extts_check);
mutex_lock(idt82p33->lock);
- err = idt82p33_load_firmware(idt82p33);
+ /* cold reset before loading firmware */
+ idt82p33_reset(idt82p33, true);
+ err = idt82p33_load_firmware(idt82p33);
if (err)
dev_warn(idt82p33->dev,
"loading firmware failed with %d\n", err);
+ /* soft reset after loading firmware */
+ idt82p33_reset(idt82p33, false);
+
if (idt82p33->pll_mask) {
for (i = 0; i < MAX_PHC_PLL; i++) {
- if (idt82p33->pll_mask & (1 << i)) {
+ if (idt82p33->pll_mask & (1 << i))
err = idt82p33_enable_channel(idt82p33, i);
- if (err) {
- dev_err(idt82p33->dev,
- "Failed in %s with err %d!\n",
- __func__, err);
- break;
- }
+ else
+ err = idt82p33_channel_init(idt82p33, i);
+ if (err) {
+ dev_err(idt82p33->dev,
+ "Failed in %s with err %d!\n",
+ __func__, err);
+ break;
}
}
} else {
@@ -928,6 +1451,8 @@ static int idt82p33_remove(struct platform_device *pdev)
{
struct idt82p33 *idt82p33 = platform_get_drvdata(pdev);
+ cancel_delayed_work_sync(&idt82p33->extts_work);
+
idt82p33_ptp_clock_unregister_all(idt82p33);
return 0;
diff --git a/drivers/ptp/ptp_idt82p33.h b/drivers/ptp/ptp_idt82p33.h
index 0ea1c35c0f9f..8fcb0b17d207 100644
--- a/drivers/ptp/ptp_idt82p33.h
+++ b/drivers/ptp/ptp_idt82p33.h
@@ -13,6 +13,8 @@
#define FW_FILENAME "idt82p33xxx.bin"
#define MAX_PHC_PLL (2)
+#define MAX_TRIG_CLK (3)
+#define MAX_PER_OUT (11)
#define TOD_BYTE_COUNT (10)
#define DCO_MAX_PPB (92000)
#define MAX_MEASURMENT_COUNT (5)
@@ -20,7 +22,6 @@
#define IMMEDIATE_SNAP_THRESHOLD_NS (50000)
#define DDCO_THRESHOLD_NS (5)
#define IDT82P33_MAX_WRITE_COUNT (512)
-#define PEROUT_ENABLE_OUTPUT_MASK (0xdeadbeef)
#define PLLMASK_ADDR_HI 0xFF
#define PLLMASK_ADDR_LO 0xA5
@@ -60,8 +61,18 @@ struct idt82p33_channel {
struct ptp_clock *ptp_clock;
struct idt82p33 *idt82p33;
enum pll_mode pll_mode;
- s32 current_freq_ppb;
+ /* Workaround for TOD-to-output alignment issue */
+ struct delayed_work adjtime_work;
+ s32 current_freq;
+ /* double dco mode */
+ bool ddco;
u8 output_mask;
+ /* last input trigger for extts */
+ u8 tod_trigger;
+ bool discard_next_extts;
+ u8 plln;
+ /* remember last tod_sts for extts */
+ u8 extts_tod_sts[TOD_BYTE_COUNT];
u16 dpll_tod_cnfg;
u16 dpll_tod_trigger;
u16 dpll_tod_sts;
@@ -76,6 +87,12 @@ struct idt82p33 {
struct idt82p33_channel channel[MAX_PHC_PLL];
struct device *dev;
u8 pll_mask;
+ /* Polls for external time stamps */
+ u8 extts_mask;
+ bool extts_single_shot;
+ struct delayed_work extts_work;
+ /* Remember the ptp channel to report extts */
+ struct idt82p33_channel *event_channel[MAX_PHC_PLL];
/* Mutex to protect operations from being interrupted */
struct mutex *lock;
struct regmap *regmap;
diff --git a/drivers/ptp/ptp_kvm_common.c b/drivers/ptp/ptp_kvm_common.c
index fcae32f56f25..9141162c4237 100644
--- a/drivers/ptp/ptp_kvm_common.c
+++ b/drivers/ptp/ptp_kvm_common.c
@@ -66,7 +66,7 @@ static int ptp_kvm_getcrosststamp(struct ptp_clock_info *ptp,
* PTP clock operations
*/
-static int ptp_kvm_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
+static int ptp_kvm_adjfine(struct ptp_clock_info *ptp, long delta)
{
return -EOPNOTSUPP;
}
@@ -115,7 +115,7 @@ static const struct ptp_clock_info ptp_kvm_caps = {
.n_ext_ts = 0,
.n_pins = 0,
.pps = 0,
- .adjfreq = ptp_kvm_adjfreq,
+ .adjfine = ptp_kvm_adjfine,
.adjtime = ptp_kvm_adjtime,
.gettime64 = ptp_kvm_gettime,
.settime64 = ptp_kvm_settime,
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index a48d9b7d2921..4bbaccd543ad 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -13,9 +13,11 @@
#include <linux/clk-provider.h>
#include <linux/platform_device.h>
#include <linux/platform_data/i2c-xiic.h>
+#include <linux/platform_data/i2c-ocores.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/spi/spi.h>
#include <linux/spi/xilinx_spi.h>
+#include <linux/spi/altera.h>
#include <net/devlink.h>
#include <linux/i2c.h>
#include <linux/mtd/mtd.h>
@@ -28,6 +30,9 @@
#define PCI_VENDOR_ID_CELESTICA 0x18d4
#define PCI_DEVICE_ID_CELESTICA_TIMECARD 0x1008
+#define PCI_VENDOR_ID_OROLIA 0x1ad7
+#define PCI_DEVICE_ID_OROLIA_ARTCARD 0xa000
+
static struct class timecard_class = {
.owner = THIS_MODULE,
.name = "timecard",
@@ -203,6 +208,11 @@ struct frequency_reg {
u32 ctrl;
u32 status;
};
+
+struct board_config_reg {
+ u32 mro50_serial_activate;
+};
+
#define FREQ_STATUS_VALID BIT(31)
#define FREQ_STATUS_ERROR BIT(30)
#define FREQ_STATUS_OVERRUN BIT(29)
@@ -278,6 +288,11 @@ struct ptp_ocp_signal {
bool running;
};
+struct ptp_ocp_serial_port {
+ int line;
+ int baud;
+};
+
#define OCP_BOARD_ID_LEN 13
#define OCP_SERIAL_LEN 6
@@ -289,6 +304,7 @@ struct ptp_ocp {
struct tod_reg __iomem *tod;
struct pps_reg __iomem *pps_to_ext;
struct pps_reg __iomem *pps_to_clk;
+ struct board_config_reg __iomem *board_config;
struct gpio_reg __iomem *pps_select;
struct gpio_reg __iomem *sma_map1;
struct gpio_reg __iomem *sma_map2;
@@ -305,6 +321,7 @@ struct ptp_ocp {
struct ptp_ocp_ext_src *ts2;
struct ptp_ocp_ext_src *ts3;
struct ptp_ocp_ext_src *ts4;
+ struct ocp_art_gpio_reg __iomem *art_sma;
struct img_reg __iomem *image;
struct ptp_clock *ptp;
struct ptp_clock_info ptp_info;
@@ -318,10 +335,10 @@ struct ptp_ocp {
time64_t gnss_lost;
int id;
int n_irqs;
- int gnss_port;
- int gnss2_port;
- int mac_port; /* miniature atomic clock */
- int nmea_port;
+ struct ptp_ocp_serial_port gnss_port;
+ struct ptp_ocp_serial_port gnss2_port;
+ struct ptp_ocp_serial_port mac_port; /* miniature atomic clock */
+ struct ptp_ocp_serial_port nmea_port;
bool fw_loader;
u8 fw_tag;
u16 fw_version;
@@ -365,8 +382,12 @@ static int ptp_ocp_signal_from_perout(struct ptp_ocp *bp, int gen,
static int ptp_ocp_signal_enable(void *priv, u32 req, bool enable);
static int ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr);
+static int ptp_ocp_art_board_init(struct ptp_ocp *bp, struct ocp_resource *r);
+
static const struct ocp_attr_group fb_timecard_groups[];
+static const struct ocp_attr_group art_timecard_groups[];
+
struct ptp_ocp_eeprom_map {
u16 off;
u16 len;
@@ -389,6 +410,12 @@ static struct ptp_ocp_eeprom_map fb_eeprom_map[] = {
{ }
};
+static struct ptp_ocp_eeprom_map art_eeprom_map[] = {
+ { EEPROM_ENTRY(0x200 + 0x43, board_id) },
+ { EEPROM_ENTRY(0x200 + 0x63, serial) },
+ { }
+};
+
#define bp_assign_entry(bp, res, val) ({ \
uintptr_t addr = (uintptr_t)(bp) + (res)->bp_offset; \
*(typeof(val) *)addr = val; \
@@ -430,6 +457,13 @@ static struct ptp_ocp_eeprom_map fb_eeprom_map[] = {
* 14: Signal Generator 4
* 15: TS3
* 16: TS4
+ --
+ * 8: Orolia TS1
+ * 10: Orolia TS2
+ * 11: Orolia TS0 (GNSS)
+ * 12: Orolia PPS
+ * 14: Orolia TS3
+ * 15: Orolia TS4
*/
static struct ocp_resource ocp_fb_resource[] = {
@@ -596,14 +630,23 @@ static struct ocp_resource ocp_fb_resource[] = {
{
OCP_SERIAL_RESOURCE(gnss_port),
.offset = 0x00160000 + 0x1000, .irq_vec = 3,
+ .extra = &(struct ptp_ocp_serial_port) {
+ .baud = 115200,
+ },
},
{
OCP_SERIAL_RESOURCE(gnss2_port),
.offset = 0x00170000 + 0x1000, .irq_vec = 4,
+ .extra = &(struct ptp_ocp_serial_port) {
+ .baud = 115200,
+ },
},
{
OCP_SERIAL_RESOURCE(mac_port),
.offset = 0x00180000 + 0x1000, .irq_vec = 5,
+ .extra = &(struct ptp_ocp_serial_port) {
+ .baud = 57600,
+ },
},
{
OCP_SERIAL_RESOURCE(nmea_port),
@@ -647,9 +690,141 @@ static struct ocp_resource ocp_fb_resource[] = {
{ }
};
+#define OCP_ART_CONFIG_SIZE 144
+#define OCP_ART_TEMP_TABLE_SIZE 368
+
+struct ocp_art_gpio_reg {
+ struct {
+ u32 gpio;
+ u32 __pad[3];
+ } map[4];
+};
+
+static struct ocp_resource ocp_art_resource[] = {
+ {
+ OCP_MEM_RESOURCE(reg),
+ .offset = 0x01000000, .size = 0x10000,
+ },
+ {
+ OCP_SERIAL_RESOURCE(gnss_port),
+ .offset = 0x00160000 + 0x1000, .irq_vec = 3,
+ .extra = &(struct ptp_ocp_serial_port) {
+ .baud = 115200,
+ },
+ },
+ {
+ OCP_MEM_RESOURCE(art_sma),
+ .offset = 0x003C0000, .size = 0x1000,
+ },
+ /* Timestamp associated with GNSS1 receiver PPS */
+ {
+ OCP_EXT_RESOURCE(ts0),
+ .offset = 0x360000, .size = 0x20, .irq_vec = 12,
+ .extra = &(struct ptp_ocp_ext_info) {
+ .index = 0,
+ .irq_fcn = ptp_ocp_ts_irq,
+ .enable = ptp_ocp_ts_enable,
+ },
+ },
+ {
+ OCP_EXT_RESOURCE(ts1),
+ .offset = 0x380000, .size = 0x20, .irq_vec = 8,
+ .extra = &(struct ptp_ocp_ext_info) {
+ .index = 1,
+ .irq_fcn = ptp_ocp_ts_irq,
+ .enable = ptp_ocp_ts_enable,
+ },
+ },
+ {
+ OCP_EXT_RESOURCE(ts2),
+ .offset = 0x390000, .size = 0x20, .irq_vec = 10,
+ .extra = &(struct ptp_ocp_ext_info) {
+ .index = 2,
+ .irq_fcn = ptp_ocp_ts_irq,
+ .enable = ptp_ocp_ts_enable,
+ },
+ },
+ {
+ OCP_EXT_RESOURCE(ts3),
+ .offset = 0x3A0000, .size = 0x20, .irq_vec = 14,
+ .extra = &(struct ptp_ocp_ext_info) {
+ .index = 3,
+ .irq_fcn = ptp_ocp_ts_irq,
+ .enable = ptp_ocp_ts_enable,
+ },
+ },
+ {
+ OCP_EXT_RESOURCE(ts4),
+ .offset = 0x3B0000, .size = 0x20, .irq_vec = 15,
+ .extra = &(struct ptp_ocp_ext_info) {
+ .index = 4,
+ .irq_fcn = ptp_ocp_ts_irq,
+ .enable = ptp_ocp_ts_enable,
+ },
+ },
+ /* Timestamp associated with Internal PPS of the card */
+ {
+ OCP_EXT_RESOURCE(pps),
+ .offset = 0x00330000, .size = 0x20, .irq_vec = 11,
+ .extra = &(struct ptp_ocp_ext_info) {
+ .index = 5,
+ .irq_fcn = ptp_ocp_ts_irq,
+ .enable = ptp_ocp_ts_enable,
+ },
+ },
+ {
+ OCP_SPI_RESOURCE(spi_flash),
+ .offset = 0x00310000, .size = 0x10000, .irq_vec = 9,
+ .extra = &(struct ptp_ocp_flash_info) {
+ .name = "spi_altera", .pci_offset = 0,
+ .data_size = sizeof(struct altera_spi_platform_data),
+ .data = &(struct altera_spi_platform_data) {
+ .num_chipselect = 1,
+ .num_devices = 1,
+ .devices = &(struct spi_board_info) {
+ .modalias = "spi-nor",
+ },
+ },
+ },
+ },
+ {
+ OCP_I2C_RESOURCE(i2c_ctrl),
+ .offset = 0x350000, .size = 0x100, .irq_vec = 4,
+ .extra = &(struct ptp_ocp_i2c_info) {
+ .name = "ocores-i2c",
+ .fixed_rate = 400000,
+ .data_size = sizeof(struct ocores_i2c_platform_data),
+ .data = &(struct ocores_i2c_platform_data) {
+ .clock_khz = 125000,
+ .bus_khz = 400,
+ .num_devices = 1,
+ .devices = &(struct i2c_board_info) {
+ I2C_BOARD_INFO("24c08", 0x50),
+ },
+ },
+ },
+ },
+ {
+ OCP_SERIAL_RESOURCE(mac_port),
+ .offset = 0x00190000, .irq_vec = 7,
+ .extra = &(struct ptp_ocp_serial_port) {
+ .baud = 9600,
+ },
+ },
+ {
+ OCP_MEM_RESOURCE(board_config),
+ .offset = 0x210000, .size = 0x1000,
+ },
+ {
+ .setup = ptp_ocp_art_board_init,
+ },
+ { }
+};
+
static const struct pci_device_id ptp_ocp_pcidev_id[] = {
{ PCI_DEVICE_DATA(FACEBOOK, TIMECARD, &ocp_fb_resource) },
{ PCI_DEVICE_DATA(CELESTICA, TIMECARD, &ocp_fb_resource) },
+ { PCI_DEVICE_DATA(OROLIA, ARTCARD, &ocp_art_resource) },
{ }
};
MODULE_DEVICE_TABLE(pci, ptp_ocp_pcidev_id);
@@ -714,6 +889,19 @@ static const struct ocp_selector ptp_ocp_sma_out[] = {
{ }
};
+static const struct ocp_selector ptp_ocp_art_sma_in[] = {
+ { .name = "PPS1", .value = 0x0001 },
+ { .name = "10Mhz", .value = 0x0008 },
+ { }
+};
+
+static const struct ocp_selector ptp_ocp_art_sma_out[] = {
+ { .name = "PHC", .value = 0x0002 },
+ { .name = "GNSS", .value = 0x0004 },
+ { .name = "10Mhz", .value = 0x0010 },
+ { }
+};
+
struct ocp_sma_op {
const struct ocp_selector *tbl[2];
void (*init)(struct ptp_ocp *bp);
@@ -1342,11 +1530,9 @@ ptp_ocp_devlink_fw_image(struct devlink *devlink, const struct firmware *fw,
hdr = (const struct ptp_ocp_firmware_header *)fw->data;
if (memcmp(hdr->magic, OCP_FIRMWARE_MAGIC_HEADER, 4)) {
devlink_flash_update_status_notify(devlink,
- "No firmware header found, flashing raw image",
+ "No firmware header found, cancel firmware upgrade",
NULL, 0, 0);
- offset = 0;
- length = fw->size;
- goto out;
+ return -EINVAL;
}
if (be16_to_cpu(hdr->pci_vendor_id) != bp->pdev->vendor ||
@@ -1374,7 +1560,6 @@ ptp_ocp_devlink_fw_image(struct devlink *devlink, const struct firmware *fw,
return -EINVAL;
}
-out:
*data = &fw->data[offset];
*size = length;
@@ -1462,10 +1647,6 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
char buf[32];
int err;
- err = devlink_info_driver_name_put(req, KBUILD_MODNAME);
- if (err)
- return err;
-
fw_image = bp->fw_loader ? "loader" : "fw";
sprintf(buf, "%d.%d", bp->fw_tag, bp->fw_version);
err = devlink_info_version_running_put(req, fw_image, buf);
@@ -1872,11 +2053,15 @@ ptp_ocp_serial_line(struct ptp_ocp *bp, struct ocp_resource *r)
static int
ptp_ocp_register_serial(struct ptp_ocp *bp, struct ocp_resource *r)
{
- int port;
+ struct ptp_ocp_serial_port *p = (struct ptp_ocp_serial_port *)r->extra;
+ struct ptp_ocp_serial_port port = {};
+
+ port.line = ptp_ocp_serial_line(bp, r);
+ if (port.line < 0)
+ return port.line;
- port = ptp_ocp_serial_line(bp, r);
- if (port < 0)
- return port;
+ if (p)
+ port.baud = p->baud;
bp_assign_entry(bp, r, port);
@@ -2257,6 +2442,121 @@ ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data)
return err;
}
+static void
+ptp_ocp_art_sma_init(struct ptp_ocp *bp)
+{
+ u32 reg;
+ int i;
+
+ /* defaults */
+ bp->sma[0].mode = SMA_MODE_IN;
+ bp->sma[1].mode = SMA_MODE_IN;
+ bp->sma[2].mode = SMA_MODE_OUT;
+ bp->sma[3].mode = SMA_MODE_OUT;
+
+ bp->sma[0].default_fcn = 0x08; /* IN: 10Mhz */
+ bp->sma[1].default_fcn = 0x01; /* IN: PPS1 */
+ bp->sma[2].default_fcn = 0x10; /* OUT: 10Mhz */
+ bp->sma[3].default_fcn = 0x02; /* OUT: PHC */
+
+ /* If no SMA map, the pin functions and directions are fixed. */
+ if (!bp->art_sma) {
+ for (i = 0; i < 4; i++) {
+ bp->sma[i].fixed_fcn = true;
+ bp->sma[i].fixed_dir = true;
+ }
+ return;
+ }
+
+ for (i = 0; i < 4; i++) {
+ reg = ioread32(&bp->art_sma->map[i].gpio);
+
+ switch (reg & 0xff) {
+ case 0:
+ bp->sma[i].fixed_fcn = true;
+ bp->sma[i].fixed_dir = true;
+ break;
+ case 1:
+ case 8:
+ bp->sma[i].mode = SMA_MODE_IN;
+ break;
+ default:
+ bp->sma[i].mode = SMA_MODE_OUT;
+ break;
+ }
+ }
+}
+
+static u32
+ptp_ocp_art_sma_get(struct ptp_ocp *bp, int sma_nr)
+{
+ if (bp->sma[sma_nr - 1].fixed_fcn)
+ return bp->sma[sma_nr - 1].default_fcn;
+
+ return ioread32(&bp->art_sma->map[sma_nr - 1].gpio) & 0xff;
+}
+
+/* note: store 0 is considered invalid. */
+static int
+ptp_ocp_art_sma_set(struct ptp_ocp *bp, int sma_nr, u32 val)
+{
+ unsigned long flags;
+ u32 __iomem *gpio;
+ int err = 0;
+ u32 reg;
+
+ val &= SMA_SELECT_MASK;
+ if (hweight32(val) > 1)
+ return -EINVAL;
+
+ gpio = &bp->art_sma->map[sma_nr - 1].gpio;
+
+ spin_lock_irqsave(&bp->lock, flags);
+ reg = ioread32(gpio);
+ if (((reg >> 16) & val) == 0) {
+ err = -EOPNOTSUPP;
+ } else {
+ reg = (reg & 0xff00) | (val & 0xff);
+ iowrite32(reg, gpio);
+ }
+ spin_unlock_irqrestore(&bp->lock, flags);
+
+ return err;
+}
+
+static const struct ocp_sma_op ocp_art_sma_op = {
+ .tbl = { ptp_ocp_art_sma_in, ptp_ocp_art_sma_out },
+ .init = ptp_ocp_art_sma_init,
+ .get = ptp_ocp_art_sma_get,
+ .set_inputs = ptp_ocp_art_sma_set,
+ .set_output = ptp_ocp_art_sma_set,
+};
+
+/* ART specific board initializers; last "resource" registered. */
+static int
+ptp_ocp_art_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
+{
+ int err;
+
+ bp->flash_start = 0x1000000;
+ bp->eeprom_map = art_eeprom_map;
+ bp->fw_cap = OCP_CAP_BASIC;
+ bp->fw_version = ioread32(&bp->reg->version);
+ bp->fw_tag = 2;
+ bp->sma_op = &ocp_art_sma_op;
+
+ /* Enable MAC serial port during initialisation */
+ iowrite32(1, &bp->board_config->mro50_serial_activate);
+
+ ptp_ocp_sma_init(bp);
+
+ err = ptp_ocp_attr_group_add(bp, art_timecard_groups);
+ if (err)
+ return err;
+
+ return ptp_ocp_init_clock(bp);
+}
+
static ssize_t
ptp_ocp_show_output(const struct ocp_selector *tbl, u32 val, char *buf,
int def_val)
@@ -3030,6 +3330,130 @@ DEVICE_FREQ_GROUP(freq2, 1);
DEVICE_FREQ_GROUP(freq3, 2);
DEVICE_FREQ_GROUP(freq4, 3);
+static ssize_t
+disciplining_config_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
+ size_t size = OCP_ART_CONFIG_SIZE;
+ struct nvmem_device *nvmem;
+ ssize_t err;
+
+ nvmem = ptp_ocp_nvmem_device_get(bp, NULL);
+ if (IS_ERR(nvmem))
+ return PTR_ERR(nvmem);
+
+ if (off > size) {
+ err = 0;
+ goto out;
+ }
+
+ if (off + count > size)
+ count = size - off;
+
+ // the configuration is in the very beginning of the EEPROM
+ err = nvmem_device_read(nvmem, off, count, buf);
+ if (err != count) {
+ err = -EFAULT;
+ goto out;
+ }
+
+out:
+ ptp_ocp_nvmem_device_put(&nvmem);
+
+ return err;
+}
+
+static ssize_t
+disciplining_config_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
+ struct nvmem_device *nvmem;
+ ssize_t err;
+
+ /* Allow write of the whole area only */
+ if (off || count != OCP_ART_CONFIG_SIZE)
+ return -EFAULT;
+
+ nvmem = ptp_ocp_nvmem_device_get(bp, NULL);
+ if (IS_ERR(nvmem))
+ return PTR_ERR(nvmem);
+
+ err = nvmem_device_write(nvmem, 0x00, count, buf);
+ if (err != count)
+ err = -EFAULT;
+
+ ptp_ocp_nvmem_device_put(&nvmem);
+
+ return err;
+}
+static BIN_ATTR_RW(disciplining_config, OCP_ART_CONFIG_SIZE);
+
+static ssize_t
+temperature_table_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
+ size_t size = OCP_ART_TEMP_TABLE_SIZE;
+ struct nvmem_device *nvmem;
+ ssize_t err;
+
+ nvmem = ptp_ocp_nvmem_device_get(bp, NULL);
+ if (IS_ERR(nvmem))
+ return PTR_ERR(nvmem);
+
+ if (off > size) {
+ err = 0;
+ goto out;
+ }
+
+ if (off + count > size)
+ count = size - off;
+
+ // the configuration is in the very beginning of the EEPROM
+ err = nvmem_device_read(nvmem, 0x90 + off, count, buf);
+ if (err != count) {
+ err = -EFAULT;
+ goto out;
+ }
+
+out:
+ ptp_ocp_nvmem_device_put(&nvmem);
+
+ return err;
+}
+
+static ssize_t
+temperature_table_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
+ struct nvmem_device *nvmem;
+ ssize_t err;
+
+ /* Allow write of the whole area only */
+ if (off || count != OCP_ART_TEMP_TABLE_SIZE)
+ return -EFAULT;
+
+ nvmem = ptp_ocp_nvmem_device_get(bp, NULL);
+ if (IS_ERR(nvmem))
+ return PTR_ERR(nvmem);
+
+ err = nvmem_device_write(nvmem, 0x90, count, buf);
+ if (err != count)
+ err = -EFAULT;
+
+ ptp_ocp_nvmem_device_put(&nvmem);
+
+ return err;
+}
+static BIN_ATTR_RW(temperature_table, OCP_ART_TEMP_TABLE_SIZE);
+
static struct attribute *fb_timecard_attrs[] = {
&dev_attr_serialnum.attr,
&dev_attr_gnss_sync.attr,
@@ -3049,9 +3473,11 @@ static struct attribute *fb_timecard_attrs[] = {
&dev_attr_tod_correction.attr,
NULL,
};
+
static const struct attribute_group fb_timecard_group = {
.attrs = fb_timecard_attrs,
};
+
static const struct ocp_attr_group fb_timecard_groups[] = {
{ .cap = OCP_CAP_BASIC, .group = &fb_timecard_group },
{ .cap = OCP_CAP_SIGNAL, .group = &fb_timecard_signal0_group },
@@ -3065,6 +3491,37 @@ static const struct ocp_attr_group fb_timecard_groups[] = {
{ },
};
+static struct attribute *art_timecard_attrs[] = {
+ &dev_attr_serialnum.attr,
+ &dev_attr_clock_source.attr,
+ &dev_attr_available_clock_sources.attr,
+ &dev_attr_utc_tai_offset.attr,
+ &dev_attr_ts_window_adjust.attr,
+ &dev_attr_sma1.attr,
+ &dev_attr_sma2.attr,
+ &dev_attr_sma3.attr,
+ &dev_attr_sma4.attr,
+ &dev_attr_available_sma_inputs.attr,
+ &dev_attr_available_sma_outputs.attr,
+ NULL,
+};
+
+static struct bin_attribute *bin_art_timecard_attrs[] = {
+ &bin_attr_disciplining_config,
+ &bin_attr_temperature_table,
+ NULL,
+};
+
+static const struct attribute_group art_timecard_group = {
+ .attrs = art_timecard_attrs,
+ .bin_attrs = bin_art_timecard_attrs,
+};
+
+static const struct ocp_attr_group art_timecard_groups[] = {
+ { .cap = OCP_CAP_BASIC, .group = &art_timecard_group },
+ { },
+};
+
static void
gpio_input_map(char *buf, struct ptp_ocp *bp, u16 map[][2], u16 bit,
const char *def)
@@ -3177,14 +3634,16 @@ ptp_ocp_summary_show(struct seq_file *s, void *data)
bp = dev_get_drvdata(dev);
seq_printf(s, "%7s: /dev/ptp%d\n", "PTP", ptp_clock_index(bp->ptp));
- if (bp->gnss_port != -1)
- seq_printf(s, "%7s: /dev/ttyS%d\n", "GNSS1", bp->gnss_port);
- if (bp->gnss2_port != -1)
- seq_printf(s, "%7s: /dev/ttyS%d\n", "GNSS2", bp->gnss2_port);
- if (bp->mac_port != -1)
- seq_printf(s, "%7s: /dev/ttyS%d\n", "MAC", bp->mac_port);
- if (bp->nmea_port != -1)
- seq_printf(s, "%7s: /dev/ttyS%d\n", "NMEA", bp->nmea_port);
+ if (bp->gnss_port.line != -1)
+ seq_printf(s, "%7s: /dev/ttyS%d\n", "GNSS1",
+ bp->gnss_port.line);
+ if (bp->gnss2_port.line != -1)
+ seq_printf(s, "%7s: /dev/ttyS%d\n", "GNSS2",
+ bp->gnss2_port.line);
+ if (bp->mac_port.line != -1)
+ seq_printf(s, "%7s: /dev/ttyS%d\n", "MAC", bp->mac_port.line);
+ if (bp->nmea_port.line != -1)
+ seq_printf(s, "%7s: /dev/ttyS%d\n", "NMEA", bp->nmea_port.line);
memset(sma_val, 0xff, sizeof(sma_val));
if (bp->sma_map1) {
@@ -3508,10 +3967,10 @@ ptp_ocp_device_init(struct ptp_ocp *bp, struct pci_dev *pdev)
bp->ptp_info = ptp_ocp_clock_info;
spin_lock_init(&bp->lock);
- bp->gnss_port = -1;
- bp->gnss2_port = -1;
- bp->mac_port = -1;
- bp->nmea_port = -1;
+ bp->gnss_port.line = -1;
+ bp->gnss2_port.line = -1;
+ bp->mac_port.line = -1;
+ bp->nmea_port.line = -1;
bp->pdev = pdev;
device_initialize(&bp->dev);
@@ -3569,20 +4028,20 @@ ptp_ocp_complete(struct ptp_ocp *bp)
struct pps_device *pps;
char buf[32];
- if (bp->gnss_port != -1) {
- sprintf(buf, "ttyS%d", bp->gnss_port);
+ if (bp->gnss_port.line != -1) {
+ sprintf(buf, "ttyS%d", bp->gnss_port.line);
ptp_ocp_link_child(bp, buf, "ttyGNSS");
}
- if (bp->gnss2_port != -1) {
- sprintf(buf, "ttyS%d", bp->gnss2_port);
+ if (bp->gnss2_port.line != -1) {
+ sprintf(buf, "ttyS%d", bp->gnss2_port.line);
ptp_ocp_link_child(bp, buf, "ttyGNSS2");
}
- if (bp->mac_port != -1) {
- sprintf(buf, "ttyS%d", bp->mac_port);
+ if (bp->mac_port.line != -1) {
+ sprintf(buf, "ttyS%d", bp->mac_port.line);
ptp_ocp_link_child(bp, buf, "ttyMAC");
}
- if (bp->nmea_port != -1) {
- sprintf(buf, "ttyS%d", bp->nmea_port);
+ if (bp->nmea_port.line != -1) {
+ sprintf(buf, "ttyS%d", bp->nmea_port.line);
ptp_ocp_link_child(bp, buf, "ttyNMEA");
}
sprintf(buf, "ptp%d", ptp_clock_index(bp->ptp));
@@ -3638,16 +4097,20 @@ ptp_ocp_info(struct ptp_ocp *bp)
ptp_ocp_phc_info(bp);
- ptp_ocp_serial_info(dev, "GNSS", bp->gnss_port, 115200);
- ptp_ocp_serial_info(dev, "GNSS2", bp->gnss2_port, 115200);
- ptp_ocp_serial_info(dev, "MAC", bp->mac_port, 57600);
- if (bp->nmea_out && bp->nmea_port != -1) {
- int baud = -1;
+ ptp_ocp_serial_info(dev, "GNSS", bp->gnss_port.line,
+ bp->gnss_port.baud);
+ ptp_ocp_serial_info(dev, "GNSS2", bp->gnss2_port.line,
+ bp->gnss2_port.baud);
+ ptp_ocp_serial_info(dev, "MAC", bp->mac_port.line, bp->mac_port.baud);
+ if (bp->nmea_out && bp->nmea_port.line != -1) {
+ bp->nmea_port.baud = -1;
reg = ioread32(&bp->nmea_out->uart_baud);
if (reg < ARRAY_SIZE(nmea_baud))
- baud = nmea_baud[reg];
- ptp_ocp_serial_info(dev, "NMEA", bp->nmea_port, baud);
+ bp->nmea_port.baud = nmea_baud[reg];
+
+ ptp_ocp_serial_info(dev, "NMEA", bp->nmea_port.line,
+ bp->nmea_port.baud);
}
}
@@ -3688,14 +4151,14 @@ ptp_ocp_detach(struct ptp_ocp *bp)
for (i = 0; i < 4; i++)
if (bp->signal_out[i])
ptp_ocp_unregister_ext(bp->signal_out[i]);
- if (bp->gnss_port != -1)
- serial8250_unregister_port(bp->gnss_port);
- if (bp->gnss2_port != -1)
- serial8250_unregister_port(bp->gnss2_port);
- if (bp->mac_port != -1)
- serial8250_unregister_port(bp->mac_port);
- if (bp->nmea_port != -1)
- serial8250_unregister_port(bp->nmea_port);
+ if (bp->gnss_port.line != -1)
+ serial8250_unregister_port(bp->gnss_port.line);
+ if (bp->gnss2_port.line != -1)
+ serial8250_unregister_port(bp->gnss2_port.line);
+ if (bp->mac_port.line != -1)
+ serial8250_unregister_port(bp->mac_port.line);
+ if (bp->nmea_port.line != -1)
+ serial8250_unregister_port(bp->nmea_port.line);
platform_device_unregister(bp->spi_flash);
platform_device_unregister(bp->i2c_ctrl);
if (bp->i2c_clk)
diff --git a/drivers/ptp/ptp_pch.c b/drivers/ptp/ptp_pch.c
index 7d4da9e605ef..33355d5eb033 100644
--- a/drivers/ptp/ptp_pch.c
+++ b/drivers/ptp/ptp_pch.c
@@ -336,24 +336,13 @@ static irqreturn_t isr(int irq, void *priv)
* PTP clock operations
*/
-static int ptp_pch_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
+static int ptp_pch_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
{
- u64 adj;
- u32 diff, addend;
- int neg_adj = 0;
+ u32 addend;
struct pch_dev *pch_dev = container_of(ptp, struct pch_dev, caps);
struct pch_ts_regs __iomem *regs = pch_dev->regs;
- if (ppb < 0) {
- neg_adj = 1;
- ppb = -ppb;
- }
- addend = DEFAULT_ADDEND;
- adj = addend;
- adj *= ppb;
- diff = div_u64(adj, 1000000000ULL);
-
- addend = neg_adj ? addend - diff : addend + diff;
+ addend = adjust_by_scaled_ppm(DEFAULT_ADDEND, scaled_ppm);
iowrite32(addend, &regs->addend);
@@ -440,7 +429,7 @@ static const struct ptp_clock_info ptp_pch_caps = {
.n_ext_ts = N_EXT_TS,
.n_pins = 0,
.pps = 0,
- .adjfreq = ptp_pch_adjfreq,
+ .adjfine = ptp_pch_adjfine,
.adjtime = ptp_pch_adjtime,
.gettime64 = ptp_pch_gettime,
.settime64 = ptp_pch_settime,
diff --git a/drivers/ptp/ptp_vmw.c b/drivers/ptp/ptp_vmw.c
index 0dcbabd1533d..27c5547aa8a9 100644
--- a/drivers/ptp/ptp_vmw.c
+++ b/drivers/ptp/ptp_vmw.c
@@ -47,7 +47,7 @@ static int ptp_vmw_adjtime(struct ptp_clock_info *info, s64 delta)
return -EOPNOTSUPP;
}
-static int ptp_vmw_adjfreq(struct ptp_clock_info *info, s32 delta)
+static int ptp_vmw_adjfine(struct ptp_clock_info *info, long delta)
{
return -EOPNOTSUPP;
}
@@ -79,7 +79,7 @@ static struct ptp_clock_info ptp_vmw_clock_info = {
.name = "ptp_vmw",
.max_adj = 0,
.adjtime = ptp_vmw_adjtime,
- .adjfreq = ptp_vmw_adjfreq,
+ .adjfine = ptp_vmw_adjfine,
.gettime64 = ptp_vmw_gettime,
.settime64 = ptp_vmw_settime,
.enable = ptp_vmw_enable,